From 965661bdad9bb654200ad0d3d8bce990b80e067e Mon Sep 17 00:00:00 2001 From: Oznogon Date: Thu, 23 Oct 2025 08:40:03 -0700 Subject: [PATCH 1/7] Refactor GuiProgressbar for improved accuracy GuiProgressbar can fail to show pixels near the min and max values, such as for 98-100% power or coolant on the Power Management screen or the final jump drive charge points in the jump drive controls. This appears to be caused by the GuiProgressbar being filled by a PNG with a transparent border of pixels, which become the only pixels rendered at these extremes, particularly on high-resolution displays. - Remove transparent pixels from edges of the default progress bar fill PNG that caused inaccuracies for values near min and max. Screens that implement GuiProgressbar should manage its width or height using size or margin attributes if necessary. - Conditionally reduce the default width of the fill rect when using a background. This is set to an arbitrary value that approximates the proportions used in existing behavior, but results in a tighter and more accurate fit on high-resolution displays. - Simplify min/max logic using clamps. --- resources/gui/widget/ProgressbarFill.png | Bin 100 -> 217 bytes src/gui/gui2_progressbar.cpp | 12 +++++++++++- src/gui/gui2_progressslider.cpp | 18 ++++++------------ 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/resources/gui/widget/ProgressbarFill.png b/resources/gui/widget/ProgressbarFill.png index aa695d0b28a943529da2a3fcd111755cc07c6362..699446ecb5bcc5189392b2b6e58eca348e4bac24 100644 GIT binary patch literal 217 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC&H|6fVg?3oArNM~bhqvgP_Wz6 z#W5tq`R%2HyayBnjyO2|w{@N(_DpbY%D=eAv&Hu|v(2~t@$UZB>I3&#wF4MUE{Hod z_JlWxK5}3UP^0*5GC+V%8BMnpl e20wT`ZmnYEzj#~ry70$*kdUXVpUXO@geCy1ei#b? diff --git a/src/gui/gui2_progressbar.cpp b/src/gui/gui2_progressbar.cpp index 301f828391..33c566db75 100644 --- a/src/gui/gui2_progressbar.cpp +++ b/src/gui/gui2_progressbar.cpp @@ -15,13 +15,18 @@ void GuiProgressbar::onDraw(sp::RenderTarget& renderer) const auto& front = front_style->get(getState()); float f = (value - min_value) / (max_value - min_value); + sp::Rect fill_rect = rect; if (drawBackground) renderer.drawStretched(rect, back.texture, back.color); - sp::Rect fill_rect = rect; if (rect.size.x >= rect.size.y) { + if (drawBackground) + { + fill_rect.position.y += fill_rect.size.y * 0.125f; + fill_rect.size.y *= 0.75f; + } fill_rect.size.x *= f; if (max_value < min_value) fill_rect.position.x = rect.position.x + rect.size.x - fill_rect.size.x; @@ -29,6 +34,11 @@ void GuiProgressbar::onDraw(sp::RenderTarget& renderer) } else { + if (drawBackground) + { + fill_rect.position.x += fill_rect.size.x * 0.125f; + fill_rect.size.x *= 0.75f; + } fill_rect.size.y *= f; fill_rect.position.y = rect.position.y + rect.size.y - fill_rect.size.y; renderer.drawStretchedHVClipped(rect, fill_rect, front.size, front.texture, color); diff --git a/src/gui/gui2_progressslider.cpp b/src/gui/gui2_progressslider.cpp index fc85155b14..a5b09ee649 100644 --- a/src/gui/gui2_progressslider.cpp +++ b/src/gui/gui2_progressslider.cpp @@ -1,7 +1,6 @@ #include "gui2_progressslider.h" #include "theme.h" - GuiProgressSlider::GuiProgressSlider(GuiContainer* owner, string id, float min_value, float max_value, float start_value, func_t func) : GuiProgressbar(owner, id, min_value, max_value, start_value), callback(func) { @@ -20,19 +19,14 @@ void GuiProgressSlider::onMouseDrag(glm::vec2 position, sp::io::Pointer::ID id) new_value = (position.x - rect.position.x) / (rect.size.x); else new_value = (position.y - rect.position.y) / (rect.size.y); + new_value = min_value + (max_value - min_value) * new_value; + if (min_value < max_value) - { - if (new_value < min_value) - new_value = min_value; - if (new_value > max_value) - new_value = max_value; - }else{ - if (new_value > min_value) - new_value = min_value; - if (new_value < max_value) - new_value = max_value; - } + new_value = std::clamp(new_value, min_value, max_value); + else + new_value = std::clamp(new_value, max_value, min_value); + if (value != new_value) { value = new_value; From ce79a17c33e00f53c5628f26f6e2e6b93526d915 Mon Sep 17 00:00:00 2001 From: Oznogon Date: Thu, 23 Oct 2025 08:47:26 -0700 Subject: [PATCH 2/7] Align GuiSlider min/max logic to revised GuiProgressbarslider logic --- src/gui/gui2_slider.cpp | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/gui/gui2_slider.cpp b/src/gui/gui2_slider.cpp index 2f41247bec..7926eee3b1 100644 --- a/src/gui/gui2_slider.cpp +++ b/src/gui/gui2_slider.cpp @@ -77,17 +77,10 @@ void GuiBasicSlider::onMouseUp(glm::vec2 position, sp::io::Pointer::ID id) GuiBasicSlider* GuiBasicSlider::setValue(float value) { if (min_value < max_value) - { - if (value < min_value) - value = min_value; - if (value > max_value) - value = max_value; - }else{ - if (value > min_value) - value = min_value; - if (value < max_value) - value = max_value; - } + this->value = std::clamp(value, min_value, max_value); + else + this->value = std::clamp(value, max_value, min_value); + this->value = value; return this; } From 600a9aaae7f0cc489bbec0a285f1f8dde0e3c564 Mon Sep 17 00:00:00 2001 From: Oznogon Date: Thu, 23 Oct 2025 13:06:12 -0700 Subject: [PATCH 3/7] Add reparenting setParent function to GuiElement Add a GuiElement::setParent function to reparent elements by removing them from their former parent element's tree, adding them to the new parent element's tree, and updating its pointer. --- src/gui/gui2_element.cpp | 25 +++++++++++++++++++++++++ src/gui/gui2_element.h | 3 +++ 2 files changed, 28 insertions(+) diff --git a/src/gui/gui2_element.cpp b/src/gui/gui2_element.cpp index 0c4e80c393..30115deca5 100644 --- a/src/gui/gui2_element.cpp +++ b/src/gui/gui2_element.cpp @@ -111,6 +111,31 @@ GuiElement* GuiElement::setMargins(float left, float top, float right, float bot return this; } +GuiElement* GuiElement::setParent(GuiContainer* new_parent) +{ + if (GuiContainer* old_owner = this->getOwner()) + { + // Works only if both the old owner and new parent are valid. + if (new_parent && old_owner != new_parent) + { + // Remove from old owner's children list. + old_owner->children.remove(this); + + // Add to new owner's children list. + new_parent->children.push_back(this); + + // Update owner pointer. + this->owner = new_parent; + } + else + LOG(Debug, "GuiElement::setParent called, but new parent is invalid."); + } + else + LOG(Debug, "GuiElement::setParent called, but old owner is invalid."); + + return this; +} + GuiElement* GuiElement::setPosition(float x, float y, sp::Alignment alignment) { layout.position.x = x; diff --git a/src/gui/gui2_element.h b/src/gui/gui2_element.h index 4831d4e0ba..55df1ad0a1 100644 --- a/src/gui/gui2_element.h +++ b/src/gui/gui2_element.h @@ -81,6 +81,9 @@ class GuiElement : public GuiContainer GuiContainer* getTopLevelContainer(); const string& getID() { return id; } + // Change this element's owner/container safely (removes from old owner and adds to new owner) + GuiElement* setParent(GuiContainer* new_owner); + //Have this GuiElement destroyed, but at a safe point&time in the code. (handled by the container) void destroy(); From 1dd93dd9f0a1baf8efe8d6ddcc0fc0f593d3e4ec Mon Sep 17 00:00:00 2001 From: Oznogon Date: Wed, 22 Oct 2025 17:39:25 -0700 Subject: [PATCH 4/7] Rewrite Power Management screen - Rewrite Power Management screen to dynamically add, remove, and populate rows as the screen size and number of systems change. - Use reparenting to dynamically assign system panels to an arbitrary number of layout rows. - Prevent responsive changes from overlapping crew screen selector and main screen controls, even if both are present. - Expand power/coolant key-value displays. - Use layout attributes to build power management panels. - Add the same snap lines to power and coolant sliders as already used on the Engineering screens. - Add heat delta arrows. - Hide coolant sliders when autocoolant is enabled, leaving only the indicator bar. - Update system power, coolant, heat labels with values. --- src/screens/extra/powerManagement.cpp | 490 +++++++++++++++++++++----- src/screens/extra/powerManagement.h | 57 ++- 2 files changed, 442 insertions(+), 105 deletions(-) diff --git a/src/screens/extra/powerManagement.cpp b/src/screens/extra/powerManagement.cpp index 6a04b89b3e..a8e8184ccb 100644 --- a/src/screens/extra/powerManagement.cpp +++ b/src/screens/extra/powerManagement.cpp @@ -10,119 +10,413 @@ #include "gui/gui2_panel.h" #include "gui/gui2_label.h" +#include "gui/gui2_arrow.h" +#include "gui/gui2_image.h" #include "gui/gui2_slider.h" #include "gui/gui2_progressbar.h" #include "gui/gui2_keyvaluedisplay.h" PowerManagementScreen::PowerManagementScreen(GuiContainer* owner) -: GuiOverlay(owner, "POWER_MANAGEMENT_SCREEN", colorConfig.background) +: GuiOverlay(owner, "POWER_MANAGEMENT_SCREEN", colorConfig.background), active_system_count(0), panel_size({290.0f, 380.0f}), previous_energy_level(0.0f), average_energy_delta(0.0f), previous_energy_measurement(0.0f), selected_system(ShipSystem::Type::None) { - selected_system = ShipSystem::Type::None; - - energy_display = new GuiKeyValueDisplay(this, "ENERGY_DISPLAY", 0.45, tr("Energy"), ""); - energy_display->setIcon("gui/icons/energy")->setTextSize(20)->setPosition(20, 20, sp::Alignment::TopLeft)->setSize(285, 40); - coolant_display = new GuiKeyValueDisplay(this, "COOLANT_DISPLAY", 0.45, tr("Coolant"), ""); - coolant_display->setIcon("gui/icons/coolant")->setTextSize(20)->setPosition(315, 20, sp::Alignment::TopLeft)->setSize(280, 40); - GuiElement* layout = new GuiElement(this, ""); - layout->setPosition(20, 60, sp::Alignment::TopLeft)->setSize(GuiElement::GuiSizeMax, 400)->setAttribute("layout", "horizontal"); - for(int n=0; nsetSize(GuiElement::GuiSizeMax, GuiElement::GuiSizeMax); + layout->setAttribute("layout", "vertical"); + layout->setAttribute("padding", string(layout_margin)); + + // Status bar across top for power and coolant gauges. + status_bar = new GuiElement(layout, "PWR_STATUS_BAR"); + status_bar->setSize(GuiElement::GuiSizeMax, status_bar_height); + status_bar->setAttribute("layout", "horizontal"); + // Add padding to preserve space at top right for the 250px-wide crew screen + // selector and main screen controls. + status_bar->setAttribute("padding", "0, 270, 0, 0"); + + // Build the status bar, which contains energy and coolant gauges. + energy_capacity_gauge = new GuiProgressbar(status_bar, "PWR_ENERGY_CAPACITY_GAUGE", 0.0f, 1000.0f, 1000.0f); + energy_capacity_gauge + ->setDrawBackground(false) + ->setColor(energy_color_background) + ->setPosition(0.0f, 0.0f, sp::Alignment::CenterLeft) + ->setSize(GuiElement::GuiSizeMax, GuiElement::GuiSizeMax) + ->setAttribute("margin", "0, 13"); + + energy_display = new GuiKeyValueDisplay(energy_capacity_gauge, "PWR_ENERGY_DISPLAY", 0.6f, tr("power management", "Available energy") + "\n" + tr("power management", "Drain/charge rate"), ""); + energy_display->setIcon("gui/icons/energy")->setTextSize(25.0f)->setSize(GuiElement::GuiSizeMax, GuiElement::GuiSizeMax)->setAttribute("margin", "0, -13"); + energy_delta_arrow = new GuiArrow(energy_display, "PWR_ENERGY_DELTA_ARROW", 0.0f); + energy_delta_arrow->setPosition(0.0f, 0.0f, sp::Alignment::CenterRight)->setSize(40.0f, 40.0f); + + GuiElement* status_bar_kv_spacer = new GuiElement(status_bar, "PWR_STATUS_BAR_KV_SPACER"); + status_bar_kv_spacer->setSize(20.0f, GuiElement::GuiSizeMax); + + coolant_distribution_gauge = new GuiProgressbar(status_bar, "PWR_COOLANT_DISTRIBUTION_GAUGE", 0.0f, 1.0f, 1.0f); + coolant_distribution_gauge + ->setDrawBackground(false) + ->setColor(coolant_color_background) + ->setPosition(0.0f, 0.0f, sp::Alignment::CenterLeft) + ->setSize(GuiElement::GuiSizeMax, GuiElement::GuiSizeMax) + ->setAttribute("margin", "0, 13"); + + coolant_display = new GuiKeyValueDisplay(coolant_distribution_gauge, "PWR_COOLANT_DISPLAY", 0.7f, tr("power management", "Available coolant"), ""); + coolant_display->setIcon("gui/icons/coolant")->setTextSize(25.0f)->setSize(GuiElement::GuiSizeMax, GuiElement::GuiSizeMax)->setAttribute("margin", "0, -13"); + + // Build the content container for the columns below status bar. + GuiElement* content = new GuiElement(layout, "PWR_CONTENT"); + content->setSize(GuiElement::GuiSizeMax, GuiElement::GuiSizeMax); + content->setAttribute("layout", "horizontal"); + + // Left column contains the grid of systems power management panels. + systems_grid = new GuiElement(content, "PWR_SYSTEMS_GRID"); + systems_grid->setSize(GuiElement::GuiSizeMax, GuiElement::GuiSizeMax); + systems_grid->setAttribute("layout", "vertical"); + + // Right column contains the custom functions element. + custom_functions = new GuiCustomShipFunctions(content, CrewPosition::powerManagement, "PWR_CUSTOM_FUNCTIONS"); + custom_functions->setSize(custom_functions_width, GuiElement::GuiSizeMax); + + // Initialize at least one row for systems panels. + GuiElement* systems_row = new GuiElement(systems_grid, "PWR_SYSTEMS_ROW_1"); + systems_row->setSize(GuiElement::GuiSizeMax, panel_size.y); + systems_row->setAttribute("layout", "horizontal"); + systems_rows.emplace_back(systems_row); + + // TODO: Hotkey help overlay +} + +bool PowerManagementScreen::populateSystemPanel(int system_index, GuiElement* systems_row) +{ + // Populate a single system's power management panel for the systems grid. + // Return if there's no my_spaceship. + if (!my_spaceship) return false; + + // If a container already exists for this system, reparent it to the new row instead of recreating widgets. + if (auto this_container = systems[system_index].container) + this_container->setParent(systems_row); + else { - if (n == 4) + // Create the container and its child widgets if they don't exist yet. + systems[system_index].container = new GuiPanel(systems_row, "PWR_SYSTEM_CONTAINER_" + string(system_index)); + systems[system_index].container->setSize(panel_size.x, GuiElement::GuiSizeMax); + systems[system_index].container->setAttribute("layout", "vertical"); + systems[system_index].container->setAttribute("padding", "20, 20, 10, 20"); + + // Build the panel label. + // Select an icon for systems that support it. + string icon_file = ""; + switch (system_index) { - //Start the 2nd row after 4 elements. - layout = new GuiElement(this, ""); - layout->setPosition(20, 450, sp::Alignment::TopLeft)->setSize(GuiElement::GuiSizeMax, 400)->setAttribute("layout", "horizontal");; + case int(ShipSystem::Type::Reactor): + icon_file = "gui/icons/system_reactor"; + break; + case int(ShipSystem::Type::BeamWeapons): + icon_file = "gui/icons/system_beam"; + break; + case int(ShipSystem::Type::MissileSystem): + icon_file = "gui/icons/system_missile"; + break; + case int(ShipSystem::Type::Maneuver): + icon_file = "gui/icons/system_maneuver"; + break; + case int(ShipSystem::Type::Impulse): + icon_file = "gui/icons/system_impulse"; + break; + case int(ShipSystem::Type::Warp): + icon_file = "gui/icons/system_warpdrive"; + break; + case int(ShipSystem::Type::JumpDrive): + icon_file = "gui/icons/system_jumpdrive"; + break; + case int(ShipSystem::Type::FrontShield): + icon_file = "gui/icons/shields-fore"; + break; + case int(ShipSystem::Type::RearShield): + icon_file = "gui/icons/shields-aft"; + break; } - GuiPanel* box = new GuiPanel(layout, ""); - systems[n].box = box; - box->setSize(290, 400); + // Panel labels use GuiKeyValueDisplay for their icon support. + systems[system_index].system_label = new GuiKeyValueDisplay(systems[system_index].container, "PWR_SYSTEM_" + string(system_index) + "_NAME_LABEL", 0.15f, "", getLocaleSystemName(ShipSystem::Type(system_index))); + systems[system_index].system_label->setIcon(icon_file)->setTextSize(20.0f)->setSize(GuiElement::GuiSizeMax, 50.0f); + systems[system_index].system_label->setAttribute("margin", "0, 0, -12, 0"); - (new GuiLabel(box, "", getLocaleSystemName(ShipSystem::Type(n)), 30))->addBackground()->setAlignment(sp::Alignment::Center)->setPosition(0, 0, sp::Alignment::TopLeft)->setSize(290, 50); - (new GuiLabel(box, "", tr("button", "Power"), 30))->setVertical()->setAlignment(sp::Alignment::CenterLeft)->setPosition(20, 50, sp::Alignment::TopLeft)->setSize(30, 340); - (new GuiLabel(box, "", tr("button", "Coolant"), 30))->setVertical()->setAlignment(sp::Alignment::CenterLeft)->setPosition(100, 50, sp::Alignment::TopLeft)->setSize(30, 340); - (new GuiLabel(box, "", tr("button", "Heat"), 30))->setVertical()->setAlignment(sp::Alignment::CenterLeft)->setPosition(180, 50, sp::Alignment::TopLeft)->setSize(30, 340); + // Build the panel's sliders. + systems[system_index].system_container_sliders = new GuiElement(systems[system_index].container, "PWR_SYSTEM_" + string(system_index) + "_SLIDERS"); + systems[system_index].system_container_sliders->setSize(GuiElement::GuiSizeMax, GuiElement::GuiSizeMax); + systems[system_index].system_container_sliders->setAttribute("layout", "horizontal"); - systems[n].power_bar = new GuiProgressbar(box, "", 0.0, 3.0, 1.0); - systems[n].power_bar->setDrawBackground(false)->setPosition(52.5, 60, sp::Alignment::TopLeft)->setSize(50, 320); + // Build the power slider. + systems[system_index].power_label = new GuiLabel(systems[system_index].system_container_sliders, "PWR_SYSTEM_" + string(system_index) + "_POWER_LABEL", tr("button", "Power"), 30.0f); + systems[system_index].power_label + ->setVertical() + ->setAlignment(sp::Alignment::CenterLeft) + ->setSize(30.0f, GuiElement::GuiSizeMax) + ->setAttribute("margin", "0, 10, 0, 0"); - systems[n].power_slider = new GuiSlider(box, "", 3.0, 0.0, 1.0, [n](float value) { - my_player_info->commandSetSystemPowerRequest(ShipSystem::Type(n), value); - }); - systems[n].power_slider->addSnapValue(1.0, 0.1)->setPosition(50, 50, sp::Alignment::TopLeft)->setSize(55, 340); + // Build an element to contain the power slider/progressbar combo. + systems[system_index].power_control = new GuiElement(systems[system_index].system_container_sliders, "PWR_SYSTEM_" + string(system_index) + "_CONTROLS"); + systems[system_index].power_control->setSize(40.0f, GuiElement::GuiSizeMax); - systems[n].coolant_bar = new GuiProgressbar(box, "", 0.0, 10.0, 0.0); - systems[n].coolant_bar->setDrawBackground(false)->setPosition(132.5, 60, sp::Alignment::TopLeft)->setSize(50, 320); + systems[system_index].power_bar = new GuiProgressbar(systems[system_index].power_control, "PWR_SYSTEM_" + string(system_index) + "_POWER_BAR", 0.0f, 3.0f, 1.0f); + systems[system_index].power_bar->setDrawBackground(false)->setColor(energy_color_background)->setSize(GuiElement::GuiSizeMax, GuiElement::GuiSizeMax); + // Vertical margins fit the bar to the 0-100 marks on the slider, which differ from the top and bottom extents. + // Horizontal margins needed to fit the bar inside of the default slider background image's borders. + systems[system_index].power_bar->setAttribute("margin", "3, 20"); - systems[n].coolant_slider = new GuiSlider(box, "", 10.0, 0.0, 0.0, [n](float value) { - my_player_info->commandSetSystemCoolantRequest(ShipSystem::Type(n), value); - }); - systems[n].coolant_slider->setPosition(130, 50, sp::Alignment::TopLeft)->setSize(55, 340); + systems[system_index].power_slider = new GuiSlider(systems[system_index].power_control, "PWR_SYSTEM_" + string(system_index) + "_POWER_SLIDER", 3.0f, 0.0f, 1.0f, + [system_index](float value) + { + my_player_info->commandSetSystemPowerRequest(ShipSystem::Type(system_index), value); + } + ); + systems[system_index].power_slider->setPosition(0.0f, 0.0f, sp::Alignment::TopLeft)->setSize(GuiElement::GuiSizeMax, GuiElement::GuiSizeMax); + // Snap points copied from Engineering. + for (float snap_point = 0.0f; snap_point <= 3.0f; snap_point += 0.5f) + systems[system_index].power_slider->addSnapValue(snap_point, snap_point == 1.0f ? 0.1f : 0.01f); - systems[n].heat_bar = new GuiProgressbar(box, "", 0.0, 1.0, 0.0); - systems[n].heat_bar->setPosition(210, 60, sp::Alignment::TopLeft)->setSize(50, 320); - } + // Build the coolant slider. + systems[system_index].coolant_label = new GuiLabel(systems[system_index].system_container_sliders, "PWR_SYSTEM_" + string(system_index) + "_COOLANT_LABEL", tr("button", "Coolant"), 30.0f); + systems[system_index].coolant_label + ->setVertical() + ->setAlignment(sp::Alignment::CenterLeft) + ->setSize(30.0f, GuiElement::GuiSizeMax) + ->setAttribute("margin", "0, 10, 0, 0"); - (new GuiCustomShipFunctions(this, CrewPosition::powerManagement, ""))->setPosition(-20, 120, sp::Alignment::TopRight)->setSize(250, GuiElement::GuiSizeMax); + // Build an element to contain the slider/progressbar combos. + systems[system_index].coolant_control = new GuiElement(systems[system_index].system_container_sliders, "PWR_SYSTEM_" + string(system_index) + "_CONTROLS"); + systems[system_index].coolant_control->setSize(40.0f, GuiElement::GuiSizeMax); - previous_energy_level = 0.0; - average_energy_delta = 0.0; - previous_energy_measurement = 0.0; + systems[system_index].coolant_bar = new GuiProgressbar(systems[system_index].coolant_control, "PWR_SYSTEM_" + string(system_index) + "_COOLANT_BAR", 0.0f, 10.0f, 1.0f); + systems[system_index].coolant_bar->setDrawBackground(false)->setColor(coolant_color_background)->setSize(GuiElement::GuiSizeMax, GuiElement::GuiSizeMax); + // Vertical margins fit the bar to the 0-100 marks on the slider, which differ from the top and bottom extents. + // Horizontal margins needed to fit the bar inside of the default slider background image's borders. + systems[system_index].coolant_bar->setAttribute("margin", "3, 20"); - // TODO: Hotkey help overlay + systems[system_index].coolant_slider = new GuiSlider(systems[system_index].coolant_control, "PWR_SYSTEM_" + string(system_index) + "_COOLANT_SLIDER", 10.0f, 0.0f, 1.0f, + [system_index](float value) + { + my_player_info->commandSetSystemCoolantRequest(ShipSystem::Type(system_index), value); + } + ); + systems[system_index].coolant_slider->setPosition(0.0f, 0.0f, sp::Alignment::TopLeft)->setSize(GuiElement::GuiSizeMax, GuiElement::GuiSizeMax); + // Snap points copied from Engineering. + for (float snap_point = 0.0f; snap_point <= 10.0f; snap_point += 2.5f) + systems[system_index].coolant_slider->addSnapValue(snap_point, 0.1f); + + // Build the heat bar. + systems[system_index].heat_label = new GuiLabel(systems[system_index].system_container_sliders, "PWR_SYSTEM_" + string(system_index) + "_HEAT_LABEL", tr("button", "Heat"), 30.0f); + systems[system_index].heat_label + ->setVertical() + ->setAlignment(sp::Alignment::CenterLeft) + ->setSize(30.0f, GuiElement::GuiSizeMax) + ->setAttribute("margin", "0, 10, 0, 0"); + systems[system_index].heat_bar = new GuiProgressbar(systems[system_index].system_container_sliders, "PWR_SYSTEM_" + string(system_index) + "_HEAT_BAR", 0.0f, 1.0f, 0.0f); + systems[system_index].heat_bar->setSize(40.0f, GuiElement::GuiSizeMax); + // Vertical margins keep the bar aligned with the sliders' Progressbars. + // If sliders gain an uncapped option, we might be able to do away with + // this. + systems[system_index].heat_bar->setAttribute("margin", "0, 20"); + + systems[system_index].heat_arrow = new GuiArrow(systems[system_index].heat_bar, "PWR_SYSTEM_" + string(system_index) + "_HEAT_ARROW", 90.0f); + systems[system_index].heat_arrow->setSize(GuiElement::GuiSizeMax, GuiElement::GuiSizeMax); + } + + // If this system doesn't exist on this ship, it'll be hidden. + // Return false to communicate this, for instance to skip assigning a row + // index upon panel population, so subsequent panels behave as expected. + if (!ShipSystem::get(my_spaceship, ShipSystem::Type(system_index))) return false; + + return true; } void PowerManagementScreen::onDraw(sp::RenderTarget& renderer) { GuiOverlay::onDraw(renderer); - if (my_spaceship) + if (!my_spaceship) return; + + auto coolant = my_spaceship.getComponent(); + float unused_coolant = 0.0f; + // Get view size and calculate content dimensions. + const glm::vec2 old_size = view_size; + view_size = getRect().size; + // "Main content" refers to the area containing the systems grid. + const float main_content_height = view_size.y - float(layout_margin * 2) - status_bar_height; + + // Show or hide custom functions depending on whether any exist. + // Reduce the horizontal view size if showoing the custom functions column. + if (custom_functions->hasEntries()) { - auto reactor = my_spaceship.getComponent(); - auto coolant = my_spaceship.getComponent(); - if (reactor) { - //Update the energy usage. - if (previous_energy_measurement == 0.0f) - { - previous_energy_level = reactor->energy; - previous_energy_measurement = engine->getElapsedTime(); - }else{ - if (previous_energy_measurement != engine->getElapsedTime()) - { - float delta_t = engine->getElapsedTime() - previous_energy_measurement; - float delta_e = reactor->energy - previous_energy_level; - float delta_e_per_second = delta_e / delta_t; - average_energy_delta = average_energy_delta * 0.99f + delta_e_per_second * 0.01f; + custom_functions->show(); + view_size.x -= custom_functions_width; + } + else custom_functions->hide(); - previous_energy_level = reactor->energy; - previous_energy_measurement = engine->getElapsedTime(); - } - } - energy_display->setValue(string(int(reactor->energy)) + " (" + string(int(average_energy_delta * 60.0f)) + "/m)"); + // Force a hard limit on status bar width in wide views. + if (view_size.x > 1360.0f) + { + status_bar->layout.fill_width = false; + status_bar->setSize(panel_size.x * 4.0f, status_bar_height); + } + else status_bar->setSize(GuiElement::GuiSizeMax, status_bar_height); + + // Count active systems for row assignments. + // We don't want systems that aren't added to this ship to be counted + // toward row assignments. + const int old_system_count = active_system_count; + active_system_count = 0; + for (int n = 0; n < ShipSystem::COUNT; n++) + active_system_count += ShipSystem::get(my_spaceship, ShipSystem::Type(n)) ? 1 : 0; + + // If the view size or active systems count have changed, update the + // systems grid. + if (old_size != view_size || old_system_count != active_system_count) + { + // Determine number of panels per row by available width / panel width. + // Ensure at least one panel per row. + const int panels_per_row = std::max(1, int((view_size.x - 40.0f) / panel_size.x)); + + // Compute how many rows are required. + const int new_rows = std::max(1, (active_system_count + (panels_per_row - 1)) / panels_per_row); + + // Reduce panel size height to squeeze in more panels, but only if necessary. + panel_size.y = std::clamp(main_content_height / new_rows, 250.0f, 380.0f); + for (auto resized_row : systems_rows) resized_row->setSize(GuiElement::GuiSizeMax, panel_size.y); + + // If the systems or window size have changed to require more rows, + // add rows. + while (int(systems_rows.size()) < new_rows) + { + GuiElement* systems_row = new GuiElement(this->systems_grid, "PWR_SYSTEMS_ROW_" + string(int(systems_rows.size()) + 1)); + systems_row->setSize(GuiElement::GuiSizeMax, panel_size.y); + systems_row->setAttribute("layout", "horizontal"); + systems_rows.emplace_back(systems_row); } + + // If the systems or window size have changed to require fewer rows, + // destroy unnecessary rows. + while (!systems_rows.empty() && int(systems_rows.size()) > new_rows) + { + systems_rows.back()->destroy(); + systems_rows.pop_back(); + } + + // Place each system into a row while skipping absent systems. + // If a system is added or removed from a ship during play, the rows + // should automatically repopulate. + int row_index = 0; + for (int n = 0; n < ShipSystem::COUNT; n++) + { + if (populateSystemPanel(n, systems_rows[std::clamp(row_index / panels_per_row, 0, int(systems_rows.size()) - 1)])) + row_index++; + } + } + + // Update reactor-related properties of energy and heat. + if (auto reactor = my_spaceship.getComponent()) + { + // Update energy usage. + if (previous_energy_measurement == 0.0f) + { + previous_energy_level = reactor->energy; + previous_energy_measurement = engine->getElapsedTime(); + } + else if (previous_energy_measurement != engine->getElapsedTime()) + { + float delta_t = engine->getElapsedTime() - previous_energy_measurement; + float delta_e = reactor->energy - previous_energy_level; + float delta_e_per_second = delta_e / delta_t; + average_energy_delta = average_energy_delta * 0.99f + delta_e_per_second * 0.01f; + + previous_energy_level = reactor->energy; + previous_energy_measurement = engine->getElapsedTime(); + } + + // Update energy status bar. + const float energy_delta_per_minute = average_energy_delta * 60.0f; + + energy_display->setValue( + string(int(reactor->energy)) + "/" + string(int(reactor->max_energy)) + "\n" + tr("{energy_delta}/min.").format( + { + {"energy_delta", string(int(energy_delta_per_minute))} + } + )); + + energy_capacity_gauge->setRange(0.0f, reactor->max_energy)->setValue(reactor->energy); + + energy_delta_arrow + ->setAngle(energy_delta_per_minute > 0.0f ? 180.0f : 0.0f) + ->setColor(glm::u8vec4(255, 255, 255, std::min(255, int(255.0f * fabs(energy_delta_per_minute / 250.0f))))) + ->setVisible(energy_delta_per_minute != 0.0f); + } + else energy_capacity_gauge->hide(); + + if (coolant) + { coolant_display->setVisible(coolant); - if (coolant) - coolant_display->setValue(string(int(coolant->max * 100.0f / coolant->max_coolant_per_system)) + "%"); + coolant_distribution_gauge->setVisible(coolant); + unused_coolant = coolant->max; + } - for(int n=0; nsetVisible(sys); - if (sys) { - systems[n].power_slider->setValue(sys->power_request); - systems[n].coolant_slider->setVisible(coolant); - if (coolant) { - systems[n].coolant_slider->setValue(std::min(sys->coolant_request, coolant->max)); - systems[n].coolant_slider->setEnable(!coolant->auto_levels); - } + systems[n].container->show(); + + // Update the system's power request label, slider, and bar. + systems[n].power_label->setText(tr("Power: {value}%").format({{"value", static_cast(nearbyint(sys->power_level * 100.0f))}})); + systems[n].power_slider->setValue(sys->power_request); + systems[n].power_bar->setValue(sys->power_level); - float heat = sys->heat_level; - float power = sys->power_level; - float coolant = sys->coolant_level; - systems[n].heat_bar->setValue(heat)->setColor(glm::u8vec4(128, 128 * (1.0f - heat), 0, 255)); - systems[n].power_bar->setValue(power)->setColor(glm::u8vec4(255, 255, 0, 255)); - systems[n].coolant_bar->setValue(coolant)->setColor(glm::u8vec4(0, 128, 255, 255)); + // Update the system's coolant request slider and actual allocation. + if (coolant) + { + const float coolant_level = sys->coolant_level; + unused_coolant -= coolant_level; + + systems[n].coolant_label->setText(tr("Coolant: {value}%").format({{"value", static_cast(nearbyint(coolant_level * 10.0f))}})); + systems[n].coolant_slider + ->setValue(std::min(sys->coolant_request, coolant->max)) + ->setVisible(!coolant->auto_levels); + systems[n].coolant_bar + ->setDrawBackground(coolant->auto_levels) + ->setValue(coolant_level) + ->setColor(coolant->auto_levels ? coolant_color_foreground : coolant_color_background) + ->setAttribute("margin", coolant->auto_levels ? "0, 20" : "3, 20"); } + else systems[n].coolant_slider->hide(); + + // Update the system's heat level. + // The bar turns redder as the heat level increases. + const float heat_level = sys->heat_level; + const float heat_delta = sys->getHeatingDelta(); + + systems[n].heat_label->setText(tr("Heat: {value}%").format({{"value", static_cast(nearbyint(heat_level * 100.0f))}})); + + systems[n].heat_bar + ->setValue(heat_level) + ->setColor(glm::u8vec4(128, int(128.0f * (1.0f - heat_level)), 0, 255)); + + + // Point the heat arrow to indicate system heat delta. + systems[n].heat_arrow->setAngle(heat_delta > 0.0f ? 90.0f : -90.0f); + systems[n].heat_arrow->setVisible(heat_level > 0.0f); + systems[n].heat_arrow->setColor(glm::u8vec4(255, 255, 255, std::min(255, int(255.0f * fabs(heat_delta))))); } + else systems[n].container->hide(); + } + + // Update coolant distribution and capacity gauges in the status bar. + if (coolant) + { + coolant_display->setValue(tr("{unused}/{capacity}").format( + { + {"unused", string(int(nearbyint(unused_coolant * 100.0f / coolant->max_coolant_per_system)))}, + {"capacity", string(int(nearbyint(coolant->max * 100.0f / coolant->max_coolant_per_system)))} + } + )); + coolant_distribution_gauge->setValue(unused_coolant / coolant->max); } } @@ -130,37 +424,47 @@ void PowerManagementScreen::onUpdate() { if (my_spaceship && isVisible()) { - auto coolant = my_spaceship.getComponent(); - for(unsigned int n=0; n(n); + // Handle hotkeys for setting power values. float set_value = keys.engineering_set_power_for_system[n].getValue() * 3.0f; auto sys = ShipSystem::get(my_spaceship, static_cast(n)); + if (sys && set_value != sys->power_request && (set_value != 0.0f || set_power_active[n])) { my_player_info->commandSetSystemPowerRequest(static_cast(n), set_value); - set_power_active[n] = set_value != 0.0f; //Make sure the next update is send, even if it is back to zero. + // Ensure the next update is sent, even if it is back to zero. + set_power_active[n] = set_value != 0.0f; } - if (coolant) { + + // Handle hotkeys for setting coolant values. + if (auto coolant = my_spaceship.getComponent()) + { set_value = keys.engineering_set_coolant_for_system[n].getValue() * coolant->max_coolant_per_system; + if (sys && set_value != sys->coolant_request && (set_value != 0.0f || set_coolant_active[n])) { my_player_info->commandSetSystemCoolantRequest(static_cast(n), set_value); - set_coolant_active[n] = set_value != 0.0f; //Make sure the next update is send, even if it is back to zero. + // Ensure the next update is sent, even if it is back to zero. + set_coolant_active[n] = set_value != 0.0f; } } } // Don't act if the selected system doesn't exist. - if (!ShipSystem::get(my_spaceship, selected_system)) - return; + if (!ShipSystem::get(my_spaceship, selected_system)) return; // If we selected a system, check for the power/coolant modifier. if (selected_system != ShipSystem::Type::None) { GuiSlider* power_slider = systems[int(selected_system)].power_slider; - // Note the code duplication with crew6/engineeringScreen + // Handle hotkeys for setting power for the selected system to a given level. + // Note code duplication with crew6/engineeringScreen. + // Power Management should probably instead use Engineering's hotkeys for these. if (keys.engineering_set_power_000.getDown()) { power_slider->setValue(0.0f); @@ -202,22 +506,24 @@ void PowerManagementScreen::onUpdate() my_player_info->commandSetSystemPowerRequest(selected_system, power_slider->getValue()); } + // Handle hotkeys for incremental power changes to the selected system. auto power_adjust = (keys.engineering_increase_power.getValue() - keys.engineering_decrease_power.getValue()) * 0.1f; if (power_adjust != 0.0f) { - auto sys = ShipSystem::get(my_spaceship, selected_system); - if (sys) { + if (auto sys = ShipSystem::get(my_spaceship, selected_system)) + { power_slider->setValue(sys->power_request + power_adjust); my_player_info->commandSetSystemPowerRequest(selected_system, power_slider->getValue()); } } + // Handle hotkeys for incremental coolant changes to the selected system. GuiSlider* coolant_slider = systems[int(selected_system)].coolant_slider; auto coolant_adjust = (keys.engineering_increase_coolant.getValue() - keys.engineering_decrease_coolant.getValue()) * 0.5f; if (coolant_adjust != 0.0f) { - auto sys = ShipSystem::get(my_spaceship, selected_system); - if (sys) { + if (auto sys = ShipSystem::get(my_spaceship, selected_system)) + { coolant_slider->setValue(sys->coolant_request + coolant_adjust); my_player_info->commandSetSystemCoolantRequest(selected_system, coolant_slider->getValue()); } diff --git a/src/screens/extra/powerManagement.h b/src/screens/extra/powerManagement.h index ee1d5975a7..5c1ad29f97 100644 --- a/src/screens/extra/powerManagement.h +++ b/src/screens/extra/powerManagement.h @@ -1,43 +1,74 @@ -#ifndef POWER_MANAGEMENT_H -#define POWER_MANAGEMENT_H +#pragma once #include "gui/gui2_overlay.h" #include "components/shipsystem.h" +class GuiArrow; +class GuiCustomShipFunctions; +class GuiKeyValueDisplay; +class GuiLabel; class GuiPanel; -class GuiSlider; class GuiProgressbar; -class GuiKeyValueDisplay; +class GuiSlider; class PowerManagementScreen : public GuiOverlay { private: + glm::vec2 view_size; + int active_system_count; + GuiElement* status_bar; GuiKeyValueDisplay* energy_display; + GuiProgressbar* energy_capacity_gauge; + GuiArrow* energy_delta_arrow; GuiKeyValueDisplay* coolant_display; + GuiProgressbar* coolant_distribution_gauge; + GuiElement* systems_grid; + GuiCustomShipFunctions* custom_functions; - float previous_energy_measurement; + constexpr static int layout_margin = 20; + constexpr static float status_bar_height = 100.0f; + constexpr static float custom_functions_width = 250.0f; + constexpr static glm::u8vec3 coolant_color{32, 128, 128}; + constexpr static glm::u8vec4 coolant_color_foreground{coolant_color, 128}; + constexpr static glm::u8vec4 coolant_color_background{coolant_color, 255}; + constexpr static glm::u8vec3 energy_color{192, 192, 32}; + constexpr static glm::u8vec4 energy_color_background{energy_color, 255}; + + glm::vec2 panel_size; float previous_energy_level; float average_energy_delta; - ShipSystem::Type selected_system = ShipSystem::Type::None; + float previous_energy_measurement; + ShipSystem::Type selected_system; - class SystemRow + class SystemPanel { public: - GuiPanel* box; + SystemPanel() + : container(nullptr), system_label(nullptr), system_container_sliders(nullptr), power_control(nullptr), power_slider(nullptr), power_bar(nullptr), coolant_control(nullptr), coolant_slider(nullptr), coolant_bar(nullptr), heat_bar(nullptr) + {} + GuiPanel* container; + GuiKeyValueDisplay* system_label; + GuiElement* system_container_sliders; + GuiLabel* power_label; + GuiElement* power_control; GuiSlider* power_slider; - GuiSlider* coolant_slider; - GuiProgressbar* heat_bar; GuiProgressbar* power_bar; + GuiLabel* coolant_label; + GuiElement* coolant_control; + GuiSlider* coolant_slider; GuiProgressbar* coolant_bar; + GuiLabel* heat_label; + GuiArrow* heat_arrow; + GuiProgressbar* heat_bar; }; - SystemRow systems[ShipSystem::COUNT]; + SystemPanel systems[ShipSystem::COUNT]; + std::vector systems_rows; bool set_power_active[ShipSystem::COUNT] = {false}; bool set_coolant_active[ShipSystem::COUNT] = {false}; public: PowerManagementScreen(GuiContainer* owner); void onDraw(sp::RenderTarget& target) override; + bool populateSystemPanel(int system_index, GuiElement* systems_row); virtual void onUpdate() override; }; - -#endif//POWER_MANAGEMENT_H From c4e95a322cf91ecbf6dd7a0abaa2fd57f258ead7 Mon Sep 17 00:00:00 2001 From: oznogon Date: Sat, 11 Apr 2026 23:04:38 -0700 Subject: [PATCH 5/7] Hide, resize Power Mgmt systems when no Reactor + Coolant --- src/screens/extra/powerManagement.cpp | 39 ++++++++++++++++++++++----- src/screens/extra/powerManagement.h | 5 +++- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/src/screens/extra/powerManagement.cpp b/src/screens/extra/powerManagement.cpp index f18b9ff7b3..4b3928dc6a 100644 --- a/src/screens/extra/powerManagement.cpp +++ b/src/screens/extra/powerManagement.cpp @@ -92,7 +92,10 @@ bool PowerManagementScreen::populateSystemPanel(int system_index, GuiElement* sy // If a container already exists for this system, reparent it to the new row instead of recreating widgets. if (auto this_container = systems[system_index].container) + { this_container->setParent(systems_row); + this_container->setSize(panel_size.x, GuiElement::GuiSizeMax); + } else { // Create the container and its child widgets if they don't exist yet. @@ -136,6 +139,7 @@ bool PowerManagementScreen::populateSystemPanel(int system_index, GuiElement* sy } // Panel labels use GuiKeyValueDisplay for their icon support. + systems[system_index].icon_file = icon_file; systems[system_index].system_label = new GuiKeyValueDisplay(systems[system_index].container, "PWR_SYSTEM_" + string(system_index) + "_NAME_LABEL", 0.15f, "", getLocaleSystemName(ShipSystem::Type(system_index))); systems[system_index].system_label->setIcon(icon_file)->setTextSize(20.0f)->setSize(GuiElement::GuiSizeMax, 50.0f); systems[system_index].system_label->setAttribute("margin", "0, 0, -12, 0"); @@ -267,10 +271,13 @@ void PowerManagementScreen::onDraw(sp::RenderTarget& renderer) for (int n = 0; n < ShipSystem::COUNT; n++) active_system_count += ShipSystem::get(my_spaceship, ShipSystem::Type(n)) ? 1 : 0; - // If the view size or active systems count have changed, update the - // systems grid. - if (old_size != view_size || old_system_count != active_system_count) + // If the view size, active systems count, or panel width have changed, update + // the systems grid. + const float desired_panel_width = coolant ? panel_width_with_coolant : panel_width_without_coolant; + if (old_size != view_size || old_system_count != active_system_count || panel_size.x != desired_panel_width) { + panel_size.x = desired_panel_width; + // Determine number of panels per row by available width / panel width. // Ensure at least one panel per row. const int panels_per_row = std::max(1, int((view_size.x - 40.0f) / panel_size.x)); @@ -309,10 +316,12 @@ void PowerManagementScreen::onDraw(sp::RenderTarget& renderer) if (populateSystemPanel(n, systems_rows[std::clamp(row_index / panels_per_row, 0, int(systems_rows.size()) - 1)])) row_index++; } + } // Update reactor-related properties of energy and heat. - if (auto reactor = my_spaceship.getComponent()) + auto* reactor = my_spaceship.getComponent(); + if (reactor) { // Update energy usage. if (previous_energy_measurement == 0.0f) @@ -357,6 +366,9 @@ void PowerManagementScreen::onDraw(sp::RenderTarget& renderer) unused_coolant = coolant->max; } + // Limit power to 100% when neither a reactor nor coolant is present. + const float power_max = (reactor || coolant) ? 3.0f : 1.0f; + // Update system properties. for (int n = 0; n < ShipSystem::COUNT; n++) { @@ -366,12 +378,18 @@ void PowerManagementScreen::onDraw(sp::RenderTarget& renderer) // Update the system's power request label, slider, and bar. systems[n].power_label->setText(tr("Power: {value}%").format({{"value", static_cast(nearbyint(sys->power_level * 100.0f))}})); - systems[n].power_slider->setValue(sys->power_request); - systems[n].power_bar->setValue(sys->power_level); + systems[n].power_slider->setRange(power_max, 0.0f)->setValue(sys->power_request); + systems[n].power_bar->setRange(0.0f, power_max)->setValue(sys->power_level); // Update the system's coolant request slider and actual allocation. if (coolant) { + systems[n].system_label->setIcon(systems[n].icon_file); + systems[n].coolant_label->show(); + systems[n].coolant_control->show(); + systems[n].heat_label->show(); + systems[n].heat_bar->show(); + const float coolant_level = sys->coolant_level; unused_coolant -= coolant_level; @@ -385,7 +403,14 @@ void PowerManagementScreen::onDraw(sp::RenderTarget& renderer) ->setColor(coolant->auto_levels ? coolant_color_foreground : coolant_color_background) ->setAttribute("margin", coolant->auto_levels ? "0, 20" : "3, 20"); } - else systems[n].coolant_slider->hide(); + else + { + systems[n].system_label->setIcon(""); + systems[n].coolant_label->hide(); + systems[n].coolant_control->hide(); + systems[n].heat_label->hide(); + systems[n].heat_bar->hide(); + } // Update the system's heat level. // The bar turns redder as the heat level increases. diff --git a/src/screens/extra/powerManagement.h b/src/screens/extra/powerManagement.h index 5c1ad29f97..2dcda79eaa 100644 --- a/src/screens/extra/powerManagement.h +++ b/src/screens/extra/powerManagement.h @@ -33,8 +33,10 @@ class PowerManagementScreen : public GuiOverlay constexpr static glm::u8vec4 coolant_color_background{coolant_color, 255}; constexpr static glm::u8vec3 energy_color{192, 192, 32}; constexpr static glm::u8vec4 energy_color_background{energy_color, 255}; + constexpr static float panel_width_with_coolant = 300.0f; + constexpr static float panel_width_without_coolant = 200.0f; - glm::vec2 panel_size; + glm::vec2 panel_size{panel_width_with_coolant, 380.0f}; float previous_energy_level; float average_energy_delta; float previous_energy_measurement; @@ -48,6 +50,7 @@ class PowerManagementScreen : public GuiOverlay {} GuiPanel* container; GuiKeyValueDisplay* system_label; + string icon_file; GuiElement* system_container_sliders; GuiLabel* power_label; GuiElement* power_control; From 54e5ea8c3c17a4aacb29c6ddaa54ac7e48db38ea Mon Sep 17 00:00:00 2001 From: oznogon Date: Sun, 3 May 2026 01:49:00 -0700 Subject: [PATCH 6/7] Fix PowerManagement uninitialized rate vars --- src/screens/extra/powerManagement.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/screens/extra/powerManagement.h b/src/screens/extra/powerManagement.h index 2dcda79eaa..546d415d7b 100644 --- a/src/screens/extra/powerManagement.h +++ b/src/screens/extra/powerManagement.h @@ -37,9 +37,9 @@ class PowerManagementScreen : public GuiOverlay constexpr static float panel_width_without_coolant = 200.0f; glm::vec2 panel_size{panel_width_with_coolant, 380.0f}; - float previous_energy_level; - float average_energy_delta; - float previous_energy_measurement; + float previous_energy_level = 0.0f; + float average_energy_delta = 0.0f; + float previous_energy_measurement = 0.0f; ShipSystem::Type selected_system; class SystemPanel From 55adf91e97d7b6c44f95ec92123f89266f0a547f Mon Sep 17 00:00:00 2001 From: Oznogon Date: Thu, 14 May 2026 10:00:51 -0700 Subject: [PATCH 7/7] Avoid spurious 'new parent is invalid' debug messages when exiting --- src/gui/gui2_element.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/gui/gui2_element.cpp b/src/gui/gui2_element.cpp index 79203c7668..3d2b5cfb82 100644 --- a/src/gui/gui2_element.cpp +++ b/src/gui/gui2_element.cpp @@ -127,8 +127,10 @@ GuiElement* GuiElement::setParent(GuiContainer* new_parent) // Update owner pointer. this->owner = new_parent; } - else + else if (!new_parent) + { LOG(Debug, "GuiElement::setParent called, but new parent is invalid."); + } } else LOG(Debug, "GuiElement::setParent called, but old owner is invalid.");