#include "workbench_secici.hpp" #include #include #include #include #include #include #include "fiko/cekirdek/cekirdek.hpp" #include "fiko/ui/animasyon.hpp" #include "fiko/ui/sektor.hpp" #include "fiko/ui/simge.hpp" #include "fiko/ui/tema.hpp" #include "fiko/ayar/ayar_sistemi.hpp" #include "dogrudan_ayar.hpp" // DM gizmo oto aç/kapa hızlı düğmesi #include "imgui.h" #include "imgui_internal.h" namespace fiko { namespace { constexpr float PI = 3.14159265f; constexpr float R_MERKEZ = 40.0f; // merkez FAB / kapat yarıçapı constexpr float R_IC1 = 54.0f, R_DIS1 = 158.0f; // 1. halka (workbench/grup) bandı constexpr float R_IC2 = 166.0f, R_DIS2 = 280.0f; // 2. halka (alt-menü) bandı — dışta constexpr float GAP = 0.05f; // dilimler arası açı boşluğu constexpr float GLOW_PAY = 16.0f; // glow için ek pencere payı constexpr float ABAS = -PI; // sol (180°) constexpr float ABIT = -PI * 0.5f; // yukarı (270°) constexpr float SPAN = ABIT - ABAS; // çeyrek (π/2) // ---- Nested menü modeli (bir kez kurulur; workbench listesi sabit) ---- struct WbOge { const char* ad = nullptr; ui::Simge simge; }; struct WbMenu { bool kuruldu = false; std::deque depo; // ad/simge kalıcı depo (pointer geçersizleşmez) std::vector ust; // üst-seviye (grup/leaf) std::vector ustKimlik; // leaf → workbench kimlik, grup → "" std::vector> cocuk; // üst-öğe başına yapraklar std::vector> cocukKimlik; }; WbMenu g_menu; struct GrupTanim { const char* ad; const char* simge; const char* uyeler[4]; int n; }; constexpr GrupTanim GRUPLAR[] = { {"Modelleme", "⬡", {"dogrudan"}, 1}, // TEK modelleme ortamı (DM); part/sketcher/gsd komutları DM içinde {"Üretim", "🛠", {"cam"}, 1}, {"Analiz", "📊", {"analiz", "dokum"}, 2}, }; // Seçiciden GİZLİ workbench'ler: part/sketcher/gsd artık DM içinde birleşti (ayrı seçilmez; // sketcher yalnız "Sketch Oluştur" ile girilen bir mod). CAM/Analiz/Döküm ayrı kalır. constexpr const char* GIZLI_WB[] = {"part", "sketcher", "gsd"}; void menuKur(const WorkbenchKaydi& wbk) { if (g_menu.kuruldu) return; const auto& liste = wbk.hepsi(); const auto S = [&](const std::string& s) -> const char* { g_menu.depo.push_back(s); return g_menu.depo.back().c_str(); }; const auto bul = [&](const char* k) -> const WorkbenchOge* { for (const auto& w : liste) if (w.kimlik == k) return &w; return nullptr; }; const auto leaf = [&](const WorkbenchOge& w) { WbOge o; o.ad = S(w.ad); o.simge = ui::Simge(S(w.simge.empty() ? "•" : w.simge)); return o; }; std::vector alindi(liste.size(), false); for (const GrupTanim& g : GRUPLAR) { std::vector uy; for (int i = 0; i < g.n; ++i) if (const WorkbenchOge* w = bul(g.uyeler[i])) { uy.push_back(w); for (size_t k = 0; k < liste.size(); ++k) if (&liste[k] == w) alindi[k] = true; } if (uy.empty()) continue; if (uy.size() == 1) { g_menu.ust.push_back(leaf(*uy[0])); g_menu.ustKimlik.push_back(uy[0]->kimlik); g_menu.cocuk.emplace_back(); g_menu.cocukKimlik.emplace_back(); } else { WbOge gg; gg.ad = S(g.ad); gg.simge = ui::Simge(S(g.simge)); g_menu.ust.push_back(gg); g_menu.ustKimlik.push_back(""); std::vector cc; std::vector ck; for (const WorkbenchOge* w : uy) { cc.push_back(leaf(*w)); ck.push_back(w->kimlik); } g_menu.cocuk.push_back(std::move(cc)); g_menu.cocukKimlik.push_back(std::move(ck)); } } for (size_t k = 0; k < liste.size(); ++k) { if (alindi[k]) continue; bool gizli = false; // DM'de birleşen workbench'ler seçicide görünmez for (const char* h : GIZLI_WB) if (liste[k].kimlik == h) { gizli = true; break; } if (gizli) continue; g_menu.ust.push_back(leaf(liste[k])); g_menu.ustKimlik.push_back(liste[k].kimlik); g_menu.cocuk.emplace_back(); g_menu.cocukKimlik.emplace_back(); } g_menu.kuruldu = true; } // smoothstep + tek zaman kaynağından stagger belirme (alloc 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); } inline float stagger(float a, int i, int n, float gecikme, float pencere) { if (n <= 1) return a; return a * puruz((a - (float)i * gecikme) / pencere); } // Yuvarlak neon glow (konsantrik kontur — shader gerekmez). void daireGlow(ImDrawList* dl, ImVec2 m, float r, const ImVec4& renk, float siddet) { for (int k = 0; k < 4; ++k) dl->AddCircle(m, r + 1.0f + (float)k * 2.6f, ui::u32(renk, siddet * (0.30f - 0.06f * (float)k)), 0, 2.4f); } // UTF-8 güvenli: ad'ı maxW'ye sığdır, taşarsa "…" ekle (tam ad tooltip'te). void sigdir(char* out, int outSz, const char* ad, ImFontBaked* baked, float olcek, float maxW) { float W = 0.0f; for (const char* s = ad; *s;) { unsigned c = 0; s += ImTextCharFromUtf8(&c, s, nullptr); if (!c) break; if (const ImFontGlyph* g = baked->FindGlyph((ImWchar)c)) W += g->AdvanceX * olcek; } if (W <= maxW) { std::strncpy(out, ad, outSz - 1); out[outSz - 1] = 0; return; } const ImFontGlyph* eg = baked->FindGlyph((ImWchar)0x2026); // … const float lim = maxW - (eg ? eg->AdvanceX * olcek : 0.0f); float acc = 0.0f; int oi = 0; for (const char* s = ad; *s;) { const char* prev = s; unsigned c = 0; s += ImTextCharFromUtf8(&c, s, nullptr); if (!c) break; const ImFontGlyph* g = baked->FindGlyph((ImWchar)c); const float aw = g ? g->AdvanceX * olcek : 0.0f; if (acc + aw > lim) break; const int len = (int)(s - prev); if (oi + len >= outSz - 4) break; std::memcpy(out + oi, prev, len); oi += len; acc += aw; } const char ell[] = "\xE2\x80\xA6"; // … if (oi + 3 < outSz - 1) { std::memcpy(out + oi, ell, 3); oi += 3; } out[oi] = 0; } // Radyal (dilim ekseni boyunca) döndürülmüş metin — dilim açısına eğik, içe okunur, // harfler DİK (ters değil). yayMetin per-glyph desenini düz radyal eksene uyarlar. void radyalMetin(ImDrawList* dl, ImVec2 m, float aci, float rC, const char* metin, ImU32 renk, float fontBoyut) { if (!metin || !metin[0] || (renk & IM_COL32_A_MASK) == 0) return; ImFont* font = ImGui::GetFont(); ImFontBaked* baked = font->GetFontBaked(fontBoyut); if (!baked) return; const float olcek = fontBoyut / baked->Size; unsigned kod[64]; float adv[64]; int n = 0; float W = 0.0f; for (const char* s = metin; *s && n < 64;) { unsigned c = 0; s += ImTextCharFromUtf8(&c, s, nullptr); if (!c) break; const ImFontGlyph* g = baked->FindGlyph((ImWchar)c); if (!g) continue; kod[n] = c; adv[n] = g->AdvanceX * olcek; W += adv[n]; ++n; } if (n == 0) return; const float ca = std::cos(aci), sa = std::sin(aci); const ImVec2 tHat{-ca, -sa}; // okuma yönü: içe (dik + ters değil) const ImVec2 nHat{-sa, ca}; // harf üstü: yukarı const ImVec2 c0{m.x + ca * rC, m.y + sa * rC}; float lx = -W * 0.5f; const float yOfs = fontBoyut * 0.34f; ImTextureRef tex = font->OwnerAtlas->TexRef; dl->PushTexture(tex); dl->PrimReserve(n * 6, n * 4); const auto L2E = [&](float x, float y) -> ImVec2 { return {c0.x + tHat.x * x + nHat.x * y, c0.y + tHat.y * x + nHat.y * y}; }; for (int i = 0; i < n; ++i) { const ImFontGlyph* g = baked->FindGlyph((ImWchar)kod[i]); const float gx0 = lx + g->X0 * olcek, gx1 = lx + g->X1 * olcek; const float gy0 = yOfs - g->Y0 * olcek, gy1 = yOfs - g->Y1 * olcek; dl->PrimQuadUV(L2E(gx0, gy0), L2E(gx1, gy0), L2E(gx1, gy1), L2E(gx0, gy1), {g->U0, g->V0}, {g->U1, g->V0}, {g->U1, g->V1}, {g->U0, g->V1}, renk); lx += adv[i]; } dl->PopTexture(); } } // namespace void workbenchSecici(Cekirdek& cekirdek) { WorkbenchKaydi& wbk = cekirdek.workbenchler(); if (wbk.hepsi().empty()) return; // çökme guard menuKur(wbk); if (g_menu.ust.empty()) return; const ui::Tema& t = ui::tema(); ImGuiViewport* vp = ImGui::GetMainViewport(); const ImVec2 kose{vp->WorkPos.x + vp->WorkSize.x, vp->WorkPos.y + vp->WorkSize.y}; const ImVec2 m{kose.x, kose.y}; // TAM sağ-alt köşe → yalnız yukarı-sol çeyrek görünür // Görünen çeyreğin bisektörü (yukarı-sol): merkez ikon/✕ köşede kaybolmasın diye buraya kaydırılır. const ImVec2 ikonKon{m.x - R_MERKEZ * 0.42f, m.y - R_MERKEZ * 0.42f}; const ImGuiID base = ImHashStr("wb_pie"); static bool acik = false; static int aktifGrup = -1; const int N1 = (int)g_menu.ust.size(); const float a = ui::animf(base, acik ? 1.0f : 0.0f, 16.0f); const float disAcil = ui::animf(base + 1u, (acik && aktifGrup >= 0) ? 1.0f : 0.0f, 14.0f); const bool cizYay = a > 0.02f; const bool cizDis = disAcil > 0.02f; // ---- DM gizmo oto aç/kapa hızlı düğmesi: yalnız Doğrudan Modelleme aktif + menü KAPALIYKEN, // FAB'ın üst-sol bisektöründe. Tek tık → transform + push/pull oku "seçince belirme"yi topluca // aç/kapa (Ayarlar'a girmeden). ---- const bool dmAktif = wbk.aktif() == "dogrudan"; const bool dmTogGoster = dmAktif && a < 0.5f; const float dTog = R_MERKEZ + 30.0f, rTog = 17.0f; const ImVec2 togKon{m.x - dTog * 0.7071f, m.y - dTog * 0.7071f}; // ---- Overlay penceresi: kapalıyken YALNIZ merkez FAB kutusu (minimal ayak izi) ---- const float rDisWin = cizDis ? R_DIS2 : R_DIS1; ImVec2 winMin, winMax; if (!cizYay) { winMin = {m.x - R_MERKEZ - GLOW_PAY, m.y - R_MERKEZ - GLOW_PAY}; winMax = {m.x + R_MERKEZ + GLOW_PAY, m.y + R_MERKEZ + GLOW_PAY}; if (dmTogGoster) { // düğmeyi + glow'unu kapsayacak şekilde pencereyi büyüt (üst-sola). // GLOW_PAY payı: 4px tık kutusuna değil glow yarıçapına göre → gölge kesilmez. winMin.x = ImMin(winMin.x, togKon.x - rTog - GLOW_PAY); winMin.y = ImMin(winMin.y, togKon.y - rTog - GLOW_PAY); } } else { winMin = {m.x - rDisWin - GLOW_PAY, m.y - rDisWin - GLOW_PAY}; winMax = {m.x + R_MERKEZ + GLOW_PAY, m.y + R_MERKEZ + GLOW_PAY}; } winMin.x = ImMax(winMin.x, vp->WorkPos.x); winMin.y = ImMax(winMin.y, vp->WorkPos.y); winMax.x = ImMin(winMax.x, kose.x); winMax.y = ImMin(winMax.y, kose.y); 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)); ImGui::Begin("##wb_pie", 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(); ImGui::SetCursorScreenPos(winMin); const bool tikOldu = ImGui::InvisibleButton("##wbhit", {winMax.x - winMin.x, winMax.y - winMin.y}); const bool winHover = ImGui::IsItemHovered(); // ---- Fare → dilim çözümü (geometri) ---- const ImVec2 fare = ImGui::GetMousePos(); const float dx = fare.x - m.x, dy = fare.y - m.y; const float r = std::sqrt(dx * dx + dy * dy); const float ang = std::atan2(dy, dx); const bool inBand = ang >= ABAS - 0.02f && ang <= ABIT + 0.02f; const bool merkezHover = winHover && r < R_MERKEZ + 2.0f; int seg1 = -1; if (cizYay && winHover && !merkezHover && r >= R_IC1 && r <= R_DIS1 && inBand) { const int i = (int)((ang - ABAS) / SPAN * (float)N1); seg1 = i < 0 ? 0 : (i >= N1 ? N1 - 1 : i); } const int N2 = (aktifGrup >= 0 && aktifGrup < N1) ? (int)g_menu.cocuk[aktifGrup].size() : 0; int seg2 = -1; if (cizDis && N2 > 0 && winHover && r >= R_IC2 && r <= R_DIS2 && inBand) { const int i = (int)((ang - ABAS) / SPAN * (float)N2); seg2 = i < 0 ? 0 : (i >= N2 ? N2 - 1 : i); } // ---- Dolu wedge dilim çizimi (simge + radyal isim) ---- const auto cizDilim = [&](float s0, float s1, float rIc, float rDis, const WbOge& o, bool grup, bool aktif, float hf, float belir) { if (belir < 0.02f) return; const float a0 = s0 + GAP * 0.5f, a1 = s1 - GAP * 0.5f; const float rDisE = rIc + (rDis - rIc) * belir + 4.0f * hf; // köşeden dışa açılır const ImVec4 vr = aktif ? t.vurgu2 : (grup ? ui::karisim(t.vurgu, t.vurgu2, 0.35f) : t.vurgu); ImVec4 dolgu = ui::karisim(t.panel, vr, 0.16f + 0.30f * hf + (aktif ? 0.16f : 0.0f)); dolgu.w = 0.95f * belir; if (hf > 0.01f || aktif) ui::sektorGlow(dl, m, rDisE, a0, a1, vr, (aktif ? 0.5f : 0.0f) + hf); ui::sektorCiz(dl, m, rIc, rDisE, a0, a1, ui::u32(dolgu), ui::u32(vr, (0.35f + 0.55f * hf) * belir), 1.3f + 1.0f * hf); const float aciM = (a0 + a1) * 0.5f; const ImU32 ir = ui::u32(ui::karisim(t.metinSoluk, t.metin, 0.45f + 0.55f * hf), belir); const ImVec2 ikM{m.x + std::cos(aciM) * (rIc + 18.0f), m.y + std::sin(aciM) * (rIc + 18.0f)}; if (!o.simge.bosMu()) ui::simgeCiz(dl, o.simge, ikM, 20.0f + 2.0f * hf, ir); if (belir > 0.55f && o.ad) { const float ic = rIc + 36.0f, ds = rDisE - 8.0f; const float availLen = ds - ic; if (availLen > 20.0f) { const float fb = 14.0f; ImFont* f = ImGui::GetFont(); ImFontBaked* bk = f->GetFontBaked(fb); char buf[80]; if (bk) sigdir(buf, sizeof(buf), o.ad, bk, fb / bk->Size, availLen); else { std::strncpy(buf, o.ad, 79); buf[79] = 0; } radyalMetin(dl, m, aciM, (ic + ds) * 0.5f, buf, ir, fb); } } if (grup) { // dış kenarda çok-halka (alt-menü) işareti const ImVec2 ok{m.x + std::cos(aciM) * (rDisE - 7.0f), m.y + std::sin(aciM) * (rDisE - 7.0f)}; dl->AddCircleFilled(ok, 3.0f, ui::u32(t.vurgu2, 0.9f * belir)); } }; if (cizYay) for (int i = 0; i < N1; ++i) { const float s0 = ABAS + SPAN * (float)i / (float)N1; const float s1 = ABAS + SPAN * (float)(i + 1) / (float)N1; const bool grup = !g_menu.cocuk[i].empty(); bool aktif = !grup && g_menu.ustKimlik[i] == wbk.aktif(); if (grup) for (const auto& ck : g_menu.cocukKimlik[i]) if (ck == wbk.aktif()) { aktif = true; break; } const float hf = ui::animf(base + 10u + (ImGuiID)i, seg1 == i ? 1.0f : 0.0f, 14.0f); const float belir = stagger(a, i, N1, 0.05f, 0.28f); cizDilim(s0, s1, R_IC1, R_DIS1, g_menu.ust[i], grup, aktif, hf, belir); } if (cizDis && N2 > 0) for (int i = 0; i < N2; ++i) { const float s0 = ABAS + SPAN * (float)i / (float)N2; const float s1 = ABAS + SPAN * (float)(i + 1) / (float)N2; const bool aktif = g_menu.cocukKimlik[aktifGrup][i] == wbk.aktif(); const float hf = ui::animf(base + 40u + (ImGuiID)i, seg2 == i ? 1.0f : 0.0f, 14.0f); const float belir = stagger(disAcil, i, N2, 0.045f, 0.30f); cizDilim(s0, s1, R_IC2, R_DIS2, g_menu.cocuk[aktifGrup][i], false, aktif, hf, belir); } // ---- Merkez: kapalıyken aktif workbench FAB'ı, açıkken ✕/↩ kapat ---- const float mh = ui::animf(base + 2u, merkezHover ? 1.0f : 0.0f, 14.0f); const float nb = 0.82f + 0.18f * ui::nabiz(0.7f); const float rM = R_MERKEZ * (1.0f + 0.05f * mh); const ImVec4 mv = (a > 0.5f) ? ui::karisim(t.vurgu, t.hata, 0.55f) : t.vurgu; daireGlow(dl, m, rM, mv, (0.5f + 0.5f * mh) * nb); const int vtx0 = dl->VtxBuffer.Size; dl->AddCircleFilled(m, rM, ui::u32(t.panel, 0.97f)); ImGui::ShadeVertsLinearColorGradientKeepAlpha( dl, vtx0, dl->VtxBuffer.Size, {m.x, m.y - rM}, {m.x, m.y + rM}, ui::u32(ui::karisim(t.panel, mv, 0.20f), 1.0f), ui::u32(ui::karisim(t.panel, ImVec4{0, 0, 0, 1}, t.koyu ? 0.25f : 0.0f), 1.0f)); dl->AddCircle(m, rM, ui::u32(mv, (0.6f + 0.35f * mh) * nb), 0, 2.4f); const WorkbenchOge* akt = wbk.bul(wbk.aktif()); if (1.0f - a > 0.02f && akt && !akt->simge.empty()) ui::simgeCiz(dl, ui::Simge(akt->simge.c_str()), ikonKon, 22.0f, ui::u32(ui::karisim(t.metin, t.vurgu, 0.2f), 1.0f - a)); if (a > 0.02f) { const char* ct = (aktifGrup >= 0) ? "↩" : "✕"; ImFont* f = ImGui::GetFont(); const float fs = 15.0f + 3.0f * mh; const ImVec2 ts = f->CalcTextSizeA(fs, FLT_MAX, 0.0f, ct); dl->AddText(f, fs, {ikonKon.x - ts.x * 0.5f, ikonKon.y - ts.y * 0.5f}, ui::u32(ui::karisim(t.metin, t.hata, mh), a), ct); } // ---- DM gizmo oto aç/kapa düğmesi (FAB üst-solu) ---- bool togHover = false; if (dmTogGoster) { const bool oto = dogrudanAyar().yuzTasiOto || dogrudanAyar().pushPullOtoOk; const float tdx = fare.x - togKon.x, tdy = fare.y - togKon.y; togHover = winHover && (tdx * tdx + tdy * tdy) < (rTog + 2.0f) * (rTog + 2.0f); const float th = ui::animf(base + 3u, togHover ? 1.0f : 0.0f, 14.0f); const float rT = rTog * (1.0f + 0.06f * th); const ImVec4 tv = oto ? t.vurgu2 : t.metinSoluk; // açık = vurgu, kapalı = soluk if (oto || th > 0.01f) daireGlow(dl, togKon, rTog, tv, (oto ? 0.35f : 0.0f) + 0.4f * th); dl->AddCircleFilled(togKon, rT, ui::u32(t.panel, 0.97f)); dl->AddCircle(togKon, rT, ui::u32(tv, 0.75f + 0.25f * th), 0, 2.0f); // Simge: ✥ (taşı/gizmo). Kapalıyken üzerine eğik çizgi (⊘ hissi). ui::simgeCiz(dl, ui::Simge("✥"), togKon, 17.0f, ui::u32(ui::karisim(t.metinSoluk, t.metin, oto ? 0.7f : 0.25f), 1.0f)); if (!oto) // pasif → çapraz kesme çizgisi dl->AddLine({togKon.x - rTog * 0.6f, togKon.y + rTog * 0.6f}, {togKon.x + rTog * 0.6f, togKon.y - rTog * 0.6f}, ui::u32(t.hata, 0.95f), 2.4f); if (togHover) ImGui::SetTooltip(oto ? "Gizmo otomatik: AÇIK (seçince belirir) — kapatmak için tıkla" : "Gizmo otomatik: KAPALI (yalnız Ctrl'de belirir) — açmak için tıkla"); } // ---- Boşluk tık-geçişi: kapalıyken (menü yay değil) FAB/düğme ÜSTÜ değilse pencereye delik aç → // köşedeki boş pay sahneye tık geçirir (overlay yalnız gerçek kontrolleri yakalar, arkayı kesmez). // Karar SAF geometriden (r, togKon) — winHover'a bağlanırsa delik↔hover döngüsü kilitlenirdi. // Begin() deliği her kare sıfırlar → yalnız istediğimizde set ederiz. ---- if (!cizYay) { const float tgx = fare.x - togKon.x, tgy = fare.y - togKon.y; const bool geoFab = r < R_MERKEZ + 2.0f; const bool geoTog = dmTogGoster && (tgx * tgx + tgy * tgy) < (rTog + 2.0f) * (rTog + 2.0f); if (!geoFab && !geoTog) ImGui::SetWindowHitTestHole(ImGui::GetCurrentWindow(), winMin, {winMax.x - winMin.x, winMax.y - winMin.y}); } // ---- Tooltip (workbench adı) ---- if (cizYay) { const char* ip = nullptr; if (seg2 >= 0) ip = g_menu.cocuk[aktifGrup][seg2].ad; else if (seg1 >= 0) ip = g_menu.ust[seg1].ad; if (ip) ImGui::SetTooltip("%s", ip); } else if (merkezHover && akt) { ImGui::SetTooltip("Workbench: %s", akt->ad.c_str()); } // ---- Tık işleme ---- if (tikOldu) { if (togHover) { // gizmo oto aç/kapa (transform + push/pull topluca) — menü açılmaz const bool yeni = !(dogrudanAyar().yuzTasiOto || dogrudanAyar().pushPullOtoOk); dogrudanAyar().yuzTasiOto = yeni; dogrudanAyar().pushPullOtoOk = yeni; ayar::sistem().degerAta("dogrudan.yuz_tasi_oto", ayar::dBool(yeni)); ayar::sistem().degerAta("dogrudan.push_pull_oto_ok", ayar::dBool(yeni)); } else if (!acik) { if (merkezHover) acik = true; // FAB → çeyrek pasta aç } else if (merkezHover) { if (aktifGrup >= 0) aktifGrup = -1; // geri (alt-menü kapat) else acik = false; // kapat } else if (seg2 >= 0) { wbk.aktifAyarla(cekirdek, g_menu.cocukKimlik[aktifGrup][seg2]); // yaprak → geç acik = false; aktifGrup = -1; } else if (seg1 >= 0) { if (!g_menu.cocuk[seg1].empty()) { aktifGrup = (aktifGrup == seg1) ? -1 : seg1; // grup → alt-menü toggle ui::animSifirla(base + 1u, 0.0f); } else { wbk.aktifAyarla(cekirdek, g_menu.ustKimlik[seg1]); acik = false; aktifGrup = -1; } } else { acik = false; // pencere içi boş → kapat aktifGrup = -1; } } else if (acik && ImGui::IsMouseClicked(ImGuiMouseButton_Left) && (fare.x < winMin.x || fare.x > winMax.x || fare.y < winMin.y || fare.y > winMax.y)) { acik = false; // dışa tık → kapat aktifGrup = -1; } ImGui::End(); } } // namespace fiko