From 5a92c22d90cd6f3720a6620e8d7f77a3cbeb2441 Mon Sep 17 00:00:00 2001 From: Vantha Date: Thu, 5 Mar 2026 12:31:47 +0100 Subject: [PATCH] Compute actual size of GUI objects lazily This shifts the responsibility of updating the actual size more towards IGUIObject, and enables only ever doing it when the value is actually needed. This allows us to remove the delay of size changed notifications, since the value is now already recalculated as infrequently as possible anyways. All of that ensures that the actual size (returned by GetActualSize) is always up-to-date e.g. when reading it from the parent, which was previously broken. Fixes #8200 --- .../gui/settings/cguisize/getcomputedsize.js | 1 + source/gui/CGUI.cpp | 5 +- source/gui/CGUI.h | 4 +- source/gui/CGUISetting.h | 15 +-- source/gui/ObjectBases/IGUIObject.cpp | 46 ++++--- source/gui/ObjectBases/IGUIObject.h | 37 ++++-- source/gui/ObjectBases/IGUIPanel.cpp | 15 +-- source/gui/ObjectBases/IGUIPanel.h | 8 +- source/gui/ObjectBases/IGUIScrollBarOwner.h | 4 +- source/gui/ObjectBases/IGUITextOwner.cpp | 6 +- source/gui/ObjectBases/IGUITextOwner.h | 8 +- source/gui/ObjectTypes/CButton.cpp | 16 +-- source/gui/ObjectTypes/CButton.h | 6 +- source/gui/ObjectTypes/CChart.cpp | 20 ++-- source/gui/ObjectTypes/CChart.h | 6 +- source/gui/ObjectTypes/CCheckBox.cpp | 4 +- source/gui/ObjectTypes/CDropDown.cpp | 70 +++++------ source/gui/ObjectTypes/CDropDown.h | 6 +- source/gui/ObjectTypes/CImage.cpp | 4 +- source/gui/ObjectTypes/CInput.cpp | 112 +++++++++--------- source/gui/ObjectTypes/CInput.h | 6 +- source/gui/ObjectTypes/CList.cpp | 8 +- source/gui/ObjectTypes/CList.h | 10 +- source/gui/ObjectTypes/CMiniMap.cpp | 18 +-- source/gui/ObjectTypes/COList.cpp | 14 +-- source/gui/ObjectTypes/CProgressBar.cpp | 8 +- source/gui/ObjectTypes/CScrollPanel.cpp | 109 ++++++----------- source/gui/ObjectTypes/CScrollPanel.h | 9 +- source/gui/ObjectTypes/CSlider.cpp | 10 +- source/gui/ObjectTypes/CText.cpp | 34 +++--- source/gui/ObjectTypes/CText.h | 6 +- source/gui/ObjectTypes/CTooltip.cpp | 12 +- source/gui/ObjectTypes/CTooltip.h | 6 +- source/gui/Scripting/JSInterface_CGUISize.cpp | 5 +- source/gui/Scripting/JSInterface_GUIProxy.cpp | 4 +- source/gui/tests/test_GUISetting.h | 38 +++--- source/pch/gui/precompiled.h | 4 +- 37 files changed, 323 insertions(+), 371 deletions(-) create mode 100644 binaries/data/mods/_test.gui/gui/settings/cguisize/getcomputedsize.js diff --git a/binaries/data/mods/_test.gui/gui/settings/cguisize/getcomputedsize.js b/binaries/data/mods/_test.gui/gui/settings/cguisize/getcomputedsize.js new file mode 100644 index 0000000000..521d540b64 --- /dev/null +++ b/binaries/data/mods/_test.gui/gui/settings/cguisize/getcomputedsize.js @@ -0,0 +1 @@ +testObject.getComputedSize(); \ No newline at end of file diff --git a/source/gui/CGUI.cpp b/source/gui/CGUI.cpp index 59e8f39d99..083434ffea 100644 --- a/source/gui/CGUI.cpp +++ b/source/gui/CGUI.cpp @@ -348,7 +348,6 @@ JSObject* CGUI::TickObjects(const ScriptRequest& rq, Script::StructuredClone ini sendingPromise = CallPageInit(rq, initData, hotloadData, scriptName); } - m_BaseObject->RecurseObject(&IGUIObject::IsHidden, &IGUIObject::DispatchDelayedSettingChanges); m_BaseObject->RecurseObject(&IGUIObject::IsHiddenOrGhostOrOutOfBoundaries, &IGUIObject::Tick); SendEventToAll(EventNameTick); m_Tooltip.Update(FindObjectUnderMouse(), m_MousePos, *this); @@ -387,7 +386,7 @@ void CGUI::DrawSprite(const CGUISpriteInstance& Sprite, CCanvas2D& canvas, const void CGUI::UpdateResolution() { - m_BaseObject->RecurseObject(nullptr, &IGUIObject::UpdateCachedSize); + m_BaseObject->RecurseObject(nullptr, &IGUIObject::HandleSizeChanged); } std::unique_ptr CGUI::ConstructObject(const CStr& str) @@ -593,7 +592,7 @@ void CGUI::LoadXmlFile(const VfsPath& Filename, std::unordered_set& Pat void CGUI::LoadedXmlFiles() { - m_BaseObject->RecurseObject(nullptr, &IGUIObject::UpdateCachedSize); + m_BaseObject->RecurseObject(nullptr, &IGUIObject::HandleSizeChanged); SGUIMessage msg(GUIM_LOAD); m_BaseObject->RecurseObject(nullptr, &IGUIObject::HandleMessage, msg); diff --git a/source/gui/CGUI.h b/source/gui/CGUI.h index cbf3ceaaf4..f442663309 100644 --- a/source/gui/CGUI.h +++ b/source/gui/CGUI.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -250,7 +250,7 @@ public: * * Needs no input since screen resolution is global. * - * @see IGUIObject#UpdateCachedSize() + * @see IGUIObject#HandleSizeChanged() */ void UpdateResolution(); diff --git a/source/gui/CGUISetting.h b/source/gui/CGUISetting.h index a06815f6a5..1f0a6e559f 100644 --- a/source/gui/CGUISetting.h +++ b/source/gui/CGUISetting.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -54,18 +54,6 @@ public: */ virtual void ToJSVal(const ScriptRequest& rq, JS::MutableHandleValue value) = 0; - void DeferSettingChange() - { - delayNotification = true; - } - - void DispatchDelayedSettingChange() - { - if (!std::exchange(delayNotification, false)) - return; - - OnSettingChange(GetName(), true); - } protected: IGUISetting(IGUISetting&& other); IGUISetting& operator=(IGUISetting&& other) = delete; @@ -95,7 +83,6 @@ protected: */ IGUIObject& m_Object; - bool delayNotification{false}; private: CStr m_Name; }; diff --git a/source/gui/ObjectBases/IGUIObject.cpp b/source/gui/ObjectBases/IGUIObject.cpp index f2e9b61996..9651210c27 100644 --- a/source/gui/ObjectBases/IGUIObject.cpp +++ b/source/gui/ObjectBases/IGUIObject.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -137,8 +137,8 @@ void IGUIObject::SettingChanged(const CStr& Setting, const bool SendMessage) { if (Setting == "size") { - // If setting was "size", we need to re-cache itself and all children - RecurseObject(nullptr, &IGUIObject::UpdateCachedSize); + // Notify all children that they'll have to re-cache their actual size. + RecurseObject(nullptr, &IGUIObject::HandleSizeChanged); } else if (Setting == "hidden") { @@ -181,7 +181,7 @@ bool IGUIObject::IsMouseOver() const if (m_VisibleArea) return m_VisibleArea.PointInside(m_pGUI.GetMousePos()); - return m_CachedActualSize.PointInside(m_pGUI.GetMousePos()); + return GetActualSize().PointInside(m_pGUI.GetMousePos()); } void IGUIObject::UpdateMouseOver(IGUIObject* const& pMouseOver) @@ -241,13 +241,25 @@ void IGUIObject::ResetStates() UpdateMouseOver(nullptr); } -void IGUIObject::UpdateCachedSize() +void IGUIObject::HandleSizeChanged() +{ + m_CachedActualSizeDirty = true; +} + +const CRect& IGUIObject::GetActualSize() const +{ + if (std::exchange(m_CachedActualSizeDirty, false)) + RecalculateActualSize(); + + return m_CachedActualSize; +} + +void IGUIObject::RecalculateActualSize() const { // If absolute="false" and the object has got a parent, - // use its cached size instead of the screen. Notice - // it must have just been cached for it to work. + // use its cached size instead of the screen. if (!m_Absolute && m_pParent && !IsRootObject()) - m_CachedActualSize = m_Size->GetSize(m_pParent->m_CachedActualSize); + m_CachedActualSize = m_Size->GetSize(m_pParent->GetActualSize()); else m_CachedActualSize = m_Size->GetSize(CRect(m_pGUI.GetWindowSize())); @@ -271,14 +283,6 @@ void IGUIObject::UpdateCachedSize() } } -CRect IGUIObject::GetComputedSize() -{ - // Ensure the size is up to date before we use it. - m_Settings.at("size")->DispatchDelayedSettingChange(); - UpdateCachedSize(); - return m_CachedActualSize; -} - bool IGUIObject::ApplyStyle(const CStr& StyleName) { if (!m_pGUI.HasStyle(StyleName)) @@ -548,10 +552,10 @@ void IGUIObject::DrawInArea(CCanvas2D& canvas, CRect& area) { bool isInsideBoundaries = false; RecurseObject(nullptr, &IGUIObject::SetIsInsideBoundaries, isInsideBoundaries); - if (!area.IntersectWith(m_CachedActualSize)) + if (!area.IntersectWith(GetActualSize())) return; - CRect intersection = area.Intersection(m_CachedActualSize); + CRect intersection = area.Intersection(GetActualSize()); m_VisibleArea = intersection; @@ -564,9 +568,3 @@ void IGUIObject::DrawInArea(CCanvas2D& canvas, CRect& area) bool IGUIObject::IsHiddenOrGhostOrOutOfBoundaries() const { return !m_IsInsideBoundaries || IsHiddenOrGhost(); } - -void IGUIObject::DispatchDelayedSettingChanges() -{ - for (const auto& setting : m_Settings) - setting.second->DispatchDelayedSettingChange(); -} diff --git a/source/gui/ObjectBases/IGUIObject.h b/source/gui/ObjectBases/IGUIObject.h index 8c7169bc99..74a14ac170 100644 --- a/source/gui/ObjectBases/IGUIObject.h +++ b/source/gui/ObjectBases/IGUIObject.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -194,12 +194,13 @@ public: * is not wanted in real time, therefore it is cached, update * the cached size with this function. */ - virtual void UpdateCachedSize(); + virtual void HandleSizeChanged(); /** - * Updates and returns the size of the object. + * Get the size of the object. + * Besides m_Size it also depends on the parent's size, hence "actual". */ - virtual CRect GetComputedSize(); + virtual const CRect& GetActualSize() const; virtual const CStrW& GetTooltipText() const { return m_Tooltip; } virtual const CStr& GetTooltipStyle() const { return m_TooltipStyle; } @@ -277,8 +278,6 @@ public: virtual void DrawInArea(CCanvas2D& canvas, CRect& area); - virtual void DispatchDelayedSettingChanges(); - protected: /** * Draws the object. @@ -390,13 +389,6 @@ protected: */ virtual void AdditionalChildrenHandled() {} - /** - * Cached size, real size m_Size is actually dependent on resolution - * and can have different *real* outcomes, this is the real outcome - * cached to avoid slow calculations in real time. - */ - CRect m_CachedActualSize; - /** * Execute the script for a particular action. * Does nothing if no script has been registered for that action. @@ -428,6 +420,11 @@ protected: */ void UpdateMouseOver(IGUIObject* const& pMouseOver); + /** + * Recompute the actual (absolute) size from scratch again and cache it. + */ + virtual void RecalculateActualSize() const; + //@} private: //-------------------------------------------------------- @@ -518,6 +515,20 @@ protected: CRect m_VisibleArea; bool m_IsInsideBoundaries = true; + /** + * The actual size (usually) depends on the size of the parent. + * And to avoid recomputing all the time, it is cached here. + * Do not read from this directly, it is not guaranteed to be up-to-date, + * call GetActualSize() insead. + */ + mutable CRect m_CachedActualSize; + + /** + * Whether m_CacheActualSize is not up-to-date and will have to be recomputed + * when accessing it the next time. + */ + mutable bool m_CachedActualSizeDirty = true; + CGUISimpleSetting m_Enabled; CGUISimpleSetting m_Hidden; CGUISimpleSetting m_Size; diff --git a/source/gui/ObjectBases/IGUIPanel.cpp b/source/gui/ObjectBases/IGUIPanel.cpp index 65355e072a..7c8912ff2c 100644 --- a/source/gui/ObjectBases/IGUIPanel.cpp +++ b/source/gui/ObjectBases/IGUIPanel.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -38,23 +38,16 @@ IGUIPanel::~IGUIPanel() bool IGUIPanel::IsMouseOver() const { + GetActualSize(); // Updates m_CachedLayoutActualSize if necessary. return m_CachedLayoutActualSize.PointInside(m_pGUI.GetMousePos()); } -void IGUIPanel::UpdateCachedSize() +void IGUIPanel::RecalculateActualSize() const { - IGUIObject::UpdateCachedSize(); + IGUIObject::RecalculateActualSize(); m_CachedLayoutActualSize = m_CachedActualSize; } -CRect IGUIPanel::GetComputedSize() -{ - // Ensure the size is up to date before we use it. - m_Settings.at("size")->DispatchDelayedSettingChange(); - UpdateCachedSize(); - return m_CachedLayoutActualSize; -} - const std::vector& IGUIPanel::GetVisibleChildren() const { if (m_Drawing) diff --git a/source/gui/ObjectBases/IGUIPanel.h b/source/gui/ObjectBases/IGUIPanel.h index 30aa35993d..ea213f64fa 100644 --- a/source/gui/ObjectBases/IGUIPanel.h +++ b/source/gui/ObjectBases/IGUIPanel.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -29,15 +29,13 @@ public: IGUIPanel(CGUI& pGUI); virtual ~IGUIPanel(); - virtual void UpdateCachedSize(); - virtual CRect GetComputedSize(); - virtual bool IsMouseOver() const; virtual const std::vector& GetVisibleChildren() const; protected: - CRect m_CachedLayoutActualSize; + virtual void RecalculateActualSize() const; + mutable CRect m_CachedLayoutActualSize; bool m_Drawing = false; }; diff --git a/source/gui/ObjectBases/IGUIScrollBarOwner.h b/source/gui/ObjectBases/IGUIScrollBarOwner.h index adfae306df..23cf1e1d6f 100644 --- a/source/gui/ObjectBases/IGUIScrollBarOwner.h +++ b/source/gui/ObjectBases/IGUIScrollBarOwner.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -70,7 +70,7 @@ public: * Get Scroll Bar reference (it should be transparent it's actually * pointers). */ - virtual IGUIScrollBar& GetScrollBar(const int& index) + virtual IGUIScrollBar& GetScrollBar(const int& index) const { return *m_ScrollBars[index]; } diff --git a/source/gui/ObjectBases/IGUITextOwner.cpp b/source/gui/ObjectBases/IGUITextOwner.cpp index b5ca20b1d0..57907447a7 100644 --- a/source/gui/ObjectBases/IGUITextOwner.cpp +++ b/source/gui/ObjectBases/IGUITextOwner.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -79,7 +79,7 @@ void IGUITextOwner::HandleMessage(SGUIMessage& Message) } } -void IGUITextOwner::UpdateCachedSize() +void IGUITextOwner::HandleSizeChanged() { // update our text positions m_GeneratedTextsValid = false; @@ -103,7 +103,7 @@ void IGUITextOwner::DrawText(CCanvas2D& canvas, size_t index, const CGUIColor& c m_GeneratedTexts.at(index).Draw(m_pObject.GetGUI(), canvas, color, pos, clipping); } -void IGUITextOwner::CalculateTextPosition(CRect& ObjSize, CVector2D& TextPos, CGUIText& Text) +void IGUITextOwner::CalculateTextPosition(const CRect& ObjSize, CVector2D& TextPos, CGUIText& Text) { // The horizontal Alignment is now computed in GenerateText in order to not have to // loop through all of the TextCall objects again. diff --git a/source/gui/ObjectBases/IGUITextOwner.h b/source/gui/ObjectBases/IGUITextOwner.h index ad35aabd34..59b62aa6df 100644 --- a/source/gui/ObjectBases/IGUITextOwner.h +++ b/source/gui/ObjectBases/IGUITextOwner.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -75,9 +75,9 @@ public: virtual void HandleMessage(SGUIMessage& Message); /** - * @see IGUIObject#UpdateCachedSize() + * @see IGUIObject#HandleSizeChanged() */ - virtual void UpdateCachedSize(); + virtual void HandleSizeChanged(); /** * Draws the Text. @@ -115,7 +115,7 @@ protected: /** * Calculate the position for the text, based on the alignment. */ - void CalculateTextPosition(CRect& ObjSize, CVector2D& TextPos, CGUIText& Text); + void CalculateTextPosition(const CRect& ObjSize, CVector2D& TextPos, CGUIText& Text); CGUISimpleSetting m_TextAlign; CGUISimpleSetting m_TextVAlign; diff --git a/source/gui/ObjectTypes/CButton.cpp b/source/gui/ObjectTypes/CButton.cpp index 1f0ac79b93..28c4fc1bb4 100644 --- a/source/gui/ObjectTypes/CButton.cpp +++ b/source/gui/ObjectTypes/CButton.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -53,8 +53,8 @@ void CButton::SetupText() { ENSURE(m_GeneratedTexts.size() == 1); - m_GeneratedTexts[0] = CGUIText(m_pGUI, m_Caption, m_Font, m_CachedActualSize.GetWidth(), m_BufferZone, m_TextAlign, this); - CalculateTextPosition(m_CachedActualSize, m_TextPos, m_GeneratedTexts[0]); + m_GeneratedTexts[0] = CGUIText(m_pGUI, m_Caption, m_Font, GetActualSize().GetWidth(), m_BufferZone, m_TextAlign, this); + CalculateTextPosition(GetActualSize(), m_TextPos, m_GeneratedTexts[0]); } void CButton::ResetStates() @@ -63,10 +63,10 @@ void CButton::ResetStates() IGUIButtonBehavior::ResetStates(); } -void CButton::UpdateCachedSize() +void CButton::HandleSizeChanged() { - IGUIObject::UpdateCachedSize(); - IGUITextOwner::UpdateCachedSize(); + IGUIObject::HandleSizeChanged(); + IGUITextOwner::HandleSizeChanged(); } CSize2D CButton::GetTextSize() @@ -92,7 +92,7 @@ void CButton::Draw(CCanvas2D& canvas) m_pGUI.DrawSprite( GetButtonSprite(m_Sprite, m_SpriteOver, m_SpritePressed, m_SpriteDisabled), canvas, - m_CachedActualSize, + GetActualSize(), m_VisibleArea); DrawText(canvas, 0, ChooseColor(), m_TextPos, m_VisibleArea); @@ -104,7 +104,7 @@ bool CButton::IsMouseOver() const return false; if (!m_MouseEventMask) return true; - return m_MouseEventMask.IsMouseOver(m_pGUI.GetMousePos(), m_CachedActualSize); + return m_MouseEventMask.IsMouseOver(m_pGUI.GetMousePos(), GetActualSize()); } const CGUIColor& CButton::ChooseColor() diff --git a/source/gui/ObjectTypes/CButton.h b/source/gui/ObjectTypes/CButton.h index efea1ddb1b..e27d6b27b4 100644 --- a/source/gui/ObjectTypes/CButton.h +++ b/source/gui/ObjectTypes/CButton.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -44,9 +44,9 @@ public: virtual void ResetStates(); /** - * @see IGUIObject#UpdateCachedSize() + * @see IGUIObject#HandleSizeChanged() */ - virtual void UpdateCachedSize(); + virtual void HandleSizeChanged(); /** * @return the object text size. diff --git a/source/gui/ObjectTypes/CChart.cpp b/source/gui/ObjectTypes/CChart.cpp index 930e39db18..ee703cca7e 100644 --- a/source/gui/ObjectTypes/CChart.cpp +++ b/source/gui/ObjectTypes/CChart.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -53,10 +53,10 @@ CChart::CChart(CGUI& pGUI) { } -void CChart::UpdateCachedSize() +void CChart::HandleSizeChanged() { - IGUIObject::UpdateCachedSize(); - IGUITextOwner::UpdateCachedSize(); + IGUIObject::HandleSizeChanged(); + IGUITextOwner::HandleSizeChanged(); } void CChart::HandleMessage(SGUIMessage& Message) @@ -72,11 +72,11 @@ void CChart::HandleMessage(SGUIMessage& Message) void CChart::DrawAxes(CCanvas2D& canvas) const { canvas.DrawRect(CRect( - m_CachedActualSize.TopLeft(), - m_CachedActualSize.BottomLeft() + CVector2D(m_AxisWidth, 0.0f)), m_AxisColor); + GetActualSize().TopLeft(), + GetActualSize().BottomLeft() + CVector2D(m_AxisWidth, 0.0f)), m_AxisColor); canvas.DrawRect(CRect( - m_CachedActualSize.BottomLeft() - CVector2D(0.0f, m_AxisWidth), - m_CachedActualSize.BottomRight()), m_AxisColor); + GetActualSize().BottomLeft() - CVector2D(0.0f, m_AxisWidth), + GetActualSize().BottomRight()), m_AxisColor); } void CChart::Draw(CCanvas2D& canvas) @@ -130,8 +130,8 @@ void CChart::Draw(CCanvas2D& canvas) CRect CChart::GetChartRect() const { return CRect( - m_CachedActualSize.TopLeft() + CVector2D(m_AxisWidth, m_AxisWidth), - m_CachedActualSize.BottomRight() - CVector2D(m_AxisWidth, m_AxisWidth) + GetActualSize().TopLeft() + CVector2D(m_AxisWidth, m_AxisWidth), + GetActualSize().BottomRight() - CVector2D(m_AxisWidth, m_AxisWidth) ); } diff --git a/source/gui/ObjectTypes/CChart.h b/source/gui/ObjectTypes/CChart.h index 9e3fc2000f..6a45c5792c 100644 --- a/source/gui/ObjectTypes/CChart.h +++ b/source/gui/ObjectTypes/CChart.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -58,9 +58,9 @@ public: protected: /** - * @see IGUIObject#UpdateCachedSize() + * @see IGUIObject#HandleSizeChanged() */ - void UpdateCachedSize(); + void HandleSizeChanged(); /** * @see IGUIObject#HandleMessage() diff --git a/source/gui/ObjectTypes/CCheckBox.cpp b/source/gui/ObjectTypes/CCheckBox.cpp index 1a686c9d6e..9f47c667a9 100644 --- a/source/gui/ObjectTypes/CCheckBox.cpp +++ b/source/gui/ObjectTypes/CCheckBox.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -69,6 +69,6 @@ void CCheckBox::Draw(CCanvas2D& canvas) GetButtonSprite(m_SpriteChecked, m_SpriteCheckedOver, m_SpriteCheckedPressed, m_SpriteCheckedDisabled) : GetButtonSprite(m_SpriteUnchecked, m_SpriteUncheckedOver, m_SpriteUncheckedPressed, m_SpriteUncheckedDisabled), canvas, - m_CachedActualSize, + GetActualSize(), m_VisibleArea); } diff --git a/source/gui/ObjectTypes/CDropDown.cpp b/source/gui/ObjectTypes/CDropDown.cpp index b5764205bd..5a606621e1 100644 --- a/source/gui/ObjectTypes/CDropDown.cpp +++ b/source/gui/ObjectTypes/CDropDown.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -73,9 +73,9 @@ void CDropDown::SetupText() CList::SetupText(); } -void CDropDown::UpdateCachedSize() +void CDropDown::HandleSizeChanged() { - CList::UpdateCachedSize(); + CList::HandleSizeChanged(); SetupText(); } @@ -191,7 +191,7 @@ void CDropDown::HandleMessage(SGUIMessage& Message) const CVector2D& mouse = m_pGUI.GetMousePos(); // If the regular area is pressed, then abort, and close. - if (m_CachedActualSize.PointInside(mouse)) + if (GetActualSize().PointInside(mouse)) { m_Open = false; GetScrollBar(0).SetZ(GetBufferedZ()); @@ -370,51 +370,51 @@ void CDropDown::SetupListRect() if (m_ItemsYPositions.empty()) { - m_CachedListRect = CRect(m_CachedActualSize.left, m_CachedActualSize.bottom + m_DropDownBuffer, - m_CachedActualSize.right, m_CachedActualSize.bottom + m_DropDownBuffer + m_DropDownSize); + m_CachedListRect = CRect(GetActualSize().left, GetActualSize().bottom + m_DropDownBuffer, + GetActualSize().right, GetActualSize().bottom + m_DropDownBuffer + m_DropDownSize); m_HideScrollBar = false; } // Too many items so use a scrollbar else if (m_ItemsYPositions.back() > m_DropDownSize) { // Place items below if at least some items can be placed below - if (m_CachedActualSize.bottom + m_DropDownBuffer + m_DropDownSize <= windowSize.Height) - m_CachedListRect = CRect(m_CachedActualSize.left, m_CachedActualSize.bottom + m_DropDownBuffer, - m_CachedActualSize.right, m_CachedActualSize.bottom + m_DropDownBuffer + m_DropDownSize); - else if ((m_ItemsYPositions.size() > m_MinimumVisibleItems && windowSize.Height - m_CachedActualSize.bottom - m_DropDownBuffer >= m_ItemsYPositions[m_MinimumVisibleItems]) || - m_CachedActualSize.top < windowSize.Height - m_CachedActualSize.bottom) - m_CachedListRect = CRect(m_CachedActualSize.left, m_CachedActualSize.bottom + m_DropDownBuffer, - m_CachedActualSize.right, windowSize.Height); + if (GetActualSize().bottom + m_DropDownBuffer + m_DropDownSize <= windowSize.Height) + m_CachedListRect = CRect(GetActualSize().left, GetActualSize().bottom + m_DropDownBuffer, + GetActualSize().right, GetActualSize().bottom + m_DropDownBuffer + m_DropDownSize); + else if ((m_ItemsYPositions.size() > m_MinimumVisibleItems && windowSize.Height - GetActualSize().bottom - m_DropDownBuffer >= m_ItemsYPositions[m_MinimumVisibleItems]) || + GetActualSize().top < windowSize.Height - GetActualSize().bottom) + m_CachedListRect = CRect(GetActualSize().left, GetActualSize().bottom + m_DropDownBuffer, + GetActualSize().right, windowSize.Height); // Not enough space below, thus place items above else - m_CachedListRect = CRect(m_CachedActualSize.left, std::max(0.f, m_CachedActualSize.top - m_DropDownBuffer - m_DropDownSize), - m_CachedActualSize.right, m_CachedActualSize.top - m_DropDownBuffer); + m_CachedListRect = CRect(GetActualSize().left, std::max(0.f, GetActualSize().top - m_DropDownBuffer - m_DropDownSize), + GetActualSize().right, GetActualSize().top - m_DropDownBuffer); m_HideScrollBar = false; } else { // Enough space below, no scrollbar needed - if (m_CachedActualSize.bottom + m_DropDownBuffer + m_ItemsYPositions.back() <= windowSize.Height) + if (GetActualSize().bottom + m_DropDownBuffer + m_ItemsYPositions.back() <= windowSize.Height) { - m_CachedListRect = CRect(m_CachedActualSize.left, m_CachedActualSize.bottom + m_DropDownBuffer, - m_CachedActualSize.right, m_CachedActualSize.bottom + m_DropDownBuffer + m_ItemsYPositions.back()); + m_CachedListRect = CRect(GetActualSize().left, GetActualSize().bottom + m_DropDownBuffer, + GetActualSize().right, GetActualSize().bottom + m_DropDownBuffer + m_ItemsYPositions.back()); m_HideScrollBar = true; } // Enough space below for some items, but not all, so place items below and use a scrollbar - else if ((m_ItemsYPositions.size() > m_MinimumVisibleItems && windowSize.Height - m_CachedActualSize.bottom - m_DropDownBuffer >= m_ItemsYPositions[m_MinimumVisibleItems]) || - m_CachedActualSize.top < windowSize.Height - m_CachedActualSize.bottom) + else if ((m_ItemsYPositions.size() > m_MinimumVisibleItems && windowSize.Height - GetActualSize().bottom - m_DropDownBuffer >= m_ItemsYPositions[m_MinimumVisibleItems]) || + GetActualSize().top < windowSize.Height - GetActualSize().bottom) { - m_CachedListRect = CRect(m_CachedActualSize.left, m_CachedActualSize.bottom + m_DropDownBuffer, - m_CachedActualSize.right, windowSize.Height); + m_CachedListRect = CRect(GetActualSize().left, GetActualSize().bottom + m_DropDownBuffer, + GetActualSize().right, windowSize.Height); m_HideScrollBar = false; } // Not enough space below, thus place items above. Hide the scrollbar accordingly else { - m_CachedListRect = CRect(m_CachedActualSize.left, std::max(0.f, m_CachedActualSize.top - m_DropDownBuffer - m_ItemsYPositions.back()), - m_CachedActualSize.right, m_CachedActualSize.top - m_DropDownBuffer); - m_HideScrollBar = m_CachedActualSize.top > m_ItemsYPositions.back() + m_DropDownBuffer; + m_CachedListRect = CRect(GetActualSize().left, std::max(0.f, GetActualSize().top - m_DropDownBuffer - m_ItemsYPositions.back()), + GetActualSize().right, GetActualSize().top - m_DropDownBuffer); + m_HideScrollBar = GetActualSize().top > m_ItemsYPositions.back() + m_DropDownBuffer; } } } @@ -428,8 +428,8 @@ bool CDropDown::IsMouseOver() const { if (m_Open) { - CRect rect(m_CachedActualSize.left, std::min(m_CachedActualSize.top, GetListRect().top), - m_CachedActualSize.right, std::max(m_CachedActualSize.bottom, GetListRect().bottom)); + CRect rect(GetActualSize().left, std::min(GetActualSize().top, GetListRect().top), + GetActualSize().right, std::max(GetActualSize().bottom, GetListRect().bottom)); return rect.PointInside(m_pGUI.GetMousePos()); } else @@ -441,12 +441,12 @@ void CDropDown::Draw(CCanvas2D& canvas) const CGUISpriteInstance& sprite = m_Enabled ? m_Sprite : m_SpriteDisabled; const CGUISpriteInstance& spriteOverlay = m_Enabled ? m_SpriteOverlay : m_SpriteOverlayDisabled; - m_pGUI.DrawSprite(sprite, canvas, m_CachedActualSize, m_VisibleArea); + m_pGUI.DrawSprite(sprite, canvas, GetActualSize(), m_VisibleArea); if (m_ButtonWidth > 0.f) { - CRect rect(m_CachedActualSize.right - m_ButtonWidth, m_CachedActualSize.top, - m_CachedActualSize.right, m_CachedActualSize.bottom); + CRect rect(GetActualSize().right - m_ButtonWidth, GetActualSize().top, + GetActualSize().right, GetActualSize().bottom); if (!m_Enabled) { @@ -466,11 +466,11 @@ void CDropDown::Draw(CCanvas2D& canvas) if (m_Selected != -1) // TODO: Maybe check validity completely? { - CRect cliparea = m_VisibleArea != CRect() ? m_VisibleArea : m_CachedActualSize; - if (cliparea.right > m_CachedActualSize.right - m_ButtonWidth) - cliparea.right = m_CachedActualSize.right - m_ButtonWidth; + CRect cliparea = m_VisibleArea != CRect() ? m_VisibleArea : GetActualSize(); + if (cliparea.right > GetActualSize().right - m_ButtonWidth) + cliparea.right = GetActualSize().right - m_ButtonWidth; - CVector2D pos(m_CachedActualSize.left, m_CachedActualSize.top); + CVector2D pos(GetActualSize().left, GetActualSize().top); DrawText(canvas, m_Selected, m_Enabled ? m_TextColorSelected : m_TextColorDisabled, pos, cliparea); } @@ -488,7 +488,7 @@ void CDropDown::Draw(CCanvas2D& canvas) if (m_HideScrollBar) m_ScrollBar.Set(old, false); } - m_pGUI.DrawSprite(spriteOverlay, canvas, m_CachedActualSize, m_VisibleArea); + m_pGUI.DrawSprite(spriteOverlay, canvas, GetActualSize(), m_VisibleArea); } // When a dropdown list is opened, it needs to be visible above all the other diff --git a/source/gui/ObjectTypes/CDropDown.h b/source/gui/ObjectTypes/CDropDown.h index 8550686d9b..07c83a0100 100644 --- a/source/gui/ObjectTypes/CDropDown.h +++ b/source/gui/ObjectTypes/CDropDown.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -93,7 +93,7 @@ protected: * If the size changed, the texts have to be updated as * the word wrapping depends on the size. */ - virtual void UpdateCachedSize(); + virtual void HandleSizeChanged(); /** * Sets up text, should be called every time changes has been @@ -117,7 +117,7 @@ protected: bool m_Open; // I didn't cache this at first, but it's just as easy as caching - // m_CachedActualSize, so I thought, what the heck it's used a lot. + // GetActualSize(), so I thought, what the heck it's used a lot. CRect m_CachedListRect; // Hide scrollbar when it's not needed diff --git a/source/gui/ObjectTypes/CImage.cpp b/source/gui/ObjectTypes/CImage.cpp index f9466c3e7f..c2a5d1fd1a 100644 --- a/source/gui/ObjectTypes/CImage.cpp +++ b/source/gui/ObjectTypes/CImage.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -30,5 +30,5 @@ CImage::CImage(CGUI& pGUI) void CImage::Draw(CCanvas2D& canvas) { - m_pGUI.DrawSprite(m_Sprite, canvas, m_CachedActualSize, m_VisibleArea); + m_pGUI.DrawSprite(m_Sprite, canvas, GetActualSize(), m_VisibleArea); } diff --git a/source/gui/ObjectTypes/CInput.cpp b/source/gui/ObjectTypes/CInput.cpp index 5bbb8167e7..1a7bf3bd37 100644 --- a/source/gui/ObjectTypes/CInput.cpp +++ b/source/gui/ObjectTypes/CInput.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -913,10 +913,10 @@ void CInput::HandleMessage(SGUIMessage& Message) Message.value == "z" || Message.value == "absolute")) { - GetScrollBar(0).SetX(m_CachedActualSize.right); - GetScrollBar(0).SetY(m_CachedActualSize.top); + GetScrollBar(0).SetX(GetActualSize().right); + GetScrollBar(0).SetY(GetActualSize().top); GetScrollBar(0).SetZ(GetBufferedZ()); - GetScrollBar(0).SetLength(m_CachedActualSize.bottom - m_CachedActualSize.top); + GetScrollBar(0).SetLength(GetActualSize().bottom - GetActualSize().top); } // Update scrollbar @@ -945,7 +945,7 @@ void CInput::HandleMessage(SGUIMessage& Message) if (!m_MultiLine) GetScrollBar(0).SetLength(0.f); else - GetScrollBar(0).SetLength(m_CachedActualSize.bottom - m_CachedActualSize.top); + GetScrollBar(0).SetLength(GetActualSize().bottom - GetActualSize().top); UpdateText(); } @@ -970,7 +970,7 @@ void CInput::HandleMessage(SGUIMessage& Message) m_MultiLine && GetScrollBar(0).GetStyle()) { - if (m_pGUI.GetMousePos().X > m_CachedActualSize.right - GetScrollBar(0).GetStyle()->m_Width) + if (m_pGUI.GetMousePos().X > GetActualSize().right - GetScrollBar(0).GetStyle()->m_Width) break; } @@ -1127,10 +1127,10 @@ void CInput::HandleMessage(SGUIMessage& Message) } case GUIM_LOAD: { - GetScrollBar(0).SetX(m_CachedActualSize.right); - GetScrollBar(0).SetY(m_CachedActualSize.top); + GetScrollBar(0).SetX(GetActualSize().right); + GetScrollBar(0).SetY(GetActualSize().top); GetScrollBar(0).SetZ(GetBufferedZ()); - GetScrollBar(0).SetLength(m_CachedActualSize.bottom - m_CachedActualSize.top); + GetScrollBar(0).SetLength(GetActualSize().bottom - GetActualSize().top); GetScrollBar(0).SetScrollBarStyle(m_ScrollBarStyle); UpdateText(); @@ -1148,10 +1148,10 @@ void CInput::HandleMessage(SGUIMessage& Message) // Tell the IME where to draw the candidate list SDL_Rect rect; - rect.h = m_CachedActualSize.GetSize().Height; - rect.w = m_CachedActualSize.GetSize().Width; - rect.x = m_CachedActualSize.TopLeft().X; - rect.y = m_CachedActualSize.TopLeft().Y; + rect.h = GetActualSize().GetSize().Height; + rect.w = GetActualSize().GetSize().Width; + rect.x = GetActualSize().TopLeft().X; + rect.y = GetActualSize().TopLeft().Y; SDL_SetTextInputRect(&rect); SDL_StartTextInput(); break; @@ -1182,19 +1182,19 @@ void CInput::HandleMessage(SGUIMessage& Message) UpdateBufferPositionSetting(); } -void CInput::UpdateCachedSize() +void CInput::HandleSizeChanged() { // If an ancestor's size changed, this will let us intercept the change and // update our scrollbar positions - IGUIObject::UpdateCachedSize(); + IGUIObject::HandleSizeChanged(); if (m_ScrollBar) { - GetScrollBar(0).SetX(m_CachedActualSize.right); - GetScrollBar(0).SetY(m_CachedActualSize.top); + GetScrollBar(0).SetX(GetActualSize().right); + GetScrollBar(0).SetY(GetActualSize().top); GetScrollBar(0).SetZ(GetBufferedZ()); - GetScrollBar(0).SetLength(m_CachedActualSize.bottom - m_CachedActualSize.top); + GetScrollBar(0).SetLength(GetActualSize().bottom - GetActualSize().top); } m_GeneratedPlaceholderTextValid = false; @@ -1222,7 +1222,7 @@ void CInput::DrawContent(CCanvas2D& canvas) if (m_Mask && m_MaskChar->length() > 0) mask_char = (*m_MaskChar)[0]; - m_pGUI.DrawSprite(m_Sprite, canvas, m_CachedActualSize); + m_pGUI.DrawSprite(m_Sprite, canvas, GetActualSize()); float scroll = 0.f; if (m_ScrollBar && m_MultiLine) @@ -1252,8 +1252,8 @@ void CInput::DrawContent(CCanvas2D& canvas) textRenderer.SetCurrentFont(font_name, CStrIntern{}); textRenderer.Translate( - (float)(int)(m_CachedActualSize.left) + m_BufferZone, - (float)(int)(m_CachedActualSize.top + h) + m_BufferZone); + (float)(int)(GetActualSize().left) + m_BufferZone, + (float)(int)(GetActualSize().top + h) + m_BufferZone); // U+FE33: PRESENTATION FORM FOR VERTICAL LOW LINE // (sort of like a | which is aligned to the left of most characters) @@ -1306,7 +1306,7 @@ void CInput::DrawContent(CCanvas2D& canvas) it != m_CharacterPositions.end(); ++it, buffered_y += ls, x_pointer = 0.f) { - if (m_MultiLine && buffered_y > m_CachedActualSize.GetHeight()) + if (m_MultiLine && buffered_y > GetActualSize().GetHeight()) break; // We might as well use 'i' here to iterate, because we need it @@ -1348,33 +1348,33 @@ void CInput::DrawContent(CCanvas2D& canvas) if (m_MultiLine) { rect = CRect( - m_CachedActualSize.left + box_x + m_BufferZone, - m_CachedActualSize.top + buffered_y + (h - ls) / 2, - m_CachedActualSize.left + x_pointer + m_BufferZone, - m_CachedActualSize.top + buffered_y + (h + ls) / 2); + GetActualSize().left + box_x + m_BufferZone, + GetActualSize().top + buffered_y + (h - ls) / 2, + GetActualSize().left + x_pointer + m_BufferZone, + GetActualSize().top + buffered_y + (h + ls) / 2); - if (rect.bottom < m_CachedActualSize.top) + if (rect.bottom < GetActualSize().top) continue; - if (rect.top < m_CachedActualSize.top) - rect.top = m_CachedActualSize.top; + if (rect.top < GetActualSize().top) + rect.top = GetActualSize().top; - if (rect.bottom > m_CachedActualSize.bottom) - rect.bottom = m_CachedActualSize.bottom; + if (rect.bottom > GetActualSize().bottom) + rect.bottom = GetActualSize().bottom; } else // if one-line { rect = CRect( - m_CachedActualSize.left + box_x + m_BufferZone - m_HorizontalScroll, - m_CachedActualSize.top + buffered_y + (h - ls) / 2, - m_CachedActualSize.left + x_pointer + m_BufferZone - m_HorizontalScroll, - m_CachedActualSize.top + buffered_y + (h + ls) / 2); + GetActualSize().left + box_x + m_BufferZone - m_HorizontalScroll, + GetActualSize().top + buffered_y + (h - ls) / 2, + GetActualSize().left + x_pointer + m_BufferZone - m_HorizontalScroll, + GetActualSize().top + buffered_y + (h + ls) / 2); - if (rect.left < m_CachedActualSize.left) - rect.left = m_CachedActualSize.left; + if (rect.left < GetActualSize().left) + rect.left = GetActualSize().left; - if (rect.right > m_CachedActualSize.right) - rect.right = m_CachedActualSize.right; + if (rect.right > GetActualSize().right) + rect.right = GetActualSize().right; } m_pGUI.DrawSprite(m_SpriteSelectArea, canvas, rect); @@ -1414,7 +1414,7 @@ void CInput::DrawContent(CCanvas2D& canvas) { if (buffered_y + m_BufferZone >= -ls || !m_MultiLine) { - if (m_MultiLine && buffered_y + m_BufferZone > m_CachedActualSize.GetHeight()) + if (m_MultiLine && buffered_y + m_BufferZone > GetActualSize().GetHeight()) break; const CVector2D savedTranslate = textRenderer.GetTranslate(); @@ -1474,7 +1474,7 @@ void CInput::DrawContent(CCanvas2D& canvas) // check it's now outside a one-liner, then we'll break if (!m_MultiLine && i < (int)it->m_ListOfX.size() && - it->m_ListOfX[i] - m_HorizontalScroll > m_CachedActualSize.GetWidth() - m_BufferZone) + it->m_ListOfX[i] - m_HorizontalScroll > GetActualSize().GetWidth() - m_BufferZone) break; } @@ -1500,7 +1500,7 @@ void CInput::DrawContent(CCanvas2D& canvas) void CInput::Draw(CCanvas2D& canvas) { // We'll have to setup clipping manually, since we're doing the rendering manually. - CRect cliparea = m_VisibleArea ? m_VisibleArea : m_CachedActualSize; + CRect cliparea = m_VisibleArea ? m_VisibleArea : GetActualSize(); // First we'll figure out the clipping area, which is the cached actual size // substracted by an optional scrollbar @@ -1535,7 +1535,7 @@ void CInput::Draw(CCanvas2D& canvas) IGUIScrollBarOwner::Draw(canvas); // Draw the overlays last - m_pGUI.DrawSprite(m_SpriteOverlay, canvas, m_CachedActualSize, m_VisibleArea); + m_pGUI.DrawSprite(m_SpriteOverlay, canvas, GetActualSize(), m_VisibleArea); } void CInput::DrawPlaceholderText(CCanvas2D& canvas, const CRect& clipping) @@ -1543,7 +1543,7 @@ void CInput::DrawPlaceholderText(CCanvas2D& canvas, const CRect& clipping) if (!m_GeneratedPlaceholderTextValid) SetupGeneratedPlaceholderText(); - m_GeneratedPlaceholderText.Draw(m_pGUI, canvas, m_PlaceholderColor, m_CachedActualSize.TopLeft(), clipping); + m_GeneratedPlaceholderText.Draw(m_pGUI, canvas, m_PlaceholderColor, GetActualSize().TopLeft(), clipping); } void CInput::UpdateText(int from, int to_before, int to_after) @@ -1894,7 +1894,7 @@ void CInput::UpdateText(int from, int to_before, int to_after) if (m_ScrollBar) { GetScrollBar(0).SetScrollRange(m_CharacterPositions.size() * font.GetHeight() + m_BufferZone * 2.f); - GetScrollBar(0).SetScrollSpace(m_CachedActualSize.GetHeight()); + GetScrollBar(0).SetScrollSpace(GetActualSize().GetHeight()); } } @@ -1920,7 +1920,7 @@ int CInput::GetMouseHoveringTextPosition() const const float spacing{font.GetHeight()}; // Change mouse position relative to text. - mouse -= m_CachedActualSize.TopLeft(); + mouse -= GetActualSize().TopLeft(); mouse.X -= m_BufferZone; mouse.Y += scroll - m_BufferZone; @@ -1942,7 +1942,7 @@ int CInput::GetMouseHoveringTextPosition() const { // current is already set to begin, // but we'll change the mouse.x to fit our horizontal scrolling - mouse -= m_CachedActualSize.TopLeft(); + mouse -= GetActualSize().TopLeft(); mouse.X -= m_BufferZone - m_HorizontalScroll; // mouse.y is moot } @@ -2028,9 +2028,9 @@ bool CInput::SelectingText() const float CInput::GetTextAreaWidth() { if (m_ScrollBar && GetScrollBar(0).GetStyle()) - return m_CachedActualSize.GetWidth() - m_BufferZone * 2.f - GetScrollBar(0).GetStyle()->m_Width; + return GetActualSize().GetWidth() - m_BufferZone * 2.f - GetScrollBar(0).GetStyle()->m_Width; - return m_CachedActualSize.GetWidth() - m_BufferZone * 2.f; + return GetActualSize().GetWidth() - m_BufferZone * 2.f; } void CInput::UpdateAutoScroll() @@ -2062,10 +2062,10 @@ void CInput::UpdateAutoScroll() } // If scrolling down - if (-scroll + static_cast(row + 1) * spacing + m_BufferZone * 2.f > m_CachedActualSize.GetHeight()) + if (-scroll + static_cast(row + 1) * spacing + m_BufferZone * 2.f > GetActualSize().GetHeight()) { // Scroll so the selected row is shown completely, also with m_BufferZone length to the edge. - GetScrollBar(0).SetPos(static_cast(row + 1) * spacing - m_CachedActualSize.GetHeight() + m_BufferZone * 2.f); + GetScrollBar(0).SetPos(static_cast(row + 1) * spacing - GetActualSize().GetHeight() + m_BufferZone * 2.f); } // If scrolling up else if (-scroll + (float)row * spacing < 0.f) @@ -2095,8 +2095,8 @@ void CInput::UpdateAutoScroll() } // Check if outside to the right - if (x_position - m_HorizontalScroll + m_BufferZone * 2.f > m_CachedActualSize.GetWidth()) - m_HorizontalScroll = x_position - m_CachedActualSize.GetWidth() + m_BufferZone * 2.f; + if (x_position - m_HorizontalScroll + m_BufferZone * 2.f > GetActualSize().GetWidth()) + m_HorizontalScroll = x_position - GetActualSize().GetWidth() + m_BufferZone * 2.f; // Check if outside to the left if (x_position - m_HorizontalScroll < 0.f) @@ -2104,12 +2104,12 @@ void CInput::UpdateAutoScroll() // Check if the text doesn't even fill up to the right edge even though scrolling is done. if (m_HorizontalScroll != 0.f && - x_total - m_HorizontalScroll + m_BufferZone * 2.f < m_CachedActualSize.GetWidth()) - m_HorizontalScroll = x_total - m_CachedActualSize.GetWidth() + m_BufferZone * 2.f; + x_total - m_HorizontalScroll + m_BufferZone * 2.f < GetActualSize().GetWidth()) + m_HorizontalScroll = x_total - GetActualSize().GetWidth() + m_BufferZone * 2.f; // Now this is the fail-safe, if x_total isn't even the length of the control, // remove all scrolling - if (x_total + m_BufferZone * 2.f < m_CachedActualSize.GetWidth()) + if (x_total + m_BufferZone * 2.f < GetActualSize().GetWidth()) m_HorizontalScroll = 0.f; } } diff --git a/source/gui/ObjectTypes/CInput.h b/source/gui/ObjectTypes/CInput.h index 4faffe94ff..deeb4a9ead 100644 --- a/source/gui/ObjectTypes/CInput.h +++ b/source/gui/ObjectTypes/CInput.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -98,9 +98,9 @@ protected: virtual InReaction ManuallyHandleHotkeyEvent(const SDL_Event_* ev); /** - * @see IGUIObject#UpdateCachedSize() + * @see IGUIObject#HandleSizeChanged() */ - virtual void UpdateCachedSize(); + virtual void HandleSizeChanged(); /** * Draws the Text diff --git a/source/gui/ObjectTypes/CList.cpp b/source/gui/ObjectTypes/CList.cpp index 8a1d454148..5a8dc8d9f8 100644 --- a/source/gui/ObjectTypes/CList.cpp +++ b/source/gui/ObjectTypes/CList.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -158,10 +158,10 @@ void CList::ResetStates() IGUIScrollBarOwner::ResetStates(); } -void CList::UpdateCachedSize() +void CList::HandleSizeChanged() { - IGUIObject::UpdateCachedSize(); - IGUITextOwner::UpdateCachedSize(); + IGUIObject::HandleSizeChanged(); + IGUITextOwner::HandleSizeChanged(); } void CList::HandleMessage(SGUIMessage& Message) diff --git a/source/gui/ObjectTypes/CList.h b/source/gui/ObjectTypes/CList.h index ee58badbab..deb8769dd4 100644 --- a/source/gui/ObjectTypes/CList.h +++ b/source/gui/ObjectTypes/CList.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -57,9 +57,9 @@ public: virtual void ResetStates(); /** - * @see IGUIObject#UpdateCachedSize() + * @see IGUIObject#HandleSizeChanged() */ - virtual void UpdateCachedSize(); + virtual void HandleSizeChanged(); /** * Adds an item last to the list. @@ -119,8 +119,8 @@ protected: const CGUISpriteInstance& spriteSelectArea, const CGUISpriteInstance& spriteSelectAreaOverlay, const CGUIColor& textColor); // Get the area of the list. This is so that it can easily be changed, like in CDropDown - // where the area is not equal to m_CachedActualSize. - virtual CRect GetListRect() const { return m_CachedActualSize; } + // where the area is not equal to GetActualSize(). + virtual CRect GetListRect() const { return GetActualSize(); } // Returns whether SetupText() has run since the last message was received // (and thus whether list items have possibly changed). diff --git a/source/gui/ObjectTypes/CMiniMap.cpp b/source/gui/ObjectTypes/CMiniMap.cpp index 132c4a9c70..609e756296 100644 --- a/source/gui/ObjectTypes/CMiniMap.cpp +++ b/source/gui/ObjectTypes/CMiniMap.cpp @@ -206,9 +206,9 @@ bool CMiniMap::IsMouseOver() const { const CVector2D& mousePos = m_pGUI.GetMousePos(); // Take the magnitude of the difference of the mouse position and minimap center. - const float distanceFromCenter = (mousePos - m_CachedActualSize.CenterPoint()).Length(); + const float distanceFromCenter = (mousePos - GetActualSize().CenterPoint()).Length(); // If the distance is less then the radius of the minimap (half the width) the mouse is over the minimap. - return distanceFromCenter < m_CachedActualSize.GetWidth() / 2.0; + return distanceFromCenter < GetActualSize().GetWidth() / 2.0; } void CMiniMap::GetMouseWorldCoordinates(float& x, float& z) const @@ -216,8 +216,8 @@ void CMiniMap::GetMouseWorldCoordinates(float& x, float& z) const // Determine X and Z according to proportion of mouse position and minimap. const CVector2D& mousePos = m_pGUI.GetMousePos(); - float px = (mousePos.X - m_CachedActualSize.left) / m_CachedActualSize.GetWidth(); - float py = (m_CachedActualSize.bottom - mousePos.Y) / m_CachedActualSize.GetHeight(); + float px = (mousePos.X - GetActualSize().left) / GetActualSize().GetWidth(); + float py = (GetActualSize().bottom - mousePos.Y) / GetActualSize().GetHeight(); float angle = GetAngle(); @@ -256,8 +256,8 @@ CVector2D CMiniMap::WorldSpaceToMiniMapSpace(const CVector3D& worldPosition) con // Calculate coordinates in GUI space. return CVector2D( - m_CachedActualSize.left + (0.5f + rotatedX) * m_CachedActualSize.GetWidth(), - m_CachedActualSize.bottom - (0.5f + rotatedY) * m_CachedActualSize.GetHeight()); + GetActualSize().left + (0.5f + rotatedX) * GetActualSize().GetWidth(), + GetActualSize().bottom - (0.5f + rotatedY) * GetActualSize().GetHeight()); } bool CMiniMap::FireWorldClickEvent(int button, int /*clicks*/) @@ -364,7 +364,7 @@ void CMiniMap::Draw(CCanvas2D& canvas) return; if (!m_Mask) - canvas.DrawRect(m_CachedActualSize, CColor(0.0f, 0.0f, 0.0f, 1.0f)); + canvas.DrawRect(GetActualSize(), CColor(0.0f, 0.0f, 0.0f, 1.0f)); CSimulation2* sim = g_Game->GetSimulation2(); CmpPtr cmpRangeManager(*sim, SYSTEM_ENTITY); @@ -379,13 +379,13 @@ void CMiniMap::Draw(CCanvas2D& canvas) CMiniMapTexture& miniMapTexture = g_Game->GetView()->GetMiniMapTexture(); if (miniMapTexture.GetTexture()) { - const CVector2D center = m_CachedActualSize.CenterPoint(); + const CVector2D center = GetActualSize().CenterPoint(); const CRect source( 0, miniMapTexture.IsFlipped() ? 0 : miniMapTexture.GetTexture()->GetHeight(), miniMapTexture.GetTexture()->GetWidth(), miniMapTexture.IsFlipped() ? miniMapTexture.GetTexture()->GetHeight() : 0); - const CSize2D size(m_CachedActualSize.GetSize() / m_MapScale); + const CSize2D size(GetActualSize().GetSize() / m_MapScale); const CRect destination(center - size / 2.0f, size); canvas.DrawRotatedTexture( miniMapTexture.GetTexture(), destination, source, diff --git a/source/gui/ObjectTypes/COList.cpp b/source/gui/ObjectTypes/COList.cpp index 113ecbd29f..20aefc8995 100644 --- a/source/gui/ObjectTypes/COList.cpp +++ b/source/gui/ObjectTypes/COList.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2024 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -132,7 +132,7 @@ void COList::SetupText() CRect COList::GetListRect() const { - return m_CachedActualSize + CRect(0, m_HeadingHeight, 0, 0); + return GetActualSize() + CRect(0, m_HeadingHeight, 0, 0); } void COList::HandleMessage(SGUIMessage& Message) @@ -156,7 +156,7 @@ void COList::HandleMessage(SGUIMessage& Message) return; const CVector2D& mouse = m_pGUI.GetMousePos(); - if (!m_CachedActualSize.PointInside(mouse)) + if (!GetActualSize().PointInside(mouse)) return; float xpos = 0; @@ -169,7 +169,7 @@ void COList::HandleMessage(SGUIMessage& Message) // Check if it's a decimal value, and if so, assume relative positioning. if (column.m_Width < 1 && column.m_Width > 0) width *= m_TotalAvailableColumnWidth; - CVector2D leftTopCorner = m_CachedActualSize.TopLeft() + CVector2D(xpos, 0); + CVector2D leftTopCorner = GetActualSize().TopLeft() + CVector2D(xpos, 0); if (mouse.X >= leftTopCorner.X && mouse.X < leftTopCorner.X + width && mouse.Y < leftTopCorner.Y + m_HeadingHeight) @@ -362,8 +362,8 @@ void COList::DrawList(CCanvas2D& canvas, const int& selected, const CGUISpriteIn } // Draw line above column header - CRect rect_head(m_CachedActualSize.left, m_CachedActualSize.top, m_CachedActualSize.right, - m_CachedActualSize.top + m_HeadingHeight); + CRect rect_head(GetActualSize().left, GetActualSize().top, GetActualSize().right, + GetActualSize().top + m_HeadingHeight); m_pGUI.DrawSprite(m_SpriteHeading, canvas, rect_head, m_VisibleArea); // Draw column headers @@ -382,7 +382,7 @@ void COList::DrawList(CCanvas2D& canvas, const int& selected, const CGUISpriteIn if (column.m_Width < 1 && column.m_Width > 0) width *= m_TotalAvailableColumnWidth; - CVector2D leftTopCorner = m_CachedActualSize.TopLeft() + CVector2D(xpos, 0); + CVector2D leftTopCorner = GetActualSize().TopLeft() + CVector2D(xpos, 0); // Draw sort arrows in colum header if (m_Sortable) diff --git a/source/gui/ObjectTypes/CProgressBar.cpp b/source/gui/ObjectTypes/CProgressBar.cpp index 764da5e119..a14301af84 100644 --- a/source/gui/ObjectTypes/CProgressBar.cpp +++ b/source/gui/ObjectTypes/CProgressBar.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -56,10 +56,10 @@ void CProgressBar::HandleMessage(SGUIMessage& Message) void CProgressBar::Draw(CCanvas2D& canvas) { - m_pGUI.DrawSprite(m_SpriteBackground, canvas, m_CachedActualSize, m_VisibleArea); + m_pGUI.DrawSprite(m_SpriteBackground, canvas, GetActualSize(), m_VisibleArea); // Get size of bar (notice it is drawn slightly closer, to appear above the background) - CRect size = m_CachedActualSize; - size.right = size.left + m_CachedActualSize.GetWidth() * (m_Progress / 100.f), + CRect size = GetActualSize(); + size.right = size.left + GetActualSize().GetWidth() * (m_Progress / 100.f), m_pGUI.DrawSprite(m_SpriteBar, canvas, size, m_VisibleArea); } diff --git a/source/gui/ObjectTypes/CScrollPanel.cpp b/source/gui/ObjectTypes/CScrollPanel.cpp index 91540f8c98..3d5d69cc6f 100644 --- a/source/gui/ObjectTypes/CScrollPanel.cpp +++ b/source/gui/ObjectTypes/CScrollPanel.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -56,10 +56,36 @@ void CScrollPanel::ResetStates() IGUIScrollBarOwner::ResetStates(); } -void CScrollPanel::UpdateCachedSize() +void CScrollPanel::RecalculateActualSize() const { - IGUIPanel::UpdateCachedSize(); - Setup(); + IGUIPanel::RecalculateActualSize(); + + IGUIScrollBar& scrollbar0 = GetScrollBar(0); + float vscroll = scrollbar0.GetPos(); + IGUIScrollBar& scrollbar1 = GetScrollBar(1); + float hscroll = scrollbar1.GetPos(); + + m_CachedActualSize = m_CachedLayoutActualSize; + + if (HasVerticalScrollBar() && m_CachedLayoutActualSize.GetHeight() < m_MinHeight) + m_CachedActualSize.bottom = m_CachedLayoutActualSize.top + m_MinHeight; + + if (HasHorizontalScrollBar() && m_CachedLayoutActualSize.GetWidth() < m_MinWidth) + m_CachedActualSize.right = m_CachedLayoutActualSize.left + m_MinWidth; + + m_CachedActualSize.top -= vscroll; + m_CachedActualSize.bottom -= vscroll; + + m_CachedActualSize.left -= hscroll; + m_CachedActualSize.right -= hscroll; + + if (scrollbar0.IsVisible()) + m_CachedActualSize.right -= scrollbar0.GetOuterRect().GetWidth(); + if (scrollbar1.IsVisible()) + m_CachedActualSize.bottom -= scrollbar1.GetOuterRect().GetHeight(); + + for (IGUIObject* child : m_Children) + child->RecurseObject(&IGUIObject::IsHiddenOrGhost, &IGUIObject::HandleSizeChanged); } void CScrollPanel::HandleMessage(SGUIMessage& Message) @@ -71,24 +97,9 @@ void CScrollPanel::HandleMessage(SGUIMessage& Message) float vscroll = scrollbar0.GetPos(); float hscroll = scrollbar1.GetPos(); - bool updateScrollPosition = false; - IGUIScrollBarOwner::HandleMessage(Message); - - if (vscroll != scrollbar0.GetPos()) - { - vscroll = scrollbar0.GetPos(); - updateScrollPosition = true; - } - - if (hscroll != scrollbar1.GetPos()) - { - hscroll = scrollbar1.GetPos(); - updateScrollPosition = true; - } - - if (updateScrollPosition) - UpdateScrollPosition(vscroll, hscroll); + if (vscroll != scrollbar0.GetPos() || hscroll != scrollbar1.GetPos()) + m_CachedActualSizeDirty = true; switch (Message.type) { @@ -97,20 +108,20 @@ void CScrollPanel::HandleMessage(SGUIMessage& Message) { scrollbar0.SetScrollBarStyle(m_ScrollBarStyle); scrollbar1.SetScrollBarStyle(m_ScrollBarStyle); - Setup(); + UpdateScrollBars(); } if (Message.value == "orientation" || Message.value == "size" || Message.value == "min_width" || Message.value == "min_height") { scrollbar0.SetPos(0); scrollbar1.SetPos(0); - Setup(); + UpdateScrollBars(); } break; case GUIM_CHILD_RESIZED: case GUIM_CHILD_TOGGLE_VISIBILITY: - Setup(); + UpdateScrollBars(); Message.Skip(false); break; @@ -118,7 +129,7 @@ void CScrollPanel::HandleMessage(SGUIMessage& Message) scrollbar0.SetScrollBarStyle(m_ScrollBarStyle); scrollbar1.SetScrollBarStyle(m_ScrollBarStyle); - Setup(); + UpdateScrollBars(); break; default: @@ -146,32 +157,21 @@ void CScrollPanel::Draw(CCanvas2D& canvas) m_Drawing = false; } -void CScrollPanel::Setup() +void CScrollPanel::UpdateScrollBars() { IGUIScrollBar& scrollbar0 = GetScrollBar(0); IGUIScrollBar& scrollbar1 = GetScrollBar(1); - m_CachedActualSize = m_CachedLayoutActualSize; - - if (HasVerticalScrollBar() && m_CachedLayoutActualSize.GetHeight() < m_MinHeight) - m_CachedActualSize.bottom = m_CachedLayoutActualSize.top + m_MinHeight; - - if (HasHorizontalScrollBar() && m_CachedLayoutActualSize.GetWidth() < m_MinWidth) - m_CachedActualSize.right = m_CachedLayoutActualSize.left + m_MinWidth; - - for (IGUIObject* child : m_Children) - child->RecurseObject(&IGUIObject::IsHiddenOrGhost, &IGUIObject::UpdateCachedSize); - float vscroll = scrollbar0.GetPos(); float hscroll = scrollbar1.GetPos(); float maxVRange = 0; float maxHRange = 0; - for (IGUIObject* child : m_Children) + for (const IGUIObject* child : m_Children) { if (child->IsHiddenOrGhost()) continue; - CRect childSize = child->GetComputedSize(); + const CRect& childSize = child->GetActualSize(); maxVRange = std::max(maxVRange, childSize.bottom); maxHRange = std::max(maxHRange, childSize.right); } @@ -216,37 +216,6 @@ void CScrollPanel::Setup() hscroll = maxHRange; scrollbar1.SetPos(hscroll); } - - UpdateScrollPosition(vscroll, hscroll); -} - -void CScrollPanel::UpdateScrollPosition(float scroll, float hscroll) -{ - IGUIScrollBar& scrollbar0 = GetScrollBar(0); - IGUIScrollBar& scrollbar1 = GetScrollBar(1); - - m_CachedActualSize = m_CachedLayoutActualSize; - - if (HasVerticalScrollBar() && m_CachedLayoutActualSize.GetHeight() < m_MinHeight) - m_CachedActualSize.bottom = m_CachedLayoutActualSize.top + m_MinHeight; - - if (HasHorizontalScrollBar() && m_CachedLayoutActualSize.GetWidth() < m_MinWidth) - m_CachedActualSize.right = m_CachedLayoutActualSize.left + m_MinWidth; - - m_CachedActualSize.top -= scroll; - m_CachedActualSize.bottom -= scroll; - - m_CachedActualSize.left -= hscroll; - m_CachedActualSize.right -= hscroll; - - // upddate scroll bars size base on m_Width - if (scrollbar0.IsVisible()) - m_CachedActualSize.right -= scrollbar0.GetOuterRect().GetWidth(); - if (scrollbar1.IsVisible()) - m_CachedActualSize.bottom -= scrollbar1.GetOuterRect().GetHeight(); - - for (IGUIObject* child : m_Children) - child->RecurseObject(&IGUIObject::IsHiddenOrGhost, &IGUIObject::UpdateCachedSize); } void CScrollPanel::ResetScrollPosition(EScrollOrientation orientation) diff --git a/source/gui/ObjectTypes/CScrollPanel.h b/source/gui/ObjectTypes/CScrollPanel.h index 21df8e38d0..7ad66173d5 100644 --- a/source/gui/ObjectTypes/CScrollPanel.h +++ b/source/gui/ObjectTypes/CScrollPanel.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -35,11 +35,8 @@ class CScrollPanel : public IGUIPanel, public IGUIScrollBarOwner public: CScrollPanel(CGUI& pGUI); - virtual void UpdateCachedSize(); virtual void ResetStates(); - void Setup(); - void ResetScrollPosition(EScrollOrientation orientation = EScrollOrientation::BOTH); protected: @@ -48,7 +45,9 @@ protected: */ virtual void HandleMessage(SGUIMessage& Message); - void UpdateScrollPosition(float vscroll, float hscroll); + virtual void RecalculateActualSize() const; + + void UpdateScrollBars(); bool HasHorizontalScrollBar() const { return *m_Orientation == EScrollOrientation::HORIZONTAL || *m_Orientation == EScrollOrientation::BOTH; }; bool HasVerticalScrollBar() const { return *m_Orientation == EScrollOrientation::VERTICAL || *m_Orientation == EScrollOrientation::BOTH; }; diff --git a/source/gui/ObjectTypes/CSlider.cpp b/source/gui/ObjectTypes/CSlider.cpp index 4847e339b8..cc76abc4fa 100644 --- a/source/gui/ObjectTypes/CSlider.cpp +++ b/source/gui/ObjectTypes/CSlider.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -49,7 +49,7 @@ void CSlider::ResetStates() float CSlider::GetSliderRatio() const { - return (m_MaxValue - m_MinValue) / (m_CachedActualSize.GetWidth() - m_ButtonSide); + return (m_MaxValue - m_MinValue) / (GetActualSize().GetWidth() - m_ButtonSide); } void CSlider::IncrementallyChangeValue(const float difference) @@ -105,7 +105,7 @@ void CSlider::HandleMessage(SGUIMessage& Message) void CSlider::Draw(CCanvas2D& canvas) { - CRect sliderLine(m_CachedActualSize); + CRect sliderLine(GetActualSize()); sliderLine.left += m_ButtonSide / 2.0f; sliderLine.right -= m_ButtonSide / 2.0f; m_pGUI.DrawSprite(IsEnabled() ? m_SpriteBar : m_SpriteBarDisabled, canvas, sliderLine, m_VisibleArea); @@ -124,7 +124,7 @@ CRect CSlider::GetButtonRect() const // config for debug purposes. const float value = Clamp(m_Value, m_MinValue, m_MaxValue); float ratio = m_MaxValue > m_MinValue ? (value - m_MinValue) / (m_MaxValue - m_MinValue) : 0.0f; - float x = m_CachedActualSize.left + ratio * (m_CachedActualSize.GetWidth() - m_ButtonSide); - float y = m_CachedActualSize.top + (m_CachedActualSize.GetHeight() - m_ButtonSide) / 2.0; + float x = GetActualSize().left + ratio * (GetActualSize().GetWidth() - m_ButtonSide); + float y = GetActualSize().top + (GetActualSize().GetHeight() - m_ButtonSide) / 2.0; return CRect(x, y, x + m_ButtonSide, y + m_ButtonSide); } diff --git a/source/gui/ObjectTypes/CText.cpp b/source/gui/ObjectTypes/CText.cpp index 0a6834c49d..a251210c2b 100644 --- a/source/gui/ObjectTypes/CText.cpp +++ b/source/gui/ObjectTypes/CText.cpp @@ -64,7 +64,7 @@ void CText::SetupText() if (m_GeneratedTexts.empty()) return; - float width = m_CachedActualSize.GetWidth(); + float width = GetActualSize().GetWidth(); // remove scrollbar if applicable if (m_ScrollBar && GetScrollBar(0).GetStyle()) width -= GetScrollBar(0).GetStyle()->m_Width; @@ -83,12 +83,12 @@ void CText::SetupText() bottom = true; GetScrollBar(0).SetScrollRange(m_GeneratedTexts[0].GetSize().Height); - GetScrollBar(0).SetScrollSpace(m_CachedActualSize.GetHeight()); + GetScrollBar(0).SetScrollSpace(GetActualSize().GetHeight()); - GetScrollBar(0).SetX(m_CachedActualSize.right); - GetScrollBar(0).SetY(m_CachedActualSize.top); + GetScrollBar(0).SetX(GetActualSize().right); + GetScrollBar(0).SetY(GetActualSize().top); GetScrollBar(0).SetZ(GetBufferedZ()); - GetScrollBar(0).SetLength(m_CachedActualSize.bottom - m_CachedActualSize.top); + GetScrollBar(0).SetLength(GetActualSize().bottom - GetActualSize().top); if (bottom) GetScrollBar(0).SetPos(GetScrollBar(0).GetMaxPos()); @@ -98,7 +98,7 @@ void CText::SetupText() } if (!m_ScrollBar || !std::ranges::any_of(m_ScrollBars, &IGUIScrollBar::IsVisible)) - CalculateTextPosition(m_CachedActualSize, m_TextPos, m_GeneratedTexts[0]); + CalculateTextPosition(GetActualSize(), m_TextPos, m_GeneratedTexts[0]); } void CText::ResetStates() @@ -107,10 +107,10 @@ void CText::ResetStates() IGUIScrollBarOwner::ResetStates(); } -void CText::UpdateCachedSize() +void CText::HandleSizeChanged() { - IGUIObject::UpdateCachedSize(); - IGUITextOwner::UpdateCachedSize(); + IGUIObject::HandleSizeChanged(); + IGUITextOwner::HandleSizeChanged(); } CSize2D CText::GetTextSize() @@ -132,7 +132,7 @@ const CStrW& CText::GetTooltipText() const if (textChunk.m_Tooltip.empty()) continue; CRect area(textChunk.m_Pos - CVector2D(0.f, textChunk.m_Size.Height), textChunk.m_Size); - if (area.PointInside(m_pGUI.GetMousePos() - m_CachedActualSize.TopLeft())) + if (area.PointInside(m_pGUI.GetMousePos() - GetActualSize().TopLeft())) return textChunk.m_Tooltip; } return m_Tooltip; @@ -162,10 +162,10 @@ void CText::HandleMessage(SGUIMessage& Message) case GUIM_LOAD: { - GetScrollBar(0).SetX(m_CachedActualSize.right); - GetScrollBar(0).SetY(m_CachedActualSize.top); + GetScrollBar(0).SetX(GetActualSize().right); + GetScrollBar(0).SetY(GetActualSize().top); GetScrollBar(0).SetZ(GetBufferedZ()); - GetScrollBar(0).SetLength(m_CachedActualSize.bottom - m_CachedActualSize.top); + GetScrollBar(0).SetLength(GetActualSize().bottom - GetActualSize().top); GetScrollBar(0).SetScrollBarStyle(m_ScrollBarStyle); break; } @@ -179,14 +179,14 @@ void CText::HandleMessage(SGUIMessage& Message) void CText::Draw(CCanvas2D& canvas) { - m_pGUI.DrawSprite(m_Sprite, canvas, m_CachedActualSize, m_VisibleArea); + m_pGUI.DrawSprite(m_Sprite, canvas, GetActualSize(), m_VisibleArea); float scroll = 0.f; if (m_ScrollBar) scroll = GetScrollBar(0).GetPos(); // Clipping area (we'll have to subtract the scrollbar) - CRect cliparea = m_VisibleArea ? m_VisibleArea : m_CachedActualSize; + CRect cliparea = m_VisibleArea ? m_VisibleArea : GetActualSize(); if (m_Clip) { if (m_ScrollBar) @@ -206,7 +206,7 @@ void CText::Draw(CCanvas2D& canvas) if (m_ScrollBar && std::ranges::any_of(m_ScrollBars, &IGUIScrollBar::IsVisible)) { - DrawText(canvas, 0, color, m_CachedActualSize.TopLeft() - CVector2D(0.f, scroll), cliparea); + DrawText(canvas, 0, color, GetActualSize().TopLeft() - CVector2D(0.f, scroll), cliparea); // Draw scrollbars on top of the content IGUIScrollBarOwner::Draw(canvas); } @@ -214,5 +214,5 @@ void CText::Draw(CCanvas2D& canvas) DrawText(canvas, 0, color, m_TextPos, cliparea); // Draw the overlays last - m_pGUI.DrawSprite(m_SpriteOverlay, canvas, m_CachedActualSize, m_VisibleArea); + m_pGUI.DrawSprite(m_SpriteOverlay, canvas, GetActualSize(), m_VisibleArea); } diff --git a/source/gui/ObjectTypes/CText.h b/source/gui/ObjectTypes/CText.h index af04c58ae4..c12a6a3b84 100644 --- a/source/gui/ObjectTypes/CText.h +++ b/source/gui/ObjectTypes/CText.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -46,9 +46,9 @@ public: virtual void ResetStates(); /** - * @see IGUIObject#UpdateCachedSize() + * @see IGUIObject#HandleSizeChanged() */ - virtual void UpdateCachedSize(); + virtual void HandleSizeChanged(); /** * @return the object text size. diff --git a/source/gui/ObjectTypes/CTooltip.cpp b/source/gui/ObjectTypes/CTooltip.cpp index a3a2073ac0..4a39e83c4a 100644 --- a/source/gui/ObjectTypes/CTooltip.cpp +++ b/source/gui/ObjectTypes/CTooltip.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -121,10 +121,10 @@ void CTooltip::SetupText() m_Size.Set(size, true); } -void CTooltip::UpdateCachedSize() +void CTooltip::HandleSizeChanged() { - IGUIObject::UpdateCachedSize(); - IGUITextOwner::UpdateCachedSize(); + IGUIObject::HandleSizeChanged(); + IGUITextOwner::HandleSizeChanged(); } void CTooltip::HandleMessage(SGUIMessage& Message) @@ -143,8 +143,8 @@ void CTooltip::Draw(CCanvas2D& canvas) m_GeneratedTextsValid = true; } - m_pGUI.DrawSprite(m_Sprite, canvas, m_CachedActualSize); - DrawText(canvas, 0, m_TextColor, m_CachedActualSize.TopLeft()); + m_pGUI.DrawSprite(m_Sprite, canvas, GetActualSize()); + DrawText(canvas, 0, m_TextColor, GetActualSize().TopLeft()); } float CTooltip::GetBufferedZ() const diff --git a/source/gui/ObjectTypes/CTooltip.h b/source/gui/ObjectTypes/CTooltip.h index dc804ea99d..277dffdafa 100644 --- a/source/gui/ObjectTypes/CTooltip.h +++ b/source/gui/ObjectTypes/CTooltip.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -50,9 +50,9 @@ protected: void SetupText(); /** - * @see IGUIObject#UpdateCachedSize() + * @see IGUIObject#HandleSizeChanged() */ - void UpdateCachedSize(); + void HandleSizeChanged(); /** * @see IGUIObject#HandleMessage() diff --git a/source/gui/Scripting/JSInterface_CGUISize.cpp b/source/gui/Scripting/JSInterface_CGUISize.cpp index bcddadb882..0910ec3fbb 100644 --- a/source/gui/Scripting/JSInterface_CGUISize.cpp +++ b/source/gui/Scripting/JSInterface_CGUISize.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -73,9 +73,10 @@ bool SetCRectField(JSContext* cx, unsigned argc, JS::Value* vp) return false; wrapper->GetMutable().*RectMember.*Member = static_cast(val); + wrapper->Set(wrapper->GetMutable(), true); // causes CGUISimpleSetting to actually process the change + args.rval().setUndefined(); - wrapper->DeferSettingChange(); return true; } diff --git a/source/gui/Scripting/JSInterface_GUIProxy.cpp b/source/gui/Scripting/JSInterface_GUIProxy.cpp index 69931ed5b8..5b0ca09209 100644 --- a/source/gui/Scripting/JSInterface_GUIProxy.cpp +++ b/source/gui/Scripting/JSInterface_GUIProxy.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -41,7 +41,7 @@ void JSI_GUIProxy::CreateFunctions(const ScriptRequest& rq, GUIProxy CreateFunction<&IGUIObject::GetName>(rq, cache, "toSource"); CreateFunction<&IGUIObject::SetFocus>(rq, cache, "focus"); CreateFunction<&IGUIObject::ReleaseFocus>(rq, cache, "blur"); - CreateFunction<&IGUIObject::GetComputedSize>(rq, cache, "getComputedSize"); + CreateFunction<&IGUIObject::GetActualSize>(rq, cache, "getComputedSize"); } DECLARE_GUIPROXY(IGUIObject); diff --git a/source/gui/tests/test_GUISetting.h b/source/gui/tests/test_GUISetting.h index 58a1a85b10..1e0c834119 100644 --- a/source/gui/tests/test_GUISetting.h +++ b/source/gui/tests/test_GUISetting.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -63,9 +63,9 @@ public: TestGUIObject(CGUI& gui) : IGUIObject(gui) {} void Draw(CCanvas2D&) {} - void UpdateCachedSize() { - haveUpdateCachedSize = true; - IGUIObject::UpdateCachedSize(); + void RecalculateActualSize() const { + cachedSizeRecalculated = true; + IGUIObject::RecalculateActualSize(); } CGUISimpleSetting* GetSizeSetting() const @@ -73,7 +73,7 @@ public: return static_cast*>(m_Settings.at("size")); } - bool haveUpdateCachedSize{false}; + mutable bool cachedSizeRecalculated{false}; }; void setUp() @@ -139,7 +139,6 @@ public: CGUISimpleSetting* setting{object.GetSizeSetting()}; object.SetSettingFromString("size", L"2 2 20 20", false); - object.haveUpdateCachedSize = false; ScriptRequest rq{gui.GetScriptInterface()}; JS::RootedValue val(rq.cx); @@ -151,36 +150,33 @@ public: TS_ASSERT(gui.GetScriptInterface()->LoadGlobalScriptFile(L"gui/settings/cguisize/lazyassign.js")); TS_ASSERT_EQUALS(setting->GetMutable().pixel, (CRect{5, 2, 20, 20})); TS_ASSERT_EQUALS(setting->GetMutable().percent, (CRect{0, 0, 0, 0})); - TS_ASSERT_EQUALS(object.haveUpdateCachedSize, false); + TS_ASSERT(!object.cachedSizeRecalculated); - // Force update of cached size. - object.GetComputedSize(); - object.haveUpdateCachedSize = false; + // This should finally recalculate the dirty (cached) actual size in order to return an up-to-date value. + TS_ASSERT_EQUALS(object.GetActualSize(), (CRect{5, 2, 20, 20})); + TS_ASSERT(object.cachedSizeRecalculated); + + object.cachedSizeRecalculated = false; // Compound assignment operator. TS_ASSERT(gui.GetScriptInterface()->LoadGlobalScriptFile(L"gui/settings/cguisize/compoundassignmentoperator.js")); TS_ASSERT_EQUALS(setting->GetMutable().pixel, (CRect{10, 2, 20, 20})); TS_ASSERT_EQUALS(setting->GetMutable().percent, (CRect{0, 0, 0, 0})); - TS_ASSERT_EQUALS(object.haveUpdateCachedSize, false); - - // Force update of cached size. - object.GetComputedSize(); - object.haveUpdateCachedSize = false; // Object assignment. TS_ASSERT(gui.GetScriptInterface()->LoadGlobalScriptFile(L"gui/settings/cguisize/objectassign.js")); TS_ASSERT_EQUALS(setting->GetMutable().pixel, (CRect{10, 2, 20, 20})); TS_ASSERT_EQUALS(setting->GetMutable().percent, (CRect{4, 0, 0, 20})); - TS_ASSERT_EQUALS(object.haveUpdateCachedSize, false); - - // Force update of cached size. - object.GetComputedSize(); - object.haveUpdateCachedSize = false; // assign TS_ASSERT(gui.GetScriptInterface()->LoadGlobalScriptFile(L"gui/settings/cguisize/assign.js")); TS_ASSERT_EQUALS(setting->GetMutable().pixel, (CRect{3, 0, 0, 2})); TS_ASSERT_EQUALS(setting->GetMutable().percent, (CRect{0, 0, 0, 0})); - TS_ASSERT_EQUALS(object.haveUpdateCachedSize, true); + + TS_ASSERT(!object.cachedSizeRecalculated); + // Call the getComputedSize method on it, which should finally recalculate the dirty (cached) actual size in + // order to return an up-to-date value. + TS_ASSERT(gui.GetScriptInterface()->LoadGlobalScriptFile(L"gui/settings/cguisize/getcomputedsize.js")); + TS_ASSERT(object.cachedSizeRecalculated); } }; diff --git a/source/pch/gui/precompiled.h b/source/pch/gui/precompiled.h index 402626fa77..3d9bec2766 100644 --- a/source/pch/gui/precompiled.h +++ b/source/pch/gui/precompiled.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -21,7 +21,7 @@ #include "lib/precompiled.h" // common precompiled header #if MSC_VERSION -# pragma warning(disable:4250) // "inherits 'IGUITextOwner::IGUITextOwner::UpdateCachedSize' via dominance" +# pragma warning(disable:4250) // "inherits 'IGUITextOwner::IGUITextOwner::HandleSizeChanged' via dominance" #endif #if CONFIG_ENABLE_PCH