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.

AspekRetained mode (Qt Widgets)Immediate mode (Dear ImGui)
Representasi UIpohon objek widget yang tersimpandraw list yang dibangun ulang tiap frame
Keadaan antar framedijaga oleh widgetdisimpan sendiri oleh kode aplikasi
Menambah/mengubah UIbuat atau panggil method widgetubah kondisi dalam fungsi UI
Kerja per framemenggambar saat berubah sajamengeksekusi dan menggambar seluruh UI
Ukuran dan dependensipustaka besar, build system Qtbeberapa file, minim dependensi, MIT
Cocok untukaplikasi untuk pengguna akhirdebug 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).

cpp Sketsa Dear ImGui — boilerplate inti (ILUSTRATIF: pustaka tidak terinstal di sandbox praktikum, bentuk API untuk proyek nyata)
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:

bash Sesi build Dear ImGui — ILUSTRATIF: perintah untuk proyek nyata, tidak dieksekusi di sandbox
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 frame

Program 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.

cpp
#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:

text
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 frame

Mari kita bedah baris demi baris:

  1. 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.
  2. Baris 12-15 (PerintahGambar): draw list tersusun dari perintah sederhana bertipe tombol atau teks — meniru hasil ImGui::Render() yang berupa daftar perintah gambar.
  3. 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.
  4. Baris 23-25 (kondisi widget): perintah teks: "detail: ..." hanya masuk bila state.tampilkanDetail benar — menambah widget berarti menambah kondisi, bukan menambah objek ke pohon.
  5. Baris 31-37 (render): renderer mencetak draw list satu frame lalu membuangnya; tidak ada penampungan untuk frame berikutnya.
  6. Baris 43 (frame 1): bangunFrame(state) dipanggil dengan state awal — hasilnya dua perintah, tanpa baris detail.
  7. Baris 46-47 (interaksi): state.hitungan += 1 dan state.tampilkanDetail = true mengubah state — analogi pengguna menekan tombol dan mencentang kotak detail.
  8. 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