Index: ps/trunk/source/gui/CDropDown.cpp =================================================================== --- ps/trunk/source/gui/CDropDown.cpp (revision 12318) +++ ps/trunk/source/gui/CDropDown.cpp (revision 12319) @@ -1,375 +1,377 @@ /* Copyright (C) 2012 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 . */ /* CDropDown */ #include "precompiled.h" #include "GUI.h" #include "CDropDown.h" #include "lib/ogl.h" #include "lib/external_libraries/libsdl.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::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::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)) { set = i; } } if (set != -1) { //GUI::SetSetting(this, "selected", set); m_ElementHighlight = set; //UpdateAutoScroll(); } } } break; } case GUIM_MOUSE_LEAVE: { GUI::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::GetSetting(this, "enabled", enabled); if (!enabled) break; if (!m_Open) { m_Open = true; - GetScrollBar(0).SetPos(0.f); 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] ); 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); // 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) { 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::SetSetting(this, "selected", m_ElementHighlight); update_highlight = true; break; default: break; } CList::ManuallyHandleEvent(ev); if (update_highlight) GUI::GetSetting(this, "selected", m_ElementHighlight); return IN_HANDLED; } 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, *sprite2, *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, 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; }