#include "fiko/ui/arac_cubugu.hpp" #include #include #include #include "fiko/cekirdek/cekirdek.hpp" #include "fiko/cekirdek/kayit.hpp" // MenuOge / MenuKaydi #include "fiko/ui/animasyon.hpp" #include "fiko/ui/bilesen.hpp" #include "fiko/ui/tema.hpp" #include "imgui_internal.h" // ============================================================================ // KENAR-SNAP vs ImGui-DOCKING kararı (gerekçe) // ---------------------------------------------------------------------------- // Kendi kenar-snap mantığı seçildi. ImGui docking (DockBuilder/DockNode) pencere // yerleşimi için tasarlanmıştır; ince tek-satır bir toolbar şeridinde: // • grip/tutamaç görünümü ve sürükleme geri bildirimi üzerinde denetim zayıf, // • yatay↔dikey yönelim ve "kenara yapış ama içerikten pay çalma" davranışı // dock-node grid'iyle çakışır (dock alanı içeriği daraltır — biz overlay // istiyoruz), // • snap-zon görselini (neon highlight) FikoUi diliyle boyamak mümkün değil. // Kendi mantığımız: DrawList overlay + ekran kenarına yakınlık testi → tam görsel // ve davranış denetimi, çoklu-viewport bağımsız, tema token'la birebir kimlik. // Ana viewport work-area kenarlarına snap ediyoruz (SetNextWindowViewport ile ana // viewport'a sabitlenir; ayrı OS penceresine sürüklenmesi gerekmez — toolbar HUD'dur). // ============================================================================ namespace fiko::ui { namespace { constexpr float kSnapEsik = 46.0f; // kenara bu mesafede snap adayı belirir constexpr float kButonBoy = 34.0f; // buton kare kenarı constexpr float kBosluk = 4.0f; // butonlar arası constexpr float kIcPay = 6.0f; // şerit iç payı constexpr float kTutamac = 16.0f; // sürükleme tutamacı kalınlığı ImVec2 workMin() { const ImGuiViewport* v = ImGui::GetMainViewport(); return v->WorkPos; } ImVec2 workMax() { const ImGuiViewport* v = ImGui::GetMainViewport(); return {v->WorkPos.x + v->WorkSize.x, v->WorkPos.y + v->WorkSize.y}; } bool dikeyMi(const AracCubugu& c) { if (c.kenar == DockKenar::Sol || c.kenar == DockKenar::Sag) return true; if (c.kenar == DockKenar::Ust || c.kenar == DockKenar::Alt) return false; return c.yonelim == Yonelim::Dikey; // serbest → kullanıcı yönelimi } // Görünür (workbench filtresi geçmiş + kullanıcı açık) buton sayısı. int etkinButonSayisi(const AracCubugu& c) { return (int)c.butonlar.size(); } // Şerit boyutu (yönelim + buton sayısına göre). ImVec2 cubukBoyut(const AracCubugu& c) { const int n = etkinButonSayisi(c); const float uzun = kIcPay * 2 + kTutamac + n * kButonBoy + (n > 0 ? (n - 1) * kBosluk : 0); const float kisa = kIcPay * 2 + kButonBoy; return dikeyMi(c) ? ImVec2{kisa, uzun} : ImVec2{uzun, kisa}; } // Dock kenarına göre sol-üst köşe (ekran koord). Serbest ise serbestKonum. ImVec2 cubukKonum(const AracCubugu& c, ImVec2 boyut) { if (c.kenar == DockKenar::Serbest) return c.serbestKonum; const ImVec2 mn = workMin(), mx = workMax(); switch (c.kenar) { case DockKenar::Sol: return {mn.x + kBosluk, mn.y + (mx.y - mn.y - boyut.y) * 0.5f}; case DockKenar::Sag: return {mx.x - boyut.x - kBosluk, mn.y + (mx.y - mn.y - boyut.y) * 0.5f}; case DockKenar::Ust: return {mn.x + (mx.x - mn.x - boyut.x) * 0.5f, mn.y + kBosluk}; case DockKenar::Alt: return {mn.x + (mx.x - mn.x - boyut.x) * 0.5f, mx.y - boyut.y - kBosluk}; default: return c.serbestKonum; } } } // namespace // ---------------------------------------------------------------- yönetim ---- AracCubugu& AracCubuguSistemi::ekle(AracCubugu c) { if (AracCubugu* v = bul(c.kimlik)) { *v = std::move(c); return *v; } cubuklar_.push_back(std::move(c)); return cubuklar_.back(); } AracCubugu* AracCubuguSistemi::bul(const std::string& kimlik) { for (auto& c : cubuklar_) if (c.kimlik == kimlik) return &c; return nullptr; } void AracCubuguSistemi::sil(const std::string& kimlik) { std::erase_if(cubuklar_, [&](const AracCubugu& c) { return c.kimlik == kimlik; }); } void AracCubuguSistemi::gorunurluk(const std::string& kimlik, bool g) { if (AracCubugu* c = bul(kimlik)) c->gorunur = g; } void AracCubuguSistemi::workbenchDegisti(const std::string& wb) { aktifWorkbench_ = wb; for (auto& c : cubuklar_) { if (c.workbenchler.empty()) { c._dahiliGorunur = true; continue; } c._dahiliGorunur = std::find(c.workbenchler.begin(), c.workbenchler.end(), wb) != c.workbenchler.end(); } } // ------------------------------------------------------- registry köprüsü ---- void AracCubuguSistemi::menudenAdaylar(Cekirdek& c, const std::string& filtreWb) { Cekirdek* cp = &c; for (const MenuOge& o : c.menuler().hepsi()) { if (!filtreWb.empty() && !o.workbench.empty() && o.workbench != filtreWb) continue; AracAday a; a.simgeMetin = o.simge.empty() ? "◻" : o.simge; // struct sahiplenir (pointer sızıntısı yok) a.etiket = o.ad; a.kaynak = o.grup + "/" + o.ad; // Merkezi dispatch (lisans gate + KomutCalisti) üzerinden — doğrudan calistir DEĞİL. if (o.calistir) a.calistir = [cp, oge = o] { cp->komutCalistir(oge, "toolbar"); }; if (o.etkinMi) a.etkinMi = [cp, etkinMi = o.etkinMi] { return etkinMi(*cp); }; adaylar_.push_back(std::move(a)); } } void AracCubuguSistemi::menudenDoldur(Cekirdek& c, const std::string& cubukKimlik, const std::string& grup) { AracCubugu* cb = bul(cubukKimlik); if (!cb) return; Cekirdek* cp = &c; cb->butonlar.clear(); for (const MenuOge& o : c.menuler().hepsi()) { if (o.grup != grup) continue; AracButon b; b.simgeMetin = o.simge.empty() ? "◻" : o.simge; b.etiket = o.ad; b.kaynak = o.grup + "/" + o.ad; // Merkezi dispatch (lisans gate + KomutCalisti) üzerinden — doğrudan calistir DEĞİL. if (o.calistir) b.calistir = [cp, oge = o] { cp->komutCalistir(oge, "toolbar"); }; if (o.etkinMi) b.etkinMi = [cp, etkinMi = o.etkinMi] { return etkinMi(*cp); }; cb->butonlar.push_back(std::move(b)); } } // ---------------------------------------------------------------- çizim ---- DockKenar AracCubuguSistemi::kenarTahmin(ImVec2 f) const { const ImVec2 mn = workMin(), mx = workMax(); const float dSol = f.x - mn.x, dSag = mx.x - f.x; const float dUst = f.y - mn.y, dAlt = mx.y - f.y; const float en = std::min(std::min(dSol, dSag), std::min(dUst, dAlt)); if (en > kSnapEsik) return DockKenar::Serbest; if (en == dSol) return DockKenar::Sol; if (en == dSag) return DockKenar::Sag; if (en == dUst) return DockKenar::Ust; return DockKenar::Alt; } void AracCubuguSistemi::snapOnizleCiz(DockKenar k) const { if (k == DockKenar::Serbest) return; const Tema& t = tema(); const ImVec2 mn = workMin(), mx = workMax(); ImVec2 a, b; const float kal = 56.0f; switch (k) { case DockKenar::Sol: a = mn; b = {mn.x + kal, mx.y}; break; case DockKenar::Sag: a = {mx.x - kal, mn.y}; b = mx; break; case DockKenar::Ust: a = mn; b = {mx.x, mn.y + kal}; break; case DockKenar::Alt: a = {mn.x, mx.y - kal}; b = mx; break; default: return; } ImDrawList* dl = ImGui::GetForegroundDrawList(); const float p = 0.5f + 0.5f * nabiz(1.6f); dl->AddRectFilled(a, b, u32(t.vurgu, 0.10f + 0.10f * p), t.yuvarlak); glowCerceve(dl, a, b, t.vurgu, t.yuvarlak, 1.0f + p); dl->AddRect(a, b, u32(t.vurgu, 0.6f), t.yuvarlak, 0, 2.0f); } void AracCubuguSistemi::ciz() { for (auto& c : cubuklar_) { if (!c.gorunur || !c._dahiliGorunur) continue; cubukCiz(c); } // Sürükleme sırasında snap önizleme (en üstte). if (!suruklenen_.empty() && snapAdayi_ != DockKenar::Serbest) snapOnizleCiz(snapAdayi_); } void AracCubuguSistemi::cubukCiz(AracCubugu& c) { const Tema& t = tema(); const bool dikey = dikeyMi(c); ImVec2 boyut = cubukBoyut(c); ImVec2 konum = cubukKonum(c, boyut); // Ana viewport'a sabitle (HUD): ayrı OS penceresine kaçmasın, work-area içinde kalsın. ImGui::SetNextWindowViewport(ImGui::GetMainViewport()->ID); ImGui::SetNextWindowPos(konum); ImGui::SetNextWindowSize(boyut); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, {0, 0}); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4{0, 0, 0, 0}); char pencereId[128]; std::snprintf(pencereId, sizeof(pencereId), "##arac_%s", c.kimlik.c_str()); const ImGuiWindowFlags bayrak = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoNav; ImGui::Begin(pencereId, nullptr, bayrak); // Toolbar HER ZAMAN görünür kalır: her kare display-front'a al (odak ÇALMADAN). // Eski NoBringToFrontOnFocus bayrağı toolbar'ı 3D/panel pencerelerinin arkasına // gömüyordu (kendini asla öne çıkaramıyordu) — profesyonel MDI'da toolbar üsttedir. ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow()); ImDrawList* dl = ImGui::GetWindowDrawList(); const ImVec2 mn = ImGui::GetWindowPos(); const ImVec2 mx = {mn.x + boyut.x, mn.y + boyut.y}; // Zemin: gradient panel + neon kenar + hafif dış parlama (FikoUi kimliği). const int vtx0 = dl->VtxBuffer.Size; dl->AddRectFilled(mn, mx, u32(karisim(t.panel, t.arka, 0.15f), 0.96f), t.yuvarlak); ImGui::ShadeVertsLinearColorGradientKeepAlpha( dl, vtx0, dl->VtxBuffer.Size, mn, {mn.x, mx.y}, u32(karisim(t.panel, t.vurgu, 0.05f), 0.96f), u32(karisim(t.panel, t.arka, 0.30f), 0.96f)); glowCerceve(dl, mn, mx, t.vurgu, t.yuvarlak, 0.5f); dl->AddRect(mn, mx, u32(t.kenar, 0.9f), t.yuvarlak, 0, 1.0f); // ---- Tutamaç (sürükleme) ---- const ImVec2 tutMin = mn; const ImVec2 tutMax = dikey ? ImVec2{mx.x, mn.y + kTutamac} : ImVec2{mn.x + kTutamac, mx.y}; ImGui::SetCursorScreenPos(tutMin); ImGui::InvisibleButton("##tutamac", {tutMax.x - tutMin.x, tutMax.y - tutMin.y}); const bool tutHover = ImGui::IsItemHovered(); const bool tutBasili = ImGui::IsItemActive(); const float th = animf(ImGui::GetItemID(), (tutHover || tutBasili) ? 1.0f : 0.0f, 14.0f); // Grip noktaları (2 sıra) — CATIA/pro toolbar tutamacı. const ImU32 gripRenk = u32(karisim(t.metinSoluk, t.vurgu, th), 0.5f + 0.4f * th); const ImVec2 tc{(tutMin.x + tutMax.x) * 0.5f, (tutMin.y + tutMax.y) * 0.5f}; for (int i = -2; i <= 2; ++i) for (int j = 0; j < 2; ++j) { const float o = (j == 0 ? -2.5f : 2.5f); const ImVec2 p = dikey ? ImVec2{tc.x + o, tc.y + i * 4.0f} : ImVec2{tc.x + i * 4.0f, tc.y + o}; dl->AddCircleFilled(p, 1.3f, gripRenk); } if (tutHover) ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll); // Sürükleme başlat/sürdür/bitir if (tutBasili && ImGui::IsMouseDragging(ImGuiMouseButton_Left)) { if (suruklenen_ != c.kimlik) { suruklenen_ = c.kimlik; tutamacOfset_ = {ImGui::GetMousePos().x - mn.x, ImGui::GetMousePos().y - mn.y}; } const ImVec2 f = ImGui::GetMousePos(); c.serbestKonum = {f.x - tutamacOfset_.x, f.y - tutamacOfset_.y}; c.kenar = DockKenar::Serbest; // çekilince ayrıl (floating) snapAdayi_ = kenarTahmin(f); } else if (suruklenen_ == c.kimlik) { // bırakıldı if (snapAdayi_ != DockKenar::Serbest) { c.kenar = snapAdayi_; c.yonelim = (snapAdayi_ == DockKenar::Sol || snapAdayi_ == DockKenar::Sag) ? Yonelim::Dikey : Yonelim::Yatay; } suruklenen_.clear(); snapAdayi_ = DockKenar::Serbest; } // ---- Kapat düğmesi (✕) — tutamacın uzak ucunda ---- if (c.kapatilabilir) { const float sz = 14.0f; const ImVec2 kMin = dikey ? ImVec2{mx.x - sz - 4, mn.y + 2} : ImVec2{mn.x + 2, mx.y - sz - 4}; ImGui::SetCursorScreenPos(kMin); ImGui::PushID("kapat"); if (ImGui::InvisibleButton("##x", {sz, sz})) c.gorunur = false; const bool kh = ImGui::IsItemHovered(); ImGui::PopID(); const ImVec2 kc{kMin.x + sz * 0.5f, kMin.y + sz * 0.5f}; const ImU32 kr = u32(kh ? t.hata : t.metinSoluk, kh ? 1.0f : 0.7f); dl->AddLine({kc.x - 3, kc.y - 3}, {kc.x + 3, kc.y + 3}, kr, 1.6f); dl->AddLine({kc.x + 3, kc.y - 3}, {kc.x - 3, kc.y + 3}, kr, 1.6f); if (kh) ImGui::SetTooltip("Kapat"); } // ---- Butonlar ---- float ilk = dikey ? mn.y + kTutamac + kIcPay : mn.x + kTutamac + kIcPay; const float sabit = dikey ? mn.x + kIcPay : mn.y + kIcPay; for (size_t i = 0; i < c.butonlar.size(); ++i) { AracButon& b = c.butonlar[i]; const ImVec2 bMin = dikey ? ImVec2{sabit, ilk} : ImVec2{ilk, sabit}; const ImVec2 bMax = {bMin.x + kButonBoy, bMin.y + kButonBoy}; ImGui::SetCursorScreenPos(bMin); ImGui::PushID((int)i); const bool etkin = !b.etkinMi || b.etkinMi(); const bool tik = ImGui::InvisibleButton("##b", {kButonBoy, kButonBoy}); const ImGuiID bid = ImGui::GetItemID(); const bool hover = etkin && ImGui::IsItemHovered(); const float h = animf(bid, hover ? 1.0f : 0.0f, 14.0f); const float pr = yay(bid + 7, (etkin && ImGui::IsItemActive()) ? 1.0f : 0.0f); const float d = 1.5f * pr; const ImVec2 rMin{bMin.x + d, bMin.y + d}, rMax{bMax.x - d, bMax.y - d}; if (hover) glowCerceve(dl, rMin, rMax, t.vurgu, t.yuvarlak, h); dl->AddRectFilled(rMin, rMax, u32(karisim(t.cerceve, t.vurgu, 0.30f * h), etkin ? 1.0f : 0.35f), t.yuvarlak); dl->AddRect(rMin, rMax, u32(t.vurgu, 0.12f + 0.5f * h), t.yuvarlak, 0, 1.0f); const ImU32 sr = u32(etkin ? karisim(t.metinSoluk, t.metin, 0.4f + 0.6f * h) : t.metinSoluk, etkin ? 1.0f : 0.4f); simgeCiz(dl, b.etkinSimge(), {(rMin.x + rMax.x) * 0.5f, (rMin.y + rMax.y) * 0.5f}, kButonBoy * 0.5f, sr); if (hover && !b.etiket.empty()) ImGui::SetTooltip("%s", b.etiket.c_str()); if (tik && etkin && b.calistir) b.calistir(); ImGui::PopID(); ilk += kButonBoy + kBosluk; } // ---- Sağ-tık bağlam menüsü (özelleştir) ---- if (ImGui::IsWindowHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) ImGui::OpenPopup("##arac_baglam"); baglamMenu(c); ImGui::End(); ImGui::PopStyleColor(); ImGui::PopStyleVar(2); } void AracCubuguSistemi::baglamMenu(AracCubugu& c) { if (!ImGui::BeginPopup("##arac_baglam")) return; const Tema& t = tema(); ImGui::PushStyleColor(ImGuiCol_Text, t.vurgu); ImGui::TextUnformatted(c.baslik.empty() ? "Araç Çubuğu" : c.baslik.c_str()); ImGui::PopStyleColor(); ImGui::Separator(); // Yönelim if (ImGui::MenuItem("Yatay", nullptr, c.yonelim == Yonelim::Yatay)) { c.yonelim = Yonelim::Yatay; if (c.kenar == DockKenar::Sol || c.kenar == DockKenar::Sag) c.kenar = DockKenar::Serbest; } if (ImGui::MenuItem("Dikey", nullptr, c.yonelim == Yonelim::Dikey)) { c.yonelim = Yonelim::Dikey; if (c.kenar == DockKenar::Ust || c.kenar == DockKenar::Alt) c.kenar = DockKenar::Serbest; } ImGui::Separator(); // Dock kenarı hızlı seçim if (ImGui::BeginMenu("Yapıştır")) { struct { const char* ad; DockKenar k; } sec[] = { {"Sol", DockKenar::Sol}, {"Sağ", DockKenar::Sag}, {"Üst", DockKenar::Ust}, {"Alt", DockKenar::Alt}, {"Serbest", DockKenar::Serbest}}; for (auto& s : sec) if (ImGui::MenuItem(s.ad, nullptr, c.kenar == s.k)) { c.kenar = s.k; if (s.k == DockKenar::Sol || s.k == DockKenar::Sag) c.yonelim = Yonelim::Dikey; else if (s.k == DockKenar::Ust || s.k == DockKenar::Alt) c.yonelim = Yonelim::Yatay; } ImGui::EndMenu(); } ImGui::Separator(); // Buton çıkar (mevcut butonlar) if (ImGui::BeginMenu("Buton Çıkar", !c.butonlar.empty())) { for (size_t i = 0; i < c.butonlar.size(); ++i) { ImGui::PushID((int)i); if (ImGui::MenuItem(c.butonlar[i].etiket.c_str())) { c.butonlar.erase(c.butonlar.begin() + i); ImGui::PopID(); break; } ImGui::PopID(); } ImGui::EndMenu(); } // Buton ekle (aday havuzundan; toolbar'da olmayanlar) if (ImGui::BeginMenu("Buton Ekle", !adaylar_.empty())) { for (const AracAday& a : adaylar_) { const bool zaten = std::any_of(c.butonlar.begin(), c.butonlar.end(), [&](const AracButon& b) { return b.kaynak == a.kaynak; }); if (zaten) continue; if (ImGui::MenuItem(a.etiket.c_str())) { AracButon b; b.simge = a.simge; b.simgeMetin = a.simgeMetin; b.etiket = a.etiket; b.kaynak = a.kaynak; b.calistir = a.calistir; b.etkinMi = a.etkinMi; c.butonlar.push_back(std::move(b)); } } ImGui::EndMenu(); } ImGui::Separator(); if (c.kapatilabilir && ImGui::MenuItem("Kapat")) c.gorunur = false; ImGui::EndPopup(); } // -------------------------------------------------------------- kalıcılık ---- // Basit metin biçim: her satır "kimlik|kenar|yonelim|x|y|gorunur". Butonlar kod // tarafında tanımlı → yalnız yerleşim/görünürlük saklanır. Ayar sistemi köprüsü hazır. std::string AracCubuguSistemi::durumDisaAktar() const { std::string s; char buf[192]; for (const auto& c : cubuklar_) { std::snprintf(buf, sizeof(buf), "%s|%d|%d|%.0f|%.0f|%d\n", c.kimlik.c_str(), (int)c.kenar, (int)c.yonelim, c.serbestKonum.x, c.serbestKonum.y, c.gorunur ? 1 : 0); s += buf; } return s; } void AracCubuguSistemi::durumIceAktar(const std::string& veri) { size_t p = 0; while (p < veri.size()) { size_t son = veri.find('\n', p); if (son == std::string::npos) son = veri.size(); std::string sat = veri.substr(p, son - p); p = son + 1; if (sat.empty()) continue; char kim[96]; int kenar = 0, yon = 0, gor = 1; float x = 0, y = 0; if (std::sscanf(sat.c_str(), "%95[^|]|%d|%d|%f|%f|%d", kim, &kenar, &yon, &x, &y, &gor) >= 3) { if (AracCubugu* c = bul(kim)) { c->kenar = (DockKenar)kenar; c->yonelim = (Yonelim)yon; c->serbestKonum = {x, y}; c->gorunur = gor != 0; } } } } } // namespace fiko::ui