#include "ayarlar_penceresi.hpp" #include "lisans_yukle.hpp" // lisansAyarBolumu (canlı lisans durumu) #include #include #include #include #include #include #include "dosya_islemleri.hpp" #include "fiko/ayar/ayar_sistemi.hpp" #include "fiko/ui/agac_simge_kayit.hpp" #include "fiko/ui/pencere_cerceve.hpp" #include "fiko/ui/tema.hpp" #include "imgui.h" namespace fiko { namespace { using ayar::AyarDeger; using ayar::AyarSistemi; using ayar::AyarTanim; using ayar::AyarTip; // ASCII büyük/küçük duyarsız alt-dize (Türkçe çok-bayt harfler bayt-eş kalır — yeterli). bool icerir(std::string_view kaynak, std::string_view aranan) { if (aranan.empty()) return true; if (aranan.size() > kaynak.size()) return false; for (size_t i = 0; i + aranan.size() <= kaynak.size(); ++i) { size_t j = 0; while (j < aranan.size() && std::tolower((unsigned char)kaynak[i + j]) == std::tolower((unsigned char)aranan[j])) ++j; if (j == aranan.size()) return true; } return false; } bool aramayaUyar(const AyarTanim& t, const char* arama) { return icerir(t.ad, arama) || icerir(t.kimlik, arama) || icerir(t.aciklama, arama) || icerir(t.kategori, arama); } // Tek ayar satırının widget'ı. Değişiklik ANINDA uygulanır (t.ata); kalıcılık/olay // köprüsü (s.bildir) sürükleme bitince tetiklenir → kare-başı disk yazımı olmaz. void ayarWidget(AyarSistemi& s, const AyarTanim& t) { AyarDeger d = t.al(); bool degisti = false; bool aninBildir = false; // tek-tık widget'lar (sürükleme yok) hemen kalıcılaşır ImGui::SetNextItemWidth(-FLT_MIN); switch (t.tip) { case AyarTip::Bool: degisti = ImGui::Checkbox("##v", &d.b); aninBildir = degisti; break; case AyarTip::Int: { int v = (int)d.i; degisti = (t.min < t.maks) ? ImGui::SliderInt("##v", &v, (int)t.min, (int)t.maks) : ImGui::DragInt("##v", &v); if (degisti) d.i = v; break; } case AyarTip::F32: case AyarTip::F64: { f32 v = (f32)d.f; degisti = (t.min < t.maks) ? ImGui::SliderFloat("##v", &v, (f32)t.min, (f32)t.maks, "%.3f") : ImGui::DragFloat("##v", &v, 0.01f, 0.0f, 0.0f, "%.3f"); if (degisti) d.f = v; break; } case AyarTip::Renk: degisti = ImGui::ColorEdit3("##v", &d.renk.x, ImGuiColorEditFlags_Float); break; case AyarTip::Secenek: if (ImGui::BeginCombo("##v", d.metin.c_str())) { ImGui::PushStyleColor(ImGuiCol_Text, ui::tema().metin); for (const std::string& sec : t.secenekler) { const bool secili = (sec == d.metin); if (ImGui::Selectable(sec.c_str(), secili) && !secili) { d.metin = sec; degisti = aninBildir = true; } if (secili) ImGui::SetItemDefaultFocus(); } ImGui::PopStyleColor(); ImGui::EndCombo(); } break; case AyarTip::Metin: { char buf[260]; std::snprintf(buf, sizeof(buf), "%s", d.metin.c_str()); // Simge ayarları ("simge.*"): canlı önizleme (emoji/doku) + Dosya Seç butonu. if (t.kimlik.rfind("simge.", 0) == 0) { const std::string anah = t.kimlik.substr(6); const ui::Simge pv = ui::AgacSimgeKayit::global().onizleSimge(anah.c_str()); const float fh = ImGui::GetFrameHeight(); if (pv.doku) ImGui::Image(pv.doku, {fh, fh}); else { ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted(pv.glif && pv.glif[0] ? pv.glif : "·"); } ImGui::SameLine(); ImGui::SetNextItemWidth(-(fh + ImGui::GetStyle().ItemSpacing.x + 2.0f)); if (ImGui::InputTextWithHint("##v", "emoji veya .svg/.png yolu", buf, sizeof(buf))) { d.metin = buf; degisti = true; } ImGui::SameLine(); if (ImGui::Button("📁", {fh, fh})) { const std::string yol = resimDosyaSec((HWND)ImGui::GetMainViewport()->PlatformHandleRaw); if (!yol.empty()) { d.metin = yol; degisti = aninBildir = true; } } if (ImGui::IsItemHovered()) ImGui::SetTooltip("Dosya seç (SVG / PNG)"); } else if (ImGui::InputText("##v", buf, sizeof(buf))) { d.metin = buf; degisti = true; } break; } } if (degisti) t.ata(d); // anında uygula (canlı önizleme) if (aninBildir || (ImGui::IsItemDeactivatedAfterEdit())) s.bildir(t.kimlik); // kalıcı yaz + olay (sürükleme bitiminde bir kez) } // Ad + widget + sıfırla sütunlu satır (tablo hücreleri çağıran tarafından açılır). void ayarSatiri(AyarSistemi& s, const AyarTanim& t) { const ui::Tema& tm = ui::tema(); ImGui::PushID(t.kimlik.c_str()); ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::AlignTextToFramePadding(); const bool farkli = s.degistiMi(t); if (farkli) { ImGui::TextColored(tm.uyari, "◆"); ImGui::SameLine(0, 4); } ImGui::TextUnformatted(t.ad.c_str()); if (!t.aciklama.empty() && ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort)) ImGui::SetTooltip("%s\n(%s)", t.aciklama.c_str(), t.kimlik.c_str()); ImGui::TableNextColumn(); ayarWidget(s, t); ImGui::TableNextColumn(); if (farkli && ImGui::SmallButton("↺")) s.sifirla(t.kimlik); if (farkli && ImGui::IsItemHovered()) ImGui::SetTooltip("Varsayılana sıfırla"); ImGui::PopID(); } // Bir ayar listesini hizalı tabloda çiz (başlık = kategori adı; bosBaslik=başlıksız). void ayarTablosu(AyarSistemi& s, const std::vector& liste, const char* tabloKimlik) { if (!ImGui::BeginTable(tabloKimlik, 3, ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_PadOuterX)) return; ImGui::TableSetupColumn("ad", ImGuiTableColumnFlags_WidthStretch, 0.42f); ImGui::TableSetupColumn("deger", ImGuiTableColumnFlags_WidthStretch, 0.58f); ImGui::TableSetupColumn("sifirla", ImGuiTableColumnFlags_WidthFixed, 22.0f); for (const AyarTanim* t : liste) ayarSatiri(s, *t); ImGui::EndTable(); } // "Simgeler" kategorisi SAĞDA sekmeli çizilir: Feature / GSD / Genel (kimlik önekine // göre). Sol listede tek "Simgeler" düğümü kalır; 100+ tip alt-sekmelerde toplanır. void simgeSekmeleriCiz(AyarSistemi& s, const std::vector& liste) { struct Sekme { const char* etiket; const char* onek; // nullptr = "diğerleri" (feature./gsd. olmayan) }; static const Sekme sekmeler[] = { {"⬒ Feature", "simge.feature."}, {"〰 GSD", "simge.gsd."}, {"⚙ Genel", nullptr}, }; if (!ImGui::BeginTabBar("##simgeSekmeleri", ImGuiTabBarFlags_FittingPolicyScroll)) return; for (const Sekme& sk : sekmeler) { if (!ImGui::BeginTabItem(sk.etiket)) continue; std::vector alt; alt.reserve(liste.size()); for (const AyarTanim* t : liste) { const bool feat = t->kimlik.rfind("simge.feature.", 0) == 0; const bool gsd = t->kimlik.rfind("simge.gsd.", 0) == 0; const bool uyar = sk.onek ? (t->kimlik.rfind(sk.onek, 0) == 0) : (!feat && !gsd); if (uyar) alt.push_back(t); } // Alt-sekme içeriği kendi içinde kaydırılır; altta "sıfırla" + ipucu için yer bırak. const float footer = ImGui::GetFrameHeightWithSpacing() + ImGui::GetTextLineHeightWithSpacing() + 6.0f; ImGui::BeginChild("##simgeAlt", {0, -footer}); ayarTablosu(s, alt, "tblSimgeSekme"); ImGui::EndChild(); ImGui::EndTabItem(); } ImGui::EndTabBar(); } // Kategori adına göre kompakt ikon (ikon-sekme şeridi). Bilinmeyen → jenerik ⚙. const char* kategoriIkon(const std::string& kat) { struct Esle { const char* ad; const char* ikon; }; static const Esle harita[] = { {"Görünüm", "🎨"}, {"Performans", "🚀"}, {"Aydınlatma", "💡"}, {"Izgara & Snap", "🔲"}, {"Sketcher", "✏"}, {"Kamera", "🎥"}, {"Hızlı 3D", "🧊"}, {"Döküm", "🏭"}, {"Seçim", "🎯"}, {"Seçim Yığını", "🗂"}, {"Simgeler", "🎨"}, {"Lisans", "🔒"}, {"Pencere", "🪟"}, {"Toolbar", "🧰"}, }; for (const Esle& e : harita) if (kat == e.ad) return e.ikon; return "⚙"; } } // namespace void ayarlarPenceresiCiz(ayar::AyarSistemi& s, AyarlarPencereDurum& d) { if (!d.acik) return; const ui::Tema& tm = ui::tema(); ui::Pencere pen{"⚙ Ayarlar###ayarlar", &d.acik, {.boyut = {640, 460}, .minBoyut = {440, 320}, .pinDurum = &d.ustte}}; if (!pen) return; ImGui::SetNextItemWidth(-FLT_MIN); ImGui::InputTextWithHint("##ara", "🔍 Ayar ara…", d.arama, sizeof(d.arama)); const bool aramada = d.arama[0] != '\0'; ImGui::Spacing(); const std::vector kategoriler = s.kategoriler(); if (kategoriler.empty()) { ImGui::TextDisabled("Kayıtlı ayar yok."); return; } if (d.seciliKategori >= (int)kategoriler.size()) d.seciliKategori = 0; // ---- Sol: ikonlu kategori "sekme" şeridi ---- ImGui::BeginChild("kategoriler", {172, 0}, ImGuiChildFlags_Borders); for (int i = 0; i < (int)kategoriler.size(); ++i) { const bool secili = !aramada && i == d.seciliKategori; char etiket[128]; std::snprintf(etiket, sizeof(etiket), "%s %s", kategoriIkon(kategoriler[i]), kategoriler[i].c_str()); if (secili) ImGui::PushStyleColor(ImGuiCol_Text, tm.vurgu); if (ImGui::Selectable(etiket, secili)) { d.seciliKategori = i; d.arama[0] = '\0'; // kategoriye tıklama aramayı bitirir } if (secili) ImGui::PopStyleColor(); } ImGui::EndChild(); ImGui::SameLine(); // ---- Sağ: seçili kategori (veya arama sonuçları) ---- ImGui::BeginChild("icerik", {0, 0}); if (aramada) { // Eşleşenler kategori-başlıklı gruplar halinde. bool bulundu = false; for (const std::string& kat : kategoriler) { std::vector liste; s.gez(kat, [&](const AyarTanim& t) { if (aramayaUyar(t, d.arama)) liste.push_back(&t); }); if (liste.empty()) continue; bulundu = true; ImGui::TextColored(tm.vurgu, "%s %s", kategoriIkon(kat), kat.c_str()); ayarTablosu(s, liste, ("tbl_" + kat).c_str()); ImGui::Spacing(); } if (!bulundu) ImGui::TextDisabled("Eşleşen ayar yok: %s", d.arama); } else { const std::string& kat = kategoriler[(size_t)d.seciliKategori]; std::vector liste; int farkli = 0; s.gez(kat, [&](const AyarTanim& t) { liste.push_back(&t); if (s.degistiMi(t)) ++farkli; }); ImGui::TextColored(tm.vurgu, "%s %s", kategoriIkon(kat), kat.c_str()); if (farkli > 0) { ImGui::SameLine(); ImGui::TextColored(tm.uyari, "— %d değişti", farkli); } ImGui::Separator(); if (kat == "Simgeler") { simgeSekmeleriCiz(s, liste); // sağda sekmeli (Feature/GSD/Genel) } else { ayarTablosu(s, liste, "tblKategori"); // Lisans sayfası: ayarların ALTINDA canlı lisans durumu (paketler, kalan süre, // kota sayaçları). Salt-okuma — yönetim (yükle/katalog) Komut Denetim'de. if (kat == "Lisans") lisansAyarBolumu(); } ImGui::Spacing(); ImGui::BeginDisabled(farkli == 0); if (ImGui::Button("↺ Bu kategoriyi varsayılana döndür", {-FLT_MIN, 0})) s.kategoriSifirla(kat); ImGui::EndDisabled(); ImGui::TextColored(tm.metinSoluk, "Değişiklikler anında uygulanır ve kalıcıdır."); } ImGui::EndChild(); } } // namespace fiko