Index: ps/trunk/source/gui/CDropDown.cpp
===================================================================
--- ps/trunk/source/gui/CDropDown.cpp (revision 17148)
+++ ps/trunk/source/gui/CDropDown.cpp (revision 17149)
@@ -1,522 +1,524 @@
/* Copyright (C) 2015 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 .
*/
#include "precompiled.h"
#include "CDropDown.h"
#include "lib/external_libraries/libsdl.h"
#include "lib/ogl.h"
#include "lib/timer.h"
#include "ps/CLogger.h"
#include "soundmanager/ISoundManager.h"
CDropDown::CDropDown()
: m_Open(false), m_HideScrollBar(false), m_ElementHighlight(-1)
{
AddSetting(GUIST_float, "button_width");
AddSetting(GUIST_float, "dropdown_size");
AddSetting(GUIST_float, "dropdown_buffer");
// AddSetting(GUIST_CStrW, "font");
AddSetting(GUIST_CStrW, "sound_closed");
AddSetting(GUIST_CStrW, "sound_disabled");
AddSetting(GUIST_CStrW, "sound_enter");
AddSetting(GUIST_CStrW, "sound_leave");
AddSetting(GUIST_CStrW, "sound_opened");
// AddSetting(GUIST_CGUISpriteInstance, "sprite"); // Background that sits around the size
AddSetting(GUIST_CGUISpriteInstance, "sprite_list"); // Background of the drop down list
AddSetting(GUIST_CGUISpriteInstance, "sprite2"); // Button that sits to the right
AddSetting(GUIST_CGUISpriteInstance, "sprite2_over");
AddSetting(GUIST_CGUISpriteInstance, "sprite2_pressed");
AddSetting(GUIST_CGUISpriteInstance, "sprite2_disabled");
AddSetting(GUIST_EVAlign, "text_valign");
// Add these in CList! And implement TODO
//AddSetting(GUIST_CColor, "textcolor_over");
//AddSetting(GUIST_CColor, "textcolor_pressed");
//AddSetting(GUIST_CColor, "textcolor_disabled");
// Scrollbar is forced to be true.
GUI::SetSetting(this, "scrollbar", true);
}
CDropDown::~CDropDown()
{
}
void CDropDown::SetupText()
{
SetupListRect();
CList::SetupText();
}
void CDropDown::HandleMessage(SGUIMessage& Message)
{
// Important
switch (Message.type)
{
case GUIM_SETTINGS_UPDATED:
{
// Update cached list rect
if (Message.value == "size" ||
Message.value == "absolute" ||
Message.value == "dropdown_size" ||
Message.value == "dropdown_buffer" ||
Message.value == "scrollbar_style" ||
Message.value == "button_width")
{
SetupListRect();
}
break;
}
case GUIM_MOUSE_MOTION:
{
if (!m_Open)
break;
CPos mouse = GetMousePos();
if (!GetListRect().PointInside(mouse))
break;
bool scrollbar;
CGUIList* pList;
GUI::GetSetting(this, "scrollbar", scrollbar);
GUI::GetSettingPointer(this, "list", pList);
float scroll = 0.f;
if (scrollbar)
scroll = GetScrollBar(0).GetPos();
CRect rect = GetListRect();
mouse.y += scroll;
int set = -1;
for (int i = 0; i < (int)pList->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.x >= GetScrollBar(0).GetOuterRect().left &&
- mouse.x <= GetScrollBar(0).GetOuterRect().right))
+ (m_HideScrollBar ||
+ mouse.x < GetScrollBar(0).GetOuterRect().left ||
+ mouse.x > GetScrollBar(0).GetOuterRect().right))
{
set = i;
}
}
if (set != -1)
{
m_ElementHighlight = set;
//UpdateAutoScroll();
}
break;
}
case GUIM_MOUSE_ENTER:
{
bool enabled;
GUI::GetSetting(this, "enabled", enabled);
if (!enabled)
break;
CStrW soundPath;
if (g_SoundManager && GUI::GetSetting(this, "sound_enter", soundPath) == PSRETURN_OK && !soundPath.empty())
g_SoundManager->PlayAsUI(soundPath.c_str(), false);
break;
}
case GUIM_MOUSE_LEAVE:
{
GUI::GetSetting(this, "selected", m_ElementHighlight);
bool enabled;
GUI::GetSetting(this, "enabled", enabled);
if (!enabled)
break;
CStrW soundPath;
if (g_SoundManager && GUI::GetSetting(this, "sound_leave", soundPath) == PSRETURN_OK && !soundPath.empty())
g_SoundManager->PlayAsUI(soundPath.c_str(), false);
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.
case GUIM_MOUSE_PRESS_LEFT:
{
bool enabled;
GUI::GetSetting(this, "enabled", enabled);
if (!enabled)
{
CStrW soundPath;
if (g_SoundManager && GUI::GetSetting(this, "sound_disabled", soundPath) == PSRETURN_OK && !soundPath.empty())
g_SoundManager->PlayAsUI(soundPath.c_str(), false);
break;
}
if (!m_Open)
{
CGUIList* pList;
GUI::GetSettingPointer(this, "list", pList);
if (pList->m_Items.empty())
return;
m_Open = true;
GetScrollBar(0).SetZ(GetBufferedZ());
GUI::GetSetting(this, "selected", m_ElementHighlight);
// Start at the position of the selected item, if possible.
GetScrollBar(0).SetPos(m_ItemsYPositions.empty() ? 0 : m_ItemsYPositions[m_ElementHighlight] - 60);
CStrW soundPath;
if (g_SoundManager && GUI::GetSetting(this, "sound_opened", soundPath) == PSRETURN_OK && !soundPath.empty())
g_SoundManager->PlayAsUI(soundPath.c_str(), false);
return; // overshadow
}
else
{
CPos mouse = GetMousePos();
// If the regular area is pressed, then abort, and close.
if (m_CachedActualSize.PointInside(mouse))
{
m_Open = false;
GetScrollBar(0).SetZ(GetBufferedZ());
CStrW soundPath;
if (g_SoundManager && GUI::GetSetting(this, "sound_closed", soundPath) == PSRETURN_OK && !soundPath.empty())
g_SoundManager->PlayAsUI(soundPath.c_str(), false);
return; // overshadow
}
- if (!(mouse.x >= GetScrollBar(0).GetOuterRect().left &&
- mouse.x <= GetScrollBar(0).GetOuterRect().right) &&
- mouse.y >= GetListRect().top)
+ if (m_HideScrollBar ||
+ mouse.x < GetScrollBar(0).GetOuterRect().left ||
+ mouse.x > GetScrollBar(0).GetOuterRect().right ||
+ mouse.y < GetListRect().top)
{
m_Open = false;
GetScrollBar(0).SetZ(GetBufferedZ());
}
}
break;
}
case GUIM_MOUSE_WHEEL_DOWN:
{
bool enabled;
GUI::GetSetting(this, "enabled", enabled);
// Don't switch elements by scrolling when open, causes a confusing interaction between this and the scrollbar.
if (m_Open || !enabled)
break;
GUI::GetSetting(this, "selected", m_ElementHighlight);
if (m_ElementHighlight + 1 >= (int)m_ItemsYPositions.size() - 1)
break;
++m_ElementHighlight;
GUI::SetSetting(this, "selected", m_ElementHighlight);
break;
}
case GUIM_MOUSE_WHEEL_UP:
{
bool enabled;
GUI::GetSetting(this, "enabled", enabled);
// Don't switch elements by scrolling when open, causes a confusing interaction between this and the scrollbar.
if (m_Open || !enabled)
break;
GUI::GetSetting(this, "selected", m_ElementHighlight);
if (m_ElementHighlight - 1 < 0)
break;
m_ElementHighlight--;
GUI::SetSetting(this, "selected", m_ElementHighlight);
break;
}
case GUIM_LOST_FOCUS:
{
if (m_Open)
{
CStrW soundPath;
if (g_SoundManager && GUI::GetSetting(this, "sound_closed", soundPath) == PSRETURN_OK && !soundPath.empty())
g_SoundManager->PlayAsUI(soundPath.c_str(), false);
}
m_Open = false;
break;
}
case GUIM_LOAD:
SetupListRect();
break;
default:
break;
}
// Important that this is after, so that overshadowed implementations aren't processed
CList::HandleMessage(Message);
// As HandleMessage functions return void, we need to manually verify
// whether the child list's items were modified.
if (CList::GetModified())
SetupText();
}
InReaction CDropDown::ManuallyHandleEvent(const SDL_Event_* ev)
{
InReaction result = IN_PASS;
bool update_highlight = false;
if (ev->ev.type == SDL_KEYDOWN)
{
int szChar = ev->ev.key.keysym.sym;
switch (szChar)
{
case '\r':
m_Open = false;
result = IN_HANDLED;
break;
case SDLK_HOME:
case SDLK_END:
case SDLK_UP:
case SDLK_DOWN:
case SDLK_PAGEUP:
case SDLK_PAGEDOWN:
if (!m_Open)
return IN_PASS;
// Set current selected item to highlighted, before
// then really processing these in CList::ManuallyHandleEvent()
GUI::SetSetting(this, "selected", m_ElementHighlight);
update_highlight = true;
break;
default:
// If we have inputed a character try to get the closest element to it.
// TODO: not too nice and doesn't deal with dashes.
if (m_Open && ((szChar >= SDLK_a && szChar <= SDLK_z) || szChar == SDLK_SPACE
|| (szChar >= SDLK_0 && szChar <= SDLK_9)
#if !SDL_VERSION_ATLEAST(2,0,0)
|| (szChar >= SDLK_KP0 && szChar <= SDLK_KP9)))
#else // SDL2
|| (szChar >= SDLK_KP_0 && szChar <= SDLK_KP_9)))
#endif
{
// arbitrary 1 second limit to add to string or start fresh.
// maximal amount of characters is 100, which imo is far more than enough.
if (timer_Time() - m_TimeOfLastInput > 1.0 || m_InputBuffer.length() >= 100)
m_InputBuffer = szChar;
else
m_InputBuffer += szChar;
m_TimeOfLastInput = timer_Time();
CGUIList* pList;
GUI::GetSettingPointer(this, "list", pList);
// let's look for the closest element
// basically it's alphabetic order and "as many letters as we can get".
int closest = -1;
int bestIndex = -1;
int difference = 1250;
for (int i = 0; i < (int)pList->m_Items.size(); ++i)
{
int indexOfDifference = 0;
int diff = 0;
for (size_t j = 0; j < m_InputBuffer.length(); ++j)
{
diff = abs(pList->m_Items[i].GetOriginalString().LowerCase()[j] - (int)m_InputBuffer[j]);
if (diff == 0)
indexOfDifference = j+1;
else
break;
}
if (indexOfDifference > bestIndex || (indexOfDifference >= bestIndex && diff < difference))
{
bestIndex = indexOfDifference;
closest = i;
difference = diff;
}
}
// let's select the closest element. There should basically always be one.
if (closest != -1)
{
GUI::SetSetting(this, "selected", closest);
update_highlight = true;
GetScrollBar(0).SetPos(m_ItemsYPositions[closest] - 60);
}
result = IN_HANDLED;
}
break;
}
}
if (CList::ManuallyHandleEvent(ev) == IN_HANDLED)
result = IN_HANDLED;
if (update_highlight)
GUI::GetSetting(this, "selected", m_ElementHighlight);
return result;
}
void CDropDown::SetupListRect()
{
float size, buffer, button_width;
GUI::GetSetting(this, "dropdown_size", size);
GUI::GetSetting(this, "dropdown_buffer", buffer);
GUI::GetSetting(this, "button_width", button_width);
if (m_ItemsYPositions.empty() || m_ItemsYPositions.back() >= size)
{
m_CachedListRect = CRect(m_CachedActualSize.left, m_CachedActualSize.bottom+buffer,
m_CachedActualSize.right, m_CachedActualSize.bottom+buffer + size);
m_HideScrollBar = false;
}
else
{
m_CachedListRect = CRect(m_CachedActualSize.left, m_CachedActualSize.bottom+buffer,
m_CachedActualSize.right - GetScrollBar(0).GetStyle()->m_Width, m_CachedActualSize.bottom+buffer + m_ItemsYPositions.back());
// We also need to hide the scrollbar
m_HideScrollBar = true;
}
}
CRect CDropDown::GetListRect() const
{
return m_CachedListRect;
}
bool CDropDown::MouseOver()
{
if(!GetGUI())
throw PSERROR_GUI_OperationNeedsGUIObject();
if (m_Open)
{
CRect rect(m_CachedActualSize.left, m_CachedActualSize.top,
m_CachedActualSize.right, GetListRect().bottom);
return rect.PointInside(GetMousePos());
}
else
return m_CachedActualSize.PointInside(GetMousePos());
}
void CDropDown::Draw()
{
if (!GetGUI())
return;
float bz = GetBufferedZ();
float dropdown_size, button_width;
GUI::GetSetting(this, "dropdown_size", dropdown_size);
GUI::GetSetting(this, "button_width", button_width);
CGUISpriteInstance* sprite;
CGUISpriteInstance* sprite2;
CGUISpriteInstance* sprite2_second;
int cell_id, selected = 0;
CColor color;
GUI::GetSettingPointer(this, "sprite", sprite);
GUI::GetSettingPointer(this, "sprite2", sprite2);
GUI::GetSetting(this, "cell_id", cell_id);
GUI::GetSetting(this, "selected", selected);
GUI::GetSetting(this, "textcolor", color);
bool enabled;
GUI::GetSetting(this, "enabled", enabled);
GetGUI()->DrawSprite(*sprite, cell_id, bz, m_CachedActualSize);
if (button_width > 0.f)
{
CRect rect(m_CachedActualSize.right-button_width, m_CachedActualSize.top,
m_CachedActualSize.right, m_CachedActualSize.bottom);
if (!enabled)
{
GUI::GetSettingPointer(this, "sprite2_disabled", sprite2_second);
GetGUI()->DrawSprite(GUI<>::FallBackSprite(*sprite2_second, *sprite2), cell_id, bz+0.05f, rect);
}
else if (m_Open)
{
GUI::GetSettingPointer(this, "sprite2_pressed", sprite2_second);
GetGUI()->DrawSprite(GUI<>::FallBackSprite(*sprite2_second, *sprite2), cell_id, bz+0.05f, rect);
}
else if (m_MouseHovering)
{
GUI::GetSettingPointer(this, "sprite2_over", sprite2_second);
GetGUI()->DrawSprite(GUI<>::FallBackSprite(*sprite2_second, *sprite2), cell_id, bz+0.05f, rect);
}
else
GetGUI()->DrawSprite(*sprite2, cell_id, bz+0.05f, rect);
}
if (selected != -1) // TODO: Maybe check validity completely?
{
// figure out clipping rectangle
CRect cliparea(m_CachedActualSize.left, m_CachedActualSize.top,
m_CachedActualSize.right-button_width, m_CachedActualSize.bottom);
CPos pos(m_CachedActualSize.left, m_CachedActualSize.top);
DrawText(selected, color, pos, bz+0.1f, cliparea);
}
bool* scrollbar = NULL;
bool old;
GUI::GetSettingPointer(this, "scrollbar", scrollbar);
old = *scrollbar;
if (m_Open)
{
if (m_HideScrollBar)
*scrollbar = false;
DrawList(m_ElementHighlight, "sprite_list", "sprite_selectarea", "textcolor");
if (m_HideScrollBar)
*scrollbar = old;
}
}
// When a dropdown list is opened, it needs to be visible above all the other
// controls on the page. The only way I can think of to do this is to increase
// its z value when opened, so that it's probably on top.
float CDropDown::GetBufferedZ() const
{
float bz = CList::GetBufferedZ();
if (m_Open)
return std::min(bz + 500.f, 1000.f); // TODO - don't use magic number for max z value
else
return bz;
}
Index: ps/trunk/source/gui/CGUIScrollBarVertical.cpp
===================================================================
--- ps/trunk/source/gui/CGUIScrollBarVertical.cpp (revision 17148)
+++ ps/trunk/source/gui/CGUIScrollBarVertical.cpp (revision 17149)
@@ -1,196 +1,196 @@
/* Copyright (C) 2015 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 .
*/
#include "precompiled.h"
#include "CGUIScrollBarVertical.h"
#include "GUI.h"
#include "ps/CLogger.h"
CGUIScrollBarVertical::CGUIScrollBarVertical()
{
}
CGUIScrollBarVertical::~CGUIScrollBarVertical()
{
}
void CGUIScrollBarVertical::SetPosFromMousePos(const CPos& 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.y - m_BarPressedAtPos.y) / emptyBackground;
}
void CGUIScrollBarVertical::Draw()
{
if (!GetStyle())
{
LOGWARNING("Attempt to draw scrollbar without a style.");
return;
}
- if (GetGUI() && GetMaxPos() != 1)
+ if (GetGUI() && IsVisible())
{
CRect outline = GetOuterRect();
// Draw background
GetGUI()->DrawSprite(GetStyle()->m_SpriteBackVertical,
0,
m_Z+0.1f,
CRect(outline.left,
outline.top+(GetStyle()->m_UseEdgeButtons?GetStyle()->m_Width:0),
outline.right,
outline.bottom-(GetStyle()->m_UseEdgeButtons?GetStyle()->m_Width:0))
);
if (GetStyle()->m_UseEdgeButtons)
{
// Get Appropriate sprites
const CGUISpriteInstance* button_top;
const CGUISpriteInstance* button_bottom;
// figure out what sprite to use for top button
if (m_ButtonMinusHovered)
{
if (m_ButtonMinusPressed)
button_top = &GUI<>::FallBackSprite(GetStyle()->m_SpriteButtonTopPressed, GetStyle()->m_SpriteButtonTop);
else
button_top = &GUI<>::FallBackSprite(GetStyle()->m_SpriteButtonTopOver, GetStyle()->m_SpriteButtonTop);
}
else
button_top = &GetStyle()->m_SpriteButtonTop;
// figure out what sprite to use for bottom button
if (m_ButtonPlusHovered)
{
if (m_ButtonPlusPressed)
button_bottom = &GUI<>::FallBackSprite(GetStyle()->m_SpriteButtonBottomPressed, GetStyle()->m_SpriteButtonBottom);
else
button_bottom = &GUI<>::FallBackSprite(GetStyle()->m_SpriteButtonBottomOver, GetStyle()->m_SpriteButtonBottom);
}
else
button_bottom = &GetStyle()->m_SpriteButtonBottom;
// Draw top button
GetGUI()->DrawSprite(*button_top,
0,
m_Z+0.2f,
CRect(outline.left,
outline.top,
outline.right,
outline.top+GetStyle()->m_Width)
);
// Draw bottom button
GetGUI()->DrawSprite(*button_bottom,
0,
m_Z+0.2f,
CRect(outline.left,
outline.bottom-GetStyle()->m_Width,
outline.right,
outline.bottom)
);
}
// Draw bar
GetGUI()->DrawSprite(GetStyle()->m_SpriteBarVertical,
0,
m_Z + 0.2f,
GetBarRect());
}
}
void CGUIScrollBarVertical::HandleMessage(SGUIMessage& Message)
{
IGUIScrollBar::HandleMessage(Message);
}
CRect CGUIScrollBarVertical::GetBarRect() const
{
CRect ret;
if (!GetStyle())
return ret;
// Get from where the scroll area begins to where it ends
float from = m_Y;
float to = m_Y + m_Length - m_BarSize;
if (GetStyle()->m_UseEdgeButtons)
{
from += GetStyle()->m_Width;
to -= GetStyle()->m_Width;
}
// Setup rectangle
ret.top = from + (to - from) * (m_Pos / GetMaxPos());
ret.bottom = ret.top + m_BarSize;
ret.right = m_X + ((m_RightAligned)?(0.f):(GetStyle()->m_Width));
ret.left = ret.right - GetStyle()->m_Width;
return ret;
}
CRect CGUIScrollBarVertical::GetOuterRect() const
{
CRect ret;
if (!GetStyle())
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;
return ret;
}
bool CGUIScrollBarVertical::HoveringButtonMinus(const CPos& mouse)
{
if (!GetStyle())
return false;
float StartX = (m_RightAligned)?(m_X-GetStyle()->m_Width):(m_X);
return (mouse.x >= StartX &&
mouse.x <= StartX + GetStyle()->m_Width &&
mouse.y >= m_Y &&
mouse.y <= m_Y + GetStyle()->m_Width);
}
bool CGUIScrollBarVertical::HoveringButtonPlus(const CPos& mouse)
{
if (!GetStyle())
return false;
float StartX = (m_RightAligned)?(m_X-GetStyle()->m_Width):(m_X);
return (mouse.x > StartX &&
mouse.x < StartX + GetStyle()->m_Width &&
mouse.y > m_Y + m_Length - GetStyle()->m_Width &&
mouse.y < m_Y + m_Length);
}
Index: ps/trunk/source/gui/CList.cpp
===================================================================
--- ps/trunk/source/gui/CList.cpp (revision 17148)
+++ ps/trunk/source/gui/CList.cpp (revision 17149)
@@ -1,503 +1,504 @@
/* Copyright (C) 2015 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 .
*/
#include "precompiled.h"
#include "CList.h"
#include "CGUIScrollBarVertical.h"
#include "lib/external_libraries/libsdl.h"
#include "ps/CLogger.h"
#include "soundmanager/ISoundManager.h"
CList::CList()
: m_Modified(false)
{
// Add sprite_disabled! TODO
AddSetting(GUIST_float, "buffer_zone");
AddSetting(GUIST_CStrW, "font");
AddSetting(GUIST_bool, "scrollbar");
AddSetting(GUIST_CStr, "scrollbar_style");
AddSetting(GUIST_CStrW, "sound_disabled");
AddSetting(GUIST_CStrW, "sound_selected");
AddSetting(GUIST_CGUISpriteInstance, "sprite");
AddSetting(GUIST_CGUISpriteInstance, "sprite_selectarea");
AddSetting(GUIST_int, "cell_id");
AddSetting(GUIST_EAlign, "text_align");
AddSetting(GUIST_CColor, "textcolor");
AddSetting(GUIST_CColor, "textcolor_selected");
AddSetting(GUIST_int, "selected"); // Index selected. -1 is none.
AddSetting(GUIST_CStrW, "tooltip");
AddSetting(GUIST_CStr, "tooltip_style");
// Each list item has both a name (in 'list') and an associated data string (in 'list_data')
AddSetting(GUIST_CGUIList, "list");
AddSetting(GUIST_CGUIList, "list_data"); // TODO: this should be a list of raw strings, not of CGUIStrings
GUI::SetSetting(this, "scrollbar", false);
// Nothing is selected as default.
GUI::SetSetting(this, "selected", -1);
// Add scroll-bar
CGUIScrollBarVertical* bar = new CGUIScrollBarVertical();
bar->SetRightAligned(true);
AddScrollBar(bar);
}
CList::~CList()
{
}
void CList::SetupText()
{
if (!GetGUI())
return;
m_Modified = true;
CGUIList* pList;
GUI::GetSettingPointer(this, "list", pList);
//ENSURE(m_GeneratedTexts.size()>=1);
m_ItemsYPositions.resize(pList->m_Items.size()+1);
// Delete all generated texts. Some could probably be saved,
// but this is easier, and this function will never be called
// continuously, or even often, so it'll probably be okay.
for (SGUIText* const& t : m_GeneratedTexts)
delete t;
m_GeneratedTexts.clear();
CStrW font;
if (GUI::GetSetting(this, "font", font) != PSRETURN_OK || font.empty())
// Use the default if none is specified
// TODO Gee: (2004-08-14) Don't define standard like this. Do it with the default style.
font = L"default";
bool scrollbar;
GUI::GetSetting(this, "scrollbar", scrollbar);
float width = GetListRect().GetWidth();
// remove scrollbar if applicable
if (scrollbar && GetScrollBar(0).GetStyle())
width -= GetScrollBar(0).GetStyle()->m_Width;
float buffer_zone = 0.f;
GUI::GetSetting(this, "buffer_zone", buffer_zone);
// Generate texts
float buffered_y = 0.f;
for (size_t i = 0; i < pList->m_Items.size(); ++i)
{
// Create a new SGUIText. Later on, input it using AddText()
SGUIText* text = new SGUIText();
*text = GetGUI()->GenerateText(pList->m_Items[i], font, width, buffer_zone, this);
m_ItemsYPositions[i] = buffered_y;
buffered_y += text->m_Size.cy;
AddText(text);
}
m_ItemsYPositions[pList->m_Items.size()] = buffered_y;
// Setup scrollbar
if (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);
}
}
void CList::HandleMessage(SGUIMessage& Message)
{
IGUIScrollBarOwner::HandleMessage(Message);
//IGUITextOwner::HandleMessage(Message); <== placed it after the switch instead!
m_Modified = false;
switch (Message.type)
{
case GUIM_SETTINGS_UPDATED:
if (Message.value == "list")
SetupText();
// If selected is changed, call "SelectionChange"
if (Message.value == "selected")
{
// TODO: Check range
// TODO only works if lower-case, shouldn't it be made case sensitive instead?
ScriptEvent("selectionchange");
}
if (Message.value == "scrollbar")
SetupText();
// Update scrollbar
if (Message.value == "scrollbar_style")
{
CStr scrollbar_style;
GUI::GetSetting(this, Message.value, scrollbar_style);
GetScrollBar(0).SetScrollBarStyle(scrollbar_style);
SetupText();
}
break;
case GUIM_MOUSE_PRESS_LEFT:
{
bool enabled;
GUI::GetSetting(this, "enabled", enabled);
if (!enabled)
{
CStrW soundPath;
if (g_SoundManager && GUI::GetSetting(this, "sound_disabled", soundPath) == PSRETURN_OK && !soundPath.empty())
g_SoundManager->PlayAsUI(soundPath.c_str(), false);
break;
}
bool scrollbar;
CGUIList* pList;
GUI::GetSetting(this, "scrollbar", scrollbar);
GUI::GetSettingPointer(this, "list", pList);
float scroll = 0.f;
if (scrollbar)
scroll = GetScrollBar(0).GetPos();
CRect rect = GetListRect();
CPos mouse = GetMousePos();
mouse.y += scroll;
int set = -1;
for (int i = 0; i < (int)pList->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.x >= GetScrollBar(0).GetOuterRect().left &&
- mouse.x <= GetScrollBar(0).GetOuterRect().right))
+ (!scrollbar || !GetScrollBar(0).IsVisible() ||
+ mouse.x < GetScrollBar(0).GetOuterRect().left ||
+ mouse.x > GetScrollBar(0).GetOuterRect().right))
{
set = i;
}
}
if (set != -1)
{
GUI::SetSetting(this, "selected", set);
UpdateAutoScroll();
CStrW soundPath;
if (g_SoundManager && GUI::GetSetting(this, "sound_selected", soundPath) == PSRETURN_OK && !soundPath.empty())
g_SoundManager->PlayAsUI(soundPath.c_str(), false);
}
break;
}
case GUIM_LOAD:
{
CStr scrollbar_style;
GUI::GetSetting(this, "scrollbar_style", scrollbar_style);
GetScrollBar(0).SetScrollBarStyle(scrollbar_style);
break;
}
default:
break;
}
IGUITextOwner::HandleMessage(Message);
}
InReaction CList::ManuallyHandleEvent(const SDL_Event_* ev)
{
InReaction result = IN_PASS;
if (ev->ev.type == SDL_KEYDOWN)
{
int szChar = ev->ev.key.keysym.sym;
switch (szChar)
{
case SDLK_HOME:
SelectFirstElement();
UpdateAutoScroll();
result = IN_HANDLED;
break;
case SDLK_END:
SelectLastElement();
UpdateAutoScroll();
result = IN_HANDLED;
break;
case SDLK_UP:
SelectPrevElement();
UpdateAutoScroll();
result = IN_HANDLED;
break;
case SDLK_DOWN:
SelectNextElement();
UpdateAutoScroll();
result = IN_HANDLED;
break;
case SDLK_PAGEUP:
GetScrollBar(0).ScrollMinusPlenty();
result = IN_HANDLED;
break;
case SDLK_PAGEDOWN:
GetScrollBar(0).ScrollPlusPlenty();
result = IN_HANDLED;
break;
default: // Do nothing
result = IN_PASS;
}
}
return result;
}
void CList::Draw()
{
int selected;
GUI::GetSetting(this, "selected", selected);
DrawList(selected, "sprite", "sprite_selectarea", "textcolor");
}
void CList::DrawList(const int& selected, const CStr& _sprite, const CStr& _sprite_selected, const CStr& _textcolor)
{
float bz = GetBufferedZ();
// First call draw on ScrollBarOwner
bool scrollbar;
GUI::GetSetting(this, "scrollbar", scrollbar);
if (scrollbar)
IGUIScrollBarOwner::Draw();
if (GetGUI())
{
CRect rect = GetListRect();
CGUISpriteInstance* sprite = NULL;
CGUISpriteInstance* sprite_selectarea = NULL;
int cell_id;
GUI::GetSettingPointer(this, _sprite, sprite);
GUI::GetSettingPointer(this, _sprite_selected, sprite_selectarea);
GUI::GetSetting(this, "cell_id", cell_id);
CGUIList* pList;
GUI::GetSettingPointer(this, "list", pList);
GetGUI()->DrawSprite(*sprite, cell_id, bz, rect);
float scroll = 0.f;
if (scrollbar)
scroll = GetScrollBar(0).GetPos();
if (selected != -1)
{
ENSURE(selected >= 0 && selected+1 < (int)m_ItemsYPositions.size());
// Get rectangle of selection:
CRect rect_sel(rect.left, rect.top + m_ItemsYPositions[selected] - scroll,
rect.right, rect.top + m_ItemsYPositions[selected+1] - scroll);
if (rect_sel.top <= rect.bottom &&
rect_sel.bottom >= rect.top)
{
if (rect_sel.bottom > rect.bottom)
rect_sel.bottom = rect.bottom;
if (rect_sel.top < rect.top)
rect_sel.top = rect.top;
if (scrollbar)
{
// Remove any overlapping area of the scrollbar.
if (rect_sel.right > GetScrollBar(0).GetOuterRect().left &&
rect_sel.right <= GetScrollBar(0).GetOuterRect().right)
rect_sel.right = GetScrollBar(0).GetOuterRect().left;
if (rect_sel.left >= GetScrollBar(0).GetOuterRect().left &&
rect_sel.left < GetScrollBar(0).GetOuterRect().right)
rect_sel.left = GetScrollBar(0).GetOuterRect().right;
}
GetGUI()->DrawSprite(*sprite_selectarea, cell_id, bz+0.05f, rect_sel);
}
}
CColor color;
GUI::GetSetting(this, _textcolor, color);
for (size_t i = 0; i < pList->m_Items.size(); ++i)
{
if (m_ItemsYPositions[i+1] - scroll < 0 ||
m_ItemsYPositions[i] - scroll > rect.GetHeight())
continue;
// Clipping area (we'll have to substract the scrollbar)
CRect cliparea = GetListRect();
if (scrollbar)
{
if (cliparea.right > GetScrollBar(0).GetOuterRect().left &&
cliparea.right <= GetScrollBar(0).GetOuterRect().right)
cliparea.right = GetScrollBar(0).GetOuterRect().left;
if (cliparea.left >= GetScrollBar(0).GetOuterRect().left &&
cliparea.left < GetScrollBar(0).GetOuterRect().right)
cliparea.left = GetScrollBar(0).GetOuterRect().right;
}
DrawText(i, color, rect.TopLeft() - CPos(0.f, scroll - m_ItemsYPositions[i]), bz+0.1f, cliparea);
}
}
}
void CList::AddItem(const CStrW& str, const CStrW& data)
{
CGUIList* pList;
CGUIList* pListData;
GUI::GetSettingPointer(this, "list", pList);
GUI::GetSettingPointer(this, "list_data", pListData);
CGUIString gui_string;
gui_string.SetValue(str);
pList->m_Items.push_back(gui_string);
CGUIString data_string;
data_string.SetValue(data);
pListData->m_Items.push_back(data_string);
// TODO Temp
SetupText();
}
bool CList::HandleAdditionalChildren(const XMBElement& child, CXeromyces* pFile)
{
int elmt_item = pFile->GetElementID("item");
if (child.GetNodeName() == elmt_item)
{
AddItem(child.GetText().FromUTF8(), child.GetText().FromUTF8());
return true;
}
return false;
}
void CList::SelectNextElement()
{
int selected;
GUI::GetSetting(this, "selected", selected);
CGUIList* pList;
GUI::GetSettingPointer(this, "list", pList);
if (selected != (int)pList->m_Items.size()-1)
{
++selected;
GUI::SetSetting(this, "selected", selected);
CStrW soundPath;
if (g_SoundManager && GUI::GetSetting(this, "sound_selected", soundPath) == PSRETURN_OK && !soundPath.empty())
g_SoundManager->PlayAsUI(soundPath.c_str(), false);
}
}
void CList::SelectPrevElement()
{
int selected;
GUI::GetSetting(this, "selected", selected);
if (selected > 0)
{
--selected;
GUI::SetSetting(this, "selected", selected);
CStrW soundPath;
if (g_SoundManager && GUI::GetSetting(this, "sound_selected", soundPath) == PSRETURN_OK && !soundPath.empty())
g_SoundManager->PlayAsUI(soundPath.c_str(), false);
}
}
void CList::SelectFirstElement()
{
int selected;
GUI::GetSetting(this, "selected", selected);
if (selected >= 0)
GUI::SetSetting(this, "selected", 0);
}
void CList::SelectLastElement()
{
int selected;
GUI::GetSetting(this, "selected", selected);
CGUIList* pList;
GUI::GetSettingPointer(this, "list", pList);
if (selected != (int)pList->m_Items.size()-1)
GUI::SetSetting(this, "selected", (int)pList->m_Items.size()-1);
}
void CList::UpdateAutoScroll()
{
int selected;
bool scrollbar;
float scroll;
GUI::GetSetting(this, "selected", selected);
GUI::GetSetting(this, "scrollbar", scrollbar);
CRect rect = GetListRect();
// No scrollbar, no scrolling (at least it's not made to work properly).
if (!scrollbar)
return;
scroll = GetScrollBar(0).GetPos();
// Check upper boundary
if (m_ItemsYPositions[selected] < scroll)
{
GetScrollBar(0).SetPos(m_ItemsYPositions[selected]);
return; // this means, if it wants to align both up and down at the same time
// this will have precedence.
}
// Check lower boundary
if (m_ItemsYPositions[selected+1]-rect.GetHeight() > scroll)
GetScrollBar(0).SetPos(m_ItemsYPositions[selected+1]-rect.GetHeight());
}
Index: ps/trunk/source/gui/IGUIScrollBar.h
===================================================================
--- ps/trunk/source/gui/IGUIScrollBar.h (revision 17148)
+++ ps/trunk/source/gui/IGUIScrollBar.h (revision 17149)
@@ -1,447 +1,452 @@
/* Copyright (C) 2015 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 .
*/
/*
A GUI ScrollBar
--Overview--
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.
--Usage--
Used in everywhere scrollbars are needed, like in a combobox for instance.
--More info--
Check GUI.h
*/
#ifndef INCLUDED_IGUISCROLLBAR
#define INCLUDED_IGUISCROLLBAR
#include "GUI.h"
/**
* The GUI Scroll-bar style. Tells us how scroll-bars look and feel.
*
* A scroll-bar style can choose whether to support horizontal, vertical
* or both.
*
* @see IGUIScrollBar
*/
struct SGUIScrollBarStyle
{
//--------------------------------------------------------
/** @name General Settings */
//--------------------------------------------------------
//@{
/**
* Width of bar, also both sides of the edge buttons.
*/
float m_Width;
/**
* Scrollable with the wheel.
*/
bool m_ScrollWheel;
/**
* How much (in percent, 0.1f = 10%) to scroll each time
* the wheel is admitted, or the buttons are pressed.
*/
float m_ScrollSpeed;
/**
* Whether or not the edge buttons should appear or not. Sometimes
* you actually don't want them, like perhaps in a combo box.
*/
bool m_ScrollButtons;
/**
* Sometimes there is *a lot* to scroll, but to prevent the scroll "bar"
* from being almost invisible (or ugly), you can set a minimum in pixel
* size.
*/
float m_MinimumBarSize;
/**
* Sometimes you would like your scroll bar to have a fixed maximum size
* so that the texture does not get too stretched, you can set a maximum
* in pixels.
*/
float m_MaximumBarSize;
/**
* True if you want edge buttons, i.e. buttons that can be pressed in order
* to scroll.
*/
bool m_UseEdgeButtons;
//@}
//--------------------------------------------------------
/** @name Vertical Sprites */
//--------------------------------------------------------
//@{
CGUISpriteInstance m_SpriteButtonTop;
CGUISpriteInstance m_SpriteButtonTopPressed;
CGUISpriteInstance m_SpriteButtonTopDisabled;
CGUISpriteInstance m_SpriteButtonTopOver;
CGUISpriteInstance m_SpriteButtonBottom;
CGUISpriteInstance m_SpriteButtonBottomPressed;
CGUISpriteInstance m_SpriteButtonBottomDisabled;
CGUISpriteInstance m_SpriteButtonBottomOver;
CGUISpriteInstance m_SpriteBarVertical;
CGUISpriteInstance m_SpriteBarVerticalOver;
CGUISpriteInstance m_SpriteBarVerticalPressed;
CGUISpriteInstance m_SpriteBackVertical;
//@}
//--------------------------------------------------------
/** @name Horizontal Sprites */
//--------------------------------------------------------
//@{
CGUISpriteInstance m_SpriteButtonLeft;
CGUISpriteInstance m_SpriteButtonLeftPressed;
CGUISpriteInstance m_SpriteButtonLeftDisabled;
CGUISpriteInstance m_SpriteButtonRight;
CGUISpriteInstance m_SpriteButtonRightPressed;
CGUISpriteInstance m_SpriteButtonRightDisabled;
CGUISpriteInstance m_SpriteBackHorizontal;
CGUISpriteInstance m_SpriteBarHorizontal;
//@}
};
/**
* The GUI Scroll-bar, used everywhere there is a scroll-bar in the game.
*
* To include a scroll-bar to an object, inherent the object from
* IGUIScrollBarOwner and call AddScrollBar() to add the scroll-bars.
*
* 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.
*
* 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.
*/
class IGUIScrollBar
{
public:
IGUIScrollBar();
virtual ~IGUIScrollBar();
public:
/**
* Draw the scroll-bar
*/
virtual void Draw() = 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
* itself.
*
* @see IGUIObject#HandleMessage()
*/
virtual void HandleMessage(SGUIMessage& Message) = 0;
/**
* Set m_Pos with g_mouse_x/y input, i.e. when draggin.
*/
virtual void SetPosFromMousePos(const CPos& mouse) = 0;
/**
* Hovering the scroll minus button
*
* @param mouse current mouse position
* @return True if mouse positions are hovering the button
*/
virtual bool HoveringButtonMinus(const CPos& UNUSED(mouse)) { return false; }
/**
* Hovering the scroll plus button
*
* @param mouse current mouse position
* @return True if mouse positions are hovering the button
*/
virtual bool HoveringButtonPlus(const CPos& UNUSED(mouse)) { return false; }
/**
* Get scroll-position
*/
float GetPos() const { return m_Pos; }
/**
* Set scroll-position by hand
*/
virtual void SetPos(float f) { m_Pos = f; UpdatePosBoundaries(); }
/**
* Get the value of m_Pos that corresponds to the bottom of the scrollable region
*/
float GetMaxPos() const { return std::max(1.f, m_ScrollRange - m_ScrollSpace); }
/**
+ * Get the value of m_Pos that corresponds to the bottom of the scrollable region
+ */
+ float IsVisible() const { return GetMaxPos() != 1.f; }
+
+ /**
* Increase scroll one step
*/
virtual void ScrollPlus() { m_Pos += 30.f; UpdatePosBoundaries(); }
/**
* Decrease scroll one step
*/
virtual void ScrollMinus() { m_Pos -= 30.f; UpdatePosBoundaries(); }
/**
* Increase scroll three steps
*/
virtual void ScrollPlusPlenty() { m_Pos += 90.f; UpdatePosBoundaries(); }
/**
* Decrease scroll three steps
*/
virtual void ScrollMinusPlenty() { m_Pos -= 90.f; UpdatePosBoundaries(); }
/**
* Set host object, must be done almost at creation of scroll bar.
* @param pOwner Pointer to host object.
*/
void SetHostObject(IGUIScrollBarOwner* pOwner) { m_pHostObject = pOwner; }
/**
* Get GUI pointer
* @return CGUI pointer
*/
CGUI* GetGUI() const;
/**
* Set GUI pointer
* @param pGUI pointer to CGUI object.
*/
void SetGUI(CGUI* pGUI) { m_pGUI = pGUI; }
/**
* Set Width
* @param width Width
*/
void SetWidth(float width) { m_Width = width; }
/**
* Set X Position
* @param x Position in this axis
*/
void SetX(float x) { m_X = x; }
/**
* Set Y Position
* @param y Position in this axis
*/
void SetY(float y) { m_Y = y; }
/**
* Set Z Position
* @param z Position in this axis
*/
void SetZ(float z) { m_Z = z; }
/**
* Set Length of scroll bar
* @param length Length
*/
void SetLength(float length) { m_Length = length; }
/**
* Set content length
* @param range Maximum scrollable range
*/
void SetScrollRange(float range) { m_ScrollRange = std::max(range, 1.f); SetupBarSize(); UpdatePosBoundaries(); }
/**
* Set space that is visible in the scrollable control.
* @param space Visible area in the scrollable control.
*/
void SetScrollSpace(float space) { m_ScrollSpace = space; SetupBarSize(); UpdatePosBoundaries(); }
/**
* Set bar pressed
* @param b True if bar is pressed
*/
void SetBarPressed(bool b) { m_BarPressed = b; }
/**
* Set Scroll bar style string
* @param style String with scroll bar style reference name
*/
void SetScrollBarStyle(const CStr& style) { m_ScrollBarStyle = style; }
/**
* Get style used by the scrollbar
* @return Scroll bar style struct.
*/
const SGUIScrollBarStyle* GetStyle() const;
/**
* Get the rectangle of the actual BAR. not the whole scroll-bar.
* @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.
* @return Rectangle, CRect
*/
virtual CRect GetOuterRect() const = 0;
protected:
/**
* Sets up bar size
*/
void SetupBarSize();
/**
* Call every time m_Pos has been updated.
*/
void UpdatePosBoundaries();
protected:
//@}
//--------------------------------------------------------
/** @name Settings */
//--------------------------------------------------------
//@{
/**
* Width of the scroll bar
*/
float m_Width;
/**
* Absolute X Position
*/
float m_X;
/**
* Absolute Y Position
*/
float m_Y;
/**
* Absolute Z Position
*/
float m_Z;
/**
* Total length of scrollbar, including edge buttons.
*/
float m_Length;
/**
* Content that can be scrolled, in pixels
*/
float m_ScrollRange;
/**
* Content that can be viewed at a time, in pixels
*/
float m_ScrollSpace;
/**
* Use input from the scroll-wheel? True or false.
*/
float m_BarSize;
/**
* Scroll bar style reference name
*/
CStr m_ScrollBarStyle;
/**
* Pointer to scroll bar style used.
*/
SGUIScrollBarStyle *m_pStyle;
/**
* Host object, prerequisite!
*/
IGUIScrollBarOwner *m_pHostObject;
/**
* Reference to CGUI object, these cannot work stand-alone
*/
CGUI *m_pGUI;
/**
* Mouse position when bar was pressed
*/
CPos m_BarPressedAtPos;
//@}
//--------------------------------------------------------
/** @name States */
//--------------------------------------------------------
//@{
/**
* If the bar is currently being pressed and dragged.
*/
bool m_BarPressed;
/**
* Bar being hovered or not
*/
bool m_BarHovered;
/**
* Scroll buttons hovered
*/
bool m_ButtonMinusHovered, m_ButtonPlusHovered;
/**
* Scroll buttons pressed
*/
bool m_ButtonMinusPressed, m_ButtonPlusPressed;
/**
* Position of scroll bar, 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.
*/
float m_Pos;
/**
* Position from 0.f to 1.f it had when the bar was pressed.
*/
float m_PosWhenPressed;
//@}
};
#endif // INCLUDED_IGUISCROLLBAR