#include "fiko/ui/bilesen_ek.hpp" #include #include #include #include #include #include #include "fiko/ui/animasyon.hpp" #include "fiko/ui/bilesen.hpp" #include "fiko/ui/tema.hpp" #include "imgui_internal.h" namespace fiko::ui { namespace { // Bileşen-yerel stabil ID: aynı karedeki birden çok örnek çakışmaz, scroll etkilemez. ImGuiID yerelId(ImVec2 p, int tuz = 0) { ImGuiWindow* w = ImGui::GetCurrentWindow(); const int y = (int)(p.x - w->Pos.x + w->Scroll.x) * 8191 + (int)(p.y - w->Pos.y + w->Scroll.y) + tuz * 131071; return w->GetID((void*)(intptr_t)y); } ImVec4 durumRengi(ToastTip t) { const Tema& tm = tema(); switch (t) { case ToastTip::Basari: return tm.basari; case ToastTip::Uyari: return tm.uyari; case ToastTip::Hata: return tm.hata; default: return tm.vurgu; } } const char* durumIkonu(ToastTip t) { switch (t) { case ToastTip::Basari: return "✓"; case ToastTip::Uyari: return "⚠"; case ToastTip::Hata: return "✕"; default: return "ℹ"; } } struct Toast { std::string metin; ToastTip tip; float omur; // toplam saniye float kalan; // geri sayım float giris = 0.0f; }; std::vector g_toastlar; } // namespace // ── Toast ──────────────────────────────────────────────────────────────────── void toastGoster(const char* metin, ToastTip tip, float saniye) { g_toastlar.push_back({metin ? metin : "", tip, saniye, saniye, 0.0f}); } void toastlariCiz() { if (g_toastlar.empty()) return; const Tema& t = tema(); const float dt = ImGui::GetIO().DeltaTime; ImGuiViewport* vp = ImGui::GetMainViewport(); ImDrawList* dl = ImGui::GetForegroundDrawList(vp); const float en = 300.0f, pad = 12.0f, aralik = 8.0f; float y = vp->WorkPos.y + 16.0f; const float sagX = vp->WorkPos.x + vp->WorkSize.x - 16.0f; for (int i = (int)g_toastlar.size() - 1; i >= 0; --i) { Toast& ts = g_toastlar[i]; ts.kalan -= dt; ts.giris = std::fmin(1.0f, ts.giris + dt * 6.0f); if (ts.kalan <= 0.0f) { g_toastlar.erase(g_toastlar.begin() + i); continue; } } for (auto& ts : g_toastlar) { const ImVec4 renk = durumRengi(ts.tip); const float cikis = std::fmin(1.0f, ts.kalan / 0.4f); // son 0.4s soluklaşma const float a = ts.giris * cikis; const float kaydir = (1.0f - ts.giris) * 40.0f; // sağdan içeri kayma const ImVec2 mb = ImGui::CalcTextSize(ts.metin.c_str(), nullptr, false, en - pad * 3); const float yuk = std::fmax(34.0f, mb.y + pad * 2); const ImVec2 min{sagX - en + kaydir, y}; const ImVec2 max{sagX + kaydir, y + yuk}; glowCerceve(dl, min, max, renk, t.yuvarlak, 0.6f * a); dl->AddRectFilled(min, max, u32(t.panel, 0.97f * a), t.yuvarlak); dl->AddRect(min, max, u32(renk, 0.55f * a), t.yuvarlak, 0, 1.2f); dl->AddRectFilled(min, {min.x + 4.0f, max.y}, u32(renk, 0.9f * a), t.yuvarlak); // ilerleme (kalan süre) alt çizgisi const float oran = ts.kalan / ts.omur; dl->AddRectFilled({min.x, max.y - 2.0f}, {min.x + (max.x - min.x) * oran, max.y}, u32(renk, 0.5f * a)); dl->AddText({min.x + pad, min.y + pad}, u32(renk, a), durumIkonu(ts.tip)); dl->AddText(nullptr, 0.0f, {min.x + pad + 20.0f, min.y + pad}, u32(t.metin, a), ts.metin.c_str(), nullptr, en - pad * 3); y = max.y + aralik; } } // ── Modal / Onay ───────────────────────────────────────────────────────────── int onayDiyalogu(const char* baslik, const char* mesaj, bool* acik, const char* onayEtiket, const char* iptalEtiket) { if (!acik || !*acik) return 0; const Tema& t = tema(); ImGuiViewport* vp = ImGui::GetMainViewport(); ImDrawList* fg = ImGui::GetForegroundDrawList(vp); // karartılmış zemin fg->AddRectFilled(vp->Pos, {vp->Pos.x + vp->Size.x, vp->Pos.y + vp->Size.y}, u32({0, 0, 0, 1}, 0.55f)); ImGui::SetNextWindowPos({vp->Pos.x + vp->Size.x * 0.5f, vp->Pos.y + vp->Size.y * 0.5f}, ImGuiCond_Always, {0.5f, 0.5f}); ImGui::SetNextWindowSize({360.0f, 0.0f}); int sonuc = 0; ImGui::PushStyleColor(ImGuiCol_Border, t.vurgu); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.5f); char id[160]; std::snprintf(id, sizeof(id), "%s##onay", baslik ? baslik : "Onay"); if (ImGui::Begin(id, nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings)) { baslikSeridi("❓", baslik ? baslik : "Onay"); ImGui::Spacing(); ImGui::TextWrapped("%s", mesaj ? mesaj : ""); ImGui::Spacing(); neonAyrac(); const float gen = ImGui::GetContentRegionAvail().x; if (buton(iptalEtiket, {gen * 0.48f, 0}, ButonTip::Hayalet)) sonuc = -1; ImGui::SameLine(0, gen * 0.04f); if (buton(onayEtiket, {gen * 0.48f, 0}, ButonTip::Birincil)) sonuc = 1; if (ImGui::IsKeyPressed(ImGuiKey_Escape)) sonuc = -1; if (ImGui::IsKeyPressed(ImGuiKey_Enter)) sonuc = 1; } ImGui::End(); ImGui::PopStyleVar(); ImGui::PopStyleColor(); if (sonuc != 0) *acik = false; return sonuc; } // ── Sekme çubuğu ───────────────────────────────────────────────────────────── bool sekmeCubugu(const char* kimlik, const char* const* etiketler, int sayi, int* secili) { const Tema& t = tema(); ImGui::PushID(kimlik); const ImVec2 p0 = ImGui::GetCursorScreenPos(); const float yuk = ImGui::GetFrameHeight() + 4.0f; const float gen = ImGui::GetContentRegionAvail().x; const float parca = gen / (float)std::max(1, sayi); ImGui::Dummy({gen, yuk}); ImDrawList* dl = ImGui::GetWindowDrawList(); bool degisti = false; const ImGuiID gid = yerelId(p0); const float hedefX = p0.x + parca * (*secili + 0.5f); const float gostergeX = animf(gid, hedefX, 16.0f); for (int i = 0; i < sayi; ++i) { const ImVec2 mn{p0.x + parca * i, p0.y}; const ImVec2 mx{mn.x + parca, p0.y + yuk}; const bool hover = ImGui::IsMouseHoveringRect(mn, mx); if (hover && ImGui::IsMouseClicked(0) && *secili != i) { *secili = i; degisti = true; } const bool sec = (i == *secili); const float h = animf(gid + 1 + i, (hover || sec) ? 1.0f : 0.0f, 14.0f); const ImVec4 mr = sec ? t.vurgu : karisim(t.metinSoluk, t.metin, h); const ImVec2 tb = ImGui::CalcTextSize(etiketler[i]); dl->AddText({(mn.x + mx.x - tb.x) * 0.5f, (mn.y + mx.y - tb.y) * 0.5f}, u32(mr), etiketler[i]); } // kayan neon gösterge const float yarim = parca * 0.42f; const float ly = p0.y + yuk - 2.5f; const int vtx0 = dl->VtxBuffer.Size; dl->AddRectFilled({gostergeX - yarim, ly}, {gostergeX + yarim, ly + 2.5f}, u32(t.vurgu), 1.5f); ImGui::ShadeVertsLinearColorGradientKeepAlpha(dl, vtx0, dl->VtxBuffer.Size, {gostergeX - yarim, ly}, {gostergeX + yarim, ly}, u32(t.vurgu2), u32(t.vurgu)); dl->AddLine({p0.x, ly + 1}, {p0.x + gen, ly + 1}, u32(t.kenar, 0.4f), 1.0f); ImGui::PopID(); return degisti; } // ── Segmentli kontrol ──────────────────────────────────────────────────────── bool segmentli(const char* kimlik, const char* const* etiketler, int sayi, int* secili) { const Tema& t = tema(); ImGui::PushID(kimlik); const ImVec2 p0 = ImGui::GetCursorScreenPos(); const float yuk = ImGui::GetFrameHeight(); const float gen = ImGui::GetContentRegionAvail().x; const float parca = gen / (float)std::max(1, sayi); ImGui::Dummy({gen, yuk}); ImDrawList* dl = ImGui::GetWindowDrawList(); const ImGuiID gid = yerelId(p0, 3); dl->AddRectFilled(p0, {p0.x + gen, p0.y + yuk}, u32(t.cerceve), t.yuvarlak); const float hedefX = p0.x + parca * *secili; const float kayX = animf(gid, hedefX, 18.0f); // seçili kaydırıcı zemini glowCerceve(dl, {kayX + 2, p0.y + 2}, {kayX + parca - 2, p0.y + yuk - 2}, t.vurgu, t.yuvarlak, 0.4f); dl->AddRectFilled({kayX + 2, p0.y + 2}, {kayX + parca - 2, p0.y + yuk - 2}, u32(karisim(t.cerceve, t.vurgu, 0.5f)), t.yuvarlak - 1); bool degisti = false; for (int i = 0; i < sayi; ++i) { const ImVec2 mn{p0.x + parca * i, p0.y}; const ImVec2 mx{mn.x + parca, p0.y + yuk}; if (ImGui::IsMouseHoveringRect(mn, mx) && ImGui::IsMouseClicked(0) && *secili != i) { *secili = i; degisti = true; } const bool sec = (i == *secili); const ImVec2 tb = ImGui::CalcTextSize(etiketler[i]); dl->AddText({(mn.x + mx.x - tb.x) * 0.5f, (mn.y + mx.y - tb.y) * 0.5f}, u32(sec ? t.metin : t.metinSoluk), etiketler[i]); } dl->AddRect(p0, {p0.x + gen, p0.y + yuk}, u32(t.kenar, 0.6f), t.yuvarlak); ImGui::PopID(); return degisti; } // ── Arama kutusu ───────────────────────────────────────────────────────────── bool aramaKutusu(const char* kimlik, char* tampon, int tamponBoyut, const char* ipucu, float genislik) { const Tema& t = tema(); ImGui::PushID(kimlik); if (genislik <= 0) genislik = ImGui::GetContentRegionAvail().x; const ImVec2 p0 = ImGui::GetCursorScreenPos(); const float yuk = ImGui::GetFrameHeight(); ImDrawList* dl = ImGui::GetWindowDrawList(); // büyüteç ikonu (sol) dl->AddText({p0.x + 8.0f, p0.y + (yuk - ImGui::GetFontSize()) * 0.5f}, u32(t.metinSoluk), "🔍"); const bool bosMu = (tampon[0] == '\0'); const float sagPay = bosMu ? 6.0f : 24.0f; ImGui::SetCursorScreenPos({p0.x + 28.0f, p0.y}); ImGui::SetNextItemWidth(genislik - 28.0f - sagPay); bool degisti = ImGui::InputTextWithHint("##ara", ipucu, tampon, tamponBoyut); if (!bosMu) { const ImVec2 xc{p0.x + genislik - 13.0f, p0.y + yuk * 0.5f}; const bool xh = ImGui::IsMouseHoveringRect({xc.x - 9, xc.y - 9}, {xc.x + 9, xc.y + 9}); dl->AddCircleFilled(xc, 8.0f, u32(t.cerceve, xh ? 1.0f : 0.0f)); const ImU32 xr = u32(xh ? t.hata : t.metinSoluk); dl->AddLine({xc.x - 3, xc.y - 3}, {xc.x + 3, xc.y + 3}, xr, 1.5f); dl->AddLine({xc.x + 3, xc.y - 3}, {xc.x - 3, xc.y + 3}, xr, 1.5f); if (xh && ImGui::IsMouseClicked(0)) { tampon[0] = '\0'; degisti = true; } } ImGui::PopID(); return degisti; } // ── Sayı stepper ───────────────────────────────────────────────────────────── bool sayiStepper(const char* etiket, int* deger, int adim, int mn, int mx, float genislik) { ImGui::PushID(etiket); if (genislik <= 0) genislik = std::fmax(120.0f, ImGui::GetContentRegionAvail().x); const float bh = ImGui::GetFrameHeight(); const int gercekAdim = ImGui::GetIO().KeyCtrl ? adim * 10 : adim; bool degisti = false; if (buton("−", {bh, bh}, ButonTip::Hayalet)) { *deger = std::max(mn, *deger - gercekAdim); degisti = true; } ImGui::SameLine(0, 2.0f); ImGui::SetNextItemWidth(genislik - 2 * bh - 4.0f); int v = *deger; if (ImGui::DragInt("##v", &v, 0.1f, mn, mx)) { *deger = v; degisti = true; } ImGui::SameLine(0, 2.0f); if (buton("+", {bh, bh}, ButonTip::Hayalet)) { *deger = std::min(mx, *deger + gercekAdim); degisti = true; } ImGui::PopID(); return degisti; } // ── Renk seçici ────────────────────────────────────────────────────────────── bool renkSecici(const char* etiket, ImVec4* renk) { const Tema& t = tema(); ImGui::PushID(etiket); const float k = ImGui::GetFrameHeight(); const ImVec2 p0 = ImGui::GetCursorScreenPos(); const bool ac = ImGui::InvisibleButton("##ornek", {k, k}); ImDrawList* dl = ImGui::GetWindowDrawList(); if (ImGui::IsItemHovered()) glowCerceve(dl, p0, {p0.x + k, p0.y + k}, *renk, t.yuvarlak, 0.6f); dl->AddRectFilled(p0, {p0.x + k, p0.y + k}, u32(*renk), t.yuvarlak); dl->AddRect(p0, {p0.x + k, p0.y + k}, u32(t.metin, 0.5f), t.yuvarlak); if (etiket[0] && etiket[0] != '#') { ImGui::SameLine(); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted(etiket); } if (ac) ImGui::OpenPopup("##renkPop"); bool degisti = false; if (ImGui::BeginPopup("##renkPop")) { // hızlı palet (tema tokenları + temel tonlar) const ImVec4 palet[] = {t.vurgu, t.vurgu2, t.basari, t.uyari, t.hata, t.metin, {1, 1, 1, 1}, {0.1f, 0.1f, 0.12f, 1}}; for (int i = 0; i < 8; ++i) { ImGui::PushID(i); if (ImGui::ColorButton("##p", palet[i], ImGuiColorEditFlags_NoTooltip, {22, 22})) { *renk = palet[i]; degisti = true; } ImGui::PopID(); if (i % 4 != 3) ImGui::SameLine(); } neonAyrac(); degisti |= ImGui::ColorPicker4("##hsv", (float*)renk, ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_NoSidePreview); ImGui::EndPopup(); } ImGui::PopID(); return degisti; } // ── Gauge / dial halka ─────────────────────────────────────────────────────── void gaugeHalka(const char* etiket, float deger, float mn, float mx, const char* birim, float cap) { const Tema& t = tema(); const ImVec2 p0 = ImGui::GetCursorScreenPos(); ImGui::Dummy({cap, cap}); ImDrawList* dl = ImGui::GetWindowDrawList(); const ImVec2 c{p0.x + cap * 0.5f, p0.y + cap * 0.5f}; const float r = cap * 0.42f; const float bas = 2.35f, uzn = 4.6f; // 135° → 405° (alt açık yay) const float oran = (mx > mn) ? std::fmin(1.0f, std::fmax(0.0f, (deger - mn) / (mx - mn))) : 0.0f; const ImGuiID id = yerelId(p0, 7); const float o = animf(id, oran, 10.0f); dl->PathArcTo(c, r, bas, bas + uzn, 48); dl->PathStroke(u32(t.cerceve), 0, 7.0f); if (o > 0.002f) { const ImVec4 renk = karisim(t.basari, o > 0.66f ? t.hata : t.vurgu, o); dl->PathArcTo(c, r, bas, bas + uzn * o, 48); dl->PathStroke(u32(renk), 0, 7.0f); const float ea = bas + uzn * o; const ImVec2 uc{c.x + std::cos(ea) * r, c.y + std::sin(ea) * r}; dl->AddCircleFilled(uc, 5.0f, u32(renk)); dl->AddCircleFilled(uc, 8.0f, u32(renk, 0.25f * t.glowSiddet)); } char buf[48]; std::snprintf(buf, sizeof(buf), birim ? "%.0f%s" : "%.0f", deger, birim ? birim : ""); ImGui::PushFont(nullptr, ImGui::GetFontSize() * 1.4f); const ImVec2 db = ImGui::CalcTextSize(buf); dl->AddText({c.x - db.x * 0.5f, c.y - db.y * 0.5f}, u32(t.metin), buf); ImGui::PopFont(); if (etiket && etiket[0]) { const ImVec2 eb = ImGui::CalcTextSize(etiket); dl->AddText({c.x - eb.x * 0.5f, p0.y + cap - eb.y}, u32(t.metinSoluk), etiket); } } // ── Breadcrumb ─────────────────────────────────────────────────────────────── int ekmekKirintisi(const char* const* parcalar, int sayi) { const Tema& t = tema(); int tiklanan = -1; for (int i = 0; i < sayi; ++i) { const bool son = (i == sayi - 1); if (i) { ImGui::SameLine(0, 4.0f); ImGui::TextColored(t.metinSoluk, "›"); ImGui::SameLine(0, 4.0f); } ImGui::PushID(i); const ImVec2 tb = ImGui::CalcTextSize(parcalar[i]); const bool tik = ImGui::InvisibleButton("##bc", tb); if (tik) tiklanan = i; const bool hover = ImGui::IsItemHovered(); const ImVec2 mn = ImGui::GetItemRectMin(); ImGui::GetWindowDrawList()->AddText(mn, u32(son ? t.vurgu : karisim(t.metinSoluk, t.metin, hover ? 1.0f : 0.0f)), parcalar[i]); if (hover && !son) ImGui::GetWindowDrawList()->AddLine({mn.x, mn.y + tb.y}, {mn.x + tb.x, mn.y + tb.y}, u32(t.vurgu, 0.6f), 1.0f); ImGui::PopID(); } return tiklanan; } // ── Katlanır bölüm (accordion) ─────────────────────────────────────────────── bool katlanirBasla(const char* baslik, bool* acik) { const Tema& t = tema(); ImGui::PushID(baslik); const float gen = ImGui::GetContentRegionAvail().x; const float yuk = ImGui::GetFrameHeight(); const ImVec2 p0 = ImGui::GetCursorScreenPos(); if (ImGui::InvisibleButton("##bas", {gen, yuk})) *acik = !*acik; const bool hover = ImGui::IsItemHovered(); const ImGuiID id = ImGui::GetItemID(); const float h = animf(id, hover ? 1.0f : 0.0f, 14.0f); const float ar = animf(id + 1, *acik ? 1.0f : 0.0f, 16.0f); ImDrawList* dl = ImGui::GetWindowDrawList(); const ImVec2 mx{p0.x + gen, p0.y + yuk}; dl->AddRectFilled(p0, mx, u32(karisim(t.panel, t.vurgu, 0.06f + 0.10f * h)), t.yuvarlak); dl->AddRect(p0, mx, u32(t.vurgu, 0.15f + 0.3f * h), t.yuvarlak); // dönen ok const ImVec2 oc{p0.x + 12.0f, p0.y + yuk * 0.5f}; const float ac = ar * 1.5708f; // 90° yatay→dikey const float co = std::cos(ac), si = std::sin(ac); auto dondur = [&](float dx, float dy) -> ImVec2 { return {oc.x + dx * co - dy * si, oc.y + dx * si + dy * co}; }; dl->AddTriangleFilled(dondur(-3, -4), dondur(4, 0), dondur(-3, 4), u32(karisim(t.metinSoluk, t.vurgu, ar))); dl->AddText({p0.x + 26.0f, p0.y + (yuk - ImGui::GetFontSize()) * 0.5f}, u32(t.metin), baslik); if (*acik) { ImGui::SetCursorScreenPos({p0.x, mx.y + 4.0f}); ImGui::Indent(10.0f); ImGui::PushID("##govde"); return true; } // KAPALI: çağıran katlanirBitir() ÇAĞIRMAZ (sözleşme: yalnız true dönünce çağrılır), // dolayısıyla baştaki PushID(baslik) burada kapatılmalı. Kapatılmazsa her karede ID // yığınında bir kayıt birikir ve pencere End()'inde "Mismatched PushID/PopID" assert'i // patlar — kullanıcının Lisans Üretici'de gördüğü hata buydu. Çağıranlar DEĞİŞMEZ. ImGui::PopID(); return false; } void katlanirBitir() { ImGui::PopID(); ImGui::Unindent(10.0f); ImGui::PopID(); // katlanirBasla PushID(baslik) ImGui::Spacing(); } // ── Splitter ───────────────────────────────────────────────────────────────── bool ayracKaydir(const char* kimlik, bool dikey, float* boyut, float mn, float mx, float uzunluk) { const Tema& t = tema(); ImGui::PushID(kimlik); const float kalinlik = 6.0f; const ImVec2 p0 = ImGui::GetCursorScreenPos(); const float u = uzunluk > 0 ? uzunluk : (dikey ? ImGui::GetContentRegionAvail().y : ImGui::GetContentRegionAvail().x); const ImVec2 boy = dikey ? ImVec2{kalinlik, u} : ImVec2{u, kalinlik}; ImGui::InvisibleButton("##split", boy); const bool hover = ImGui::IsItemHovered(); const bool aktif = ImGui::IsItemActive(); ImGui::SetMouseCursor(dikey ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS); bool degisti = false; if (aktif) { const float d = dikey ? ImGui::GetIO().MouseDelta.x : ImGui::GetIO().MouseDelta.y; if (d != 0.0f) { *boyut = std::fmin(mx, std::fmax(mn, *boyut + d)); degisti = true; } } const float h = animf(ImGui::GetItemID(), (hover || aktif) ? 1.0f : 0.0f, 14.0f); ImDrawList* dl = ImGui::GetWindowDrawList(); const ImVec2 c{p0.x + boy.x * 0.5f, p0.y + boy.y * 0.5f}; // tutamaç noktaları const ImVec4 renk = karisim(t.kenar, t.vurgu, h); for (int i = -1; i <= 1; ++i) { const ImVec2 pc = dikey ? ImVec2{c.x, c.y + i * 6.0f} : ImVec2{c.x + i * 6.0f, c.y}; dl->AddCircleFilled(pc, 1.6f + h, u32(renk, 0.6f + 0.4f * h)); } ImGui::PopID(); return degisti; } // ── Çip / etiket girişi ────────────────────────────────────────────────────── bool cipGirisi(const char* kimlik, const char* const* cipler, int sayi, char* girisTampon, int girisBoyut, int* silinenIndeks, bool* eklendi) { const Tema& t = tema(); ImGui::PushID(kimlik); *silinenIndeks = -1; *eklendi = false; bool degisti = false; const float genSinir = ImGui::GetContentRegionAvail().x + ImGui::GetCursorScreenPos().x; for (int i = 0; i < sayi; ++i) { ImGui::PushID(i); const ImVec2 tb = ImGui::CalcTextSize(cipler[i]); const float cw = tb.x + 26.0f, ch = ImGui::GetFontSize() + 8.0f; if (i && ImGui::GetCursorScreenPos().x + cw < genSinir) ImGui::SameLine(0, 4.0f); const ImVec2 p0 = ImGui::GetCursorScreenPos(); ImGui::Dummy({cw, ch}); ImDrawList* dl = ImGui::GetWindowDrawList(); const ImVec2 mx{p0.x + cw, p0.y + ch}; dl->AddRectFilled(p0, mx, u32(karisim(t.cerceve, t.vurgu, 0.18f)), ch * 0.5f); dl->AddRect(p0, mx, u32(t.vurgu, 0.5f), ch * 0.5f); dl->AddText({p0.x + 9.0f, p0.y + 4.0f}, u32(t.metin), cipler[i]); const ImVec2 xc{mx.x - 11.0f, p0.y + ch * 0.5f}; const bool xh = ImGui::IsMouseHoveringRect({xc.x - 7, xc.y - 7}, {xc.x + 7, xc.y + 7}); const ImU32 xr = u32(xh ? t.hata : t.metinSoluk); dl->AddLine({xc.x - 3, xc.y - 3}, {xc.x + 3, xc.y + 3}, xr, 1.5f); dl->AddLine({xc.x + 3, xc.y - 3}, {xc.x - 3, xc.y + 3}, xr, 1.5f); if (xh && ImGui::IsMouseClicked(0)) { *silinenIndeks = i; degisti = true; } ImGui::PopID(); } ImGui::SetNextItemWidth(140.0f); if (ImGui::InputTextWithHint("##cipGiris", "+ ekle…", girisTampon, girisBoyut, ImGuiInputTextFlags_EnterReturnsTrue)) { if (girisTampon[0]) { *eklendi = true; degisti = true; } } ImGui::PopID(); return degisti; } // ── Spinner ────────────────────────────────────────────────────────────────── void spinner(const char* kimlik, float cap, float kalinlik) { const Tema& t = tema(); ImGui::PushID(kimlik); const ImVec2 p0 = ImGui::GetCursorScreenPos(); ImGui::Dummy({cap, cap}); ImDrawList* dl = ImGui::GetWindowDrawList(); const ImVec2 c{p0.x + cap * 0.5f, p0.y + cap * 0.5f}; const float r = cap * 0.5f - kalinlik; const float ta = zaman() * 5.0f; const int seg = 32; // izleyen soluk halka dl->AddCircle(c, r, u32(t.cerceve), seg, kalinlik); // dönen neon yay (gradient) const float yay = 2.2f; for (int i = 0; i < seg; ++i) { const float f0 = (float)i / seg, f1 = (float)(i + 1) / seg; const float a0 = ta + f0 * yay, a1 = ta + f1 * yay; const ImVec4 renk = karisim(t.vurgu2, t.vurgu, f1); dl->AddLine({c.x + std::cos(a0) * r, c.y + std::sin(a0) * r}, {c.x + std::cos(a1) * r, c.y + std::sin(a1) * r}, u32(renk, f1), kalinlik); } ImGui::PopID(); } // ── Boş-durum kartı ────────────────────────────────────────────────────────── bool bosDurum(const char* ikon, const char* baslik, const char* aciklama, const char* eylemEtiket) { const Tema& t = tema(); const float gen = ImGui::GetContentRegionAvail().x; bool tiklandi = false; ImGui::BeginGroup(); auto ortala = [&](const char* s, float olcek = 1.0f) { ImGui::PushFont(nullptr, ImGui::GetFontSize() * olcek); const float w = ImGui::CalcTextSize(s).x; ImGui::SetCursorPosX(ImGui::GetCursorPosX() + std::fmax(0.0f, (gen - w) * 0.5f)); ImGui::PopFont(); }; ImGui::Spacing(); if (ikon && ikon[0]) { ortala(ikon, 2.6f); ImGui::PushFont(nullptr, ImGui::GetFontSize() * 2.6f); ImGui::TextColored(t.metinSoluk, "%s", ikon); ImGui::PopFont(); } if (baslik && baslik[0]) { ortala(baslik, 1.15f); ImGui::PushFont(nullptr, ImGui::GetFontSize() * 1.15f); ImGui::TextUnformatted(baslik); ImGui::PopFont(); } if (aciklama && aciklama[0]) { ortala(aciklama); ImGui::TextColored(t.metinSoluk, "%s", aciklama); } if (eylemEtiket && eylemEtiket[0]) { ImGui::Spacing(); const float bw = ImGui::CalcTextSize(eylemEtiket).x + 40.0f; ImGui::SetCursorPosX(ImGui::GetCursorPosX() + std::fmax(0.0f, (gen - bw) * 0.5f)); tiklandi = buton(eylemEtiket, {bw, 0}, ButonTip::Birincil); } ImGui::Spacing(); ImGui::EndGroup(); return tiklandi; } } // namespace fiko::ui