diff --git a/src/WeatherEditor/EditorWindow.cpp b/src/WeatherEditor/EditorWindow.cpp index 126e5a064c..083f228a79 100644 --- a/src/WeatherEditor/EditorWindow.cpp +++ b/src/WeatherEditor/EditorWindow.cpp @@ -14,7 +14,7 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(EditorWindow::Settings::PaletteColorEntry, r, g, b, useCount, lastUsedTime, isFavorite) NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(EditorWindow::Settings::PaletteFavoriteColor, hasValue, r, g, b) -NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(EditorWindow::Settings, recordMarkers, markedRecords, autoApplyChanges, useTextButtons, enableInheritFromParent, editorUIScale, favoriteWidgets, recentWidgets, maxRecentWidgets, rememberOpenWidgets, lastOpenWidgets, showViewport, paletteColors, paletteFavorites) +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(EditorWindow::Settings, recordMarkers, markedRecords, autoApplyChanges, useTextButtons, enableInheritFromParent, editorUIScale, favoriteWidgets, recentWidgets, maxRecentWidgets, showViewport, widgetTypeSizes, paletteColors, paletteFavorites) void DrawIconStar(ImVec2 center, float radius, ImU32 color, bool filled) { @@ -178,11 +178,15 @@ void EditorWindow::ShowObjectsWindow() // Create a table with two columns if (ImGui::BeginTable("ObjectTable", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersInner)) { // Fixed categories column, objects column fills remaining width - ImGui::TableSetupColumn("Categories", ImGuiTableColumnFlags_WidthFixed, 180.0f * Util::GetUIScale()); + const float categoriesWidth = 180.0f * Util::GetUIScale(); + ImGui::TableSetupColumn("Categories", ImGuiTableColumnFlags_WidthFixed, categoriesWidth); ImGui::TableSetupColumn("Objects", ImGuiTableColumnFlags_WidthStretch); ImGui::TableNextRow(); + if (resetLayout) + ImGui::TableSetColumnWidth(0, categoriesWidth); + // Left column: Categories ImGui::TableSetColumnIndex(0); @@ -866,13 +870,8 @@ void EditorWindow::ShowWidgetWindow() } // Draw all open widgets using WidgetFactory template - WidgetFactory::DrawOpenWidgets(weatherWidgets, lastFocusedWidget); - WidgetFactory::DrawOpenWidgets(lightingTemplateWidgets, lastFocusedWidget); - WidgetFactory::DrawOpenWidgets(imageSpaceWidgets, lastFocusedWidget); - WidgetFactory::DrawOpenWidgets(volumetricLightingWidgets, lastFocusedWidget); - WidgetFactory::DrawOpenWidgets(precipitationWidgets, lastFocusedWidget); - WidgetFactory::DrawOpenWidgets(lensFlareWidgets, lastFocusedWidget); - WidgetFactory::DrawOpenWidgets(referenceEffectWidgets, lastFocusedWidget); + for (auto* collection : GetWidgetCollections()) + WidgetFactory::DrawOpenWidgets(*collection, lastFocusedWidget); // Draw current cell lighting widget if open if (currentCellLightingWidget && currentCellLightingWidget->IsOpen()) { @@ -916,55 +915,25 @@ void EditorWindow::RenderUI() // Save individual widgets submenu if (ImGui::BeginMenu("Save")) { - bool hasOpenWidgets = false; - - // Weather widgets - for (auto& widget : weatherWidgets) { - if (widget->IsOpen()) { - hasOpenWidgets = true; - if (ImGui::MenuItem(std::format("Save {}", widget->GetEditorID()).c_str())) { - widget->Save(); - } - } - } - - // Lighting Template widgets - for (auto& widget : lightingTemplateWidgets) { - if (widget->IsOpen()) { - hasOpenWidgets = true; - if (ImGui::MenuItem(std::format("Save {}", widget->GetEditorID()).c_str())) { - widget->Save(); - } - } - } - - // ImageSpace widgets - for (auto& widget : imageSpaceWidgets) { - if (widget->IsOpen()) { - hasOpenWidgets = true; - if (ImGui::MenuItem(std::format("Save {}", widget->GetEditorID()).c_str())) { - widget->Save(); - } - } + bool hasOpen = false; + for (auto* collection : GetWidgetCollections()) + hasOpen = WidgetFactory::DrawSaveWidgetMenuItems(*collection, hasOpen); + + if (currentCellLightingWidget && currentCellLightingWidget->IsOpen()) { + hasOpen = true; + if (ImGui::MenuItem(currentCellLightingWidget->GetEditorID().c_str())) + currentCellLightingWidget->Save(); } - if (!hasOpenWidgets) { + if (!hasOpen) ImGui::TextDisabled("No open widgets"); - } ImGui::EndMenu(); } ImGui::Separator(); - if (ImGui::MenuItem("Close All Weather Widgets")) { - for (auto& widget : weatherWidgets) widget->SetOpen(false); - } - if (ImGui::MenuItem("Close All Lighting Widgets")) { - for (auto& widget : lightingTemplateWidgets) widget->SetOpen(false); - } - if (ImGui::MenuItem("Close All ImageSpace Widgets")) { - for (auto& widget : imageSpaceWidgets) widget->SetOpen(false); - } + for (auto* collection : GetWidgetCollections()) + WidgetFactory::DrawCloseAllMenuItem(*collection); ImGui::EndMenu(); } if (ImGui::BeginMenu("Settings")) { @@ -1014,12 +983,7 @@ void EditorWindow::RenderUI() if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Automatically apply weather changes to the game as you edit"); } - if (ImGui::Checkbox("Remember Open Widgets", &settings.rememberOpenWidgets)) { - Save(); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Restore previously open widgets when editor reopens"); - } + if (ImGui::Checkbox("Enable Inherit From Parent", &settings.enableInheritFromParent)) { Save(); } @@ -1042,37 +1006,19 @@ void EditorWindow::RenderUI() ImGui::Separator(); ImGui::Text("Open Widgets:"); - ImGui::Separator(); int openCount = 0; - for (auto& widget : weatherWidgets) { - if (widget->IsOpen()) { - openCount++; - if (ImGui::MenuItem(std::format("Weather: {}", widget->GetEditorID()).c_str())) { - // Focus window (ImGui will bring to front when clicked) - } - } - } - for (auto& widget : lightingTemplateWidgets) { - if (widget->IsOpen()) { - openCount++; - if (ImGui::MenuItem(std::format("Lighting: {}", widget->GetEditorID()).c_str())) { - // Focus window - } - } - } - for (auto& widget : imageSpaceWidgets) { - if (widget->IsOpen()) { - openCount++; - if (ImGui::MenuItem(std::format("ImageSpace: {}", widget->GetEditorID()).c_str())) { - // Focus window - } - } + for (auto* collection : GetWidgetCollections()) + openCount = WidgetFactory::DrawOpenWidgetMenuItems(*collection, openCount); + + if (currentCellLightingWidget && currentCellLightingWidget->IsOpen()) { + ++openCount; + if (ImGui::MenuItem(std::format("{}: {}", currentCellLightingWidget->GetWidgetTypeName(), currentCellLightingWidget->GetEditorID()).c_str())) + ImGui::SetWindowFocus(currentCellLightingWidget->GetWindowTitle().c_str()); } - if (openCount == 0) { + if (openCount == 0) ImGui::TextDisabled("No widgets open"); - } ImGui::EndMenu(); } @@ -1375,6 +1321,8 @@ void EditorWindow::RenderUI() // Show palette window PaletteWindow::GetSingleton()->Draw(); + if (resetLayout) + ResetWidgetTypeSizes(); resetLayout = false; // Render notifications on top of everything @@ -1418,12 +1366,8 @@ EditorWindow::~EditorWindow() { ShowGameMenus(); delete tempTexture; - weatherWidgets.clear(); - lightingTemplateWidgets.clear(); - imageSpaceWidgets.clear(); - volumetricLightingWidgets.clear(); - precipitationWidgets.clear(); - referenceEffectWidgets.clear(); + for (auto* collection : GetWidgetCollections()) + collection->clear(); artObjectWidgets.clear(); effectShaderWidgets.clear(); currentCellLightingWidget.reset(); @@ -1457,12 +1401,11 @@ void EditorWindow::UpdateOpenState() DisableVanityCamera(); HideGameMenus(); BackgroundBlur::SetWeatherEditorActive(settings.showViewport); - RestoreSessionWidgets(); + } else if (!open && wasOpen) { RestoreVanityCamera(); ShowGameMenus(); BackgroundBlur::SetWeatherEditorActive(false); - SaveSessionWidgets(); } wasOpen = open; @@ -1527,26 +1470,22 @@ void EditorWindow::Draw() void EditorWindow::SaveAll() { - for (auto& weather : weatherWidgets) { - if (weather->IsOpen()) - weather->Save(); - } - - for (auto& lightingTemplate : lightingTemplateWidgets) { - if (lightingTemplate->IsOpen()) - lightingTemplate->Save(); - } - - for (auto& imageSpace : imageSpaceWidgets) { - if (imageSpace->IsOpen()) - imageSpace->Save(); - } + auto saveOpen = [](auto& widgets) { + for (auto& w : widgets) + if (w->IsOpen()) + w->Save(); + }; + for (auto* collection : GetWidgetCollections()) + saveOpen(*collection); + if (currentCellLightingWidget && currentCellLightingWidget->IsOpen()) + currentCellLightingWidget->Save(); Save(); } void EditorWindow::SaveSettings() { + settings.widgetTypeSizes = GetWidgetTypeSizesJson(); j = settings; } @@ -1554,6 +1493,7 @@ void EditorWindow::LoadSettings() { if (!j.empty()) settings = j; + SetWidgetTypeSizesFromJson(settings.widgetTypeSizes); } void EditorWindow::ShowSettingsWindow() @@ -1606,9 +1546,6 @@ void EditorWindow::ShowSettingsWindow() ImGui::TextUnformatted("Session & History"); ImGui::Spacing(); - ImGui::Checkbox("Remember open widgets", &settings.rememberOpenWidgets); - Util::AddTooltip("Automatically reopen widgets that were open when you last closed the editor"); - ImGui::SliderInt("Max recent widgets", &settings.maxRecentWidgets, 5, 20); Util::AddTooltip("Maximum number of recent widgets to remember"); @@ -2113,27 +2050,15 @@ void EditorWindow::PerformUndo() undoStack.pop_back(); if (!state.widget) { - for (auto& w : weatherWidgets) { - if (w->GetEditorID() == state.widgetId) { - state.widget = w.get(); - break; - } - } - if (!state.widget) { - for (auto& w : imageSpaceWidgets) { - if (w->GetEditorID() == state.widgetId) { - state.widget = w.get(); - break; - } - } - } - if (!state.widget) { - for (auto& w : lightingTemplateWidgets) { + for (auto* collection : GetWidgetCollections()) { + for (auto& w : *collection) { if (w->GetEditorID() == state.widgetId) { state.widget = w.get(); break; } } + if (state.widget) + break; } } @@ -2289,46 +2214,3 @@ bool EditorWindow::IsFavorite(const std::string& widgetId) const { return std::find(settings.favoriteWidgets.begin(), settings.favoriteWidgets.end(), widgetId) != settings.favoriteWidgets.end(); } - -void EditorWindow::SaveSessionWidgets() -{ - settings.lastOpenWidgets.clear(); - - // Save all currently open widgets - for (auto& widget : weatherWidgets) { - if (widget->IsOpen()) { - settings.lastOpenWidgets.push_back(widget->GetEditorID()); - } - } - for (auto& widget : lightingTemplateWidgets) { - if (widget->IsOpen()) { - settings.lastOpenWidgets.push_back(widget->GetEditorID()); - } - } - - Save(); -} - -void EditorWindow::RestoreSessionWidgets() -{ - if (!settings.rememberOpenWidgets || settings.lastOpenWidgets.empty()) { - return; - } - - // Open widgets that were open in last session - for (const auto& widgetId : settings.lastOpenWidgets) { - // Search in all widget collections - for (auto& widget : weatherWidgets) { - if (widget->GetEditorID() == widgetId) { - widget->SetOpen(true); - break; - } - } - for (auto& widget : lightingTemplateWidgets) { - if (widget->GetEditorID() == widgetId) { - widget->SetOpen(true); - break; - } - } - } -} diff --git a/src/WeatherEditor/EditorWindow.h b/src/WeatherEditor/EditorWindow.h index d531e60cfb..ab7afe576d 100644 --- a/src/WeatherEditor/EditorWindow.h +++ b/src/WeatherEditor/EditorWindow.h @@ -41,13 +41,22 @@ class EditorWindow Texture2D* tempTexture = nullptr; // Widget collections owned by EditorWindow, created in SetupResources(), released in destructor - std::vector> weatherWidgets; - std::vector> lightingTemplateWidgets; - std::vector> imageSpaceWidgets; - std::vector> volumetricLightingWidgets; - std::vector> precipitationWidgets; - std::vector> lensFlareWidgets; - std::vector> referenceEffectWidgets; + using WidgetVec = std::vector>; + WidgetVec weatherWidgets; + WidgetVec lightingTemplateWidgets; + WidgetVec imageSpaceWidgets; + WidgetVec volumetricLightingWidgets; + WidgetVec precipitationWidgets; + WidgetVec lensFlareWidgets; + WidgetVec referenceEffectWidgets; + + /// Returns references to all editable widget collections for centralized iteration. + std::array GetWidgetCollections() + { + return { &weatherWidgets, &lightingTemplateWidgets, &imageSpaceWidgets, + &volumetricLightingWidgets, &precipitationWidgets, &lensFlareWidgets, + &referenceEffectWidgets }; + } std::vector> artObjectWidgets; std::vector> effectShaderWidgets; @@ -186,10 +195,12 @@ class EditorWindow std::vector favoriteWidgets; std::map> recentWidgets; int maxRecentWidgets = 10; - bool rememberOpenWidgets = true; - std::vector lastOpenWidgets; + bool showViewport = true; + // Per-widget-type window sizes (serialized as JSON for persistence) + json widgetTypeSizes; + // Palette settings struct PaletteColorEntry { @@ -222,8 +233,6 @@ class EditorWindow void AddToRecent(const std::string& widgetId, const std::string& category); void ToggleFavorite(const std::string& widgetId); bool IsFavorite(const std::string& widgetId) const; - void SaveSessionWidgets(); - void RestoreSessionWidgets(); // Navigation helpers for weather-controlled settings void OpenWeatherFeatureSetting(RE::TESWeather* weather, const std::string& featureName, const std::string& settingName); diff --git a/src/WeatherEditor/Weather/CellLightingWidget.cpp b/src/WeatherEditor/Weather/CellLightingWidget.cpp index 46c568f314..2c9ebd8857 100644 --- a/src/WeatherEditor/Weather/CellLightingWidget.cpp +++ b/src/WeatherEditor/Weather/CellLightingWidget.cpp @@ -5,8 +5,7 @@ void CellLightingWidget::DrawWidget() { WeatherUtils::SetCurrentWidget(this); - SetupWidgetWindowDefaults(); - if (Util::BeginWithRoundedClose(GetWindowTitle().c_str(), &open, ImGuiWindowFlags_NoSavedSettings | kStickyHeaderFlags)) { + if (BeginWidgetWindow()) { DrawWidgetHeader("##CellLightingSearch", true, true); } diff --git a/src/WeatherEditor/Weather/CellLightingWidget.h b/src/WeatherEditor/Weather/CellLightingWidget.h index 29e352a21d..1740d98eeb 100644 --- a/src/WeatherEditor/Weather/CellLightingWidget.h +++ b/src/WeatherEditor/Weather/CellLightingWidget.h @@ -19,6 +19,7 @@ class CellLightingWidget : public Widget ~CellLightingWidget() override = default; void DrawWidget() override; + const char* GetWidgetTypeName() const override { return "Cell Lighting"; } void LoadSettings() override; void SaveSettings() override; void ApplyChanges() override; diff --git a/src/WeatherEditor/Weather/ImageSpaceWidget.cpp b/src/WeatherEditor/Weather/ImageSpaceWidget.cpp index ae92abd97b..b8b6d9cc71 100644 --- a/src/WeatherEditor/Weather/ImageSpaceWidget.cpp +++ b/src/WeatherEditor/Weather/ImageSpaceWidget.cpp @@ -33,8 +33,7 @@ void ImageSpaceWidget::DrawWidget() WeatherUtils::SetCurrentWidget(this); auto editorWindow = EditorWindow::GetSingleton(); - SetupWidgetWindowDefaults(); - if (Util::BeginWithRoundedClose(GetWindowTitle().c_str(), &open, ImGuiWindowFlags_NoSavedSettings | kStickyHeaderFlags)) { + if (BeginWidgetWindow()) { DrawWidgetHeader("##ImageSpaceSearch", false, true); } BeginScrollableContent("##ISScroll"); diff --git a/src/WeatherEditor/Weather/ImageSpaceWidget.h b/src/WeatherEditor/Weather/ImageSpaceWidget.h index 18ed36cda2..e51077bfb8 100644 --- a/src/WeatherEditor/Weather/ImageSpaceWidget.h +++ b/src/WeatherEditor/Weather/ImageSpaceWidget.h @@ -53,10 +53,11 @@ class ImageSpaceWidget : public Widget ~ImageSpaceWidget(); - virtual void DrawWidget() override; - virtual void LoadSettings() override; - virtual void SaveSettings() override; - virtual bool HasUnsavedChanges() const override; + void DrawWidget() override; + const char* GetWidgetTypeName() const override { return "ImageSpace"; } + void LoadSettings() override; + void SaveSettings() override; + bool HasUnsavedChanges() const override; void SetImageSpaceValues(); void LoadImageSpaceValues(); diff --git a/src/WeatherEditor/Weather/LensFlareWidget.cpp b/src/WeatherEditor/Weather/LensFlareWidget.cpp index 5e78f04ef8..42009f2bb5 100644 --- a/src/WeatherEditor/Weather/LensFlareWidget.cpp +++ b/src/WeatherEditor/Weather/LensFlareWidget.cpp @@ -4,8 +4,7 @@ void LensFlareWidget::DrawWidget() { - SetupWidgetWindowDefaults(); - if (Util::BeginWithRoundedClose(GetWindowTitle().c_str(), &open, ImGuiWindowFlags_NoSavedSettings | kStickyHeaderFlags)) { + if (BeginWidgetWindow()) { DrawWidgetHeader("##LensFlareSearch", true, true); } BeginScrollableContent("##LFScroll"); diff --git a/src/WeatherEditor/Weather/LensFlareWidget.h b/src/WeatherEditor/Weather/LensFlareWidget.h index 03f3d14083..d7ade5c4b9 100644 --- a/src/WeatherEditor/Weather/LensFlareWidget.h +++ b/src/WeatherEditor/Weather/LensFlareWidget.h @@ -19,6 +19,7 @@ class LensFlareWidget : public Widget ~LensFlareWidget() override = default; void DrawWidget() override; + const char* GetWidgetTypeName() const override { return "Lens Flare"; } void LoadSettings() override; void SaveSettings() override; void ApplyChanges() override; diff --git a/src/WeatherEditor/Weather/LightingTemplateWidget.cpp b/src/WeatherEditor/Weather/LightingTemplateWidget.cpp index 82f6a27cc4..0be0776a20 100644 --- a/src/WeatherEditor/Weather/LightingTemplateWidget.cpp +++ b/src/WeatherEditor/Weather/LightingTemplateWidget.cpp @@ -29,8 +29,7 @@ LightingTemplateWidget::~LightingTemplateWidget() void LightingTemplateWidget::DrawWidget() { WeatherUtils::SetCurrentWidget(this); - SetupWidgetWindowDefaults(); - if (Util::BeginWithRoundedClose(GetWindowTitle().c_str(), &open, ImGuiWindowFlags_NoSavedSettings | kStickyHeaderFlags)) { + if (BeginWidgetWindow()) { DrawWidgetHeader("##LightingTemplateSearch", false, true); } if (ImGui::BeginTabBar("LightingTemplateSettingsTabs", ImGuiTabBarFlags_None)) { diff --git a/src/WeatherEditor/Weather/LightingTemplateWidget.h b/src/WeatherEditor/Weather/LightingTemplateWidget.h index a59e78d59c..e317049a04 100644 --- a/src/WeatherEditor/Weather/LightingTemplateWidget.h +++ b/src/WeatherEditor/Weather/LightingTemplateWidget.h @@ -61,10 +61,11 @@ class LightingTemplateWidget : public Widget ~LightingTemplateWidget(); - virtual void DrawWidget() override; - virtual void LoadSettings() override; - virtual void SaveSettings() override; - virtual bool HasUnsavedChanges() const override; + void DrawWidget() override; + const char* GetWidgetTypeName() const override { return "Lighting"; } + void LoadSettings() override; + void SaveSettings() override; + bool HasUnsavedChanges() const override; void SetLightingTemplateValues(); void LoadLightingTemplateValues(); diff --git a/src/WeatherEditor/Weather/PrecipitationWidget.cpp b/src/WeatherEditor/Weather/PrecipitationWidget.cpp index c4ad4f7832..5b4071681f 100644 --- a/src/WeatherEditor/Weather/PrecipitationWidget.cpp +++ b/src/WeatherEditor/Weather/PrecipitationWidget.cpp @@ -5,8 +5,7 @@ void PrecipitationWidget::DrawWidget() { WeatherUtils::SetCurrentWidget(this); - SetupWidgetWindowDefaults(); - if (Util::BeginWithRoundedClose(GetWindowTitle().c_str(), &open, ImGuiWindowFlags_NoSavedSettings | kStickyHeaderFlags)) { + if (BeginWidgetWindow()) { DrawWidgetHeader("##PrecipitationSearch", true, true); bool changed = false; diff --git a/src/WeatherEditor/Weather/PrecipitationWidget.h b/src/WeatherEditor/Weather/PrecipitationWidget.h index 76775691cb..ff2c399361 100644 --- a/src/WeatherEditor/Weather/PrecipitationWidget.h +++ b/src/WeatherEditor/Weather/PrecipitationWidget.h @@ -20,6 +20,7 @@ class PrecipitationWidget : public Widget ~PrecipitationWidget() override = default; void DrawWidget() override; + const char* GetWidgetTypeName() const override { return "Precipitation"; } void LoadSettings() override; void SaveSettings() override; void ApplyChanges() override; diff --git a/src/WeatherEditor/Weather/ReferenceEffectWidget.cpp b/src/WeatherEditor/Weather/ReferenceEffectWidget.cpp index 47ffb2d510..1f6e8bd374 100644 --- a/src/WeatherEditor/Weather/ReferenceEffectWidget.cpp +++ b/src/WeatherEditor/Weather/ReferenceEffectWidget.cpp @@ -4,8 +4,7 @@ void ReferenceEffectWidget::DrawWidget() { - SetupWidgetWindowDefaults(); - if (Util::BeginWithRoundedClose(GetWindowTitle().c_str(), &open, ImGuiWindowFlags_NoSavedSettings | kStickyHeaderFlags)) { + if (BeginWidgetWindow()) { DrawWidgetHeader("##ReferenceEffectSearch", true, true); BeginScrollableContent("##REScroll"); { diff --git a/src/WeatherEditor/Weather/ReferenceEffectWidget.h b/src/WeatherEditor/Weather/ReferenceEffectWidget.h index 9a2ad66dfb..b3b72e11b0 100644 --- a/src/WeatherEditor/Weather/ReferenceEffectWidget.h +++ b/src/WeatherEditor/Weather/ReferenceEffectWidget.h @@ -19,6 +19,7 @@ class ReferenceEffectWidget : public Widget ~ReferenceEffectWidget() override = default; void DrawWidget() override; + const char* GetWidgetTypeName() const override { return "Visual Effect"; } void LoadSettings() override; void SaveSettings() override; void ApplyChanges() override; diff --git a/src/WeatherEditor/Weather/VolumetricLightingWidget.cpp b/src/WeatherEditor/Weather/VolumetricLightingWidget.cpp index 88fbc9dddb..bd323f1343 100644 --- a/src/WeatherEditor/Weather/VolumetricLightingWidget.cpp +++ b/src/WeatherEditor/Weather/VolumetricLightingWidget.cpp @@ -5,8 +5,7 @@ void VolumetricLightingWidget::DrawWidget() { WeatherUtils::SetCurrentWidget(this); - SetupWidgetWindowDefaults(); - if (Util::BeginWithRoundedClose(GetWindowTitle().c_str(), &open, ImGuiWindowFlags_NoSavedSettings | kStickyHeaderFlags)) { + if (BeginWidgetWindow()) { DrawWidgetHeader("##VolumetricLightingSearch", true, true); } bool changed = false; diff --git a/src/WeatherEditor/Weather/VolumetricLightingWidget.h b/src/WeatherEditor/Weather/VolumetricLightingWidget.h index 842d68d74f..8512dbab3a 100644 --- a/src/WeatherEditor/Weather/VolumetricLightingWidget.h +++ b/src/WeatherEditor/Weather/VolumetricLightingWidget.h @@ -19,6 +19,7 @@ class VolumetricLightingWidget : public Widget ~VolumetricLightingWidget() override = default; void DrawWidget() override; + const char* GetWidgetTypeName() const override { return "Volumetric Lighting"; } void LoadSettings() override; void SaveSettings() override; void ApplyChanges() override; diff --git a/src/WeatherEditor/Weather/WeatherWidget.cpp b/src/WeatherEditor/Weather/WeatherWidget.cpp index 828c378a14..2f1445c355 100644 --- a/src/WeatherEditor/Weather/WeatherWidget.cpp +++ b/src/WeatherEditor/Weather/WeatherWidget.cpp @@ -56,8 +56,7 @@ void WeatherWidget::DrawWidget() { WeatherUtils::SetCurrentWidget(this); const float scale = Util::GetUIScale(); - SetupWidgetWindowDefaults(); - if (Util::BeginWithRoundedClose(GetWindowTitle().c_str(), &open, ImGuiWindowFlags_NoSavedSettings | kStickyHeaderFlags)) { + if (BeginWidgetWindow()) { // Draw header with search and all buttons DrawWidgetHeader("##WeatherSearch", false, true, true, weather); diff --git a/src/WeatherEditor/Weather/WeatherWidget.h b/src/WeatherEditor/Weather/WeatherWidget.h index defa843859..577ea174f2 100644 --- a/src/WeatherEditor/Weather/WeatherWidget.h +++ b/src/WeatherEditor/Weather/WeatherWidget.h @@ -125,9 +125,10 @@ class WeatherWidget : public Widget ~WeatherWidget(); - virtual void DrawWidget() override; - virtual void LoadSettings() override; - virtual void SaveSettings() override; + void DrawWidget() override; + const char* GetWidgetTypeName() const override { return "Weather"; } + void LoadSettings() override; + void SaveSettings() override; WeatherWidget* GetParent(); bool HasParent() const; diff --git a/src/WeatherEditor/WeatherUtils.cpp b/src/WeatherEditor/WeatherUtils.cpp index 8e1a94206e..10b0e36940 100644 --- a/src/WeatherEditor/WeatherUtils.cpp +++ b/src/WeatherEditor/WeatherUtils.cpp @@ -6,13 +6,47 @@ // Global widget context for undo tracking static Widget* g_currentWidget = nullptr; -void SetupWidgetWindowDefaults() +// Per-widget-type window sizes — shared across all instances of the same widget type +static std::unordered_map s_widgetTypeSizes; + +void SetupWidgetWindowDefaults(const char* widgetType) { - const float scale = Util::GetUIScale(); - const auto cond = EditorWindow::GetSingleton()->resetLayout ? ImGuiCond_Always : ImGuiCond_FirstUseEver; - ImGui::SetNextWindowSize( - ImVec2(WidgetDefaults::kInitialWidth * scale, WidgetDefaults::kInitialHeight * scale), - cond); + const bool resetting = EditorWindow::GetSingleton()->resetLayout; + const auto cond = resetting ? ImGuiCond_Always : ImGuiCond_Appearing; + const ImVec2 defaultSize(WidgetDefaults::kInitialWidth * Util::GetUIScale(), WidgetDefaults::kInitialHeight * Util::GetUIScale()); + auto it = s_widgetTypeSizes.find(widgetType); + ImGui::SetNextWindowSize(resetting || it == s_widgetTypeSizes.end() ? defaultSize : it->second, cond); +} + +void UpdateWidgetTypeSize(const char* widgetType) +{ + if (!EditorWindow::GetSingleton()->resetLayout && ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)) + s_widgetTypeSizes[widgetType] = ImGui::GetWindowSize(); +} + +void ResetWidgetTypeSizes() +{ + s_widgetTypeSizes.clear(); +} + +json GetWidgetTypeSizesJson() +{ + json j; + for (const auto& [type, size] : s_widgetTypeSizes) + j[type] = { size.x, size.y }; + return j; +} + +void SetWidgetTypeSizesFromJson(const json& j) +{ + s_widgetTypeSizes.clear(); + for (auto& [key, val] : j.items()) { + if (val.is_array() && val.size() == 2 && val[0].is_number() && val[1].is_number()) { + float w = std::max(val[0].get(), WidgetDefaults::kMinWidth); + float h = std::max(val[1].get(), WidgetDefaults::kMinHeight); + s_widgetTypeSizes[key] = ImVec2(w, h); + } + } } bool ContainsStringIgnoreCase(const std::string_view a_string, const std::string_view a_substring) diff --git a/src/WeatherEditor/WeatherUtils.h b/src/WeatherEditor/WeatherUtils.h index 05b026f46b..c2c44da950 100644 --- a/src/WeatherEditor/WeatherUtils.h +++ b/src/WeatherEditor/WeatherUtils.h @@ -122,8 +122,22 @@ namespace WidgetDefaults } /// Apply standard DPI-scaled size constraints and initial size for widget windows. Call before ImGui::Begin(). +/// Uses per-widget-type size tracking so all instances of a widget type share window dimensions. /// Respects EditorWindow::resetLayout to re-apply defaults on demand. -void SetupWidgetWindowDefaults(); +void SetupWidgetWindowDefaults(const char* widgetType); + +/// Update stored per-widget-type size from the current ImGui window. Call after ImGui::Begin(). +/// Only updates when the window is focused so resize applies to future windows of the same type. +void UpdateWidgetTypeSize(const char* widgetType); + +/// Reset all per-widget-type sizes to defaults. Called when resetLayout is triggered. +void ResetWidgetTypeSizes(); + +/// Serialize per-widget-type sizes into a JSON object for persistence. +json GetWidgetTypeSizesJson(); + +/// Restore per-widget-type sizes from a previously serialized JSON object. +void SetWidgetTypeSizesFromJson(const json& j); // ============================================================================ // PropertyDrawer - Unified table-based property drawing with search support @@ -218,6 +232,46 @@ namespace WidgetFactory } } } + + // Draw menu items for open widgets in a container using widget type name. Returns updated open count. + template + int DrawOpenWidgetMenuItems(const Container& widgets, int count) + { + for (auto& widget : widgets) { + if (widget->IsOpen()) { + ++count; + if (ImGui::MenuItem(std::format("{}: {}", widget->GetWidgetTypeName(), widget->GetEditorID()).c_str())) + ImGui::SetWindowFocus(widget->GetWindowTitle().c_str()); + } + } + return count; + } + + // Draw save menu items for open widgets in a container. Returns true if any open widget exists. + template + bool DrawSaveWidgetMenuItems(Container& widgets, bool hasOpen = false) + { + for (auto& widget : widgets) { + if (widget->IsOpen()) { + hasOpen = true; + if (ImGui::MenuItem(std::format("Save {}", widget->GetEditorID()).c_str())) + widget->Save(); + } + } + return hasOpen; + } + + // Draw "Close All Widgets" menu item for a widget container. + template + void DrawCloseAllMenuItem(Container& widgets) + { + if (widgets.empty()) + return; + if (ImGui::MenuItem(std::format("Close All {} Widgets", widgets[0]->GetWidgetTypeName()).c_str())) { + for (auto& widget : widgets) + widget->SetOpen(false); + } + } } // namespace WidgetFactory void Float3ToColor(const float3& newColor, RE::Color& color); diff --git a/src/WeatherEditor/Widget.cpp b/src/WeatherEditor/Widget.cpp index a1cc13ed79..c66c979490 100644 --- a/src/WeatherEditor/Widget.cpp +++ b/src/WeatherEditor/Widget.cpp @@ -264,6 +264,14 @@ std::string Widget::GetFolderName() } } +bool Widget::BeginWidgetWindow() +{ + SetupWidgetWindowDefaults(GetWidgetTypeName()); + bool result = Util::BeginWithRoundedClose(GetWindowTitle().c_str(), &open, ImGuiWindowFlags_NoSavedSettings | kStickyHeaderFlags); + UpdateWidgetTypeSize(GetWidgetTypeName()); + return result; +} + void Widget::DrawWidgetHeader(const char* searchId, bool showApply, bool showSaveLoadRevert, bool showForceWeather, RE::TESWeather* weather) { auto editorWindow = EditorWindow::GetSingleton(); diff --git a/src/WeatherEditor/Widget.h b/src/WeatherEditor/Widget.h index 902aab366b..16c92c45e7 100644 --- a/src/WeatherEditor/Widget.h +++ b/src/WeatherEditor/Widget.h @@ -116,6 +116,12 @@ class Widget virtual void DrawWidget() = 0; + /// Type name for widget-type-level state sharing (window size, etc.). + virtual const char* GetWidgetTypeName() const = 0; + + /// Call instead of SetupWidgetWindowDefaults + ImGui::Begin. Tracks per-type window size. + bool BeginWidgetWindow(); + bool open = false; bool IsOpen() const @@ -171,6 +177,7 @@ class SimpleFormWidget : public Widget { public: void DrawWidget() override {} + const char* GetWidgetTypeName() const override { return ""; } void LoadSettings() override {} void SaveSettings() override {} void ApplyChanges() override {}