Add scrollpanel widget

This PR introduces a new ScrollPanel component with the following
capabilities:
- Scroll Orientation Support: Allows scrolling in horizontal, vertical,
  or both directions, providing flexibility for different use cases.
- Partial Object Rendering: Supports partial rendering of objects that
  are only partially visible within the scroll boundaries, improving
  visual accuracy and performance.
- Boundary-Constrained Mouse Interaction: Handles mouse events strictly
  within the panel's visible boundaries, preventing interaction with
  objects outside the scrollable area.
- Minimum Internal Size (min_width, min_height): Introduces support for
  virtual space management, allowing the panel to maintain a minimum
  internal size independent of its actual on-screen dimensions. Even
  when the panel is resized, this ensures that the content respects a
  defined virtual space (with min_width and min_height), effectively
  simulating a larger internal canvas. This is particularly useful for
  large content or scenarios where a more extensive scrollable area is
  required than the current visible panel.
This commit is contained in:
trompetin17 2024-10-19 13:17:30 -05:00
parent d98c93b35e
commit 8c250568e7
No known key found for this signature in database
GPG key ID: 6C585FA3FC5DB179
41 changed files with 1238 additions and 183 deletions

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4f229151ac93b005f69b5752e716c21ee8fd22b84f9e99912e8fc84f5ed4461f
size 680

View file

@ -11,8 +11,10 @@
minimum_bar_size = "15"
maximum_bar_size = "15"
show_edge_buttons = "false"
sprite_back_vertical = "ModernScrollBack"
sprite_bar_vertical = "ModernScrollBar"
sprite_back_vertical = "ModernScrollbarVerticalBackground"
sprite_slider_vertical = "ModernScrollbarVerticalSlider"
sprite_back_horizontal = "ModernScrollbarHorizontalBackground"
sprite_slider_horizontal = "ModernScrollbarHorizontalSlider"
/>
<!--

View file

@ -6,7 +6,7 @@
- Scrollbar -
==========================================
-->
<sprite name = "ModernScrollBack">
<sprite name = "ModernScrollbarVerticalBackground">
<image backcolor="43 42 40"
size="0 0 100% 100%"
/>
@ -14,12 +14,34 @@
<image backcolor="0 0 0" size="0 100%-1 100% 100%"/>
<image backcolor="0 0 0" size="0 0 1 100%"/>
<image backcolor="0 0 0" size="100%-1 0 100% 100%"/>
<image texture = "global/modern/scrollback.png"
<image texture = "global/modern/scroll-background-vertical.png"
real_texture_placement = "0 0 15 128"
size = "0 0 100% 100%"
/>
</sprite>
<sprite name = "ModernScrollBar">
<sprite name = "ModernScrollbarHorizontalBackground">
<image backcolor="43 42 40"
size="0 0 100% 100%"
/>
<image backcolor="0 0 0" size="0 0 100% 1"/>
<image backcolor="0 0 0" size="0 100%-1 100% 100%"/>
<image backcolor="0 0 0" size="0 0 1 100%"/>
<image backcolor="0 0 0" size="100%-1 0 100% 100%"/>
<image texture = "global/modern/scroll-background-horizontal.png"
real_texture_placement = "0 0 128 15"
size = "0 0 100% 100%"
/>
</sprite>
<sprite name = "ModernScrollbarVerticalSlider">
<image texture = "global/modern/scrollbar.png"
real_texture_placement = "0 0 15 15"
size = "0 0 100% 100%"
/>
</sprite>
<sprite name = "ModernScrollbarHorizontalSlider">
<image texture = "global/modern/scrollbar.png"
real_texture_placement = "0 0 15 15"
size = "0 0 100% 100%"

View file

@ -508,6 +508,21 @@
<optional>
<attribute name="tooltip_style"/>
</optional>
<optional>
<attribute name="orientation">
<choice>
<value>horizontal</value>
<value>vertical</value>
<value>both</value>
</choice>
</attribute>
</optional>
<optional>
<attribute name="min_width"/>
</optional>
<optional>
<attribute name="min_height"/>
</optional>
</interleave>
</define>
<define name="objects">
@ -759,17 +774,29 @@
<attribute name="sprite_button_bottom_over"/>
</optional>
<optional>
<attribute name="sprite_bar_vertical"/>
<attribute name="sprite_slider_vertical"/>
</optional>
<optional>
<attribute name="sprite_bar_vertical_over"/>
<attribute name="sprite_slider_vertical_over"/>
</optional>
<optional>
<attribute name="sprite_bar_vertical_pressed"/>
<attribute name="sprite_slider_vertical_pressed"/>
</optional>
<optional>
<attribute name="sprite_back_vertical"/>
</optional>
<optional>
<attribute name="sprite_slider_horizontal"/>
</optional>
<optional>
<attribute name="sprite_slider_horizontal_over"/>
</optional>
<optional>
<attribute name="sprite_slider_horizontal_pressed"/>
</optional>
<optional>
<attribute name="sprite_back_horizontal"/>
</optional>
</interleave>
</element>
</define>

View file

@ -19,6 +19,8 @@
#include "CGUI.h"
#include "GUIObjectEventBroadcaster.h"
#include "graphics/Canvas2D.h"
#include "gui/IGUIScrollBar.h"
#include "gui/ObjectBases/IGUIObject.h"
@ -27,8 +29,6 @@
#include "gui/Scripting/ScriptFunctions.h"
#include "gui/Scripting/JSInterface_GUIProxy.h"
#include "i18n/L10n.h"
#include "lib/allocators/DynamicArena.h"
#include "lib/allocators/STLAllocators.h"
#include "lib/bits.h"
#include "lib/input.h"
#include "lib/sysdep/sysdep.h"
@ -48,6 +48,7 @@
#include "scriptinterface/ScriptInterface.h"
#include <string>
#include <optional>
#include <unordered_map>
#include <unordered_set>
@ -63,38 +64,13 @@ const CStr CGUI::EventNameMouseRightPress = "MouseRightPress";
const CStr CGUI::EventNameMouseLeftPress = "MouseLeftPress";
const CStr CGUI::EventNameMouseWheelDown = "MouseWheelDown";
const CStr CGUI::EventNameMouseWheelUp = "MouseWheelUp";
const CStr CGUI::EventNameMouseWheelLeft = "MouseWheelLeft";
const CStr CGUI::EventNameMouseWheelRight = "MouseWheelRight";
const CStr CGUI::EventNameMouseLeftDoubleClick = "MouseLeftDoubleClick";
const CStr CGUI::EventNameMouseLeftRelease = "MouseLeftRelease";
const CStr CGUI::EventNameMouseRightDoubleClick = "MouseRightDoubleClick";
const CStr CGUI::EventNameMouseRightRelease = "MouseRightRelease";
namespace
{
struct VisibleObject
{
IGUIObject* object;
// Index of the object in a depth-first search inside GUI tree.
u32 index;
// Cached value of GetBufferedZ to avoid recursive calls in a deep hierarchy.
float bufferedZ;
};
template<class Container>
void CollectVisibleObjectsRecursively(const std::vector<IGUIObject*>& objects, Container* visibleObjects)
{
for (IGUIObject* const& object : objects)
{
if (!object->IsHidden())
{
visibleObjects->emplace_back(VisibleObject{object, static_cast<u32>(visibleObjects->size()), 0.0f});
CollectVisibleObjectsRecursively(object->GetChildren(), visibleObjects);
}
}
}
} // anonynous namespace
CGUI::CGUI(ScriptContext& context)
: m_BaseObject(std::make_unique<CGUIDummyObject>(*this)),
m_FocusedObject(nullptr),
@ -158,7 +134,7 @@ InReaction CGUI::HandleEvent(const SDL_Event_* ev)
m_MousePos = CVector2D((float)ev->ev.motion.x / g_VideoMode.GetScale(), (float)ev->ev.motion.y / g_VideoMode.GetScale());
SGUIMessage msg(GUIM_MOUSE_MOTION);
m_BaseObject->RecurseObject(&IGUIObject::IsHiddenOrGhost, &IGUIObject::HandleMessage, msg);
m_BaseObject->RecurseObject(&IGUIObject::IsHiddenOrGhostOrOutOfBoundaries, &IGUIObject::HandleMessage, msg);
}
// Update m_MouseButtons. (BUTTONUP is handled later.)
@ -196,7 +172,7 @@ InReaction CGUI::HandleEvent(const SDL_Event_* ev)
// Now we'll call UpdateMouseOver on *all* objects,
// we'll input the one hovered, and they will each
// update their own data and send messages accordingly
m_BaseObject->RecurseObject(&IGUIObject::IsHiddenOrGhost, &IGUIObject::UpdateMouseOver, static_cast<IGUIObject* const&>(pNearest));
m_BaseObject->RecurseObject(&IGUIObject::IsHiddenOrGhostOrOutOfBoundaries, &IGUIObject::UpdateMouseOver, static_cast<IGUIObject* const&>(pNearest));
if (ev->ev.type == SDL_MOUSEBUTTONDOWN)
{
@ -225,6 +201,11 @@ InReaction CGUI::HandleEvent(const SDL_Event_* ev)
ret = pNearest->SendMouseEvent(GUIM_MOUSE_WHEEL_DOWN, EventNameMouseWheelDown);
else if (ev->ev.wheel.y > 0)
ret = pNearest->SendMouseEvent(GUIM_MOUSE_WHEEL_UP, EventNameMouseWheelUp);
if (ev->ev.wheel.x < 0)
ret = pNearest->SendMouseEvent(GUIM_MOUSE_WHEEL_LEFT, EventNameMouseWheelLeft);
else if (ev->ev.wheel.x > 0)
ret = pNearest->SendMouseEvent(GUIM_MOUSE_WHEEL_RIGHT, EventNameMouseWheelRight);
}
else if (ev->ev.type == SDL_MOUSEBUTTONUP)
{
@ -258,7 +239,7 @@ InReaction CGUI::HandleEvent(const SDL_Event_* ev)
m_BaseObject->RecurseObject(&IGUIObject::IsHidden, &IGUIObject::ResetStates);
// Since the hover state will have been reset, we reload it.
m_BaseObject->RecurseObject(&IGUIObject::IsHiddenOrGhost, &IGUIObject::UpdateMouseOver, static_cast<IGUIObject* const&>(pNearest));
m_BaseObject->RecurseObject(&IGUIObject::IsHiddenOrGhostOrOutOfBoundaries, &IGUIObject::UpdateMouseOver, static_cast<IGUIObject* const&>(pNearest));
}
}
@ -298,7 +279,7 @@ InReaction CGUI::HandleEvent(const SDL_Event_* ev)
void CGUI::TickObjects()
{
m_BaseObject->RecurseObject(&IGUIObject::IsHiddenOrGhost, &IGUIObject::Tick);
m_BaseObject->RecurseObject(&IGUIObject::IsHiddenOrGhostOrOutOfBoundaries, &IGUIObject::Tick);
SendEventToAll(EventNameTick);
m_Tooltip.Update(FindObjectUnderMouse(), m_MousePos, *this);
}
@ -327,32 +308,18 @@ void CGUI::SendEventToAll(const CStr& eventName, const JS::HandleValueArray& par
void CGUI::Draw(CCanvas2D& canvas)
{
using Arena = Allocators::DynamicArena<128 * KiB>;
using ObjectListAllocator = ProxyAllocator<VisibleObject, Arena>;
Arena arena;
std::vector<VisibleObject, ObjectListAllocator> visibleObjects((ObjectListAllocator(arena)));
CollectVisibleObjectsRecursively(m_BaseObject->GetChildren(), &visibleObjects);
for (VisibleObject& visibleObject : visibleObjects)
visibleObject.bufferedZ = visibleObject.object->GetBufferedZ();
std::sort(visibleObjects.begin(), visibleObjects.end(), [](const VisibleObject& visibleObject1, const VisibleObject& visibleObject2) -> bool {
if (visibleObject1.bufferedZ != visibleObject2.bufferedZ)
return visibleObject1.bufferedZ < visibleObject2.bufferedZ;
return visibleObject1.index < visibleObject2.index;
});
for (const VisibleObject& visibleObject : visibleObjects)
visibleObject.object->Draw(canvas);
CGUIObjectEventBroadcaster::RecurseVisibleObject(m_BaseObject.get(), &IGUIObject::Draw, canvas);
}
void CGUI::DrawSprite(const CGUISpriteInstance& Sprite, CCanvas2D& canvas, const CRect& Rect, const CRect& UNUSED(Clipping))
void CGUI::DrawSprite(const CGUISpriteInstance& Sprite, CCanvas2D& canvas, const CRect& Rect, const CRect& Clipping)
{
// If the sprite doesn't exist (name == ""), don't bother drawing anything
if (!Sprite)
return;
// TODO: Clipping?
std::optional<CCanvas2D::ScopedScissor> scopedScissor;
if (Clipping != CRect())
scopedScissor.emplace(canvas, Clipping);
Sprite.Draw(*this, canvas, Rect, m_Sprites);
}
@ -414,7 +381,7 @@ IGUIObject* CGUI::FindObjectByName(const CStr& Name) const
IGUIObject* CGUI::FindObjectUnderMouse()
{
IGUIObject* pNearest = nullptr;
m_BaseObject->RecurseObject(&IGUIObject::IsHiddenOrGhost, &IGUIObject::ChooseMouseOverAndClosest, pNearest);
m_BaseObject->RecurseObject(&IGUIObject::IsHiddenOrGhostOrOutOfBoundaries, &IGUIObject::ChooseMouseOverAndClosest, pNearest);
return pNearest;
}
@ -1237,12 +1204,20 @@ void CGUI::Xeromyces_ReadScrollBarStyle(const XMBData& xmb, XMBElement element)
scrollbar.m_SpriteButtonBottomOver = attr_value;
else if (attr_name == "sprite_back_vertical")
scrollbar.m_SpriteBackVertical = attr_value;
else if (attr_name == "sprite_bar_vertical")
scrollbar.m_SpriteBarVertical = attr_value;
else if (attr_name == "sprite_bar_vertical_over")
scrollbar.m_SpriteBarVerticalOver = attr_value;
else if (attr_name == "sprite_bar_vertical_pressed")
scrollbar.m_SpriteBarVerticalPressed = attr_value;
else if (attr_name == "sprite_slider_vertical")
scrollbar.m_SpriteSliderVertical = attr_value;
else if (attr_name == "sprite_slider_vertical_over")
scrollbar.m_SpriteSliderVerticalOver = attr_value;
else if (attr_name == "sprite_slider_vertical_pressed")
scrollbar.m_SpriteSliderVerticalPressed = attr_value;
else if (attr_name == "sprite_back_horizontal")
scrollbar.m_SpriteBackHorizontal = attr_value;
else if (attr_name == "sprite_slider_horizontal")
scrollbar.m_SpriteSliderHorizontal = attr_value;
else if (attr_name == "sprite_slider_horizontal_over")
scrollbar.m_SpriteSliderHorizontalOver = attr_value;
else if (attr_name == "sprite_slider_horizontal_pressed")
scrollbar.m_SpriteSliderHorizontalPressed = attr_value;
}
m_ScrollBarStyles.erase(name);

View file

@ -662,6 +662,8 @@ private:
static const CStr EventNameMouseLeftPress;
static const CStr EventNameMouseWheelDown;
static const CStr EventNameMouseWheelUp;
static const CStr EventNameMouseWheelLeft;
static const CStr EventNameMouseWheelRight;
static const CStr EventNameMouseLeftDoubleClick;
static const CStr EventNameMouseLeftRelease;
static const CStr EventNameMouseRightDoubleClick;

View file

@ -0,0 +1,225 @@
/* Copyright (C) 2024 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "CGUIScrollBarHorizontal.h"
#include "gui/CGUI.h"
#include "ps/CLogger.h"
CGUIScrollBarHorizontal::CGUIScrollBarHorizontal(CGUI& pGUI)
: IGUIScrollBar(pGUI)
{
}
CGUIScrollBarHorizontal::~CGUIScrollBarHorizontal()
{
}
void CGUIScrollBarHorizontal::SetPosFromMousePos(const CVector2D& mouse)
{
if (!GetStyle())
return;
// Calculate the position for the top of the item being scrolled
float emptyBackground = m_Length - m_BarSize;
if (GetStyle()->m_UseEdgeButtons)
emptyBackground -= GetStyle()->m_Width * 2;
m_Pos = m_PosWhenPressed + GetMaxPos() * (mouse.X - m_BarPressedAtPos.X) / emptyBackground;
}
void CGUIScrollBarHorizontal::Draw(CCanvas2D& canvas)
{
if (!IsVisible())
return;
if (!GetStyle())
{
LOGWARNING("Attempt to draw scrollbar without a style.");
return;
}
CRect outline = GetOuterRect();
m_pGUI.DrawSprite(
GetStyle()->m_SpriteBackHorizontal,
canvas,
CRect(
outline.left + (GetStyle()->m_UseEdgeButtons ? GetStyle()->m_Width : 0),
outline.top,
outline.right - (GetStyle()->m_UseEdgeButtons ? GetStyle()->m_Width : 0),
outline.bottom));
if (GetStyle()->m_UseEdgeButtons)
{
const CGUISpriteInstance* button_left;
const CGUISpriteInstance* button_right;
if (m_ButtonMinusHovered)
{
if (m_ButtonMinusPressed)
button_left = &(GetStyle()->m_SpriteButtonLeftPressed ? GetStyle()->m_SpriteButtonLeftPressed : GetStyle()->m_SpriteButtonLeft);
else
button_left = &(GetStyle()->m_SpriteButtonLeftOver ? GetStyle()->m_SpriteButtonLeftOver : GetStyle()->m_SpriteButtonLeft);
}
else
button_left = &GetStyle()->m_SpriteButtonLeft;
if (m_ButtonPlusHovered)
{
if (m_ButtonPlusPressed)
button_right = &(GetStyle()->m_SpriteButtonRightPressed ? GetStyle()->m_SpriteButtonRightPressed : GetStyle()->m_SpriteButtonRight);
else
button_right = &(GetStyle()->m_SpriteButtonRightOver ? GetStyle()->m_SpriteButtonRightOver : GetStyle()->m_SpriteButtonRight);
}
else
button_right = &GetStyle()->m_SpriteButtonRight;
m_pGUI.DrawSprite(
*button_left,
canvas,
CRect(
outline.left,
outline.top,
outline.left + GetStyle()->m_Width,
outline.bottom));
m_pGUI.DrawSprite(
*button_right,
canvas,
CRect(
outline.right - GetStyle()->m_Width,
outline.top,
outline.right,
outline.bottom));
}
m_pGUI.DrawSprite(
GetStyle()->m_SpriteSliderHorizontal,
canvas,
GetBarRect()
);
}
void CGUIScrollBarHorizontal::HandleMessage(SGUIMessage& Message)
{
switch (Message.type)
{
case GUIM_MOUSE_WHEEL_LEFT:
{
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);
Message.Skip(false);
break;
}
case GUIM_MOUSE_WHEEL_RIGHT:
{
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);
Message.Skip(false);
break;
}
default:
IGUIScrollBar::HandleMessage(Message);
break;
}
}
CRect CGUIScrollBarHorizontal::GetBarRect() const
{
CRect ret;
if (!GetStyle())
return ret;
// Get from where the scroll area begins to where it ends
float from = m_X;
float to = m_X + m_Length - m_BarSize;
if (GetStyle()->m_UseEdgeButtons)
{
from += GetStyle()->m_Width;
to -= GetStyle()->m_Width;
}
ret.left = from + (to - from) * m_Pos / GetMaxPos();
ret.right = ret.left + m_BarSize;
ret.bottom = m_Y + (m_BottomAligned ? 0 : GetStyle()->m_Width);
ret.top = ret.bottom - GetStyle()->m_Width;
return ret;
}
CRect CGUIScrollBarHorizontal::GetOuterRect() const
{
CRect ret;
if (!GetStyle())
return ret;
ret.left = m_X;
ret.right = m_X + m_Length;
ret.bottom = m_Y + (m_BottomAligned ? 0 : GetStyle()->m_Width);
ret.top = ret.bottom - GetStyle()->m_Width;
return ret;
}
bool CGUIScrollBarHorizontal::HoveringButtonMinus(const CVector2D& mouse)
{
if (!GetStyle())
return false;
float StartY = m_BottomAligned ? m_Y - GetStyle()->m_Width : m_Y;
return mouse.Y >= StartY &&
mouse.Y <= StartY + GetStyle()->m_Width &&
mouse.X >= m_X &&
mouse.X <= m_X + GetStyle()->m_Width;
}
bool CGUIScrollBarHorizontal::HoveringButtonPlus(const CVector2D& mouse)
{
if (!GetStyle())
return false;
float StartY = m_BottomAligned ? m_Y - GetStyle()->m_Width : m_Y;
return mouse.Y > StartY &&
mouse.Y < StartY + GetStyle()->m_Width &&
mouse.X > m_X + m_Length - GetStyle()->m_Width &&
mouse.X < m_X + m_Length;
}
void CGUIScrollBarHorizontal::SetScrollPlentyFromMousePos(const CVector2D& mouse)
{
// Scroll plus or minus a lot, this might change, it doesn't
// have to be fancy though.
if (mouse.X < GetBarRect().left)
ScrollMinusPlenty();
else
ScrollPlusPlenty();
}

View file

@ -0,0 +1,98 @@
/* Copyright (C) 2024 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
/*
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.
*/
#ifndef INCLUDED_CGUISCROLLBARHORIZONTAL
#define INCLUDED_CGUISCROLLBARHORIZONTAL
#include "IGUIScrollBar.h"
/**
* Horizontal implementation of IGUIScrollBar
*
* @see IGUIScrollBar
*/
class CGUIScrollBarHorizontal : public IGUIScrollBar
{
public:
CGUIScrollBarHorizontal(CGUI& pGUI);
virtual ~CGUIScrollBarHorizontal();
/**
* Draw the scroll-bar
*/
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.
*
* @see IGUIObject#HandleMessage()
*/
virtual void HandleMessage(SGUIMessage& Message);
/**
* Set m_Pos with g_mouse_x/y input, i.e. when dragging.
*/
virtual void SetPosFromMousePos(const CVector2D& mouse);
virtual void SetScrollPlentyFromMousePos(const CVector2D& mouse);
/**
* @see IGUIScrollBar#HoveringButtonMinus
*/
virtual bool HoveringButtonMinus(const CVector2D& mouse);
/**
* @see IGUIScrollBar#HoveringButtonPlus
*/
virtual bool HoveringButtonPlus(const CVector2D& mouse);
/**
* Set Right Aligned
* @param align Alignment
*/
void SetBottomAligned(const bool& align) { m_BottomAligned = align; }
/**
* Get the rectangle of the actual BAR.
* @return Rectangle, CRect
*/
virtual CRect GetBarRect() const;
/**
* Get the rectangle of the outline of the scrollbar, every component of the
* scroll-bar should be inside this area.
* @return Rectangle, CRect
*/
virtual CRect GetOuterRect() const;
protected:
/**
* Should the scroll bar proceed to the top or to the bottom of the m_Y value.
* Notice, this has nothing to do with where the owner places it.
*/
bool m_BottomAligned;
};
#endif // INCLUDED_CGUISCROLLBARHORIZONTAL

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2021 Wildfire Games.
/* Copyright (C) 2024 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -119,7 +119,7 @@ void CGUIScrollBarVertical::Draw(CCanvas2D& canvas)
}
m_pGUI.DrawSprite(
GetStyle()->m_SpriteBarVertical,
GetStyle()->m_SpriteSliderVertical,
canvas,
GetBarRect()
);
@ -128,7 +128,34 @@ void CGUIScrollBarVertical::Draw(CCanvas2D& canvas)
void CGUIScrollBarVertical::HandleMessage(SGUIMessage& Message)
{
IGUIScrollBar::HandleMessage(Message);
switch (Message.type)
{
case GUIM_MOUSE_WHEEL_UP:
{
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);
Message.Skip(false);
break;
}
case GUIM_MOUSE_WHEEL_DOWN:
{
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);
Message.Skip(false);
break;
}
default:
IGUIScrollBar::HandleMessage(Message);
break;
}
}
CRect CGUIScrollBarVertical::GetBarRect() const
@ -194,3 +221,13 @@ bool CGUIScrollBarVertical::HoveringButtonPlus(const CVector2D& mouse)
mouse.Y > m_Y + m_Length - GetStyle()->m_Width &&
mouse.Y < m_Y + m_Length;
}
void CGUIScrollBarVertical::SetScrollPlentyFromMousePos(const CVector2D& 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();
}

View file

@ -57,6 +57,8 @@ public:
*/
virtual void SetPosFromMousePos(const CVector2D& mouse);
virtual void SetScrollPlentyFromMousePos(const CVector2D& mouse);
/**
* @see IGUIScrollBar#HoveringButtonMinus
*/

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2023 Wildfire Games.
/* Copyright (C) 2024 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -27,6 +27,7 @@
#include "gui/SettingTypes/CGUISize.h"
#include "gui/SettingTypes/CGUIString.h"
#include "gui/SettingTypes/EAlign.h"
#include "gui/SettingTypes/EScrollOrientation.h"
#include "ps/CLogger.h"
#include "ps/CStr.h"
#include "scriptinterface/ScriptConversions.h"
@ -126,5 +127,6 @@ TYPE(EAlign)
TYPE(EVAlign)
TYPE(CGUIList)
TYPE(CGUISeries)
TYPE(EScrollOrientation)
#undef TYPE

View file

@ -0,0 +1,75 @@
/* Copyright (C) 2024 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#ifndef GUIOBJECTEVENTBROADCASTER
#define GUIOBJECTEVENTBROADCASTER
#include "gui/ObjectBases/IGUIObject.h"
#include "gui/ObjectBases/IGUIPanel.h"
namespace
{
struct VisibleObject
{
IGUIObject* object;
// Index of the object in a depth-first search inside GUI tree.
u32 index;
// Cached value of GetBufferedZ to avoid recursive calls in a deep hierarchy.
float bufferedZ;
};
template<class Container>
void CollectVisibleObjectsRecursively(const std::vector<IGUIObject*>& objects, Container* visibleObjects)
{
for (IGUIObject* const& object : objects)
if (!object->IsHidden())
{
visibleObjects->emplace_back(VisibleObject{ object, static_cast<u32>(visibleObjects->size()), 0.0f });
CollectVisibleObjectsRecursively(object->GetVisibleChildren(), visibleObjects);
}
}
}
class CGUIObjectEventBroadcaster
{
public:
template<typename... Args>
static void RecurseVisibleObject(IGUIObject* object, void(IGUIObject::* callbackFunction)(Args... args), Args&&... args)
{
using Arena = Allocators::DynamicArena<128 * KiB>;
using ObjectListAllocator = ProxyAllocator<VisibleObject, Arena>;
Arena arena;
std::vector<VisibleObject, ObjectListAllocator> visibleObjects((ObjectListAllocator(arena)));
CollectVisibleObjectsRecursively(object->GetVisibleChildren(), &visibleObjects);
for (VisibleObject& visibleObject : visibleObjects)
visibleObject.bufferedZ = visibleObject.object->GetBufferedZ();
std::sort(visibleObjects.begin(), visibleObjects.end(), [](const VisibleObject& visibleObject1, const VisibleObject& visibleObject2) -> bool {
if (visibleObject1.bufferedZ != visibleObject2.bufferedZ)
return visibleObject1.bufferedZ < visibleObject2.bufferedZ;
return visibleObject1.index < visibleObject2.index;
});
for (const VisibleObject& visibleObject : visibleObjects)
(visibleObject.object->*callbackFunction)(std::forward<Args>(args)...);
}
};
#endif // !GUIOBJECTEVENTBROADCASTER

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2020 Wildfire Games.
/* Copyright (C) 2024 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -31,6 +31,7 @@
#include "gui/ObjectTypes/CProgressBar.h"
#include "gui/ObjectTypes/CRadioButton.h"
#include "gui/ObjectTypes/CSlider.h"
#include "gui/ObjectTypes/CScrollPanel.h"
#include "gui/ObjectTypes/CText.h"
#include "gui/ObjectTypes/CTooltip.h"
#include "gui/Scripting/JSInterface_GUIProxy.h"
@ -42,6 +43,7 @@ void CGUI::AddObjectTypes()
m_ProxyData.insert(JSI_GUIProxy<CList>::CreateData(*m_ScriptInterface));
m_ProxyData.insert(JSI_GUIProxy<CMiniMap>::CreateData(*m_ScriptInterface));
m_ProxyData.insert(JSI_GUIProxy<CButton>::CreateData(*m_ScriptInterface));
m_ProxyData.insert(JSI_GUIProxy<CScrollPanel>::CreateData(*m_ScriptInterface));
AddObjectType("button", &CButton::ConstructObject);
AddObjectType("chart", &CChart::ConstructObject);
@ -59,4 +61,5 @@ void CGUI::AddObjectTypes()
AddObjectType("slider", &CSlider::ConstructObject);
AddObjectType("text", &CText::ConstructObject);
AddObjectType("tooltip", &CTooltip::ConstructObject);
AddObjectType("scrollpanel", &CScrollPanel::ConstructObject);
}

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2021 Wildfire Games.
/* Copyright (C) 2024 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -18,6 +18,7 @@
#include "precompiled.h"
#include "gui/CGUI.h"
#include "gui/SettingTypes/EScrollOrientation.h"
#include "gui/SettingTypes/CGUIString.h"
#include "ps/CLogger.h"
@ -244,3 +245,18 @@ bool CGUI::ParseString<CGUIList>(const CGUI* UNUSED(pGUI), const CStrW& UNUSED(V
{
return false;
}
template <>
bool CGUI::ParseString<EScrollOrientation>(const CGUI* UNUSED(pGUI), const CStrW& Value, EScrollOrientation& Output)
{
if (Value == L"vertical")
Output = EScrollOrientation::VERTICAL;
else if (Value == L"horizontal")
Output = EScrollOrientation::HORIZONTAL;
else if (Value == L"both")
Output = EScrollOrientation::BOTH;
else
return false;
return true;
}

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2021 Wildfire Games.
/* Copyright (C) 2024 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -147,12 +147,8 @@ void IGUIScrollBar::HandleMessage(SGUIMessage& Message)
{
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();
SetScrollPlentyFromMousePos(mouse);
// Simulate mouse movement to see if bar now is hovered
SGUIMessage msg(GUIM_MOUSE_MOTION);
HandleMessage(msg);
@ -165,27 +161,6 @@ void IGUIScrollBar::HandleMessage(SGUIMessage& Message)
m_ButtonMinusPressed = false;
m_ButtonPlusPressed = false;
break;
case GUIM_MOUSE_WHEEL_UP:
{
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);
break;
}
case GUIM_MOUSE_WHEEL_DOWN:
{
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;
}
default:
break;
}

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2021 Wildfire Games.
/* Copyright (C) 2024 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -111,9 +111,9 @@ struct SGUIScrollBarStyle
CGUISpriteInstance m_SpriteButtonBottomDisabled;
CGUISpriteInstance m_SpriteButtonBottomOver;
CGUISpriteInstance m_SpriteBarVertical;
CGUISpriteInstance m_SpriteBarVerticalOver;
CGUISpriteInstance m_SpriteBarVerticalPressed;
CGUISpriteInstance m_SpriteSliderVertical;
CGUISpriteInstance m_SpriteSliderVerticalOver;
CGUISpriteInstance m_SpriteSliderVerticalPressed;
CGUISpriteInstance m_SpriteBackVertical;
@ -126,13 +126,17 @@ struct SGUIScrollBarStyle
CGUISpriteInstance m_SpriteButtonLeft;
CGUISpriteInstance m_SpriteButtonLeftPressed;
CGUISpriteInstance m_SpriteButtonLeftDisabled;
CGUISpriteInstance m_SpriteButtonLeftOver;
CGUISpriteInstance m_SpriteButtonRight;
CGUISpriteInstance m_SpriteButtonRightPressed;
CGUISpriteInstance m_SpriteButtonRightDisabled;
CGUISpriteInstance m_SpriteButtonRightOver;
CGUISpriteInstance m_SpriteBackHorizontal;
CGUISpriteInstance m_SpriteBarHorizontal;
CGUISpriteInstance m_SpriteSliderHorizontal;
CGUISpriteInstance m_SpriteSliderHorizontalOver;
CGUISpriteInstance m_SpriteSliderHorizontalPressed;
//@}
};
@ -179,6 +183,8 @@ public:
*/
virtual void SetPosFromMousePos(const CVector2D& mouse) = 0;
virtual void SetScrollPlentyFromMousePos(const CVector2D& mouse) = 0;
/**
* Hovering the scroll minus button
*

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2022 Wildfire Games.
/* Copyright (C) 2024 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -36,6 +36,7 @@
#include <string_view>
#include <unordered_map>
const CStr IGUIObject::EventNameMouseEnter = "MouseEnter";
const CStr IGUIObject::EventNameMouseMove = "MouseMove";
const CStr IGUIObject::EventNameMouseLeave = "MouseLeave";
@ -135,11 +136,34 @@ void IGUIObject::SettingChanged(const CStr& Setting, const bool SendMessage)
{
SGUIMessage msg(GUIM_SETTINGS_UPDATED, Setting);
HandleMessage(msg);
// inform to parents until get to the root object
// for now only size and hidden settings are bubbled up
if (GetParent() && (Setting == "size" || Setting == "hidden"))
{
EGUIMessageType type = Setting == "size" ? GUIM_CHILD_RESIZED : GUIM_CHILD_TOGGLE_VISIBILITY;
SGUIMessage msg(type, GetName());
msg.Skip(true);
IGUIObject* parent = GetParent();
while (parent)
{
parent->HandleMessage(msg);
if (!msg.skipped)
break;
parent = parent->GetParent();
}
}
}
}
bool IGUIObject::IsMouseOver() const
{
if (m_VisibleArea)
return m_VisibleArea.PointInside(m_pGUI.GetMousePos());
return m_CachedActualSize.PointInside(m_pGUI.GetMousePos());
}
@ -236,7 +260,6 @@ CRect IGUIObject::GetComputedSize()
return m_CachedActualSize;
}
bool IGUIObject::ApplyStyle(const CStr& StyleName)
{
if (!m_pGUI.HasStyle(StyleName))
@ -362,6 +385,8 @@ InReaction IGUIObject::SendMouseEvent(EGUIMessageType type, const CStr& eventNam
PROFILE2_ATTR("object: %s", m_Name.c_str());
SGUIMessage msg(type);
if (type == GUIM_MOUSE_WHEEL_UP || type == GUIM_MOUSE_WHEEL_DOWN || type == GUIM_MOUSE_WHEEL_LEFT || type == GUIM_MOUSE_WHEEL_RIGHT)
msg.Skip(true);
HandleMessage(msg);
ScriptRequest rq(m_pGUI.GetScriptInterface());
@ -381,6 +406,11 @@ InReaction IGUIObject::SendMouseEvent(EGUIMessageType type, const CStr& eventNam
ignore_result(paramData.append(mouse));
ScriptEvent(eventName, paramData);
// inform to parents until get to the root object
// for now only wheel events are bubbled up
if (GetParent() && (type == GUIM_MOUSE_WHEEL_UP || type == GUIM_MOUSE_WHEEL_DOWN || type == GUIM_MOUSE_WHEEL_LEFT || type == GUIM_MOUSE_WHEEL_RIGHT) && msg.skipped)
msg.Skip(GetParent()->SendMouseEvent(type, eventName) == IN_PASS);
return msg.skipped ? IN_PASS : IN_HANDLED;
}
@ -500,3 +530,24 @@ void IGUIObject::TraceMember(JSTracer* trc)
for (std::pair<const CStr, JS::Heap<JSObject*>>& handler : m_ScriptHandlers)
JS::TraceEdge(trc, &handler.second, "IGUIObject::m_ScriptHandlers");
}
void IGUIObject::DrawInArea(CCanvas2D& canvas, CRect& area)
{
bool isInsideBoundaries = false;
RecurseObject(nullptr, &IGUIObject::SetIsInsideBoundaries, isInsideBoundaries);
if (!area.IntersectWith(m_CachedActualSize))
return;
CRect intersection = area.Intersection(m_CachedActualSize);
m_VisibleArea = intersection;
Draw(canvas);
isInsideBoundaries = true;
RecurseObject(nullptr, &IGUIObject::SetIsInsideBoundaries, isInsideBoundaries);
}
bool IGUIObject::IsHiddenOrGhostOrOutOfBoundaries() const {
return !m_IsInsideBoundaries || IsHiddenOrGhost();
}

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2021 Wildfire Games.
/* Copyright (C) 2024 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -29,6 +29,8 @@
#include "gui/SettingTypes/CGUIHotkey.h"
#include "gui/SettingTypes/CGUISize.h"
#include "gui/SGUIMessage.h"
#include "lib/allocators/DynamicArena.h"
#include "lib/allocators/STLAllocators.h"
#include "lib/input.h" // just for IN_PASS
#include "ps/CStr.h"
#include "ps/XML/Xeromyces.h"
@ -40,6 +42,7 @@
class CCanvas2D;
class CGUI;
class CGUISize;
class CGUIObjectEventBroadcaster;
class IGUIObject;
class IGUIProxyObject;
class IGUISetting;
@ -59,6 +62,7 @@ public: \
class IGUIObject
{
friend class CGUI;
friend class CGUIObjectEventBroadcaster;
// For triggering message update handlers.
friend class IGUISetting;
@ -189,7 +193,7 @@ public:
/**
* Updates and returns the size of the object.
*/
CRect GetComputedSize();
virtual CRect GetComputedSize();
virtual const CStrW& GetTooltipText() const { return m_Tooltip; }
virtual const CStr& GetTooltipStyle() const { return m_TooltipStyle; }
@ -213,6 +217,10 @@ public:
*/
JSObject* GetJSObject();
virtual const std::vector<IGUIObject*>& GetVisibleChildren() const { return m_Children; }
void SetIsInsideBoundaries(bool& isInsideBoundaries) { m_IsInsideBoundaries = isInsideBoundaries; }
bool IsHiddenOrGhostOrOutOfBoundaries() const;
//@}
protected:
//--------------------------------------------------------
@ -261,6 +269,8 @@ public:
obj->RecurseObject(isRestricted, callbackFunction, args...);
}
virtual void DrawInArea(CCanvas2D& canvas, CRect& area);
protected:
/**
* Draws the object.
@ -525,6 +535,9 @@ protected:
// Cached JSObject representing this GUI object.
std::unique_ptr<IGUIProxyObject> m_JSObject;
CRect m_VisibleArea;
bool m_IsInsideBoundaries = true;
CGUISimpleSetting<bool> m_Enabled;
CGUISimpleSetting<bool> m_Hidden;
CGUISimpleSetting<CGUISize> m_Size;

View file

@ -0,0 +1,57 @@
/* Copyright (C) 20244 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "IGUIPanel.h"
#include "gui/CGUI.h"
IGUIPanel::IGUIPanel(CGUI& pGUI)
: IGUIObject(pGUI)
{
}
IGUIPanel::~IGUIPanel()
{
}
bool IGUIPanel::IsMouseOver() const
{
return m_CachedLayoutActualSize.PointInside(m_pGUI.GetMousePos());
}
void IGUIPanel::UpdateCachedSize()
{
IGUIObject::UpdateCachedSize();
m_CachedLayoutActualSize = m_CachedActualSize;
}
CRect IGUIPanel::GetComputedSize()
{
UpdateCachedSize();
return m_CachedLayoutActualSize;
}
const std::vector<IGUIObject*>& IGUIPanel::GetVisibleChildren() const
{
if (m_Drawing)
return m_Children;
static std::vector<IGUIObject*> emptyVector;
return emptyVector;
}

View file

@ -0,0 +1,41 @@
/* Copyright (C) 20244 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef INCLUDED_IPANEL
#define INCLUDED_IPANEL
#include "gui/ObjectBases/IGUIObject.h"
class IGUIPanel : public IGUIObject
{
public:
IGUIPanel(CGUI& pGUI);
virtual ~IGUIPanel();
virtual void UpdateCachedSize();
virtual CRect GetComputedSize();
virtual bool IsMouseOver() const;
virtual const std::vector<IGUIObject*>& GetVisibleChildren() const;
protected:
CRect m_CachedLayoutActualSize;
bool m_Drawing = false;
};
#endif // INCLUDED_IPANEL

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2021 Wildfire Games.
/* Copyright (C) 2024 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -50,7 +50,8 @@ const SGUIScrollBarStyle* IGUIScrollBarOwner::GetScrollBarStyle(const CStr& styl
void IGUIScrollBarOwner::HandleMessage(SGUIMessage& msg)
{
for (const std::unique_ptr<IGUIScrollBar>& scrollBar : m_ScrollBars)
scrollBar->HandleMessage(msg);
if (scrollBar->IsVisible())
scrollBar->HandleMessage(msg);
}
void IGUIScrollBarOwner::Draw(CCanvas2D& canvas)

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2021 Wildfire Games.
/* Copyright (C) 2024 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -85,9 +85,10 @@ void CButton::Draw(CCanvas2D& canvas)
m_pGUI.DrawSprite(
GetButtonSprite(m_Sprite, m_SpriteOver, m_SpritePressed, m_SpriteDisabled),
canvas,
m_CachedActualSize);
m_CachedActualSize,
m_VisibleArea);
DrawText(canvas, 0, ChooseColor(), m_TextPos);
DrawText(canvas, 0, ChooseColor(), m_TextPos, m_VisibleArea);
}
bool CButton::IsMouseOver() const

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2022 Wildfire Games.
/* Copyright (C) 2024 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -28,6 +28,7 @@
#include "ps/Profile.h"
#include <cmath>
#include <optional>
CChart::CChart(CGUI& pGUI)
: IGUIObject(pGUI),
@ -80,6 +81,10 @@ void CChart::Draw(CCanvas2D& canvas)
if (m_Series.empty())
return;
std::optional<CCanvas2D::ScopedScissor> scissor;
if (m_VisibleArea)
scissor.emplace(canvas, m_VisibleArea);
CRect rect = GetChartRect();
const float width = rect.GetWidth();
const float height = rect.GetHeight();
@ -90,7 +95,7 @@ void CChart::Draw(CCanvas2D& canvas)
{
if (data.m_Points.empty())
continue;
linePoints.clear();
for (const CVector2D& point : data.m_Points)
{
@ -114,7 +119,7 @@ void CChart::Draw(CCanvas2D& canvas)
DrawAxes(canvas);
for (size_t i = 0; i < m_TextPositions.size(); ++i)
DrawText(canvas, i, CGUIColor(1.f, 1.f, 1.f, 1.f), m_TextPositions[i]);
DrawText(canvas, i, CGUIColor(1.f, 1.f, 1.f, 1.f), m_TextPositions[i], m_VisibleArea);
}
CRect CChart::GetChartRect() const

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2021 Wildfire Games.
/* Copyright (C) 2024 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -71,5 +71,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);
m_CachedActualSize,
m_VisibleArea);
}

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2023 Wildfire Games.
/* Copyright (C) 2024 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -164,6 +164,9 @@ void CDropDown::HandleMessage(SGUIMessage& Message)
if (m_List->m_Items.empty())
return;
if (m_VisibleArea && !m_VisibleArea.PointInside(m_pGUI.GetMousePos()))
return;
m_Open = true;
GetScrollBar(0).SetZ(GetBufferedZ());
m_ElementHighlight = m_Selected;
@ -208,6 +211,8 @@ void CDropDown::HandleMessage(SGUIMessage& Message)
if (m_Open || !m_Enabled)
break;
Message.Skip(false);
m_ElementHighlight = m_Selected;
if (m_ElementHighlight + 1 >= (int)m_ItemsYPositions.size() - 1)
@ -224,6 +229,8 @@ void CDropDown::HandleMessage(SGUIMessage& Message)
if (m_Open || !m_Enabled)
break;
Message.Skip(false);
m_ElementHighlight = m_Selected;
if (m_ElementHighlight - 1 < 0)
break;
@ -420,7 +427,7 @@ bool CDropDown::IsMouseOver() const
return rect.PointInside(m_pGUI.GetMousePos());
}
else
return m_CachedActualSize.PointInside(m_pGUI.GetMousePos());
return IGUIObject::IsMouseOver();
}
void CDropDown::Draw(CCanvas2D& canvas)
@ -428,7 +435,7 @@ 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_pGUI.DrawSprite(sprite, canvas, m_CachedActualSize, m_VisibleArea);
if (m_ButtonWidth > 0.f)
{
@ -437,24 +444,25 @@ void CDropDown::Draw(CCanvas2D& canvas)
if (!m_Enabled)
{
m_pGUI.DrawSprite(*m_Sprite2Disabled ? m_Sprite2Disabled : m_Sprite2, canvas, rect);
m_pGUI.DrawSprite(*m_Sprite2Disabled ? m_Sprite2Disabled : m_Sprite2, canvas, rect, m_VisibleArea);
}
else if (m_Open)
{
m_pGUI.DrawSprite(*m_Sprite2Pressed ? m_Sprite2Pressed : m_Sprite2, canvas, rect);
m_pGUI.DrawSprite(*m_Sprite2Pressed ? m_Sprite2Pressed : m_Sprite2, canvas, rect, m_VisibleArea);
}
else if (m_MouseHovering)
{
m_pGUI.DrawSprite(*m_Sprite2Over ? m_Sprite2Over : m_Sprite2, canvas, rect);
m_pGUI.DrawSprite(*m_Sprite2Over ? m_Sprite2Over : m_Sprite2, canvas, rect, m_VisibleArea);
}
else
m_pGUI.DrawSprite(m_Sprite2, canvas, rect);
m_pGUI.DrawSprite(m_Sprite2, canvas, rect, m_VisibleArea);
}
if (m_Selected != -1) // TODO: Maybe check validity completely?
{
CRect cliparea(m_CachedActualSize.left, m_CachedActualSize.top,
m_CachedActualSize.right - m_ButtonWidth, m_CachedActualSize.bottom);
CRect cliparea = m_VisibleArea != CRect() ? m_VisibleArea : m_CachedActualSize;
if (cliparea.right > m_CachedActualSize.right - m_ButtonWidth)
cliparea.right = m_CachedActualSize.right - m_ButtonWidth;
CVector2D pos(m_CachedActualSize.left, m_CachedActualSize.top);
DrawText(canvas, m_Selected, m_Enabled ? m_TextColorSelected : m_TextColorDisabled, pos, cliparea);
@ -474,7 +482,7 @@ void CDropDown::Draw(CCanvas2D& canvas)
if (m_HideScrollBar)
m_ScrollBar.Set(old, false);
}
m_pGUI.DrawSprite(spriteOverlay, canvas, m_CachedActualSize);
m_pGUI.DrawSprite(spriteOverlay, canvas, m_CachedActualSize, m_VisibleArea);
}
// When a dropdown list is opened, it needs to be visible above all the other

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2021 Wildfire Games.
/* Copyright (C) 2024 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -33,5 +33,5 @@ CImage::~CImage()
void CImage::Draw(CCanvas2D& canvas)
{
m_pGUI.DrawSprite(m_Sprite, canvas, m_CachedActualSize);
m_pGUI.DrawSprite(m_Sprite, canvas, m_CachedActualSize, m_VisibleArea);
}

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2022 Wildfire Games.
/* Copyright (C) 2024 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -883,7 +883,8 @@ void CInput::ResetStates()
void CInput::HandleMessage(SGUIMessage& Message)
{
IGUIObject::HandleMessage(Message);
IGUIScrollBarOwner::HandleMessage(Message);
if (m_ScrollBar)
IGUIScrollBarOwner::HandleMessage(Message);
// Cleans up operator[] usage.
const CStrW& caption = *m_Caption;
@ -1486,7 +1487,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_CachedActualSize);
CRect cliparea = m_VisibleArea ? m_VisibleArea : m_CachedActualSize;
// First we'll figure out the clipping area, which is the cached actual size
// substracted by an optional scrollbar
@ -1521,7 +1522,7 @@ void CInput::Draw(CCanvas2D& canvas)
IGUIScrollBarOwner::Draw(canvas);
// Draw the overlays last
m_pGUI.DrawSprite(m_SpriteOverlay, canvas, m_CachedActualSize);
m_pGUI.DrawSprite(m_SpriteOverlay, canvas, m_CachedActualSize, m_VisibleArea);
}
void CInput::DrawPlaceholderText(CCanvas2D& canvas, const CRect& clipping)

View file

@ -155,7 +155,8 @@ void CList::UpdateCachedSize()
void CList::HandleMessage(SGUIMessage& Message)
{
IGUIObject::HandleMessage(Message);
IGUIScrollBarOwner::HandleMessage(Message);
if (m_ScrollBar)
IGUIScrollBarOwner::HandleMessage(Message);
//IGUITextOwner::HandleMessage(Message); <== placed it after the switch instead!
m_Modified = false;

View file

@ -302,7 +302,7 @@ void COList::DrawList(CCanvas2D& canvas, const int& selected, const CGUISpriteIn
{
CRect rect = GetListRect();
m_pGUI.DrawSprite(sprite, canvas, rect);
m_pGUI.DrawSprite(sprite, canvas, rect, m_VisibleArea);
float scroll = 0.f;
if (m_ScrollBar)
@ -341,7 +341,7 @@ void COList::DrawList(CCanvas2D& canvas, const int& selected, const CGUISpriteIn
}
// Draw item selection
m_pGUI.DrawSprite(spriteSelectArea, canvas, rectSel);
m_pGUI.DrawSprite(spriteSelectArea, canvas, rectSel, m_VisibleArea);
drawSelected = true;
}
}
@ -349,7 +349,7 @@ 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);
m_pGUI.DrawSprite(m_SpriteHeading, canvas, rect_head);
m_pGUI.DrawSprite(m_SpriteHeading, canvas, rect_head, m_VisibleArea);
// Draw column headers
float xpos = 0;
@ -386,7 +386,7 @@ void COList::DrawList(CCanvas2D& canvas, const int& selected, const CGUISpriteIn
else
pSprite = &*m_SpriteNotSorted;
m_pGUI.DrawSprite(*pSprite, canvas, CRect(leftTopCorner + CVector2D(width - SORT_SPRITE_DIM, 0), leftTopCorner + CVector2D(width, SORT_SPRITE_DIM)));
m_pGUI.DrawSprite(*pSprite, canvas, CRect(leftTopCorner + CVector2D(width - SORT_SPRITE_DIM, 0), leftTopCorner + CVector2D(width, SORT_SPRITE_DIM)), m_VisibleArea);
}
// Draw column header text
@ -453,7 +453,7 @@ void COList::DrawList(CCanvas2D& canvas, const int& selected, const CGUISpriteIn
IGUIScrollBarOwner::Draw(canvas);
// Draw the overlays last
m_pGUI.DrawSprite(spriteOverlay, canvas, rect);
m_pGUI.DrawSprite(spriteOverlay, canvas, rect, m_VisibleArea);
if (drawSelected)
m_pGUI.DrawSprite(spriteSelectAreaOverlay, canvas, rectSel);
m_pGUI.DrawSprite(spriteSelectAreaOverlay, canvas, rectSel, m_VisibleArea);
}

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2021 Wildfire Games.
/* Copyright (C) 2024 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -55,10 +55,10 @@ void CProgressBar::HandleMessage(SGUIMessage& Message)
void CProgressBar::Draw(CCanvas2D& canvas)
{
m_pGUI.DrawSprite(m_SpriteBackground, canvas, m_CachedActualSize);
m_pGUI.DrawSprite(m_SpriteBackground, canvas, m_CachedActualSize, 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),
m_pGUI.DrawSprite(m_SpriteBar, canvas, size);
m_pGUI.DrawSprite(m_SpriteBar, canvas, size, m_VisibleArea);
}

View file

@ -0,0 +1,257 @@
/* Copyright (C) 2024 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "CScrollPanel.h"
#include "gui/GUIObjectEventBroadcaster.h"
#include "gui/CGUIScrollBarHorizontal.h"
#include "gui/CGUIScrollBarVertical.h"
CScrollPanel::CScrollPanel(CGUI& pGUI)
: IGUIPanel(pGUI),
IGUIScrollBarOwner(*static_cast<IGUIObject*>(this)),
m_Orientation(this, "orientation", EScrollOrientation::VERTICAL),
m_ScrollBarStyle(this, "scrollbar_style"),
m_MinWidth(this, "min_width", 0),
m_MinHeight(this, "min_height", 0)
{
auto vbar = std::make_unique<CGUIScrollBarVertical>(pGUI);
vbar->SetRightAligned(true);
AddScrollBar(std::move(vbar));
auto hbar = std::make_unique<CGUIScrollBarHorizontal>(pGUI);
hbar->SetBottomAligned(true);
AddScrollBar(std::move(hbar));
}
CScrollPanel::~CScrollPanel()
{
}
void CScrollPanel::ResetStates()
{
IGUIPanel::ResetStates();
IGUIScrollBarOwner::ResetStates();
}
void CScrollPanel::UpdateCachedSize()
{
IGUIPanel::UpdateCachedSize();
Setup();
}
void CScrollPanel::HandleMessage(SGUIMessage& Message)
{
IGUIScrollBar& scrollbar0 = GetScrollBar(0);
IGUIScrollBar& scrollbar1 = GetScrollBar(1);
IGUIPanel::HandleMessage(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);
switch (Message.type)
{
case GUIM_SETTINGS_UPDATED:
if (Message.value == "scrollbar_style")
{
scrollbar0.SetScrollBarStyle(m_ScrollBarStyle);
scrollbar1.SetScrollBarStyle(m_ScrollBarStyle);
Setup();
}
if (Message.value == "orientation" || Message.value == "size" || Message.value == "min_width" || Message.value == "min_height")
{
scrollbar0.SetPos(0);
scrollbar1.SetPos(0);
Setup();
}
break;
case GUIM_CHILD_RESIZED:
case GUIM_CHILD_TOGGLE_VISIBILITY:
Setup();
Message.Skip(false);
break;
case GUIM_LOAD:
scrollbar0.SetScrollBarStyle(m_ScrollBarStyle);
scrollbar1.SetScrollBarStyle(m_ScrollBarStyle);
Setup();
break;
default:
break;
}
}
void CScrollPanel::Draw(CCanvas2D& canvas)
{
IGUIScrollBar& scrollbar0 = GetScrollBar(0);
IGUIScrollBar& scrollbar1 = GetScrollBar(1);
m_Drawing = true;
IGUIScrollBarOwner::Draw(canvas);
CRect cliparea(m_CachedLayoutActualSize);
// substract scrollbar from cliparea
if (scrollbar0.IsVisible())
cliparea.right -= scrollbar0.GetOuterRect().GetWidth();
if (scrollbar1.IsVisible())
cliparea.bottom -= scrollbar1.GetOuterRect().GetHeight();
CGUIObjectEventBroadcaster::RecurseVisibleObject(this, &IGUIObject::DrawInArea, canvas, cliparea);
m_Drawing = false;
}
void CScrollPanel::Setup()
{
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)
{
if (child->IsHiddenOrGhost())
continue;
CRect childSize = child->GetComputedSize();
maxVRange = std::max(maxVRange, childSize.bottom);
maxHRange = std::max(maxHRange, childSize.right);
}
maxVRange -= m_CachedLayoutActualSize.top;
maxHRange -= m_CachedLayoutActualSize.left;
scrollbar0.SetScrollRange(HasVerticalScrollBar() ? maxVRange : m_CachedLayoutActualSize.GetHeight());
scrollbar0.SetScrollSpace(m_CachedLayoutActualSize.GetHeight());
scrollbar0.SetX(m_CachedLayoutActualSize.right);
scrollbar0.SetY(m_CachedLayoutActualSize.top);
scrollbar0.SetZ(GetBufferedZ());
scrollbar0.SetLength(m_CachedLayoutActualSize.GetHeight());
scrollbar1.SetScrollRange(HasHorizontalScrollBar() ? maxHRange : m_CachedLayoutActualSize.GetWidth());
scrollbar1.SetScrollSpace(m_CachedLayoutActualSize.GetWidth());
scrollbar1.SetX(m_CachedLayoutActualSize.left);
scrollbar1.SetY(m_CachedLayoutActualSize.bottom);
scrollbar1.SetZ(GetBufferedZ());
scrollbar1.SetLength(m_CachedLayoutActualSize.GetWidth());
if (HasVerticalScrollBar() && HasHorizontalScrollBar() && scrollbar0.IsVisible())
{
scrollbar1.SetLength(m_CachedLayoutActualSize.GetWidth() - scrollbar0.GetOuterRect().GetWidth());
scrollbar1.SetScrollSpace(m_CachedLayoutActualSize.GetWidth() + scrollbar0.GetOuterRect().GetWidth());
}
if (HasHorizontalScrollBar() && HasVerticalScrollBar() && scrollbar1.IsVisible())
{
scrollbar0.SetLength(m_CachedLayoutActualSize.GetHeight() - scrollbar1.GetOuterRect().GetHeight());
scrollbar0.SetScrollSpace(m_CachedLayoutActualSize.GetHeight() - scrollbar1.GetOuterRect().GetHeight());
}
if (HasVerticalScrollBar() && maxVRange < vscroll)
{
vscroll = maxVRange;
scrollbar0.SetPos(vscroll);
}
if (HasHorizontalScrollBar() && maxHRange < hscroll)
{
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)
{
IGUIScrollBar& scrollbar0 = GetScrollBar(0);
IGUIScrollBar& scrollbar1 = GetScrollBar(1);
if (orientation == EScrollOrientation::BOTH || orientation == EScrollOrientation::VERTICAL)
scrollbar0.SetPos(0);
if (orientation == EScrollOrientation::BOTH || orientation == EScrollOrientation::HORIZONTAL)
scrollbar1.SetPos(0);
}

View file

@ -0,0 +1,62 @@
/* Copyright (C) 20244 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef INCLUDED_CSCROLLPANEL
#define INCLUDED_CSCROLLPANEL
#include "gui/ObjectBases/IGUIPanel.h"
#include "gui/ObjectBases/IGUIScrollBarOwner.h"
#include "gui/SettingTypes/EScrollOrientation.h"
#include "ps/CStr.h"
class CScrollPanel : public IGUIPanel, public IGUIScrollBarOwner
{
GUI_OBJECT(CScrollPanel)
mutable std::vector<IGUIObject*> m_ModifiedChildren; // To store the modified vector
public:
CScrollPanel(CGUI& pGUI);
virtual ~CScrollPanel();
virtual void UpdateCachedSize();
virtual void ResetStates();
void Setup();
void ResetScrollPosition(EScrollOrientation orientation = EScrollOrientation::BOTH);
protected:
/**
* @see IGUIObject#HandleMessage()
*/
virtual void HandleMessage(SGUIMessage& Message);
void UpdateScrollPosition(float vscroll, float hscroll);
bool HasHorizontalScrollBar() const { return *m_Orientation == EScrollOrientation::HORIZONTAL || *m_Orientation == EScrollOrientation::BOTH; };
bool HasVerticalScrollBar() const { return *m_Orientation == EScrollOrientation::VERTICAL || *m_Orientation == EScrollOrientation::BOTH; };
virtual void Draw(CCanvas2D& canvas);
virtual void CreateJSObject();
CGUISimpleSetting<EScrollOrientation> m_Orientation;
CGUISimpleSetting<CStr> m_ScrollBarStyle;
CGUISimpleSetting<int> m_MinWidth;
CGUISimpleSetting<int> m_MinHeight;
};
#endif // INCLUDED_CSCROLLPANEL

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2022 Wildfire Games.
/* Copyright (C) 2024 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -78,6 +78,7 @@ void CSlider::HandleMessage(SGUIMessage& Message)
{
if (m_Pressed)
break;
Message.Skip(false);
IncrementallyChangeValue(-0.01f);
break;
}
@ -85,6 +86,7 @@ void CSlider::HandleMessage(SGUIMessage& Message)
{
if (m_Pressed)
break;
Message.Skip(false);
IncrementallyChangeValue(0.01f);
break;
}
@ -109,8 +111,8 @@ void CSlider::Draw(CCanvas2D& canvas)
CRect sliderLine(m_CachedActualSize);
sliderLine.left += m_ButtonSide / 2.0f;
sliderLine.right -= m_ButtonSide / 2.0f;
m_pGUI.DrawSprite(IsEnabled() ? m_SpriteBar : m_SpriteBarDisabled, canvas, sliderLine);
m_pGUI.DrawSprite(IsEnabled() ? m_Sprite : m_SpriteDisabled, canvas, GetButtonRect());
m_pGUI.DrawSprite(IsEnabled() ? m_SpriteBar : m_SpriteBarDisabled, canvas, sliderLine, m_VisibleArea);
m_pGUI.DrawSprite(IsEnabled() ? m_Sprite : m_SpriteDisabled, canvas, GetButtonRect(), m_VisibleArea);
}
void CSlider::UpdateValue()

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2021 Wildfire Games.
/* Copyright (C) 2024 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -130,7 +130,8 @@ const CStrW& CText::GetTooltipText() const
void CText::HandleMessage(SGUIMessage& Message)
{
IGUIObject::HandleMessage(Message);
IGUIScrollBarOwner::HandleMessage(Message);
if (m_ScrollBar)
IGUIScrollBarOwner::HandleMessage(Message);
//IGUITextOwner::HandleMessage(Message); <== placed it after the switch instead!
switch (Message.type)
@ -148,24 +149,6 @@ void CText::HandleMessage(SGUIMessage& Message)
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);
break;
}
case GUIM_LOAD:
{
GetScrollBar(0).SetX(m_CachedActualSize.right);
@ -185,18 +168,16 @@ void CText::HandleMessage(SGUIMessage& Message)
void CText::Draw(CCanvas2D& canvas)
{
m_pGUI.DrawSprite(m_Sprite, canvas, m_CachedActualSize);
m_pGUI.DrawSprite(m_Sprite, canvas, m_CachedActualSize, m_VisibleArea);
float scroll = 0.f;
if (m_ScrollBar)
scroll = GetScrollBar(0).GetPos();
// Clipping area (we'll have to subtract the scrollbar)
CRect cliparea;
CRect cliparea = m_VisibleArea ? m_VisibleArea : m_CachedActualSize;
if (m_Clip)
{
cliparea = m_CachedActualSize;
if (m_ScrollBar)
{
// subtract scrollbar from cliparea
@ -222,5 +203,5 @@ void CText::Draw(CCanvas2D& canvas)
IGUIScrollBarOwner::Draw(canvas);
// Draw the overlays last
m_pGUI.DrawSprite(m_SpriteOverlay, canvas, m_CachedActualSize);
m_pGUI.DrawSprite(m_SpriteOverlay, canvas, m_CachedActualSize, m_VisibleArea);
}

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2020 Wildfire Games.
/* Copyright (C) 2024 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -35,13 +35,15 @@ enum EGUIMessageType
GUIM_MOUSE_DOWN_LEFT,
GUIM_MOUSE_DOWN_RIGHT,
GUIM_MOUSE_DBLCLICK_LEFT,
GUIM_MOUSE_DBLCLICK_LEFT_ITEM, // Triggered when doubleclicking on a list item
GUIM_MOUSE_DBLCLICK_LEFT_ITEM, // Triggered when doubleclicking on a list item.
GUIM_MOUSE_DBLCLICK_RIGHT,
GUIM_MOUSE_RELEASE_LEFT,
GUIM_MOUSE_RELEASE_RIGHT,
GUIM_MOUSE_WHEEL_UP,
GUIM_MOUSE_WHEEL_DOWN,
GUIM_SETTINGS_UPDATED, // SGUIMessage.m_Value = name of setting
GUIM_MOUSE_WHEEL_LEFT,
GUIM_MOUSE_WHEEL_RIGHT,
GUIM_SETTINGS_UPDATED, // SGUIMessage.m_Value = name of setting.
GUIM_PRESSED,
GUIM_KEYDOWN,
GUIM_RELEASED,
@ -55,8 +57,10 @@ enum EGUIMessageType
GUIM_DOUBLE_PRESSED_MOUSE_RIGHT,
GUIM_PRESSED_MOUSE_RELEASE,
GUIM_PRESSED_MOUSE_RELEASE_RIGHT,
GUIM_TAB, // Used by CInput
GUIM_TEXTEDIT
GUIM_TAB, // Used by CInput.
GUIM_TEXTEDIT,
GUIM_CHILD_RESIZED, // SGUIMessage.m_Value = name of the object that changed size, used for inform to parent.
GUIM_CHILD_TOGGLE_VISIBILITY, // SGUIMessage.m_Value = name of the object that changed visibility used for inform to parent.
};
/**

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2021 Wildfire Games.
/* Copyright (C) 2024 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -18,6 +18,7 @@
#include "precompiled.h"
#include "gui/ObjectBases/IGUIObject.h"
#include "gui/SettingTypes/EScrollOrientation.h"
#include "gui/SettingTypes/CGUIColor.h"
#include "gui/SettingTypes/CGUIList.h"
#include "gui/SettingTypes/CGUISeries.h"
@ -310,6 +311,49 @@ template<> bool Script::FromJSVal<EAlign>(const ScriptRequest& rq, JS::HandleVal
return true;
}
template<> void Script::ToJSVal<EScrollOrientation>(const ScriptRequest& rq, JS::MutableHandleValue ret, const EScrollOrientation& val)
{
std::string word;
switch (val)
{
case EScrollOrientation::HORIZONTAL:
word = "horizontal";
break;
case EScrollOrientation::VERTICAL:
word = "vertical";
break;
case EScrollOrientation::BOTH:
word = "both";
break;
default:
word = "error";
ScriptException::Raise(rq, "Invalid scroll orientation (should be 'vertical', 'horizontal' or 'both')");
break;
}
ToJSVal(rq, ret, word);
}
template <> bool Script::FromJSVal<EScrollOrientation>(const ScriptRequest& rq, JS::HandleValue v, EScrollOrientation& out)
{
std::string word;
FromJSVal(rq, v, word);
if (word == "horizontal")
out = EScrollOrientation::HORIZONTAL;
else if (word == "vertical")
out = EScrollOrientation::VERTICAL;
else if (word == "both")
out = EScrollOrientation::BOTH;
else
{
out = EScrollOrientation::VERTICAL;
LOGERROR("Invalid scroll orientation (should be 'vertical', 'horizontal' or 'both')");
return false;
}
return true;
}
template<> void Script::ToJSVal<CGUISpriteInstance>(const ScriptRequest& rq, JS::MutableHandleValue ret, const CGUISpriteInstance& val)
{
ToJSVal(rq, ret, val.GetName());

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2021 Wildfire Games.
/* Copyright (C) 2024 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -23,6 +23,7 @@
#include "gui/ObjectTypes/CButton.h"
#include "gui/ObjectTypes/CList.h"
#include "gui/ObjectTypes/CMiniMap.h"
#include "gui/ObjectTypes/CScrollPanel.h"
#include "gui/ObjectTypes/CText.h"
// Called for every specialization - adds the common interface.
@ -66,3 +67,10 @@ template<> void JSI_GUIProxy<CMiniMap>::CreateFunctions(const ScriptRequest& rq,
CreateFunction<&CMiniMap::Flare>(rq, cache, "flare");
}
DECLARE_GUIPROXY(CMiniMap);
// CScrollPanel
template<> void JSI_GUIProxy<CScrollPanel>::CreateFunctions(const ScriptRequest& rq, GUIProxyProps* cache)
{
CreateFunction<&CScrollPanel::ResetScrollPosition>(rq, cache, "resetScrollPosition");
}
DECLARE_GUIPROXY(CScrollPanel);

View file

@ -0,0 +1,28 @@
/* Copyright (C) 2024 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef INCLUDED_ESCROLLORIENTATION
#define INCLUDED_ESCROLLORIENTATION
enum class EScrollOrientation
{
HORIZONTAL,
VERTICAL,
BOTH
};
#endif // INCLUDED_ESCROLLORIENTATION

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2021 Wildfire Games.
/* Copyright (C) 2024 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -219,3 +219,16 @@ CRect CRect::Scale(float x, float y) const
{
return CRect(left * x, top * y, right * x, bottom * y);
}
bool CRect::IntersectWith(const CRect& a) const
{
return left < a.right && right > a.left && top < a.bottom && bottom > a.top;
}
CRect CRect::Intersection(const CRect& a) const
{
if (!IntersectWith(a))
return CRect();
return CRect(std::max(left, a.left), std::max(top, a.top), std::min(right, a.right), std::min(bottom, a.bottom));
}

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2021 Wildfire Games.
/* Copyright (C) 2024 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -58,6 +58,8 @@ public:
void operator-=(const CVector2D& a);
void operator-=(const CSize2D& a);
operator bool() const { return right - left > 0 && bottom - top > 0; }
/**
* @return Width of Rectangle
*/
@ -107,6 +109,10 @@ public:
CRect Scale(float x, float y) const;
bool IntersectWith(const CRect& a) const;
CRect Intersection(const CRect& a) const;
/**
* Returning CVector2D representing each corner.
*/