#include "fiko/ui/arac_halkasi.hpp" #include #include #include #include "fiko/ui/animasyon.hpp" #include "fiko/ui/bilesen.hpp" #include "fiko/ui/sektor.hpp" #include "fiko/ui/tema.hpp" #include "imgui_internal.h" namespace fiko::ui { namespace { constexpr float PI = 3.14159265f; constexpr float BUTON = 30.0f; // ikon buton kenarı (px) constexpr float BOSLUK = 4.0f; // butonlar arası constexpr float PAD = 6.0f; // pill iç kenar boşluğu constexpr float OFSET_X = 18.0f; // imleçten sağa kayma constexpr float OFSET_Y = 14.0f; // imleçten yukarı kayma constexpr float IKON_BOY = 18.0f; struct HalkaDurum { ImVec2 konum{}; // son bilinen hedef konum (kaybolurken sabit kalsın) bool kuruldu = false; int aktifAna = -1; // alt-halkası açık ana öğe (-1: yok) — nested radyal float flasZaman = -1.0f; // seçim onay flash başlangıç zamanı (sn) — -1: yok ImVec2 flasNokta{}; // onay flash merkezi (seçilen dilim ortası) int sonKare = -1; // en son çizildiği ImGui kare no — kesinti = gizli→görünür geçiş }; std::unordered_map g_durumlar; ImGuiID kimlikHash(const char* kimlik) { return ImHashStr(kimlik); } // Per-dilim animasyon kimliği — ana id + faz-tuz + indeks. Ayrı veri yapısı YOK: // animasyon.cpp'nin ImGuiID-anahtarlı kaydını kullanır (menü kapanınca 2sn'de GC). inline ImGuiID dilimId(ImGuiID id, unsigned tuz, int i) { return id + tuz * 64u + (unsigned)i; } // smoothstep (0..1) — stagger/cross-fade için ucuz ease, allocation yok. inline float puruz(float x) { x = x < 0.0f ? 0.0f : (x > 1.0f ? 1.0f : x); return x * x * (3.0f - 2.0f * x); } // Stagger belirme faktörü: tek zaman kaynağından (a) türetilir, per-dilim kayıt YOK. // Dilim i, a>i*gecikme olunca 'pencere' süresinde 0→1 açılır (sırayla belirme). inline float staggerFaktor(float a, int i, int n, float gecikme, float pencere) { if (n <= 1) return a; const float bas = (float)i * gecikme; return a * puruz((a - bas) / pencere); } // Tek bir dilimi çizer (dolgu + kontur + glow + simge/etiket). aciMerkez etrafında // ±yariAci genişliğinde. dim: sönükleştirme çarpanı (0.28 = kardeş, 1.0 = aktif). // hf: hover-anim faktörü (0..1, zaman-tabanlı ease — yumuşak glow/scale/parlaklık). // belir: per-dilim belirme faktörü (0..1, stagger scale-pop + fade). 1.0 = tam açık. void dilimCiz(ImDrawList* dl, ImVec2 m, const AracOge& o, float aciMerkez, float yariAci, float rIc, float rDis, float olcek, float a, float hf, float dim, float belir = 1.0f) { const Tema& t = tema(); const float aG = a * dim * belir; // Belirme sırasında dilim merkeze doğru hafif büzülür (stagger scale-pop). const float pop = 0.90f + 0.10f * belir; const float rIcE = rIc * pop, rDisE = rDis * pop; const float a0 = aciMerkez - yariAci, a1 = aciMerkez + yariAci; ImVec4 dolgu = karisim(t.panel, t.vurgu, 0.16f + 0.39f * hf); dolgu.w = 0.94f * aG; if (!o.etkin) dolgu = {t.panel.x, t.panel.y, t.panel.z, 0.72f * aG}; const ImVec4 kontur = o.etkin ? t.vurgu : t.metinSoluk; if (hf > 0.01f && o.etkin) sektorGlow(dl, m, rDisE, a0, a1, t.vurgu, hf); sektorCiz(dl, m, rIcE + 3.0f, rDisE, a0, a1, u32(dolgu), u32(kontur, (0.30f + 0.60f * hf) * dim * belir), 1.2f + 1.0f * hf); const float rIkon = (rIcE + 3.0f + rDisE) * 0.5f; const ImVec2 ikonM{m.x + std::cos(aciMerkez) * rIkon, m.y + std::sin(aciMerkez) * rIkon - 6.0f}; const ImU32 mRenk = u32(o.etkin ? karisim(karisim(t.metin, t.metinSoluk, 0.25f), t.metin, hf) : t.metinSoluk, aG); if (!o.simge.bosMu()) simgeCiz(dl, o.simge, ikonM, (20.0f + 2.0f * hf) * olcek, mRenk); if (o.etiket) { ImFont* f = ImGui::GetFont(); const float fs = 12.0f; const ImVec2 mb = f->CalcTextSizeA(fs, FLT_MAX, 0.0f, o.etiket); dl->AddText(f, fs, {ikonM.x - mb.x * 0.5f, ikonM.y + 12.0f}, mRenk, o.etiket); } // çocuk işareti: dış kenarda küçük ok (alt-halka var) if (o.cocukSayi > 0) { const ImVec2 okM{m.x + std::cos(aciMerkez) * (rDisE - 6.0f), m.y + std::sin(aciMerkez) * (rDisE - 6.0f)}; dl->AddCircleFilled(okM, 2.5f, u32(t.vurgu2, (o.etkin ? 0.9f : 0.3f) * dim * belir)); } if (o.etiket && hf > 0.5f && a > 0.85f && dim > 0.9f) ImGui::SetTooltip("%s", o.etiket); } // Ana pusula halkası (8 yön). aktifAna >= 0 ise o öğe DIŞINDAKİLER sönükleşir. // slot[]: 8 pusula yönüne öğe indeksi (-1: boş). Dönen: hover öğe indeksi (-1 yok). // Açılışta dilimler pusula sırasına göre STAGGER'lı belirir; hover glow zaman-tabanlı. int pusulaHalkaCiz(ImGuiID id, ImDrawList* dl, ImVec2 m, const AracOge* ogeler, const int* slot, int pn, float rIc, float rDis, float olcek, float a, int aktifAna) { const ImVec2 fare = ImGui::GetMousePos(); const float dx = fare.x - m.x, dy = fare.y - m.y; const float uzaklik = std::sqrt(dx * dx + dy * dy); int hover = -1; if (uzaklik >= rIc && uzaklik <= rDis) { const float fAci = std::atan2(dy, dx); for (int y = 0; y < 8; ++y) { if (slot[y] < 0 || !ogeler[slot[y]].etkin) continue; float fark = fAci - PUSULA_ACI[y]; while (fark > PI) fark -= 2.0f * PI; while (fark < -PI) fark += 2.0f * PI; if (std::fabs(fark) <= PUSULA_YARI + 0.02f) { hover = slot[y]; break; } } } for (int i = 0; i < 8; ++i) { const int y = PUSULA_SIRA[i]; // stagger okunur sırada (K,D,G,B,köşeler) ilerlesin if (slot[y] < 0) continue; // Alt-halka açıkken aktif ebeveyn dışındaki kardeşler soluk (görsel ait olma). const float dim = (aktifAna < 0 || slot[y] == aktifAna) ? 1.0f : 0.28f; // Hover glow: per-dilim zaman-tabanlı ease (ani değil, yumuşak giriş/çıkış). const float hf = animf(dilimId(id, 1, slot[y]), slot[y] == hover ? 1.0f : 0.0f, 14.0f); // Belirme: alt-halka açıkken stagger'ı atla (kararlı), yoksa sırayla belir. const float belir = (aktifAna >= 0) ? 1.0f : staggerFaktor(a, i, pn, 0.055f, 0.28f); dilimCiz(dl, m, ogeler[slot[y]], PUSULA_ACI[y], PUSULA_YARI, rIc, rDis, olcek, a, hf, dim, belir); } return hover; } // Alt-halka: çocuklar ebeveyn ekseninde DAR, simetrik bir yelpaze — ayrı dış bant. // Az çocukta (1-3) yay dar tutulur → komşu ana sektörlerin üstüne binmez. // Dönen: hover çocuk indeksi (-1 yok). int altYelpazeCiz(ImGuiID id, ImDrawList* dl, ImVec2 m, const AracOge* cocuklar, int sayi, float ebeveynAci, float rIc, float rDis, float olcek, float a, float acilim) { const int n = ImMin(sayi, 6); // Her çocuk için sabit dar açısal adım (komşu sektöre taşmasın). const float adim = 0.42f; // ~24° çocuklar arası const float yari = 0.19f; // dilim yarı-açısı (~11°) — dar const float toplam = adim * (float)(n - 1); const float bas = ebeveynAci - toplam * 0.5f; const ImVec2 fare = ImGui::GetMousePos(); const float dx = fare.x - m.x, dy = fare.y - m.y; const float uzaklik = std::sqrt(dx * dx + dy * dy); const float fAci = std::atan2(dy, dx); int hover = -1; if (uzaklik >= rIc && uzaklik <= rDis) { for (int i = 0; i < n; ++i) { if (!cocuklar[i].etkin) continue; float fark = fAci - (bas + adim * (float)i); while (fark > PI) fark -= 2.0f * PI; while (fark < -PI) fark += 2.0f * PI; if (std::fabs(fark) <= yari + 0.02f) { hover = i; break; } } } for (int i = 0; i < n; ++i) { const float hf = animf(dilimId(id, 2, i), i == hover ? 1.0f : 0.0f, 14.0f); // Çocuklar açılım fazından (yay) türeyen hafif stagger'la belirir — merkezden dışa. const float belir = staggerFaktor(acilim, i, n, 0.045f, 0.30f); dilimCiz(dl, m, cocuklar[i], bas + adim * (float)i, yari, rIc, rDis, olcek, a, hf, 1.0f, belir); } return hover; } } // namespace int aracHalkasi(const char* kimlik, ImVec2 konum, bool gorunur, const AracOge* ogeler, int sayi, AracDizilim dizilim, int* cocukIndeksi) { if (cocukIndeksi) *cocukIndeksi = -1; if (sayi <= 0 || !ogeler) return -1; const ImGuiID id = kimlikHash(kimlik); const Tema& t = tema(); HalkaDurum& d = g_durumlar[id]; // Gizli→görünür geçiş tespiti: caller menü kapanınca aracHalkasi'yi HİÇ çağırmaz // (gorunur hep true gelir), bu yüzden "geçen kare çizilmedi" = yeni açılış. // Ardışık olmayan kare no → menü arada gizliydi → HER YENİ AÇILIŞ ÜST SEVİYEDEN. const int kare = ImGui::GetFrameCount(); const bool yeniAcilis = gorunur && d.sonKare != kare - 1; if (gorunur) d.sonKare = kare; // İlk görünür karede (yeni açılış) pop animasyonu + alt-halka SIFIRLA (üst seviye). // YALNIZ geçişte bir kez — menü AÇIKKEN alt-menü açıldığında sıfırlamaz. if (yeniAcilis) { animSifirla(id, 0.0f); animSifirla(id + 1, 0.0f); // alt-halka animasyon fazı d.aktifAna = -1; // her yeni açılış üst seviyeden başlar d.kuruldu = true; } if (!gorunur) { d.kuruldu = false; d.aktifAna = -1; } if (gorunur) d.konum = konum; // Beliriş/kayboluş: hedefe animf ile yaklaş (fade + scale). ~0 olunca çizme. const float a = animf(id, gorunur ? 1.0f : 0.0f, 16.0f); if (a < 0.02f) return -1; const float olcek = 0.82f + 0.18f * a; // hafif pop (0.82→1.0) const float bYuk = BUTON * olcek; const bool radyal = (dizilim == AracDizilim::Radyal); const int n = ImMin(sayi, 32); // Hedef viewport (d.konum'un bulunduğu) + kenar sınırları. ImGuiViewport* vp = ImGui::GetMainViewport(); if (ImGuiViewport* v = ImGui::FindViewportByID(ImGui::GetIO().MouseHoveredViewport)) vp = v; else for (ImGuiViewport* aday : ImGui::GetPlatformIO().Viewports) if (d.konum.x >= aday->Pos.x && d.konum.x < aday->Pos.x + aday->Size.x && d.konum.y >= aday->Pos.y && d.konum.y < aday->Pos.y + aday->Size.y) { vp = aday; break; } const float vMinX = vp->Pos.x + 4.0f, vMinY = vp->Pos.y + 4.0f; const float vMaxX = vp->Pos.x + vp->Size.x - 4.0f, vMaxY = vp->Pos.y + vp->Size.y - 4.0f; // ---- Yerleşim: pencere kutusu (dizilime göre) ---- ImVec2 winMin, winMax, hub{}; ImVec2 merkezler[32]; // Yatay: buton merkezleri float rIc = 0.0f, rDis = 0.0f; // Radyal: iç/dış yarıçap (pasta) if (!radyal) { // Yatay pill: imleç sağ-üstü, kenara taşarsa kaydır. const float gen = (PAD * 2 + (float)sayi * BUTON + (float)(sayi - 1) * BOSLUK) * olcek; const float yuk = (PAD * 2 + BUTON) * olcek; float px = d.konum.x + OFSET_X; float py = d.konum.y - OFSET_Y - yuk; if (px + gen > vMaxX) px = d.konum.x - OFSET_X - gen; // sağa sığmıyor → sola if (px < vMinX) px = vMinX; if (py < vMinY) py = d.konum.y + OFSET_Y; // yukarı sığmıyor → aşağı if (py + yuk > vMaxY) py = vMaxY - yuk; winMin = {px, py}; winMax = {px + gen, py + yuk}; float bx = px + PAD * olcek; const float cy = py + yuk * 0.5f; for (int i = 0; i < n; ++i) { merkezler[i] = {bx + bYuk * 0.5f, cy}; bx += bYuk + BOSLUK * olcek; } } else { // Radyal: konum merkezli pusula dilimleri (işaret menüsü tarzı). rDis = 92.0f * olcek; rIc = 34.0f * olcek; // Alt-halka açıksa dış bant + etiket için ek pay bırak (halka ekrana sığsın). const float altPay = (d.aktifAna >= 0) ? 78.0f * olcek : 0.0f; const float half = rDis + altPay + 20.0f; // dilim etiketleri + glow + alt halka payı ImVec2 c = d.konum; // kenara yakınsa içeri nudge (halka ekranda kalsın) c.x = ImClamp(c.x, vMinX + half, ImMax(vMinX + half, vMaxX - half)); c.y = ImClamp(c.y, vMinY + half, ImMax(vMinY + half, vMaxY - half)); hub = c; winMin = {c.x - half, c.y - half}; winMax = {c.x + half, c.y + half}; } // ---- Overlay penceresi (şeffaf zeminli, viewport'a bağlı — kendi OS penceresi yok) ---- ImGui::SetNextWindowViewport(vp->ID); ImGui::SetNextWindowPos(winMin); ImGui::SetNextWindowSize({winMax.x - winMin.x, winMax.y - winMin.y}); ImGui::SetNextWindowBgAlpha(0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); char pencAd[96]; std::snprintf(pencAd, sizeof(pencAd), "##arac_halkasi_%08X", (unsigned)id); ImGui::Begin(pencAd, nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoFocusOnAppearing); ImGui::PopStyleVar(3); ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow()); ImDrawList* dl = ImGui::GetWindowDrawList(); int tiklanan = -1; if (!radyal) { // ---- Yatay pill: gradient zemin + ikon butonları ---- const float kose = (winMax.y - winMin.y) * 0.5f; glowCerceve(dl, winMin, winMax, t.vurgu, kose, 0.55f * a); const int vtx0 = dl->VtxBuffer.Size; dl->AddRectFilled(winMin, winMax, u32(t.panel, 0.96f * a), kose); ImGui::ShadeVertsLinearColorGradientKeepAlpha( dl, vtx0, dl->VtxBuffer.Size, winMin, {winMin.x, winMax.y}, u32(karisim(t.panel, t.vurgu, 0.10f), a), u32(karisim(t.panel, ImVec4{0, 0, 0, 1}, t.koyu ? 0.22f : 0.02f), a)); dl->AddRect(winMin, winMax, u32(t.vurgu, 0.45f * a), kose, 0, 1.2f); for (int i = 0; i < n; ++i) { const AracOge& o = ogeler[i]; const ImVec2 merkez = merkezler[i]; const ImVec2 bMin{merkez.x - bYuk * 0.5f, merkez.y - bYuk * 0.5f}; const ImVec2 bMax{bMin.x + bYuk, bMin.y + bYuk}; ImGui::SetCursorScreenPos(bMin); char bId[16]; std::snprintf(bId, sizeof(bId), "##b%d", i); bool tik = false; if (o.etkin && a > 0.85f) tik = ImGui::InvisibleButton(bId, {bYuk, bYuk}); else ImGui::Dummy({bYuk, bYuk}); const bool hover = o.etkin && ImGui::IsItemHovered(); const float h = animf(ImGui::GetItemID(), hover ? 1.0f : 0.0f, 14.0f); // Hafif stagger: butonlar soldan sağa sırayla belirir (tek zaman kaynağı a'dan). const float bel = staggerFaktor(a, i, n, 0.04f, 0.24f); if (h > 0.01f) glowCerceve(dl, bMin, bMax, t.vurgu, t.yuvarlak, 0.7f * h); dl->AddRectFilled( bMin, bMax, u32(karisim(t.cerceve, t.vurgu, o.etkin ? 0.22f * h : 0.0f), a * bel * (o.etkin ? 1.0f : 0.6f)), t.yuvarlak); dl->AddRect(bMin, bMax, u32(t.vurgu, (0.12f + 0.55f * h) * a * bel), t.yuvarlak); const ImU32 iRenk = u32(o.etkin ? karisim(t.metinSoluk, t.metin, 0.35f + 0.65f * h) : t.metinSoluk, a * bel); if (!o.simge.bosMu()) simgeCiz(dl, o.simge, merkez, IKON_BOY * olcek, iRenk); if (o.etiket && hover && a > 0.85f) ImGui::SetTooltip("%s", o.etiket); if (tik) tiklanan = i; } } else { // ---- Radyal: İŞARET MENÜSÜ tarzı pusula dilimleri + merkez ✕ kapat ---- // NESTED: cocukSayi>0 olan öğe → tık ile alt-halka aç; yaprak → seç. const ImVec2 m = hub; const ImVec2 fare = ImGui::GetMousePos(); const float dx = fare.x - m.x, dy = fare.y - m.y; const float uzaklik = std::sqrt(dx * dx + dy * dy); // Ana halka slotları (tek öğe → K yönü tek dilim). const int pn = ImMin(sayi, 8); int slot[8]; for (int k = 0; k < 8; ++k) slot[k] = -1; for (int i = 0; i < pn; ++i) slot[PUSULA_SIRA[i]] = i; if (d.aktifAna >= 0 && (d.aktifAna >= sayi || ogeler[d.aktifAna].cocukSayi <= 0)) d.aktifAna = -1; // güvenlik: geçersiz aktif ana // Tüm alanı yakala (tık + fare capture; dilimler açı ile manuel hit-test). ImGui::SetCursorScreenPos(winMin); bool tikOldu = false; if (a > 0.85f) tikOldu = ImGui::InvisibleButton("##pie", {winMax.x - winMin.x, winMax.y - winMin.y}); else ImGui::Dummy({winMax.x - winMin.x, winMax.y - winMin.y}); // Ana halka: alt-halka açıkken aktif ebeveyn dışındaki kardeşler sönükleşir. const int anaHover = pusulaHalkaCiz(id, dl, m, ogeler, slot, pn, rIc, rDis, olcek, a, d.aktifAna); // Alt-halka: çocuklar ebeveyn EKSENİNDE dar/simetrik yelpaze, ayrı DIŞ bant. int altHover = -1; const bool altVar = d.aktifAna >= 0 && ogeler[d.aktifAna].cocukSayi > 0; if (altVar) { const AracOge& ana = ogeler[d.aktifAna]; float ebeveynAci = -PI * 0.5f; // aktif ananın pusula ekseni for (int y = 0; y < 8; ++y) if (slot[y] == d.aktifAna) { ebeveynAci = PUSULA_ACI[y]; break; } // Yumuşak ease-out: daha yüksek sönüm → aşma yok, akıcı oturma. const float altAcilim = yay(id + 1, 1.0f, 200.0f, 26.0f); const float ac = ImClamp(altAcilim, 0.0f, 1.1f); // Belirgin BOŞLUK sonra dış bant: ana halkadan net ayrık (aidiyeti gösterir). const float altIc = rDis + 16.0f; const float altDis = altIc + (42.0f * olcek) * ac; // Bağlayıcı: ebeveyn sektöründen çocuk bandına ince neon çizgi (aidiyet). const float c = std::cos(ebeveynAci), s = std::sin(ebeveynAci); dl->AddLine({m.x + c * (rDis + 1.0f), m.y + s * (rDis + 1.0f)}, {m.x + c * (altIc - 1.0f), m.y + s * (altIc - 1.0f)}, u32(t.vurgu2, 0.75f * a * ac), 2.0f); altHover = altYelpazeCiz(id, dl, m, ana.cocuklar, ana.cocukSayi, ebeveynAci, altIc, altDis, olcek, a * altAcilim, ac); } const bool merkezHover = uzaklik < rIc; // Merkez daire: iptal (✕) / geri (alt-halka açıkken). Tık → kapat/geri. // Hover geçişi zaman-tabanlı (ani renk sıçraması yok) + hafif nabız. const bool geriModu = altVar && merkezHover; const float mh = animf(dilimId(id, 3, 0), merkezHover ? 1.0f : 0.0f, 16.0f); const float nb = 0.85f + 0.15f * nabiz(0.8f); // ince canlılık const ImVec4 mVurgu = karisim(t.vurgu, t.hata, mh); dl->AddCircleFilled(m, rIc * (1.0f + 0.04f * mh), u32(karisim(t.panel, mVurgu, 0.10f + 0.20f * mh), 0.96f * a)); dl->AddCircle(m, rIc * (1.0f + 0.04f * mh), u32(mVurgu, (0.5f + 0.3f * mh) * a * nb), 0, 1.5f + mh); { const char* ortaMetin = nullptr; if (altHover >= 0) ortaMetin = ogeler[d.aktifAna].cocuklar[altHover].etiket; else if (merkezHover) ortaMetin = geriModu ? "↩" : "✕"; else if (anaHover >= 0) ortaMetin = ogeler[anaHover].etiket; else if (altVar) ortaMetin = ogeler[d.aktifAna].etiket; // Cross-fade: metin varsa görünür, yokken yumuşak sönümlenir (ani kaybolma yok). const float mf = animf(dilimId(id, 3, 1), ortaMetin ? 1.0f : 0.0f, 18.0f); if (ortaMetin && mf > 0.02f) { ImFont* f = ImGui::GetFont(); const float fs = 11.0f + 4.0f * mh; const ImVec2 mb = f->CalcTextSizeA(fs, FLT_MAX, 0.0f, ortaMetin); dl->AddText(f, fs, {m.x - mb.x * 0.5f, m.y - mb.y * 0.5f}, u32(karisim(t.metin, t.hata, mh), a * mf), ortaMetin); } } // Seçim onay flash: tıklanan dilim merkezinden kısa, genişleyen neon halka (ucuz). if (d.flasZaman >= 0.0f) { const float ft = zaman() - d.flasZaman; if (ft > 0.32f) d.flasZaman = -1.0f; else { const float p = ft / 0.32f; // 0→1 const float r = 8.0f + 30.0f * p; // genişleyen halka const float fa = (1.0f - p) * a; // sönümlenen alfa dl->AddCircle(d.flasNokta, r, u32(t.vurgu2, 0.85f * fa), 0, 2.5f - 1.5f * p); dl->AddCircleFilled(d.flasNokta, 3.0f * (1.0f - p), u32(t.vurgu2, fa)); } } // Tık işleme. if (tikOldu) { if (altHover >= 0) { // Alt-halka yaprağı seçildi → ana + çocuk döndür. tiklanan = d.aktifAna; if (cocukIndeksi) *cocukIndeksi = altHover; d.flasZaman = zaman(); // onay flash: fare noktasında d.flasNokta = fare; } else if (merkezHover) { if (geriModu) d.aktifAna = -1; // geri: alt-halka kapat, açık kal else tiklanan = ARAC_KAPAT; } else if (anaHover >= 0) { if (ogeler[anaHover].cocukSayi > 0) { if (d.aktifAna == anaHover) d.aktifAna = -1; // aynı öğe → alt-halka kapat else { d.aktifAna = anaHover; animSifirla(id + 1, 0.0f); } } else { tiklanan = anaHover; // yaprak → seç d.flasZaman = zaman(); d.flasNokta = fare; } } else { tiklanan = ARAC_KAPAT; // pencere içi boş/köşe tık → kapat } } else if (ImGui::IsMouseClicked(ImGuiMouseButton_Left) && (fare.x < winMin.x || fare.x > winMax.x || fare.y < winMin.y || fare.y > winMax.y)) { // Halka penceresinin TAMAMEN DIŞINA sol-tık (InvisibleButton yakalamaz) → kapat. // Alt-halka açık olsa bile TÜM menü kapanır (kullanıcı isteği). tiklanan = ARAC_KAPAT; } } ImGui::End(); return tiklanan; } void aracHalkasiSifirla(const char* kimlik) { const ImGuiID id = kimlikHash(kimlik); HalkaDurum& d = g_durumlar[id]; d.aktifAna = -1; d.kuruldu = false; // sonraki çizimde pop animasyonu 0'dan yeniden kurulur d.flasZaman = -1.0f; animSifirla(id, 0.0f); animSifirla(id + 1, 0.0f); } // ---- İçerik-hazır varsayılan setler ---- const AracOge* aracSetYuz(int& sayi) { static const AracOge oge[] = { {"Kenar Yuvarlat", Simge("◜"), true}, {"Pah Kır", Simge("◺"), true}, {"Eğim (Draft)", Simge("◹"), true}, {"Ölç", Simge("📏"), true}, {"Gizle", Simge("🚫"), true}, }; sayi = (int)(sizeof(oge) / sizeof(oge[0])); return oge; } const AracOge* aracSetKenar(int& sayi) { static const AracOge oge[] = { {"Kenar Yuvarlat", Simge("◜"), true}, {"Pah Kır", Simge("◺"), true}, {"Ölç", Simge("📏"), true}, }; sayi = (int)(sizeof(oge) / sizeof(oge[0])); return oge; } const AracOge* aracSetNokta(int& sayi) { static const AracOge oge[] = { {"Ölç", Simge("📏"), true}, }; sayi = (int)(sizeof(oge) / sizeof(oge[0])); return oge; } } // namespace fiko::ui