1
1
forked from 0ad/0ad
0ad/source/gui/CDropDown.cpp
2011-07-17 17:30:07 +00:00

371 lines
9.3 KiB
C++

/* Copyright (C) 2010 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/>.
*/
/*
CDropDown
*/
#include "precompiled.h"
#include "GUI.h"
#include "CDropDown.h"
#include "lib/ogl.h"
#include "lib/external_libraries/sdl.h"
//-------------------------------------------------------------------
// Constructor / Destructor
//-------------------------------------------------------------------
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_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<bool>::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)
{
CPos mouse = GetMousePos();
if (GetListRect().PointInside(mouse))
{
bool scrollbar;
CGUIList *pList;
GUI<bool>::GetSetting(this, "scrollbar", scrollbar);
GUI<CGUIList>::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))
{
set = i;
}
}
if (set != -1)
{
//GUI<int>::SetSetting(this, "selected", set);
m_ElementHighlight = set;
//UpdateAutoScroll();
}
}
}
break;
}
case GUIM_MOUSE_LEAVE:
{
GUI<int>::GetSetting(this, "selected", m_ElementHighlight);
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<bool>::GetSetting(this, "enabled", enabled);
if (!enabled)
break;
if (!m_Open)
{
m_Open = true;
GetScrollBar(0).SetPos(0.f);
GetScrollBar(0).SetZ(GetBufferedZ());
GUI<int>::GetSetting(this, "selected", m_ElementHighlight);
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());
return; // overshadow
}
if (!(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_LOST_FOCUS:
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);
}
InReaction CDropDown::ManuallyHandleEvent(const SDL_Event_* ev)
{
int szChar = ev->ev.key.keysym.sym;
bool update_highlight = false;
switch (szChar)
{
case '\r':
m_Open=false;
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<int>::SetSetting(this, "selected", m_ElementHighlight);
update_highlight = true;
break;
default:
break;
}
CList::ManuallyHandleEvent(ev);
if (update_highlight)
GUI<int>::GetSetting(this, "selected", m_ElementHighlight);
return IN_HANDLED;
}
void CDropDown::SetupListRect()
{
float size, buffer, button_width;
GUI<float>::GetSetting(this, "dropdown_size", size);
GUI<float>::GetSetting(this, "dropdown_buffer", buffer);
GUI<float>::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<float>::GetSetting(this, "dropdown_size", dropdown_size);
GUI<float>::GetSetting(this, "button_width", button_width);
CGUISpriteInstance *sprite, *sprite2, *sprite2_second;
int cell_id, selected=0;
CColor color;
GUI<CGUISpriteInstance>::GetSettingPointer(this, "sprite", sprite);
GUI<CGUISpriteInstance>::GetSettingPointer(this, "sprite2", sprite2);
GUI<int>::GetSetting(this, "cell_id", cell_id);
GUI<int>::GetSetting(this, "selected", selected);
GUI<CColor>::GetSetting(this, "textcolor", color);
bool enabled;
GUI<bool>::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<CGUISpriteInstance>::GetSettingPointer(this, "sprite2_disabled", sprite2_second);
GetGUI()->DrawSprite(GUI<>::FallBackSprite(*sprite2_second, *sprite2), cell_id, bz+0.05f, rect);
}
else
if (m_Open)
{
GUI<CGUISpriteInstance>::GetSettingPointer(this, "sprite2_pressed", sprite2_second);
GetGUI()->DrawSprite(GUI<>::FallBackSprite(*sprite2_second, *sprite2), cell_id, bz+0.05f, rect);
}
else
if (m_MouseHovering)
{
GUI<CGUISpriteInstance>::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, old;
GUI<bool>::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;
}