#include "kontrol_merkezi.hpp" #include #include #include #include #include #include #include "fiko/cekirdek/cekirdek.hpp" #include "fiko/ui/bilesen_ek.hpp" #include "fiko/ui/komut_paleti.hpp" #include "fiko/ui/tema.hpp" #include "imgui.h" namespace fiko { using ui::Tema; namespace { // emoji + ad → tek etiket ("🗔 Ürün Ağacı"). Boş emoji → yalın ad. std::string etiketle(const char* emoji, const std::string& ad) { if (!emoji || !emoji[0]) return ad; return std::string(emoji) + " " + ad; } // Renkli toggle/eylem düğmesi. aktif=true → accent dolgulu; pasif → soluk panel. // etkin=false → soluk + tıklanamaz. Dönen: bu kare tıklandı mı. bool renkliDugme(const char* metin, bool aktif, bool etkin, const ImVec2& boyut, const ImVec4& aktifRenk) { const Tema& t = ui::tema(); ImVec4 zemin = aktif ? aktifRenk : t.cerceve; ImVec4 hover = aktif ? ui::karisim(aktifRenk, t.metin, 0.12f) : ui::karisim(t.cerceve, aktifRenk, 0.30f); ImVec4 bas = ui::karisim(zemin, ImVec4{0, 0, 0, 1}, 0.15f); if (!etkin) { zemin.w *= 0.4f; hover = zemin; bas = zemin; } ImGui::PushStyleColor(ImGuiCol_Button, zemin); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, hover); ImGui::PushStyleColor(ImGuiCol_ButtonActive, bas); ImGui::PushStyleColor(ImGuiCol_Text, etkin ? t.metin : t.metinSoluk); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 8.0f); const bool tikli = ImGui::Button(metin, boyut) && etkin; ImGui::PopStyleVar(); ImGui::PopStyleColor(4); return tikli; } // Sabit genişlikli düğmeleri kullanılabilir alana göre satıra sar (akış yerleşimi). // cizOge(i) tek öğeyi çizer (SameLine kararı burada). dugumGen = tek düğme genişliği. template void akisIzgara(int adet, float dugumGen, const F& cizOge) { if (adet <= 0) return; const float bosluk = ImGui::GetStyle().ItemSpacing.x; const float tamGen = ImGui::GetContentRegionAvail().x; int sutun = std::max(1, (int)((tamGen + bosluk) / (dugumGen + bosluk))); for (int i = 0; i < adet; ++i) { if (i % sutun != 0) ImGui::SameLine(); cizOge(i); } } void ipucuGoster(const char* ipucu) { if (ipucu && ImGui::IsItemHovered()) ImGui::SetTooltip("%s", ipucu); } // ── Sekme gövdeleri ────────────────────────────────────────────────────────── void panellerCiz(std::vector& liste) { const Tema& t = ui::tema(); const float gen = 200.0f, yuk = 34.0f; akisIzgara((int)liste.size(), gen, [&](int i) { KMToggle& g = liste[i]; const bool aktif = g.deger && *g.deger; ImGui::PushID(i); if (renkliDugme(etiketle(g.emoji, g.ad).c_str(), aktif, true, {gen, yuk}, t.vurgu)) if (g.deger) *g.deger = !*g.deger; ipucuGoster(g.ipucu); ImGui::PopID(); }); } void secimCiz(std::vector& filtreler, std::vector& trap) { const Tema& t = ui::tema(); ImGui::PushStyleColor(ImGuiCol_Text, ui::u32(t.metinSoluk)); ImGui::TextUnformatted("Seçim filtresi (çoklu — neyi seçebileceğini belirler)"); ImGui::PopStyleColor(); ImGui::Dummy({0, 4}); const float fgen = 150.0f, fyuk = 40.0f; akisIzgara((int)filtreler.size(), fgen, [&](int i) { KMToggle& g = filtreler[i]; const bool aktif = g.deger && *g.deger; ImGui::PushID(i); if (renkliDugme(etiketle(g.emoji, g.ad).c_str(), aktif, true, {fgen, fyuk}, t.vurgu)) if (g.deger) *g.deger = !*g.deger; ipucuGoster(g.ipucu); ImGui::PopID(); }); if (!trap.empty()) { ImGui::Dummy({0, 8}); ImGui::PushStyleColor(ImGuiCol_Text, ui::u32(t.metinSoluk)); ImGui::TextUnformatted("Alan seçimi (trap) — sürükle-kutu tipi"); ImGui::PopStyleColor(); ImGui::Dummy({0, 4}); const float tgen = 168.0f, tyuk = 34.0f; akisIzgara((int)trap.size(), tgen, [&](int i) { KMEylem& e = trap[i]; ImGui::PushID(1000 + i); if (renkliDugme(etiketle(e.emoji, e.ad).c_str(), e.aktif, e.etkin, {tgen, tyuk}, t.basari)) if (e.calistir) e.calistir(); ipucuGoster(e.ipucu); ImGui::PopID(); }); } } void eylemlerCiz(std::vector& liste, const ImVec4& aktifRenk, float gen, float yuk, int idBase) { akisIzgara((int)liste.size(), gen, [&](int i) { KMEylem& e = liste[i]; ImGui::PushID(idBase + i); if (renkliDugme(etiketle(e.emoji, e.ad).c_str(), e.aktif, e.etkin, {gen, yuk}, aktifRenk)) if (e.calistir) e.calistir(); ipucuGoster(e.ipucu); ImGui::PopID(); }); } // ── Arama sonuç listesi (evrensel: komut/nesne/dosya/workbench) ─────────────── // Dönen: çizilen sonuç sayısı ("eşleşme yok" mesajı üst katmanda tek yerde verilir). int aramaSonuclariCiz(KomutPaleti& palet) { const Tema& t = ui::tema(); const int adet = palet.sonucSayisi(); if (adet == 0) return 0; if (ImGui::IsKeyPressed(ImGuiKey_DownArrow)) palet.asagi(); if (ImGui::IsKeyPressed(ImGuiKey_UpArrow)) palet.yukari(); const bool nav = ImGui::IsKeyPressed(ImGuiKey_DownArrow) || ImGui::IsKeyPressed(ImGuiKey_UpArrow); const float satir = ImGui::GetTextLineHeightWithSpacing() + 6.0f; const float yuksek = satir * (float)std::min(adet, 10); const ImVec4 vs(t.vurgu.x, t.vurgu.y, t.vurgu.z, 0.22f); const ImVec4 vh(t.vurgu.x, t.vurgu.y, t.vurgu.z, 0.13f); ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0, 0, 0, 0)); ImGui::PushStyleColor(ImGuiCol_Header, vs); ImGui::PushStyleColor(ImGuiCol_HeaderHovered, vh); if (ImGui::BeginChild("##km_sonuc", {0, yuksek}, false, ImGuiWindowFlags_NoScrollbar)) { const int secili = palet.seciliIndeks(); const bool uni = palet.universalMi(); const auto& us = palet.universalSonuclar(); const auto& ks = palet.sonuclar(); for (int i = 0; i < adet && i < 60; ++i) { std::string ad, kategori; if (uni) { ad = us[i].ad; kategori = us[i].kategori; } else { ad = ks[i]->ad; kategori = ks[i]->kategori; } ImGui::PushID(i); if (ImGui::Selectable(ad.c_str(), i == secili)) { palet.seciliAyarla(i); palet.onayla(); } if (!kategori.empty()) { const ImVec2 boyut = ImGui::CalcTextSize(kategori.c_str()); const float sag = ImGui::GetItemRectMax().x; ImGui::GetWindowDrawList()->AddText( {sag - boyut.x - 6.0f, ImGui::GetItemRectMin().y}, ui::u32(t.metinSoluk), kategori.c_str()); } if (i == secili && nav) ImGui::SetScrollHereY(0.5f); ImGui::PopID(); } } ImGui::EndChild(); ImGui::PopStyleColor(3); if (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter)) palet.onayla(); return adet; } // ASCII küçük-harf duyarsız alt-dize (Türkçe çok-baytlılar bayt-eş — arama için yeterli). bool aramaUyar(const std::string& kaynak, const std::string& q) { if (q.empty()) return true; if (q.size() > kaynak.size()) return false; for (size_t i = 0; i + q.size() <= kaynak.size(); ++i) { size_t j = 0; while (j < q.size() && std::tolower((unsigned char)kaynak[i + j]) == std::tolower((unsigned char)q[j])) ++j; if (j == q.size()) return true; } return false; } // Panel/filtre/trap/compass/hızlı toggle'larını da aramaya sokar (komut/nesne/dosya // KomutPaleti'nden gelir; bunlar app-durumu → burada, dangling'siz, her kare taze). int ekstraAramaCiz(KontrolMerkeziBaglam& b, KomutPaleti& palet) { const Tema& t = ui::tema(); const std::string q = palet.sorgu(); struct Sonuc { std::string etiket; const char* kat; std::function calistir; }; std::vector s; for (KMToggle& g : b.paneller) if (aramaUyar(g.ad, q)) { bool* d = g.deger; s.push_back({etiketle(g.emoji, g.ad), "Panel", [d] { if (d) *d = !*d; }}); } for (KMToggle& g : b.secimFiltreleri) if (aramaUyar(g.ad, q)) { bool* d = g.deger; s.push_back({etiketle(g.emoji, g.ad), "Filtre", [d] { if (d) *d = !*d; }}); } auto eylemEkle = [&](std::vector& L, const char* kat) { for (KMEylem& e : L) if (e.etkin && aramaUyar(e.ad, q)) { auto f = e.calistir; s.push_back({etiketle(e.emoji, e.ad), kat, f}); } }; eylemEkle(b.trapAraclari, "Araç"); eylemEkle(b.compassEylemleri, "Compass"); eylemEkle(b.hizliEylemler, "Hızlı"); if (s.empty()) return 0; ImGui::Dummy({0, 2}); ImGui::PushStyleColor(ImGuiCol_Text, ui::u32(t.metinSoluk)); ImGui::TextUnformatted("Paneller · Filtreler · Araçlar"); ImGui::PopStyleColor(); for (int i = 0; i < (int)s.size(); ++i) { ImGui::PushID(10000 + i); if (ImGui::Selectable(s[i].etiket.c_str())) { if (s[i].calistir) s[i].calistir(); palet.kapat(); } const ImVec2 kb = ImGui::CalcTextSize(s[i].kat); ImGui::GetWindowDrawList()->AddText({ImGui::GetItemRectMax().x - kb.x - 6.0f, ImGui::GetItemRectMin().y}, ui::u32(t.metinSoluk), s[i].kat); ImGui::PopID(); } return (int)s.size(); } // ── Komutlar sekmesi (eski açılır menülerin göz-atılabilir karşılığı) ──────── // Registry'den (cekirdek.menuler) gruplu — her grup katlanır başlık, üyeler renkli // düğme. Arama kutusu zaten hepsini fuzzy bulur; bu sekme MENÜ gibi göz atmak için. void komutlarCiz(Cekirdek& c) { const Tema& t = ui::tema(); for (const std::string& grup : c.menuler().gruplar()) { if (!ImGui::CollapsingHeader(grup.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) continue; std::vector og; for (const MenuOge& o : c.menuler().hepsi()) if (o.grup == grup) og.push_back(&o); ImGui::PushID(grup.c_str()); const float gen = 210.0f, yuk = 30.0f; akisIzgara((int)og.size(), gen, [&](int i) { const MenuOge& o = *og[i]; const bool etkin = !o.etkinMi || o.etkinMi(c); ImGui::PushID(i); if (renkliDugme(etiketle(o.simge.empty() ? "" : o.simge.c_str(), o.ad).c_str(), false, etkin, {gen, yuk}, t.vurgu)) c.komutCalistir(o, "kontrol"); ImGui::PopID(); }); ImGui::PopID(); ImGui::Dummy({0, 4}); } } // ── Performans panosu (futuristik dashboard) ───────────────────────────────── // Neon alan grafiği: gradyan dolgu + parlak üst çizgi. deger sola-kaydırmalı // (en yeni sonda); yalnız son 'dolu' örnek çizilir. void neonGraf(const char* etiket, float sonDeger, const char* birim, const float* deger, int adet, int dolu, float mn, float mx, const ImVec4& renk, float yuk) { const Tema& t = ui::tema(); const float gen = ImGui::GetContentRegionAvail().x; const ImVec2 p0 = ImGui::GetCursorScreenPos(); ImGui::Dummy({gen, yuk}); ImDrawList* dl = ImGui::GetWindowDrawList(); const ImVec2 p1{p0.x + gen, p0.y + yuk}; dl->AddRectFilled(p0, p1, ui::u32(t.cerceve, 0.30f), 7.0f); dl->AddRect(p0, p1, ui::u32(renk, 0.35f), 7.0f, 0, 1.2f); const float taban = p1.y - 4.0f, tavan = p0.y + 18.0f; auto Y = [&](float v) { float o = (mx > mn) ? (v - mn) / (mx - mn) : 0.0f; o = o < 0 ? 0 : (o > 1 ? 1 : o); return taban - o * (taban - tavan); }; if (dolu >= 2) { const int bas = adet - dolu; const float dx = gen / (float)(dolu - 1); // Gradyan dolgu (segment başına konveks yamuk). for (int i = bas; i < adet - 1; ++i) { const float x0 = p0.x + (float)(i - bas) * dx, x1 = x0 + dx; const ImVec2 a{x0, Y(deger[i])}, b{x1, Y(deger[i + 1])}; dl->AddQuadFilled(a, b, {x1, taban}, {x0, taban}, ui::u32(renk, 0.16f)); } // Parlak üst çizgi (glow + ana hat). for (int i = bas; i < adet - 1; ++i) { const float x0 = p0.x + (float)(i - bas) * dx, x1 = x0 + dx; const ImVec2 a{x0, Y(deger[i])}, b{x1, Y(deger[i + 1])}; dl->AddLine(a, b, ui::u32(renk, 0.22f), 4.0f); dl->AddLine(a, b, ui::u32(renk), 1.8f); } } // Başlık + anlık değer (sol-üst). char buf[64]; std::snprintf(buf, sizeof(buf), "%s %.0f%s", etiket, sonDeger, birim ? birim : ""); dl->AddText({p0.x + 8.0f, p0.y + 4.0f}, ui::u32(t.metin), buf); } // Neon oran çubuğu (VRAM/RAM): kullanılan/toplam MB. void oranCubugu(const char* etiket, float kullanilan, float toplam, const ImVec4& renk) { const Tema& t = ui::tema(); const float gen = ImGui::GetContentRegionAvail().x, yuk = 26.0f; const ImVec2 p0 = ImGui::GetCursorScreenPos(); ImGui::Dummy({gen, yuk}); ImDrawList* dl = ImGui::GetWindowDrawList(); const ImVec2 p1{p0.x + gen, p0.y + yuk}; dl->AddRectFilled(p0, p1, ui::u32(t.cerceve, 0.5f), 6.0f); const float oran = toplam > 1.0f ? std::min(1.0f, kullanilan / toplam) : 0.0f; if (oran > 0.001f) { const ImVec2 d1{p0.x + gen * oran, p1.y}; dl->AddRectFilled(p0, d1, ui::u32(renk, 0.85f), 6.0f); dl->AddRectFilled({d1.x - 2.0f, p0.y}, d1, ui::u32(renk), 0.0f); // parlak kenar } dl->AddRect(p0, p1, ui::u32(renk, 0.4f), 6.0f); char buf[80]; if (toplam > 1.0f) std::snprintf(buf, sizeof(buf), "%s %.0f / %.0f MB", etiket, kullanilan, toplam); else std::snprintf(buf, sizeof(buf), "%s —", etiket); const ImVec2 tb = ImGui::CalcTextSize(buf); dl->AddText({p0.x + (gen - tb.x) * 0.5f, p0.y + (yuk - tb.y) * 0.5f}, ui::u32(t.metin), buf); } void performansCiz(const SistemMetrik* mp) { const Tema& t = ui::tema(); if (!mp) { ImGui::PushStyleColor(ImGuiCol_Text, ui::u32(t.metinSoluk)); ImGui::TextUnformatted(" Metrik toplayıcı yok"); ImGui::PopStyleColor(); return; } const SistemMetrik& m = *mp; // ── Üst: 4 halka gösterge (FPS / CPU / GPU / RAM) ── const float cap = 94.0f; ui::gaugeHalka("FPS", m.fps, 0.0f, 144.0f, nullptr, cap); ImGui::SameLine(); ui::gaugeHalka("CPU", m.cpuYuzde, 0.0f, 100.0f, "%", cap); ImGui::SameLine(); ui::gaugeHalka("GPU", m.gpuYuzde, 0.0f, 100.0f, "%", cap); ImGui::SameLine(); ui::gaugeHalka("RAM", m.ramSistemYuzde, 0.0f, 100.0f, "%", cap); ImGui::Dummy({0, 8}); // ── VRAM + süreç RAM çubukları ── oranCubugu("VRAM", m.vramKullanilanMB, m.vramToplamMB, t.vurgu2); ImGui::Dummy({0, 4}); oranCubugu("Sistem RAM", m.ramSistemKullanilanMB, m.ramSistemToplamMB, t.vurgu); ImGui::Dummy({0, 4}); ImGui::PushStyleColor(ImGuiCol_Text, ui::u32(t.metinSoluk)); { char buf[96]; std::snprintf(buf, sizeof(buf), "Süreç RAM: %.0f MB · Kare: %.1f ms · GPU 3D motoru", m.ramMB, m.kareSuresiMs); ImGui::TextUnformatted(buf); } ImGui::PopStyleColor(); ImGui::Dummy({0, 6}); // ── Neon geçmiş grafikleri ── const int N = SistemMetrik::GECMIS, dolu = m.gecmisSayi; neonGraf("Kare Süresi", m.kareSuresiMs, " ms", m.kareGecmis, N, dolu, 0.0f, 33.0f, t.basari, 58.0f); ImGui::Dummy({0, 4}); neonGraf("GPU", m.gpuYuzde, " %", m.gpuGecmis, N, dolu, 0.0f, 100.0f, t.vurgu2, 58.0f); ImGui::Dummy({0, 4}); neonGraf("CPU", m.cpuYuzde, " %", m.cpuGecmis, N, dolu, 0.0f, 100.0f, t.vurgu, 58.0f); } } // namespace void kontrolMerkeziCiz(KontrolMerkeziBaglam& b) { if (!b.palet || !b.palet->acikMi()) return; KomutPaleti& palet = *b.palet; const Tema& t = ui::tema(); ImGuiIO& io = ImGui::GetIO(); const ImVec2 ekran = io.DisplaySize; // Karartılmış tam-ekran zemin (odak kontrol merkezinde). ImGui::GetBackgroundDrawList()->AddRectFilled({0, 0}, ekran, IM_COL32(4, 6, 12, 185)); const float gen = std::min(760.0f, ekran.x * 0.68f); ImGui::SetNextWindowPos({ekran.x * 0.5f, ekran.y * 0.14f}, ImGuiCond_Always, {0.5f, 0.0f}); ImGui::SetNextWindowSize({gen, 0.0f}, ImGuiCond_Always); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, {16, 14}); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 14.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.5f); ImGui::PushStyleColor(ImGuiCol_WindowBg, t.panel); ImGui::PushStyleColor(ImGuiCol_Border, t.vurgu); const ImGuiWindowFlags bayrak = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize; if (ImGui::Begin("##kontrol_merkezi", nullptr, bayrak)) { if (ImGui::IsKeyPressed(ImGuiKey_Escape)) palet.kapat(); // ── Başlık satırı: ⌘ Kontrol Merkezi + sağda FPS ── ImGui::PushStyleColor(ImGuiCol_Text, ui::u32(t.vurgu)); ImGui::TextUnformatted("⌘ Kontrol Merkezi"); ImGui::PopStyleColor(); { char fps[32]; std::snprintf(fps, sizeof(fps), "%.1f ms", b.fpsMs); const float w = ImGui::CalcTextSize(fps).x; ImGui::SameLine(ImGui::GetWindowContentRegionMax().x - w); ImGui::TextColored({0.35f, 0.65f, 1.0f, 1.0f}, "%s", fps); } // ── Hızlı eylem şeridi (Sil / Sığdır / ...) ── if (!b.hizliEylemler.empty()) { ImGui::Dummy({0, 4}); eylemlerCiz(b.hizliEylemler, t.vurgu2, 128.0f, 30.0f, 5000); } ImGui::Dummy({0, 6}); // ── Evrensel arama kutusu ── char tampon[256]; std::snprintf(tampon, sizeof(tampon), "%s", palet.sorgu().c_str()); if (ImGui::IsWindowAppearing()) ImGui::SetKeyboardFocusHere(); if (ui::aramaKutusu("##km_ara", tampon, sizeof(tampon), "Komut, nesne veya dosya ara…", gen - 32.0f)) palet.sorguAyarla(tampon); ImGui::Dummy({0, 8}); // ── Sorgu doluysa arama sonuçları; boşsa sekmeli bölümler ── if (!palet.sorgu().empty()) { const int nk = aramaSonuclariCiz(palet); const int ne = ekstraAramaCiz(b, palet); if (nk + ne == 0) { ImGui::PushStyleColor(ImGuiCol_Text, ui::u32(t.metinSoluk)); ImGui::TextUnformatted(" Eşleşme yok"); ImGui::PopStyleColor(); } } else { static int sekme = 0; const char* sekmeler[] = {"⌘ Komutlar", "🗔 Paneller", "🎯 Seçim", "🧭 Workbench", "🧲 Compass", "📊 Performans"}; ui::sekmeCubugu("##km_sekme", sekmeler, 6, &sekme); ImGui::Dummy({0, 8}); ImGui::BeginChild("##km_govde", {0, 340.0f}, false); switch (sekme) { case 0: if (b.cekirdek) komutlarCiz(*b.cekirdek); break; case 1: panellerCiz(b.paneller); break; case 2: secimCiz(b.secimFiltreleri, b.trapAraclari); break; case 3: eylemlerCiz(b.workbenchler, t.vurgu, 176.0f, 40.0f, 2000); break; case 4: eylemlerCiz(b.compassEylemleri, t.uyari, 220.0f, 34.0f, 3000); break; case 5: performansCiz(b.metrik); break; } ImGui::EndChild(); } // ── Alt ipucu satırı ── ImGui::Dummy({0, 2}); ImGui::PushStyleColor(ImGuiCol_Text, ui::u32(t.metinSoluk)); ImGui::TextUnformatted("Ctrl+Space kapat/aç · ↑↓ gez · Enter çalıştır · ESC kapat"); ImGui::PopStyleColor(); } ImGui::End(); ImGui::PopStyleColor(2); ImGui::PopStyleVar(3); } } // namespace fiko