Semua yang kita bangun sepanjang section ini memakai model yang sama: widget dibuat sekali, disimpan dalam pohon kepemilikan, dan framework yang menjaga keberadaannya dari frame ke frame. Model itu bernama retained mode. Dear ImGui menawarkan lawannya — immediate mode: tidak ada pohon widget dan tidak ada state UI yang dijaga framework; setiap frame, kode UI dieksekusi dari awal dan menghasilkan daftar perintah gambar yang baru. Chapter ini membandingkan kedua model mental itu, menampilkan boilerplate minimal ImGui, dan membuktikan mekanismenya dengan simulasi yang bisa dijalankan di terminal.
Dua Model Mental: Retained vs Immediate #
Perbedaannya bukan soal fitur, melainkan soal pertanyaan “di mana UI disimpan”. Tabel berikut menyandingkan keduanya.
| Aspek | Retained mode (Qt Widgets) | Immediate mode (Dear ImGui) |
|---|---|---|
| Representasi UI | pohon objek widget yang tersimpan | draw list yang dibangun ulang tiap frame |
| Keadaan antar frame | dijaga oleh widget | disimpan sendiri oleh kode aplikasi |
| Menambah/mengubah UI | buat atau panggil method widget | ubah kondisi dalam fungsi UI |
| Kerja per frame | menggambar saat berubah saja | mengeksekusi dan menggambar seluruh UI |
| Ukuran dan dependensi | pustaka besar, build system Qt | beberapa file, minim dependensi, MIT |
| Cocok untuk | aplikasi untuk pengguna akhir | debug tooling dan editor internal |
Cara tercepat merasakan perbedaannya lewat tombol. Pada retained mode, tombol adalah objek yang ada walaupun tidak sedang digambar; kliknya tiba sebagai sinyal yang dikirim ke slot. Pada immediate mode, tombol hanya ada selama pemanggilan ImGui::Button("Tambah") di frame itu — fungsi mengembalikan true hanya pada frame ketika tombol ditekan, dan kode di sekitarnya langsung bereaksi: if (ImGui::Button("Tambah")) { hitungan += 1; }. Tidak ada sinyal, tidak ada slot, tidak ada objek yang tersimpan — hanya state aplikasi milik kita sendiri.
Boilerplate Minimal Dear ImGui #
Boilerplate ImGui terkenal pendek. Program membuat context sekali, lalu setiap frame menjalankan tiga tahap: NewFrame (mulai frame), kode UI (deretan pemanggilan fungsi), dan Render (kumpulkan draw list untuk dikirim ke renderer).
ImGui::CreateContext(); // inisialisasi: satu kali di awal
// ... buat window dan renderer, mis. GLFW + OpenGL3 ...
while (!glfwWindowShouldClose(jendela)) {
ImGui_ImplOpenGL3_NewFrame(); // mulai frame renderer
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame(); // mulai frame UI
ImGui::Begin("Panel Debug"); // jendela UI bernama
ImGui::Text("FPS: %.1f", 60.0);
if (ImGui::Button("Tambah")) { // true hanya pada frame yang ditekan
hitungan += 1; // logika langsung di tempat kejadian
}
ImGui::End(); // akhiri jendela UI
ImGui::Render(); // kumpulkan draw list frame ini
// ... gambar draw list ke layar, lalu swap buffer ...
}
ImGui::DestroyContext(); // bersih-bersih saat program usai
Bandingkan dengan Qt pada chapter Dasar Qt dan Signals-Slots: tidak ada class per jendela, tidak ada connect(), dan tidak ada sinyal — hanya pemanggilan fungsi berurutan yang membaca dan menulis state (hitungan) milik program kita. Interaksi dan logikanya menyatu di satu tempat, dan UI tidak menyimpan apa pun di antara dua frame.
Membangunnya di mesin nyata pun singkat, karena ImGui berupa kumpulan file yang dikompilasi bersama program:
git clone https://github.com/ocornut/imgui.git
g++ main.cpp imgui/*.cpp imgui/backends/imgui_impl_glfw.cpp imgui/backends/imgui_impl_opengl3.cpp -Iimgui -Iimgui/backends -lGL -lglfw -o panel-debug
./panel-debug # jendela berisi UI tergambar tiap frameProgram Utama: Simulasi Immediate Mode #
Inti immediate mode bisa dibuktikan tanpa window dan tanpa GPU: fungsi UI menerima state, mengembalikan draw list, dan renderer mencetaknya. Program berikut memanggil fungsi yang sama persis untuk dua frame dengan state berbeda — dan dari perbedaan draw list keduanya, sifat “dibangun ulang tiap frame” terlihat apa adanya.
#include <iostream>
#include <string>
#include <vector>
// State aplikasi: data yang BENAR-BENAR dimiliki program.
struct State {
int hitungan = 0;
bool tampilkanDetail = false;
};
// Satu perintah gambar pada draw list.
struct PerintahGambar {
std::string tipe; // "tombol" atau "teks"
std::string label;
};
// Fungsi UI immediate mode: MEMBANGUN ULANG seluruh draw list dari state.
// Tidak ada pohon widget yang tersimpan antar frame — hanya fungsi ini
// yang dipanggil ulang setiap kali frame digambar.
std::vector<PerintahGambar> bangunFrame(const State& state) {
std::vector<PerintahGambar> daftar;
daftar.push_back({"tombol", "Tambah (+1)"});
if (state.tampilkanDetail) {
daftar.push_back({"teks", "detail: mode debug aktif"});
}
daftar.push_back({"teks", "hitungan = " + std::to_string(state.hitungan)});
return daftar;
}
// "Renderer": mencetak draw list satu frame.
void render(const std::vector<PerintahGambar>& daftar, int nomorFrame) {
std::cout << "frame " << nomorFrame << " ("
<< daftar.size() << " perintah gambar):\n";
for (const PerintahGambar& perintah : daftar) {
std::cout << " - " << perintah.tipe << ": \"" << perintah.label << "\"\n";
}
}
int main() {
State state;
// Frame 1: draw list dari state awal.
render(bangunFrame(state), 1);
// Interaksi pengguna mengubah state — bukan objek widget.
state.hitungan += 1;
state.tampilkanDetail = true;
// Frame 2: fungsi yang SAMA dipanggil ulang, draw list baru lahir.
render(bangunFrame(state), 2);
std::cout << "pohon widget tersimpan: tidak ada; UI dibangun ulang tiap frame\n";
return 0;
}Output yang Dihasilkan:
frame 1 (2 perintah gambar):
- tombol: "Tambah (+1)"
- teks: "hitungan = 0"
frame 2 (3 perintah gambar):
- tombol: "Tambah (+1)"
- teks: "detail: mode debug aktif"
- teks: "hitungan = 1"
pohon widget tersimpan: tidak ada; UI dibangun ulang tiap frameMari kita bedah baris demi baris:
- Baris 6-9 (
State): dua data inilah seluruh keadaan aplikasi — hitungan dan penanda detail. Pada immediate mode, state semacam ini adalah satu-satunya yang bertahan antar frame. - Baris 12-15 (
PerintahGambar): draw list tersusun dari perintah sederhana bertipe tombol atau teks — meniru hasilImGui::Render()yang berupa daftar perintah gambar. - Baris 20-28 (
bangunFrame): inilah “kode UI” yang dieksekusi tiap frame. Fungsi tidak menyimpan apa pun; ia hanya membaca state dan menyalurkan perintah ke vektor baru. - Baris 23-25 (kondisi widget): perintah
teks: "detail: ..."hanya masuk bilastate.tampilkanDetailbenar — menambah widget berarti menambah kondisi, bukan menambah objek ke pohon. - Baris 31-37 (
render): renderer mencetak draw list satu frame lalu membuangnya; tidak ada penampungan untuk frame berikutnya. - Baris 43 (frame 1):
bangunFrame(state)dipanggil dengan state awal — hasilnya dua perintah, tanpa baris detail. - Baris 46-47 (interaksi):
state.hitungan += 1danstate.tampilkanDetail = truemengubah state — analogi pengguna menekan tombol dan mencentang kotak detail. - Baris 50 (frame 2): fungsi yang sama dipanggil ulang dan draw list baru lahir — tiga perintah, dengan baris detail dan hitungan yang sudah bertambah.
Perbedaan frame 1 dan frame 2 membuktikan inti modelnya: UI tidak disimpan, melainkan lahir kembali dari fungsi yang sama di setiap frame, dan perubahan UI cukup diwujudkan sebagai perubahan state. Perhatikan pula apa yang tidak dilakukan program: tidak ada pohon objek yang dihapus atau dibuat — vektor perintah lama selesai dipakai begitu frame tercetak, lalu frame berikutnya menyusun vektor baru dari awal.
Kapan Memilih ImGui, Kapan Qt #
Pertanyaan praktisnya bukan mana yang lebih baik, melainkan untuk siapa UI itu dibuat.
- Dear ImGui menang saat UI adalah alat untuk tim sendiri: panel debug game, inspector data, editor level, dashboard tooling internal. Iterasi cepat, satu executable, dan hampir tidak ada biaya integrasi.
- Qt menang saat UI ditujukan bagi pengguna akhir: menu dan dialog native, aksesibilitas, internasionalisasi, widget kompleks seperti tabel besar dengan model-view, serta dukungan deployment yang matang.
- Perhatikan biaya per frame: immediate mode menggambar seluruh UI setiap frame — untuk tooling dan panel kecil itu murah, tetapi aplikasi data-intensif layak diperhitungkan.
Section GUI dan Aplikasi Desktop berakhir di sini dengan bekal runtut: lanskap toolkit, jendela pertama dan signal-slot Qt, layout dan widget, event loop plus threading, dan alternatif immediate mode. Section berikutnya memindahkan kita ke Proyek — saatnya menggabungkan keterampilan yang tersebar sepanjang course menjadi aplikasi nyata yang dibangun dan diuji dari nol.
Referensi: github.com/ocornut/imgui , dearimgui.com