Index: binaries/data/mods/mod/gui/common/modern/setup.xml =================================================================== --- binaries/data/mods/mod/gui/common/modern/setup.xml +++ binaries/data/mods/mod/gui/common/modern/setup.xml @@ -8,7 +8,7 @@ ========================================== --> + + Index: binaries/data/mods/mod/gui/gui.rng =================================================================== --- binaries/data/mods/mod/gui/gui.rng +++ binaries/data/mods/mod/gui/gui.rng @@ -331,18 +331,18 @@ - - - - + - + + + + @@ -677,7 +677,7 @@ - + Index: binaries/data/mods/mod/gui/modmod/styles.xml =================================================================== --- binaries/data/mods/mod/gui/modmod/styles.xml +++ binaries/data/mods/mod/gui/modmod/styles.xml @@ -6,7 +6,7 @@ font="sans-13" scrollbar="true" scrollbar_style="ModernScrollBar" - scroll_bottom="true" + scrollbar_sticky_end="true" textcolor="white" text_align="left" text_valign="center" Index: binaries/data/mods/public/gui/common/styles.xml =================================================================== --- binaries/data/mods/public/gui/common/styles.xml +++ binaries/data/mods/public/gui/common/styles.xml @@ -88,7 +88,7 @@ font="sans-14" scrollbar="true" scrollbar_style="ModernScrollBar" - scroll_bottom="false" + scrollbar_sticky_end="false" textcolor="white" text_align="left" text_valign="top" @@ -99,7 +99,7 @@ font="sans-13" scrollbar="true" scrollbar_style="ModernScrollBar" - scroll_bottom="true" + scrollbar_sticky_end="true" textcolor="white" text_align="left" /> @@ -109,7 +109,7 @@ font="sans-13" scrollbar="true" scrollbar_style="ModernScrollBar" - scroll_bottom="true" + scrollbar_sticky_end="true" textcolor="white" text_align="left" /> @@ -119,7 +119,7 @@ font="sans-bold-14" scrollbar="true" scrollbar_style="ModernScrollBar" - scroll_bottom="true" + scrollbar_sticky_end="true" textcolor="white" text_align="left" text_valign="center" Index: binaries/data/mods/public/gui/credits/credits.xml =================================================================== --- binaries/data/mods/public/gui/credits/credits.xml +++ binaries/data/mods/public/gui/credits/credits.xml @@ -20,7 +20,7 @@ - + Index: binaries/data/mods/public/gui/pregame/mainmenu.js =================================================================== --- binaries/data/mods/public/gui/pregame/mainmenu.js +++ binaries/data/mods/public/gui/pregame/mainmenu.js @@ -18,6 +18,33 @@ g_BackgroundLayerData, g_ProjectInformation, g_CommunityButtons); + + + const tstCt = [ "blue", "red", "purple", "cyan", "rainbow", "tartan", "plaid", "brown", "black", "grey", "gray", "pink", "green", "magenta", "taupe", "mauve", "pecan", "orange" ]; + const tstDd = Engine.GetGUIObjectByName("testDrop"); + tstDd.list = tstCt; + tstDd.selected = 2; + + const tstLt = Engine.GetGUIObjectByName("testList"); + tstLt.list = tstCt; + tstLt.selected = 4; + + const tstOLt = Engine.GetGUIObjectByName("testOList"); + tstOLt.list_name = tstCt; + tstOLt.list_a = tstCt; + tstOLt.list_b = tstCt.reverse(); + tstOLt.list = tstCt; + tstOLt.selected = 1; + + const tstTxt = Engine.GetGUIObjectByName("testTextbox").caption; + const tstTB = Engine.GetGUIObjectByName("testTextboxScrollable"); + for (let i = 0; i < 3; ++i) + tstTB.caption += '[font="sans-bold-12"]Scrollable [/font]' + tstTxt + "\n\n"; + + const tstML = Engine.GetGUIObjectByName("testInputMultiline"); + for (let i = 0; i < 24; ++i) + tstML.caption += "\n."; + tstML.caption += "\nGroovy, baby!"; } function getHotloadData() Index: binaries/data/mods/public/gui/pregame/mainmenu.xml =================================================================== --- binaries/data/mods/public/gui/pregame/mainmenu.xml +++ binaries/data/mods/public/gui/pregame/mainmenu.xml @@ -11,6 +11,31 @@ + + + + + +[font="sans-bold-14"]Textbox:[/font] +Consider yourself, at home! +Consider yourself, part of the furniture! +We've taken to you, so strong. +It's clear. We're. Going to get along! + + + + + + + + + + Single Line Input Box + Multiple Line Input Box + + + + Index: source/gui/CGUI.h =================================================================== --- source/gui/CGUI.h +++ source/gui/CGUI.h @@ -676,7 +676,7 @@ // Styles std::map m_Styles; - // Scroll-bar styles + // Scrollbar styles std::map m_ScrollBarStyles; // Icons Index: source/gui/CGUI.cpp =================================================================== --- source/gui/CGUI.cpp +++ source/gui/CGUI.cpp @@ -1185,13 +1185,13 @@ else scrollbar.m_UseEdgeButtons = b; } - else if (attr_name == "width") + else if (attr_name == "breadth") { float f; if (!ParseString(this, attr_value.FromUTF8(), f)) LOGERROR("GUI: Error parsing '%s' (\"%s\")", attr_name, attr_value); else - scrollbar.m_Width = f; + scrollbar.m_Breadth = f; } else if (attr_name == "minimum_bar_size") { Index: source/gui/CGUIScrollBarVertical.h =================================================================== --- source/gui/CGUIScrollBarVertical.h +++ source/gui/CGUIScrollBarVertical.h @@ -16,9 +16,10 @@ */ /* - A GUI Scrollbar, this class doesn't present all functionality - to the scrollbar, it just controls the drawing and a wrapper - for interaction with it. + A vertical GUI Scrollbar, this class doesn't present all functionality + to the scrollbar, it just controls the drawing, handles some events, + and provides a wrapper for interaction with itself. Actual tracking + of how far we've scrolled inside the owner is handled elsewhere. */ #ifndef INCLUDED_CGUISCROLLBARVERTICAL @@ -39,15 +40,19 @@ public: /** - * Draw the scroll-bar + * Draw the scrollbar */ virtual void Draw(CCanvas2D& canvas); /** - * If an object that contains a scrollbar has got messages, send - * them to the scroll-bar and it will see if the message regarded - * itself. + * Setup the scrollbar, setting the size, length and position. * + * @see IGUIScrollBar#Setup() + */ + virtual void Setup(); + virtual void Setup(const CRect& content); + + /** * @see IGUIObject#HandleMessage() */ virtual void HandleMessage(SGUIMessage& Message); @@ -81,14 +86,14 @@ /** * Get the rectangle of the outline of the scrollbar, every component of the - * scroll-bar should be inside this area. + * scrollbar should be inside this area. * @return Rectangle, CRect */ virtual CRect GetOuterRect() const; protected: /** - * Should the scroll bar proceed to the left or to the right of the m_X value. + * Should the scrollbar be drawn on the left or on the right of the m_X value. * Notice, this has nothing to do with where the owner places it. */ bool m_RightAligned; Index: source/gui/CGUIScrollBarVertical.cpp =================================================================== --- source/gui/CGUIScrollBarVertical.cpp +++ source/gui/CGUIScrollBarVertical.cpp @@ -31,6 +31,26 @@ { } +void CGUIScrollBarVertical::Setup() +{ + CRect host = GetHostGUIObject().GetComputedSize(); + CRect content = GetHostGUIObject().GetContentSize(); + + SetScrollRange(content.GetHeight()); + Setup(host); +} + +void CGUIScrollBarVertical::Setup(const CRect& content) +{ + SetScrollSpace(content.GetHeight()); + + SetX(m_RightAligned ? content.right : content.left); + SetY(content.top); + SetZ(GetHostGUIObject().GetBufferedZ()); + + SetLength(content.bottom - content.top); +} + void CGUIScrollBarVertical::SetPosFromMousePos(const CVector2D& mouse) { if (!GetStyle()) @@ -42,7 +62,7 @@ float emptyBackground = m_Length - m_BarSize; if (GetStyle()->m_UseEdgeButtons) - emptyBackground -= GetStyle()->m_Width * 2; + emptyBackground -= GetStyle()->m_Breadth * 2; m_Pos = m_PosWhenPressed + GetMaxPos() * (mouse.Y - m_BarPressedAtPos.Y) / emptyBackground; } @@ -51,11 +71,11 @@ { if (!GetStyle()) { - LOGWARNING("Attempt to draw scrollbar without a style."); + LOGWARNING("Attempt to draw a vertical scrollbar without a style."); return; } - if (IsVisible()) + if (IsNeeded()) { CRect outline = GetOuterRect(); @@ -64,9 +84,9 @@ canvas, CRect( outline.left, - outline.top + (GetStyle()->m_UseEdgeButtons ? GetStyle()->m_Width : 0), + outline.top + (GetStyle()->m_UseEdgeButtons ? GetStyle()->m_Breadth : 0), outline.right, - outline.bottom - (GetStyle()->m_UseEdgeButtons ? GetStyle()->m_Width : 0) + outline.bottom - (GetStyle()->m_UseEdgeButtons ? GetStyle()->m_Breadth : 0) ) ); @@ -102,7 +122,7 @@ outline.left, outline.top, outline.right, - outline.top+GetStyle()->m_Width + outline.top + GetStyle()->m_Breadth ) ); @@ -111,7 +131,7 @@ canvas, CRect( outline.left, - outline.bottom-GetStyle()->m_Width, + outline.bottom - GetStyle()->m_Breadth, outline.right, outline.bottom ) @@ -128,6 +148,20 @@ void CGUIScrollBarVertical::HandleMessage(SGUIMessage& Message) { + switch (Message.type) + { + case GUIM_MOUSE_WHEEL_UP: + ScrollMinus(); + break; + + case GUIM_MOUSE_WHEEL_DOWN: + ScrollPlus(); + break; + + default: + break; + } + IGUIScrollBar::HandleMessage(Message); } @@ -143,14 +177,14 @@ if (GetStyle()->m_UseEdgeButtons) { - from += GetStyle()->m_Width; - to -= GetStyle()->m_Width; + from += GetStyle()->m_Breadth; + to -= GetStyle()->m_Breadth; } ret.top = from + (to - from) * m_Pos / GetMaxPos(); ret.bottom = ret.top + m_BarSize; - ret.right = m_X + (m_RightAligned ? 0 : GetStyle()->m_Width); - ret.left = ret.right - GetStyle()->m_Width; + ret.right = m_X + (m_RightAligned ? 0 : GetStyle()->m_Breadth); + ret.left = ret.right - GetStyle()->m_Breadth; return ret; } @@ -162,9 +196,9 @@ return ret; ret.top = m_Y; - ret.bottom = m_Y+m_Length; - ret.right = m_X + (m_RightAligned ? 0 : GetStyle()->m_Width); - ret.left = ret.right - GetStyle()->m_Width; + ret.bottom = m_Y + m_Length; + ret.right = m_X + (m_RightAligned ? 0 : GetStyle()->m_Breadth); + ret.left = ret.right - GetStyle()->m_Breadth; return ret; } @@ -174,12 +208,12 @@ if (!GetStyle()) return false; - float StartX = m_RightAligned ? m_X-GetStyle()->m_Width : m_X; + float StartX = m_RightAligned ? m_X - GetStyle()->m_Breadth : m_X; return mouse.X >= StartX && - mouse.X <= StartX + GetStyle()->m_Width && + mouse.X <= StartX + GetStyle()->m_Breadth && mouse.Y >= m_Y && - mouse.Y <= m_Y + GetStyle()->m_Width; + mouse.Y <= m_Y + GetStyle()->m_Breadth; } bool CGUIScrollBarVertical::HoveringButtonPlus(const CVector2D& mouse) @@ -187,10 +221,10 @@ if (!GetStyle()) return false; - float StartX = m_RightAligned ? m_X-GetStyle()->m_Width : m_X; + float StartX = m_RightAligned ? m_X - GetStyle()->m_Breadth : m_X; return mouse.X > StartX && - mouse.X < StartX + GetStyle()->m_Width && - mouse.Y > m_Y + m_Length - GetStyle()->m_Width && + mouse.X < StartX + GetStyle()->m_Breadth && + mouse.Y > m_Y + m_Length - GetStyle()->m_Breadth && mouse.Y < m_Y + m_Length; } Index: source/gui/IGUIScrollBar.h =================================================================== --- source/gui/IGUIScrollBar.h +++ source/gui/IGUIScrollBar.h @@ -25,18 +25,19 @@ #define INCLUDED_IGUISCROLLBAR #include "gui/CGUISprite.h" +#include "gui/ObjectBases/IGUIScrollBarOwner.h" #include "maths/Vector2D.h" #include "ps/CStr.h" class CCanvas2D; class CGUI; -class IGUIScrollBarOwner; +class IGUIObject; struct SGUIMessage; /** - * The GUI Scroll-bar style. Tells us how scroll-bars look and feel. + * The GUI Scrollbar style. Tells us how scrollbars look and feel. * - * A scroll-bar style can choose whether to support horizontal, vertical + * A scrollbar style can choose whether to support horizontal, vertical * or both. * * @see IGUIScrollBar @@ -54,9 +55,11 @@ //@{ /** - * Width of bar, also both sides of the edge buttons. + * Breadth of bar, from side to side. It is the width of a vertical bar + * and the height of a horizontal bar. It is also used as the dimensions + * of both sides of the edge buttons. */ - float m_Width; + float m_Breadth; /** * Scrollable with the wheel. @@ -83,7 +86,7 @@ float m_MinimumBarSize; /** - * Sometimes you would like your scroll bar to have a fixed maximum size + * Sometimes you would like your scrollbar to have a fixed maximum size * so that the texture does not get too stretched, you can set a maximum * in pixels. */ @@ -139,17 +142,17 @@ /** - * The GUI Scroll-bar, used everywhere there is a scroll-bar in the game. + * The GUI Scrollbar, used everywhere there is a scrollbar in the game. * - * To include a scroll-bar to an object, inherent the object from - * IGUIScrollBarOwner and call AddScrollBar() to add the scroll-bars. + * To include a scrollbar to an object, inherent the object from + * IGUIScrollBarOwner and call AddScrollBar() to add the scrollbars. * * It's also important that the scrollbar is located within the parent * object's mouse over area. Otherwise the input won't be sent to the - * scroll-bar. + * scrollbar. * - * The class does not provide all functionality to the scroll-bar, many - * things the parent of the scroll-bar, must provide. Like a combo-box. + * The class does not provide all functionality to the scrollbar, many + * things the parent of the scrollbar, must provide. Like a combo-box. */ class IGUIScrollBar { @@ -161,19 +164,36 @@ public: /** - * Draw the scroll-bar + * Draw the scrollbar */ virtual void Draw(CCanvas2D& canvas) = 0; /** * If an object that contains a scrollbar has got messages, send - * them to the scroll-bar and it will see if the message regarded + * them to the scrollbar and it will see if the message regarded * itself. * * @see IGUIObject#HandleMessage() */ virtual void HandleMessage(SGUIMessage& Message) = 0; + /** + * Setup the scrollbar. Sets the size, length and position. + * An object owning scrollbars still has to call this (preferably + * in the object's own Setup function), it isn't called + * automatically. There are two variations covering several common + * uses. + * + * The first assumes that the content is inside the host and thus + * uses the host's dimensions. + * + * The second permits a CRect defining the dimensions of the + * scrollable content to be passed. Any object that uses this + * variant is required to explicitly set a scroll range. + */ + virtual void Setup() = 0; + virtual void Setup(const CRect& content) = 0; + /** * Set m_Pos with g_mouse_x/y input, i.e. when draggin. */ @@ -213,7 +233,7 @@ /** * Scrollbars without height shouldn't be visible */ - bool IsVisible() const { return GetMaxPos() != 0.f; } + bool IsNeeded() const { return GetMaxPos() != 0.f; } /** * Increase scroll one step @@ -236,16 +256,16 @@ virtual void ScrollMinusPlenty() { m_Pos -= 90.f; UpdatePosBoundaries(); } /** - * Set host object, must be done almost at creation of scroll bar. + * Set host object, must be done almost at creation of the scrollbar. * @param pOwner Pointer to host object. */ void SetHostObject(IGUIScrollBarOwner* pOwner) { m_pHostObject = pOwner; } /** - * Set Width - * @param width Width + * Set the Breadth of the scrollbar + * @param breadth Breadth */ - void SetWidth(float width) { m_Width = width; } + void SetBreadth(float breadth) { m_Breadth = breadth; } /** * Set X Position @@ -266,7 +286,7 @@ void SetZ(float z) { m_Z = z; } /** - * Set Length of scroll bar + * Set the length of the scrollbar * @param length Length */ void SetLength(float length) { m_Length = length; } @@ -290,30 +310,37 @@ void SetBarPressed(bool b) { m_BarPressed = b; } /** - * Set Scroll bar style string - * @param style String with scroll bar style reference name + * Set the scrollbar style + * @param style String consisting of the name of the scrollbar style */ void SetScrollBarStyle(const CStr& style) { m_ScrollBarStyle = style; } /** * Get style used by the scrollbar - * @return Scroll bar style struct. + * @return Scrollbar style struct. */ const SGUIScrollBarStyle* GetStyle() const; /** - * Get the rectangle of the actual BAR. not the whole scroll-bar. + * Get the rectangle of the actual BAR. not the whole scrollbar. * @return Rectangle, CRect */ virtual CRect GetBarRect() const = 0; /** * Get the rectangle of the outline of the scrollbar, every component of the - * scroll-bar should be inside this area. + * scrollbar should be inside this area. * @return Rectangle, CRect */ virtual CRect GetOuterRect() const = 0; + /** + * The difference between this and m_pHostObject is the latter is the ScrollBarOwner class, + * while this retuns the IGUIObject class, and as such has access to the object attributes + * set via JS, along with other attributes. + */ + IGUIObject& GetHostGUIObject() { return m_pHostObject->m_pObject; } + protected: /** * Sets up bar size @@ -333,9 +360,9 @@ //@{ /** - * Width of the scroll bar + * Breadth of the scrollbar */ - float m_Width; + float m_Breadth; /** * Absolute X Position @@ -373,15 +400,10 @@ float m_BarSize; /** - * Scroll bar style reference name + * Scrollbar style reference name */ CStr m_ScrollBarStyle; - /** - * Pointer to scroll bar style used. - */ - SGUIScrollBarStyle* m_pStyle; - /** * Host object, prerequisite! */ @@ -424,7 +446,7 @@ bool m_ButtonMinusPressed, m_ButtonPlusPressed; /** - * Position of scroll bar, 0 means scrolled all the way to one side. + * Position of the scrollbar, 0 means scrolled all the way to one side. * It is measured in pixels, it is up to the host to make it actually * apply in pixels. */ Index: source/gui/IGUIScrollBar.cpp =================================================================== --- source/gui/IGUIScrollBar.cpp +++ source/gui/IGUIScrollBar.cpp @@ -26,10 +26,9 @@ IGUIScrollBar::IGUIScrollBar(CGUI& pGUI) : m_pGUI(pGUI), - m_pStyle(nullptr), m_X(300.f), m_Y(300.f), m_ScrollRange(1.f), m_ScrollSpace(0.f), // MaxPos: not 0, due to division. - m_Length(200.f), m_Width(20.f), + m_Length(200.f), m_Breadth(20.f), m_BarSize(0.f), m_Pos(0.f), m_ButtonPlusPressed(false), m_ButtonMinusPressed(false), @@ -55,7 +54,7 @@ // Check for edge buttons if (GetStyle()->m_UseEdgeButtons) - length -= GetStyle()->m_Width * 2.f; + length -= GetStyle()->m_Breadth * 2.f; // Check min and max are valid if (min > length) @@ -121,42 +120,37 @@ const CVector2D& mouse = m_pGUI.GetMousePos(); - // if bar is pressed + // If bar is pressed if (GetBarRect().PointInside(mouse)) { m_BarPressed = true; m_BarPressedAtPos = mouse; m_PosWhenPressed = m_Pos; } - // if button-minus is pressed + // If "minus" button is pressed else if (m_ButtonMinusHovered) { m_ButtonMinusPressed = true; ScrollMinus(); } - // if button-plus is pressed + // If "plus" button is pressed else if (m_ButtonPlusHovered) { m_ButtonPlusPressed = true; ScrollPlus(); } - // Pressing the background of the bar, to scroll - // notice the if-sentence alone does not admit that, - // it must be after the above if/elses - else + // Pressing the background of the scrollbar + else if (GetOuterRect().PointInside(mouse)) { - if (GetOuterRect().PointInside(mouse)) - { - // Scroll plus or minus a lot, this might change, it doesn't - // have to be fancy though. - if (mouse.Y < GetBarRect().top) - ScrollMinusPlenty(); - else - ScrollPlusPlenty(); - // Simulate mouse movement to see if bar now is hovered - SGUIMessage msg(GUIM_MOUSE_MOTION); - HandleMessage(msg); - } + // Scroll plus or minus a lot, this might change, it doesn't + // have to be fancy though. + if (mouse.Y < GetBarRect().top) + ScrollMinusPlenty(); + else + ScrollPlusPlenty(); + // Simulate mouse movement to see if bar now is hovered + SGUIMessage msg(GUIM_MOUSE_MOTION); + HandleMessage(msg); } break; } @@ -168,7 +162,10 @@ case GUIM_MOUSE_WHEEL_UP: { - ScrollMinus(); + // If we're at the limit, pass the message to host object's parent. + if (m_Pos <= 0) + GetHostGUIObject().GetParent()->HandleMessage(Message); + // Since the scroll was changed, let's simulate a mouse movement // to check if scrollbar now is hovered SGUIMessage msg(GUIM_MOUSE_MOTION); @@ -178,7 +175,10 @@ case GUIM_MOUSE_WHEEL_DOWN: { - ScrollPlus(); + // If we're at the limit, pass the message to host object's parent. + if (m_Pos >= GetMaxPos()) + GetHostGUIObject().GetParent()->HandleMessage(Message); + // Since the scroll was changed, let's simulate a mouse movement // to check if scrollbar now is hovered SGUIMessage msg(GUIM_MOUSE_MOTION); Index: source/gui/ObjectBases/IGUIObject.h =================================================================== --- source/gui/ObjectBases/IGUIObject.h +++ source/gui/ObjectBases/IGUIObject.h @@ -191,6 +191,11 @@ */ CRect GetComputedSize(); + /** + * Calculates and returns the size of the content of the object. + */ + virtual const CRect GetContentSize() { return CRect(); }; + virtual const CStrW& GetTooltipText() const { return m_Tooltip; } virtual const CStr& GetTooltipStyle() const { return m_TooltipStyle; } @@ -297,15 +302,6 @@ */ bool ApplyStyle(const CStr& StyleName); - /** - * Returns not the Z value, but the actual buffered Z value, i.e. if it's - * defined relative, then it will check its parent's Z value and add - * the relativity. - * - * @return Actual Z value on the screen. - */ - virtual float GetBufferedZ() const; - /** * Add an object to the hierarchy. */ @@ -336,11 +332,14 @@ */ void ReleaseFocus(); -protected: /** - * Check if object is focused. + * Returns not the Z value, but the actual buffered Z value, i.e. if it's + * defined relative, then it will check its parent's Z value and add + * the relativity. + * + * @return Actual Z value on the screen. */ - bool IsFocused() const; + virtual float GetBufferedZ() const; /** * NOTE! This will not just return m_pParent, when that is @@ -353,6 +352,12 @@ */ IGUIObject* GetParent() const; +protected: + /** + * Check if object is focused. + */ + bool IsFocused() const; + /** * Handle additional children to the \-tag. In IGUIObject, this function does * nothing. In CList and CDropDown, it handles the \, used to build the data. Index: source/gui/ObjectBases/IGUIScrollBarOwner.h =================================================================== --- source/gui/ObjectBases/IGUIScrollBarOwner.h +++ source/gui/ObjectBases/IGUIScrollBarOwner.h @@ -29,8 +29,8 @@ class IGUIObject; /** - * Base-class this if you want an object to contain - * one, or several, scroll-bars. + * Base class if you want an object to contain + * one, or several, scrollbars. */ class IGUIScrollBarOwner { @@ -60,13 +60,13 @@ virtual const SGUIScrollBarStyle* GetScrollBarStyle(const CStr8& style) const; /** - * Add a scroll-bar + * Add a scrollbar */ virtual void AddScrollBar(std::unique_ptr scrollbar); /** - * Get Scroll Bar reference (it should be transparent it's actually - * pointers). + * Get a scrollbar reference (it should be transparent it's actually a + * pointer). */ virtual IGUIScrollBar& GetScrollBar(const int& index) { @@ -74,11 +74,13 @@ } /** - * Get the position of the scroll bar at @param index. + * Get the position of the scrollbar at @param index. * Equivalent to GetScrollbar(index).GetPos(). */ virtual float GetScrollBarPos(const int index) const; + bool KeepScrollBarAtEnd(const int index) const; + protected: /** * Predominately you will only have one, but you can have @@ -86,6 +88,8 @@ */ std::vector> m_ScrollBars; + CGUISimpleSetting m_ScrollStickyEnd; + private: /** * Reference to the IGUIObject. Index: source/gui/ObjectBases/IGUIScrollBarOwner.cpp =================================================================== --- source/gui/ObjectBases/IGUIScrollBarOwner.cpp +++ source/gui/ObjectBases/IGUIScrollBarOwner.cpp @@ -24,7 +24,8 @@ #include "gui/ObjectBases/IGUIObject.h" IGUIScrollBarOwner::IGUIScrollBarOwner(IGUIObject& pObject) - : m_pObject(pObject) + : m_pObject(pObject), + m_ScrollStickyEnd(&pObject, "scrollbar_sticky_end", false) { } @@ -63,3 +64,9 @@ { return m_ScrollBars[index]->GetPos(); } + +bool IGUIScrollBarOwner::KeepScrollBarAtEnd(const int index) const +{ + // We use 1.5px delta so this is true when there's no content yet. + return m_ScrollStickyEnd && m_ScrollBars[index]->GetPos() > m_ScrollBars[index]->GetMaxPos() - 1.5f; +} Index: source/gui/ObjectTypes/CDropDown.cpp =================================================================== --- source/gui/ObjectTypes/CDropDown.cpp +++ source/gui/ObjectTypes/CDropDown.cpp @@ -88,6 +88,7 @@ Message.value == "dropdown_buffer" || Message.value == "minimum_visible_items" || Message.value == "scrollbar_style" || + Message.value == "scrollbar" || Message.value == "button_width") { SetupListRect(); @@ -114,8 +115,8 @@ for (int i = 0; i < static_cast(m_List->m_Items.size()); ++i) { if (mouse.Y >= rect.top + m_ItemsYPositions[i] && - mouse.Y < rect.top + m_ItemsYPositions[i+1] && - // mouse is not over scroll-bar + mouse.Y < rect.top + m_ItemsYPositions[i + 1] && + // Mouse is not over scrollbar. (m_HideScrollBar || mouse.X < GetScrollBar(0).GetOuterRect().left || mouse.X > GetScrollBar(0).GetOuterRect().right)) @@ -149,8 +150,8 @@ break; } - // We can't inherent this routine from CList, because we need to include - // a mouse click to open the dropdown, also the coordinates are changed. + // We can't inheret this routine from CList, because we need to include + // a mouse click to open the dropdown. Also, the coordinates are changed. case GUIM_MOUSE_PRESS_LEFT: { if (!m_Enabled) Index: source/gui/ObjectTypes/CInput.h =================================================================== --- source/gui/ObjectTypes/CInput.h +++ source/gui/ObjectTypes/CInput.h @@ -92,6 +92,8 @@ */ virtual void UpdateCachedSize(); + virtual const CRect GetContentSize(); + /** * Draws the Text */ @@ -194,8 +196,10 @@ bool m_SelectingText; /** - * Whether the cached text is currently valid (if not then SetupText will be called by Draw) + * Whether the cached texts are currently valid. If not, then an + * appropriate method to update it will be called by Draw. */ + bool m_GeneratedTextValid; bool m_GeneratedPlaceholderTextValid; CGUIText m_GeneratedPlaceholderText; Index: source/gui/ObjectTypes/CInput.cpp =================================================================== --- source/gui/ObjectTypes/CInput.cpp +++ source/gui/ObjectTypes/CInput.cpp @@ -53,6 +53,7 @@ m_CursorVisState(true), m_CursorBlinkRate(0.5), m_ComposingText(), + m_GeneratedTextValid(false), m_GeneratedPlaceholderTextValid(false), m_iComposedLength(), m_iComposedPos(), @@ -893,20 +894,14 @@ { case GUIM_SETTINGS_UPDATED: { - // Update scroll-bar - // TODO Gee: (2004-09-01) Is this really updated each time it should? if (m_ScrollBar && (Message.value == "size" || Message.value == "z" || Message.value == "absolute")) { - GetScrollBar(0).SetX(m_CachedActualSize.right); - GetScrollBar(0).SetY(m_CachedActualSize.top); - GetScrollBar(0).SetZ(GetBufferedZ()); - GetScrollBar(0).SetLength(m_CachedActualSize.bottom - m_CachedActualSize.top); + GetScrollBar(0).Setup(); } - // Update scrollbar if (Message.value == "scrollbar_style") GetScrollBar(0).SetScrollBarStyle(m_ScrollBarStyle); @@ -924,7 +919,7 @@ Message.value == "scrollbar" || Message.value == "scrollbar_style") { - UpdateText(); + m_GeneratedTextValid = false; } if (Message.value == "multiline") @@ -934,7 +929,7 @@ else GetScrollBar(0).SetLength(m_CachedActualSize.bottom - m_CachedActualSize.top); - UpdateText(); + m_GeneratedTextValid = false; } if (Message.value == "placeholder_text" || @@ -957,7 +952,7 @@ m_MultiLine && GetScrollBar(0).GetStyle()) { - if (m_pGUI.GetMousePos().X > m_CachedActualSize.right - GetScrollBar(0).GetStyle()->m_Width) + if (m_pGUI.GetMousePos().X > m_CachedActualSize.right - GetScrollBar(0).GetStyle()->m_Breadth) break; } @@ -1114,11 +1109,8 @@ } case GUIM_LOAD: { - GetScrollBar(0).SetX(m_CachedActualSize.right); - GetScrollBar(0).SetY(m_CachedActualSize.top); - GetScrollBar(0).SetZ(GetBufferedZ()); - GetScrollBar(0).SetLength(m_CachedActualSize.bottom - m_CachedActualSize.top); GetScrollBar(0).SetScrollBarStyle(m_ScrollBarStyle); + GetScrollBar(0).Setup(); UpdateText(); UpdateAutoScroll(); @@ -1171,24 +1163,30 @@ void CInput::UpdateCachedSize() { - // If an ancestor's size changed, this will let us intercept the change and - // update our scrollbar positions + // If an ancestor's size changed, this will let us flag that + // the text needs resizing/repositioning on the next Draw call. IGUIObject::UpdateCachedSize(); - if (m_ScrollBar) - { - GetScrollBar(0).SetX(m_CachedActualSize.right); - GetScrollBar(0).SetY(m_CachedActualSize.top); - GetScrollBar(0).SetZ(GetBufferedZ()); - GetScrollBar(0).SetLength(m_CachedActualSize.bottom - m_CachedActualSize.top); - } - + m_GeneratedTextValid = false; m_GeneratedPlaceholderTextValid = false; } +const CRect CInput::GetContentSize() +{ + CStrIntern font_name(m_Font->ToUTF8()); + if (font_name.empty()) + return CRect(); + CFontMetrics font(font_name); + float height = m_CharacterPositions.size() * font.GetLineSpacing() + m_BufferZone * 2.f; + return CRect(0.0f, 0.0f, GetTextAreaWidth(), height); +} + void CInput::Draw(CCanvas2D& canvas) { + if (!m_GeneratedTextValid) + UpdateText(); + if (m_CursorBlinkRate > 0.0) { // check if the cursor visibility state needs to be changed @@ -1879,11 +1877,10 @@ // add the final row (even if empty) m_CharacterPositions.insert(current_line, row); + m_GeneratedTextValid = true; + if (m_ScrollBar) - { - GetScrollBar(0).SetScrollRange(m_CharacterPositions.size() * font.GetLineSpacing() + m_BufferZone * 2.f); - GetScrollBar(0).SetScrollSpace(m_CachedActualSize.GetHeight()); - } + GetScrollBar(0).Setup(); } int CInput::GetMouseHoveringTextPosition() const @@ -2018,7 +2015,7 @@ float CInput::GetTextAreaWidth() { if (m_ScrollBar && GetScrollBar(0).GetStyle()) - return m_CachedActualSize.GetWidth() - m_BufferZone * 2.f - GetScrollBar(0).GetStyle()->m_Width; + return m_CachedActualSize.GetWidth() - m_BufferZone * 2.f - GetScrollBar(0).GetStyle()->m_Breadth; return m_CachedActualSize.GetWidth() - m_BufferZone * 2.f; } Index: source/gui/ObjectTypes/CList.h =================================================================== --- source/gui/ObjectTypes/CList.h +++ source/gui/ObjectTypes/CList.h @@ -32,7 +32,7 @@ * text-object for each element, which will be managed * by the IGUITextOwner structure. * - * A scroll-bar will appear when needed. This will be + * A scrollbar will appear when needed. This will be * achieved with the IGUIScrollBarOwner structure. */ class CList : public IGUIObject, public IGUIScrollBarOwner, public IGUITextOwner @@ -131,7 +131,6 @@ CGUISimpleSetting m_Font; CGUISimpleSetting m_ScrollBar; CGUISimpleSetting m_ScrollBarStyle; - CGUISimpleSetting m_ScrollBottom; CGUISimpleSetting m_SoundDisabled; CGUISimpleSetting m_SoundSelected; CGUISimpleSetting m_Sprite; Index: source/gui/ObjectTypes/CList.cpp =================================================================== --- source/gui/ObjectTypes/CList.cpp +++ source/gui/ObjectTypes/CList.cpp @@ -42,7 +42,6 @@ m_Font(this, "font"), m_ScrollBar(this, "scrollbar", false), m_ScrollBarStyle(this, "scrollbar_style"), - m_ScrollBottom(this, "scroll_bottom", false), m_SoundDisabled(this, "sound_disabled"), m_SoundSelected(this, "sound_selected"), m_Sprite(this, "sprite"), @@ -59,7 +58,7 @@ m_List(this, "list"), m_ListData(this, "list_data") { - // Add scroll-bar + // Add scrollbar auto bar = std::make_unique(pGUI); bar->SetRightAligned(true); AddScrollBar(std::move(bar)); @@ -88,11 +87,8 @@ bool bottom = false; if (m_ScrollBar && GetScrollBar(0).GetStyle()) { - if (m_ScrollBottom && GetScrollBar(0).GetPos() > GetScrollBar(0).GetMaxPos() - 1.5f) - bottom = true; - - // remove scrollbar if applicable - width -= GetScrollBar(0).GetStyle()->m_Width; + bottom = KeepScrollBarAtEnd(0); + width -= GetScrollBar(0).GetStyle()->m_Breadth; } // Generate texts @@ -123,17 +119,10 @@ m_ItemsYPositions[m_List->m_Items.size()] = buffered_y; - // Setup scrollbar if (m_ScrollBar) { GetScrollBar(0).SetScrollRange(m_ItemsYPositions.back()); - GetScrollBar(0).SetScrollSpace(GetListRect().GetHeight()); - - CRect rect = GetListRect(); - GetScrollBar(0).SetX(rect.right); - GetScrollBar(0).SetY(rect.top); - GetScrollBar(0).SetZ(GetBufferedZ()); - GetScrollBar(0).SetLength(rect.bottom - rect.top); + GetScrollBar(0).Setup(GetListRect()); if (bottom) GetScrollBar(0).SetPos(GetScrollBar(0).GetMaxPos()); @@ -156,7 +145,7 @@ { IGUIObject::HandleMessage(Message); IGUIScrollBarOwner::HandleMessage(Message); - //IGUITextOwner::HandleMessage(Message); <== placed it after the switch instead! + IGUITextOwner::HandleMessage(Message); m_Modified = false; switch (Message.type) @@ -176,16 +165,11 @@ ScriptEvent(EventNameSelectionChange); } - if (Message.value == "scrollbar") + if (Message.value == "scrollbar" || Message.value == "scrollbar_style") SetupText(); - // Update scrollbar if (Message.value == "scrollbar_style") - { GetScrollBar(0).SetScrollBarStyle(m_ScrollBarStyle); - SetupText(); - } - break; case GUIM_MOUSE_PRESS_LEFT: @@ -235,16 +219,12 @@ } case GUIM_LOAD: - { GetScrollBar(0).SetScrollBarStyle(m_ScrollBarStyle); break; - } default: break; } - - IGUITextOwner::HandleMessage(Message); } InReaction CList::ManuallyHandleKeys(const SDL_Event_* ev) @@ -354,7 +334,7 @@ m_ItemsYPositions[i] - scroll > rect.GetHeight()) continue; - // Clipping area (we'll have to substract the scrollbar) + // Clipping area (we'll have to subtract the scrollbar) CRect cliparea = GetListRect(); if (m_ScrollBar) @@ -473,7 +453,7 @@ mouse.Y += scroll; // Mouse is over scrollbar - if (m_ScrollBar && GetScrollBar(0).IsVisible() && + if (m_ScrollBar && GetScrollBar(0).IsNeeded() && mouse.X >= GetScrollBar(0).GetOuterRect().left && mouse.X <= GetScrollBar(0).GetOuterRect().right) return -1; Index: source/gui/ObjectTypes/COList.h =================================================================== --- source/gui/ObjectTypes/COList.h +++ source/gui/ObjectTypes/COList.h @@ -48,7 +48,7 @@ * * The list can be sorted dynamically by JS code when a * heading is clicked. - * A scroll-bar will appear when needed. + * A scrollbar will appear when needed. */ class COList : public CList { Index: source/gui/ObjectTypes/COList.cpp =================================================================== --- source/gui/ObjectTypes/COList.cpp +++ source/gui/ObjectTypes/COList.cpp @@ -53,9 +53,10 @@ m_GeneratedTexts.clear(); m_TotalAvailableColumnWidth = GetListRect().GetWidth(); - // remove scrollbar if applicable + + // Reduce width by the breadth of the scrollbar if applicable. if (m_ScrollBar && GetScrollBar(0).GetStyle()) - m_TotalAvailableColumnWidth -= GetScrollBar(0).GetStyle()->m_Width; + m_TotalAvailableColumnWidth -= GetScrollBar(0).GetStyle()->m_Breadth; m_HeadingHeight = SORT_SPRITE_DIM; // At least the size of the sorting sprite @@ -104,14 +105,8 @@ if (m_ScrollBar) { - CRect rect = GetListRect(); GetScrollBar(0).SetScrollRange(m_ItemsYPositions.back()); - GetScrollBar(0).SetScrollSpace(rect.GetHeight()); - - GetScrollBar(0).SetX(rect.right); - GetScrollBar(0).SetY(rect.top); - GetScrollBar(0).SetZ(GetBufferedZ()); - GetScrollBar(0).SetLength(rect.bottom - rect.top); + GetScrollBar(0).Setup(GetListRect()); } } Index: source/gui/ObjectTypes/CText.h =================================================================== --- source/gui/ObjectTypes/CText.h +++ source/gui/ObjectTypes/CText.h @@ -49,6 +49,8 @@ */ CSize2D GetTextSize(); + virtual const CRect GetContentSize(); + virtual const CStrW& GetTooltipText() const; protected: /** @@ -80,8 +82,7 @@ CGUISimpleSetting m_Font; CGUISimpleSetting m_ScrollBar; CGUISimpleSetting m_ScrollBarStyle; - CGUISimpleSetting m_ScrollBottom; - CGUISimpleSetting m_ScrollTop; + CGUISimpleSetting m_ScrollResetOnChange; CGUISimpleSetting m_Sprite; CGUISimpleSetting m_SpriteOverlay; CGUISimpleSetting m_TextColor; Index: source/gui/ObjectTypes/CText.cpp =================================================================== --- source/gui/ObjectTypes/CText.cpp +++ source/gui/ObjectTypes/CText.cpp @@ -33,14 +33,13 @@ m_Font(this, "font"), m_ScrollBar(this, "scrollbar", false), m_ScrollBarStyle(this, "scrollbar_style"), - m_ScrollBottom(this, "scroll_bottom"), - m_ScrollTop(this, "scroll_top"), + m_ScrollResetOnChange(this, "scrollbar_reset_on_change"), m_Sprite(this, "sprite"), m_SpriteOverlay(this, "sprite_overlay"), m_TextColor(this, "textcolor"), m_TextColorDisabled(this, "textcolor_disabled") { - // Add scroll-bar + // Add scrollbar auto bar = std::make_unique(pGUI); bar->SetRightAligned(true); AddScrollBar(std::move(bar)); @@ -59,40 +58,34 @@ return; float width = m_CachedActualSize.GetWidth(); - // remove scrollbar if applicable + + // Reduce width by scrollbar breadth if applicable. if (m_ScrollBar && GetScrollBar(0).GetStyle()) - width -= GetScrollBar(0).GetStyle()->m_Width; + width -= GetScrollBar(0).GetStyle()->m_Breadth; m_GeneratedTexts[0] = CGUIText(m_pGUI, m_Caption, m_Font, width, m_BufferZone, m_TextAlign, this); if (!m_ScrollBar) - CalculateTextPosition(m_CachedActualSize, m_TextPos, m_GeneratedTexts[0]); - - // Setup scrollbar - if (m_ScrollBar) { - // If we are currently scrolled to the bottom of the text, - // then add more lines of text, update the scrollbar so we - // stick to the bottom. - // (Use 1.5px delta so this triggers the first time caption is set) - bool bottom = false; - if (m_ScrollBottom && GetScrollBar(0).GetPos() > GetScrollBar(0).GetMaxPos() - 1.5f) - bottom = true; - - GetScrollBar(0).SetScrollRange(m_GeneratedTexts[0].GetSize().Height); - GetScrollBar(0).SetScrollSpace(m_CachedActualSize.GetHeight()); - - GetScrollBar(0).SetX(m_CachedActualSize.right); - GetScrollBar(0).SetY(m_CachedActualSize.top); - GetScrollBar(0).SetZ(GetBufferedZ()); - GetScrollBar(0).SetLength(m_CachedActualSize.bottom - m_CachedActualSize.top); - - if (bottom) - GetScrollBar(0).SetPos(GetScrollBar(0).GetMaxPos()); - - if (m_ScrollTop) - GetScrollBar(0).SetPos(0.0f); + CalculateTextPosition(m_CachedActualSize, m_TextPos, m_GeneratedTexts[0]); + return; } + + bool bottom = false; + if (m_ScrollResetOnChange) + GetScrollBar(0).SetPos(0.0f); + else + // If we are currently scrolled to the bottom of the text, the UI designer has + // specified that we should "stick" to the bottom, and more lines of text are + // being added, then we need to update the scrollbar so we stay at the bottom. + // And we need to determine this before we renew the scrollbars after adding + // the text. + bottom = KeepScrollBarAtEnd(0); + + GetScrollBar(0).Setup(); + + if (bottom) + GetScrollBar(0).SetPos(GetScrollBar(0).GetMaxPos()); } void CText::ResetStates() @@ -113,6 +106,13 @@ return m_GeneratedTexts[0].GetSize(); } +const CRect CText::GetContentSize() +{ + if (m_GeneratedTexts.empty()) + return CRect(); + return m_GeneratedTexts[0].GetSize(); +} + const CStrW& CText::GetTooltipText() const { for (const CGUIText& text : m_GeneratedTexts) @@ -131,56 +131,31 @@ { IGUIObject::HandleMessage(Message); IGUIScrollBarOwner::HandleMessage(Message); - //IGUITextOwner::HandleMessage(Message); <== placed it after the switch instead! + IGUITextOwner::HandleMessage(Message); switch (Message.type) { case GUIM_SETTINGS_UPDATED: - if (Message.value == "scrollbar") + if (Message.value == "scrollbar" || Message.value == "scrollbar_style") SetupText(); - // Update scrollbar if (Message.value == "scrollbar_style") - { GetScrollBar(0).SetScrollBarStyle(m_ScrollBarStyle); - SetupText(); - } - break; case GUIM_MOUSE_WHEEL_DOWN: - { - GetScrollBar(0).ScrollPlus(); - // Since the scroll was changed, let's simulate a mouse movement - // to check if scrollbar now is hovered - SGUIMessage msg(GUIM_MOUSE_MOTION); - HandleMessage(msg); - break; - } case GUIM_MOUSE_WHEEL_UP: - { - GetScrollBar(0).ScrollMinus(); - // Since the scroll was changed, let's simulate a mouse movement - // to check if scrollbar now is hovered - SGUIMessage msg(GUIM_MOUSE_MOTION); - HandleMessage(msg); + if (!m_ScrollBar) + m_pParent->HandleMessage(Message); break; - } + case GUIM_LOAD: - { - GetScrollBar(0).SetX(m_CachedActualSize.right); - GetScrollBar(0).SetY(m_CachedActualSize.top); - GetScrollBar(0).SetZ(GetBufferedZ()); - GetScrollBar(0).SetLength(m_CachedActualSize.bottom - m_CachedActualSize.top); GetScrollBar(0).SetScrollBarStyle(m_ScrollBarStyle); break; - } default: break; } - - IGUITextOwner::HandleMessage(Message); } void CText::Draw(CCanvas2D& canvas) @@ -213,14 +188,14 @@ const CGUIColor& color = m_Enabled ? m_TextColor : m_TextColorDisabled; if (m_ScrollBar) + { DrawText(canvas, 0, color, m_CachedActualSize.TopLeft() - CVector2D(0.f, scroll), cliparea); + // Draw scrollbars on top of the content + IGUIScrollBarOwner::Draw(canvas); + } else DrawText(canvas, 0, color, m_TextPos, cliparea); - // Draw scrollbars on top of the content - if (m_ScrollBar) - IGUIScrollBarOwner::Draw(canvas); - // Draw the overlays last m_pGUI.DrawSprite(m_SpriteOverlay, canvas, m_CachedActualSize); }