openclonk/src/gui/C4GuiMenu.cpp

665 lines
19 KiB
C++
Raw Normal View History

2009-05-08 13:28:41 +00:00
/*
* OpenClonk, http://www.openclonk.org
*
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
2016-04-03 18:18:29 +00:00
* Copyright (c) 2009-2016, The OpenClonk Team and contributors
2009-05-08 13:28:41 +00:00
*
* Distributed under the terms of the ISC license; see accompanying file
* "COPYING" for details.
2009-05-08 13:28:41 +00:00
*
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
* See accompanying file "TRADEMARK" for details.
2009-05-08 13:28:41 +00:00
*
* To redistribute this file separately, substitute the full license texts
* for the above references.
2009-05-08 13:28:41 +00:00
*/
// generic user interface
// context menu
#include "C4Include.h"
#include "gui/C4Gui.h"
#include "graphics/C4Draw.h"
#include "graphics/C4FacetEx.h"
#include "graphics/C4GraphicsResource.h"
#include "gui/C4MouseControl.h"
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
namespace C4GUI
{
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
int32_t ContextMenu::iGlobalMenuIndex = 0;
2009-05-08 13:28:41 +00:00
// ----------------------------------------------------
// ContextMenu::Entry
2010-03-28 18:58:01 +00:00
void ContextMenu::Entry::DrawElement(C4TargetFacet &cgo)
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// icon
if (icoIcon > Ico_None)
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// get icon counts
int32_t iXMax, iYMax;
::GraphicsResource.fctIcons.GetPhaseNum(iXMax, iYMax);
2010-03-28 18:58:01 +00:00
if (!iXMax)
iXMax = 6;
// load icon
const C4Facet &rfctIcon = ::GraphicsResource.fctIcons.GetPhase(icoIcon % iXMax, icoIcon / iXMax);
2010-03-28 18:58:01 +00:00
rfctIcon.DrawX(cgo.Surface, rcBounds.x + cgo.TargetX, rcBounds.y + cgo.TargetY, rcBounds.Hgt, rcBounds.Hgt);
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
// print out label
if (!!sText)
2011-10-03 14:30:18 +00:00
pDraw->TextOut(sText.getData(), ::GraphicsResource.TextFont, 1.0f, cgo.Surface, cgo.TargetX+rcBounds.x+GetIconIndent(), rcBounds.y + cgo.TargetY, C4GUI_ContextFontClr, ALeft);
2010-03-28 18:58:01 +00:00
// submenu arrow
if (pSubmenuHandler)
2009-05-08 13:28:41 +00:00
{
C4Facet &rSubFct = ::GraphicsResource.fctSubmenu;
2010-03-28 18:58:01 +00:00
rSubFct.Draw(cgo.Surface, cgo.TargetX+rcBounds.x+rcBounds.Wdt - rSubFct.Wdt, cgo.TargetY+rcBounds.y+(rcBounds.Hgt - rSubFct.Hgt)/2);
2009-05-08 13:28:41 +00:00
}
}
2010-03-28 18:58:01 +00:00
ContextMenu::Entry::Entry(const char *szText, Icons icoIcon, MenuHandler *pMenuHandler, ContextHandler *pSubmenuHandler)
: Element(), cHotkey(0), icoIcon(icoIcon), pMenuHandler(pMenuHandler), pSubmenuHandler(pSubmenuHandler)
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// set text with hotkey
if (szText)
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
sText.Copy(szText);
ExpandHotkeyMarkup(sText, cHotkey);
// adjust size
::GraphicsResource.TextFont.GetTextExtent(sText.getData(), rcBounds.Wdt, rcBounds.Hgt, true);
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
else
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
rcBounds.Wdt = 40;
rcBounds.Hgt = ::GraphicsResource.TextFont.GetLineHeight();
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
// regard icon
rcBounds.Wdt += GetIconIndent();
// submenu arrow
if (pSubmenuHandler) rcBounds.Wdt += ::GraphicsResource.fctSubmenu.Wdt+2;
2009-05-08 13:28:41 +00:00
}
// ----------------------------------------------------
// ContextMenu
ContextMenu::ContextMenu() : Window()
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
iMenuIndex = ++iGlobalMenuIndex;
// set min size
rcBounds.Wdt=40; rcBounds.Hgt=7;
// key bindings
C4CustomKey::CodeList Keys;
Keys.emplace_back(K_UP);
2010-03-28 18:58:01 +00:00
if (Config.Controls.GamepadGuiControl)
2009-05-08 13:28:41 +00:00
{
ControllerKeys::Up(Keys);
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
pKeySelUp = new C4KeyBinding(Keys, "GUIContextSelUp", KEYSCOPE_Gui,
new C4KeyCB<ContextMenu>(*this, &ContextMenu::KeySelUp), C4CustomKey::PRIO_Context);
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
Keys.clear();
Keys.emplace_back(K_DOWN);
2010-03-28 18:58:01 +00:00
if (Config.Controls.GamepadGuiControl)
2009-05-08 13:28:41 +00:00
{
ControllerKeys::Down(Keys);
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
pKeySelDown = new C4KeyBinding(Keys, "GUIContextSelDown", KEYSCOPE_Gui,
new C4KeyCB<ContextMenu>(*this, &ContextMenu::KeySelDown), C4CustomKey::PRIO_Context);
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
Keys.clear();
Keys.emplace_back(K_RIGHT);
2010-03-28 18:58:01 +00:00
if (Config.Controls.GamepadGuiControl)
2009-05-08 13:28:41 +00:00
{
ControllerKeys::Right(Keys);
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
pKeySubmenu = new C4KeyBinding(Keys, "GUIContextSubmenu", KEYSCOPE_Gui,
new C4KeyCB<ContextMenu>(*this, &ContextMenu::KeySubmenu), C4CustomKey::PRIO_Context);
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
Keys.clear();
Keys.emplace_back(K_LEFT);
2010-03-28 18:58:01 +00:00
if (Config.Controls.GamepadGuiControl)
2009-05-08 13:28:41 +00:00
{
ControllerKeys::Left(Keys);
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
pKeyBack = new C4KeyBinding(Keys, "GUIContextBack", KEYSCOPE_Gui,
new C4KeyCB<ContextMenu>(*this, &ContextMenu::KeyBack), C4CustomKey::PRIO_Context);
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
Keys.clear();
Keys.emplace_back(K_ESCAPE);
2010-03-28 18:58:01 +00:00
if (Config.Controls.GamepadGuiControl)
2009-05-08 13:28:41 +00:00
{
ControllerKeys::Cancel(Keys);
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
pKeyAbort = new C4KeyBinding(Keys, "GUIContextAbort", KEYSCOPE_Gui,
new C4KeyCB<ContextMenu>(*this, &ContextMenu::KeyAbort), C4CustomKey::PRIO_Context);
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
Keys.clear();
Keys.emplace_back(K_RETURN);
2010-03-28 18:58:01 +00:00
if (Config.Controls.GamepadGuiControl)
2009-05-08 13:28:41 +00:00
{
ControllerKeys::Ok(Keys);
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
pKeyConfirm = new C4KeyBinding(Keys, "GUIContextConfirm", KEYSCOPE_Gui,
new C4KeyCB<ContextMenu>(*this, &ContextMenu::KeyConfirm), C4CustomKey::PRIO_Context);
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
pKeyHotkey = new C4KeyBinding(C4KeyCodeEx(KEY_Any), "GUIContextHotkey", KEYSCOPE_Gui,
new C4KeyCBPassKey<ContextMenu>(*this, &ContextMenu::KeyHotkey), C4CustomKey::PRIO_Context);
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
ContextMenu::~ContextMenu()
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// del any submenu
if (pSubmenu) { delete pSubmenu; pSubmenu=nullptr; }
2010-03-28 18:58:01 +00:00
// forward RemoveElement to screen
Screen *pScreen = GetScreen();
if (pScreen) pScreen->RemoveElement(this);
// clear key bindings
delete pKeySelUp;
delete pKeySelDown;
delete pKeySubmenu;
delete pKeyBack;
delete pKeyAbort;
delete pKeyConfirm;
delete pKeyHotkey;
// clear children to get appropriate callbacks
Clear();
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
void ContextMenu::Abort(bool fByUser)
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// effect
if (fByUser) GUISound("UI::Close");
2010-03-28 18:58:01 +00:00
// simply del menu: dtor will remove itself
delete this;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
void ContextMenu::DrawElement(C4TargetFacet &cgo)
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// draw context menu bg
2011-10-03 14:30:18 +00:00
pDraw->DrawBoxDw(cgo.Surface, rcBounds.x+cgo.TargetX, rcBounds.y+cgo.TargetY,
2010-03-28 18:58:01 +00:00
rcBounds.x+rcBounds.Wdt+cgo.TargetX-1, rcBounds.y+rcBounds.Hgt+cgo.TargetY-1,
C4GUI_ContextBGColor);
// context bg: mark selected item
if (pSelectedItem)
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// get marked item bounds
C4Rect rcSelArea = pSelectedItem->GetBounds();
// do indent
rcSelArea.x += GetClientRect().x;
rcSelArea.y += GetClientRect().y;
// draw
2011-10-03 14:30:18 +00:00
pDraw->DrawBoxDw(cgo.Surface, rcSelArea.x+cgo.TargetX, rcSelArea.y+cgo.TargetY,
2010-03-28 18:58:01 +00:00
rcSelArea.x+rcSelArea.Wdt+cgo.TargetX-1, rcSelArea.y+rcSelArea.Hgt+cgo.TargetY-1,
C4GUI_ContextSelColor);
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
// draw frame
Draw3DFrame(cgo);
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
void ContextMenu::MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam)
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// inherited
Window::MouseInput(rMouse, iButton, iX, iY, dwKeyParam);
// mouse is in client area?
if (GetClientRect().Contains(iX+rcBounds.x, iY+rcBounds.y))
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// reset selection
Element *pPrevSelectedItem = pSelectedItem;
pSelectedItem = nullptr;
2010-03-28 18:58:01 +00:00
// get client component the mouse is over
iX -= GetMarginLeft(); iY -= GetMarginTop();
for (Element *pCurr = GetFirst(); pCurr; pCurr = pCurr->GetNext())
if (pCurr->GetBounds().Contains(iX, iY))
pSelectedItem = pCurr;
// selection change sound
if (pSelectedItem != pPrevSelectedItem)
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
SelectionChanged(true);
// selection by mouse: Check whether submenu can be opened
CheckOpenSubmenu();
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
// check mouse click
if (iButton == C4MC_Button_LeftDown)
{ DoOK(); return; }
2009-05-08 13:28:41 +00:00
}
}
2010-03-28 18:58:01 +00:00
void ContextMenu::MouseLeaveEntry(CMouse &rMouse, Entry *pOldEntry)
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// no submenu open? then deselect any selected item
if (pOldEntry==pSelectedItem && !pSubmenu)
2009-05-08 13:28:41 +00:00
{
pSelectedItem = nullptr;
2010-03-28 18:58:01 +00:00
SelectionChanged(true);
2009-05-08 13:28:41 +00:00
}
}
2010-03-28 18:58:01 +00:00
bool ContextMenu::KeySelUp()
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// not if focus is in submenu
if (pSubmenu) return false;
Element *pPrevSelectedItem = pSelectedItem;
// select prev
if (pSelectedItem) pSelectedItem = pSelectedItem->GetPrev();
// nothing selected or beginning reached: cycle
if (!pSelectedItem) pSelectedItem = GetLastContained();
// selection might have changed
if (pSelectedItem != pPrevSelectedItem) SelectionChanged(true);
return true;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
bool ContextMenu::KeySelDown()
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// not if focus is in submenu
if (pSubmenu) return false;
Element *pPrevSelectedItem = pSelectedItem;
// select next
if (pSelectedItem) pSelectedItem = pSelectedItem->GetNext();
// nothing selected or end reached: cycle
if (!pSelectedItem) pSelectedItem = GetFirstContained();
// selection might have changed
if (pSelectedItem != pPrevSelectedItem) SelectionChanged(true);
return true;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
bool ContextMenu::KeySubmenu()
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// not if focus is in submenu
if (pSubmenu) return false;
CheckOpenSubmenu();
return true;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
bool ContextMenu::KeyBack()
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// not if focus is in submenu
if (pSubmenu) return false;
// close submenu on keyboard input
if (IsSubmenu()) { Abort(true); return true; }
return false;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
bool ContextMenu::KeyAbort()
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// not if focus is in submenu
if (pSubmenu) return false;
Abort(true);
return true;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
bool ContextMenu::KeyConfirm()
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// not if focus is in submenu
if (pSubmenu) return false;
CheckOpenSubmenu();
DoOK();
return true;
2009-05-08 13:28:41 +00:00
}
2009-05-28 00:14:54 +00:00
2010-03-28 18:58:01 +00:00
bool ContextMenu::KeyHotkey(const C4KeyCodeEx &key)
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// not if focus is in submenu
if (pSubmenu) return false;
Element *pPrevSelectedItem = pSelectedItem;
StdStrBuf sKey = C4KeyCodeEx::KeyCode2String(key.Key, true, true);
// do hotkey procs for standard alphanumerics only
if (sKey.getLength() != 1) return false;
WORD wKey = WORD(*sKey.getData());
2010-04-28 21:43:25 +00:00
if (Inside<C4KeyCode, C4KeyCode, C4KeyCode>(wKey, 'A', 'Z') || Inside<C4KeyCode, C4KeyCode, C4KeyCode>(wKey, '0', '9'))
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// process hotkeys
uint32_t ch = wKey;
2010-03-28 18:58:01 +00:00
for (Element *pCurr = GetFirst(); pCurr; pCurr = pCurr->GetNext())
if (pCurr->OnHotkey(ch))
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
pSelectedItem = pCurr;
if (pSelectedItem != pPrevSelectedItem) SelectionChanged(true);
CheckOpenSubmenu();
DoOK();
return true;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
return false;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
// unrecognized hotkey
return false;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
void ContextMenu::UpdateElementPositions()
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// first item at zero offset
Element *pCurr = GetFirst();
if (!pCurr) return;
pCurr->GetBounds().y = 0;
int32_t iMinWdt = std::max<int32_t>(20, pCurr->GetBounds().Wdt);;
2010-03-28 18:58:01 +00:00
int32_t iOverallHgt = pCurr->GetBounds().Hgt;
// others stacked under it
while ((pCurr = pCurr->GetNext()))
2009-05-08 13:28:41 +00:00
{
iMinWdt = std::max(iMinWdt, pCurr->GetBounds().Wdt);
2010-03-28 18:58:01 +00:00
int32_t iYSpace = pCurr->GetListItemTopSpacing();
int32_t iNewY = iOverallHgt + iYSpace;
iOverallHgt += pCurr->GetBounds().Hgt + iYSpace;
if (iNewY != pCurr->GetBounds().y)
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
pCurr->GetBounds().y = iNewY;
pCurr->UpdateOwnPos();
2009-05-08 13:28:41 +00:00
}
}
2010-03-28 18:58:01 +00:00
// don't make smaller
iMinWdt = std::max(iMinWdt, rcBounds.Wdt - GetMarginLeft() - GetMarginRight());
2010-03-28 18:58:01 +00:00
// all entries same size
for (pCurr = GetFirst(); pCurr; pCurr = pCurr->GetNext())
if (pCurr->GetBounds().Wdt != iMinWdt)
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
pCurr->GetBounds().Wdt = iMinWdt;
pCurr->UpdateOwnPos();
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
// update own size
rcBounds.Wdt = iMinWdt + GetMarginLeft() + GetMarginRight();
rcBounds.Hgt = std::max<int32_t>(iOverallHgt, 8) + GetMarginTop() + GetMarginBottom();
2010-03-28 18:58:01 +00:00
UpdateSize();
}
void ContextMenu::RemoveElement(Element *pChild)
{
// inherited
Window::RemoveElement(pChild);
// target lost?
if (pChild == pTarget) { Abort(false); return; }
// submenu?
if (pChild == pSubmenu) pSubmenu = nullptr;
2010-03-28 18:58:01 +00:00
// clear selection var
if (pChild == pSelectedItem)
2009-05-08 13:28:41 +00:00
{
pSelectedItem = nullptr;
2010-03-28 18:58:01 +00:00
SelectionChanged(false);
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
// forward to any submenu
if (pSubmenu) pSubmenu->RemoveElement(pChild);
// forward to mouse
if (GetScreen())
GetScreen()->Mouse.RemoveElement(pChild);
// update positions
UpdateElementPositions();
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
bool ContextMenu::AddElement(Element *pChild)
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// add it
Window::AddElement(pChild);
// update own size and positions
UpdateElementPositions();
// success
return true;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
bool ContextMenu::InsertElement(Element *pChild, Element *pInsertBefore)
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// insert it
Window::InsertElement(pChild, pInsertBefore);
// update own size and positions
UpdateElementPositions();
// success
return true;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
void ContextMenu::ElementSizeChanged(Element *pOfElement)
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// inherited
Window::ElementSizeChanged(pOfElement);
// update positions of all list items
UpdateElementPositions();
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
void ContextMenu::ElementPosChanged(Element *pOfElement)
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// inherited
Window::ElementSizeChanged(pOfElement);
// update positions of all list items
UpdateElementPositions();
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
void ContextMenu::SelectionChanged(bool fByUser)
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// any selection?
if (pSelectedItem)
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// effect
if (fByUser) GUISound("UI::Select");
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
// close any submenu from prev selection
if (pSubmenu) pSubmenu->Abort(true);
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
Screen *ContextMenu::GetScreen()
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// context menus don't have a parent; get screen by static var
return Screen::GetScreenS();
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
bool ContextMenu::CtxMouseInput(CMouse &rMouse, int32_t iButton, int32_t iScreenX, int32_t iScreenY, DWORD dwKeyParam)
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// check submenu
if (pSubmenu)
if (pSubmenu->CtxMouseInput(rMouse, iButton, iScreenX, iScreenY, dwKeyParam)) return true;
// check bounds
if (!rcBounds.Contains(iScreenX, iScreenY)) return false;
// inside menu: do input in local coordinates
MouseInput(rMouse, iButton, iScreenX - rcBounds.x, iScreenY - rcBounds.y, dwKeyParam);
return true;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
bool ContextMenu::CharIn(const char * c)
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// forward to submenu
if (pSubmenu) return pSubmenu->CharIn(c);
return false;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
void ContextMenu::Draw(C4TargetFacet &cgo)
{
// In editor mode, the surface is not assigned
// The menu is drawn directly by the dialogue, so just exit here.
if (!cgo.Surface) return;
2010-03-28 18:58:01 +00:00
// draw self
Window::Draw(cgo);
// draw submenus on top
if (pSubmenu) pSubmenu->Draw(cgo);
}
void ContextMenu::Open(Element *pTarget, int32_t iScreenX, int32_t iScreenY)
{
// set pos
rcBounds.x = iScreenX; rcBounds.y = iScreenY;
UpdatePos();
// set target
this->pTarget = pTarget;
// effect :)
GUISound("UI::Open");
2010-03-28 18:58:01 +00:00
// done
}
void ContextMenu::CheckOpenSubmenu()
{
// safety
if (!GetScreen()) return;
// anything selected?
if (!pSelectedItem) return;
// get as entry
Entry *pSelEntry = (Entry *) pSelectedItem;
// has submenu handler?
ContextHandler *pSubmenuHandler = pSelEntry->pSubmenuHandler;
if (!pSubmenuHandler) return;
// create submenu then
if (pSubmenu) pSubmenu->Abort(false);
pSubmenu = pSubmenuHandler->OnSubcontext(pTarget);
// get open pos
int32_t iX = GetClientRect().x + pSelEntry->GetBounds().x + pSelEntry->GetBounds().Wdt;
int32_t iY = GetClientRect().y + pSelEntry->GetBounds().y + pSelEntry->GetBounds().Hgt/2;
int32_t iScreenWdt = GetScreen()->GetBounds().Wdt, iScreenHgt = GetScreen()->GetBounds().Hgt;
if (iY + pSubmenu->GetBounds().Hgt >= iScreenHgt)
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// bottom too narrow: open to top, if height is sufficient
// otherwise, open to top from bottom screen pos
if (iY < pSubmenu->GetBounds().Hgt) iY = iScreenHgt;
iY -= pSubmenu->GetBounds().Hgt;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
if (iX + pSubmenu->GetBounds().Wdt >= iScreenWdt)
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// right too narrow: try opening left of this menu
// otherwise, open to left from right screen border
if (GetClientRect().x < pSubmenu->GetBounds().Wdt)
iX = iScreenWdt;
else
iX = GetClientRect().x;
iX -= pSubmenu->GetBounds().Wdt;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
// open it
pSubmenu->Open(pTarget, iX, iY);
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
bool ContextMenu::IsSubmenu()
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
return GetScreen() && GetScreen()->pContext!=this;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
void ContextMenu::DoOK()
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// safety
if (!GetScreen()) return;
// anything selected?
if (!pSelectedItem) return;
// get as entry
Entry *pSelEntry = (Entry *) pSelectedItem;
// get CB; take over pointer
MenuHandler *pCallback = pSelEntry->GetAndZeroCallback();
Element *pTarget = this->pTarget;
if (!pCallback) return;
// close all menus (deletes this class!) w/o sound
GetScreen()->AbortContext(false);
// sound
GUISound("UI::Click");
2010-03-28 18:58:01 +00:00
// do CB
pCallback->OnOK(pTarget);
// free CB class
delete pCallback;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
void ContextMenu::SelectItem(int32_t iIndex)
2009-05-08 13:28:41 +00:00
{
// get item to be selected (may be nullptr on purpose!)
2010-03-28 18:58:01 +00:00
Element *pNewSelElement = GetElementByIndex(iIndex);
if (pNewSelElement != pSelectedItem) return;
// set new
pSelectedItem = pNewSelElement;
SelectionChanged(false);
2009-05-08 13:28:41 +00:00
}
// ----------------------------------------------------
// ContextButton
2010-03-28 18:58:01 +00:00
ContextButton::ContextButton(C4Rect &rtBounds) : Control(rtBounds), iOpenMenu(0), fMouseOver(false)
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
RegisterContextKey();
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
ContextButton::ContextButton(Element *pForEl, bool fAdd, int32_t iHIndent, int32_t iVIndent)
: Control(C4Rect(0,0,0,0)), iOpenMenu(0), fMouseOver(false)
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
SetBounds(pForEl->GetToprightCornerRect(16, 16, iHIndent, iVIndent));
// copy context handler
SetContextHandler(pForEl->GetContextHandler());
// add if desired
Container *pCont;
if (fAdd) if ((pCont = pForEl->GetContainer()))
pCont->AddElement(this);
RegisterContextKey();
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
ContextButton::~ContextButton()
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
delete pKeyContext;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
void ContextButton::RegisterContextKey()
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// reg keys for pressing the context button
C4CustomKey::CodeList ContextKeys;
ContextKeys.emplace_back(K_RIGHT);
ContextKeys.emplace_back(K_DOWN);
ContextKeys.emplace_back(K_SPACE);
ContextKeys.emplace_back(K_RIGHT, KEYS_Alt);
ContextKeys.emplace_back(K_DOWN, KEYS_Alt);
ContextKeys.emplace_back(K_SPACE, KEYS_Alt);
2010-03-28 18:58:01 +00:00
pKeyContext = new C4KeyBinding(ContextKeys, "GUIContextButtonPress", KEYSCOPE_Gui,
new ControlKeyCB<ContextButton>(*this, &ContextButton::KeyContext), C4CustomKey::PRIO_Ctrl);
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
bool ContextButton::DoContext(int32_t iX, int32_t iY)
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// get context pos
if (iX<0)
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
iX = rcBounds.Wdt/2;
iY = rcBounds.Hgt/2;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
// do context
ContextHandler *pCtx = GetContextHandler();
if (!pCtx) return false;
if (!pCtx->OnContext(this, iX, iY)) return false;
// store menu
Screen *pScr = GetScreen();
if (!pScr) return false;
iOpenMenu = pScr->GetContextMenuIndex();
// return whether all was successful
return !!iOpenMenu;
}
void ContextButton::DrawElement(C4TargetFacet &cgo)
{
// recheck open menu
Screen *pScr = GetScreen();
if (!pScr || (iOpenMenu != pScr->GetContextMenuIndex())) iOpenMenu = 0;
// calc drawing bounds
int32_t x0 = cgo.TargetX + rcBounds.x, y0 = cgo.TargetY + rcBounds.y;
// draw button; down (phase 1) if a menu is open
::GraphicsResource.fctContext.Draw(cgo.Surface, x0, y0, iOpenMenu ? 1 : 0);
2010-03-28 18:58:01 +00:00
// draw selection highlight
if (HasDrawFocus() || (fMouseOver && IsInActiveDlg(false)) || iOpenMenu)
2009-05-08 13:28:41 +00:00
{
2011-10-03 14:30:18 +00:00
pDraw->SetBlitMode(C4GFXBLIT_ADDITIVE);
::GraphicsResource.fctButtonHighlight.DrawX(cgo.Surface, x0, y0, rcBounds.Wdt, rcBounds.Hgt);
2011-10-03 14:30:18 +00:00
pDraw->ResetBlitMode();
2009-05-08 13:28:41 +00:00
}
}
2010-03-28 18:58:01 +00:00
void ContextButton::MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam)
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// left-click activates menu
if ((iButton == C4MC_Button_LeftDown) || (iButton == C4MC_Button_RightDown))
if (DoContext()) return;
// inherited
Control::MouseInput(rMouse, iButton, iX, iY, dwKeyParam);
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
void ContextButton::MouseEnter(CMouse &rMouse)
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
Control::MouseEnter(rMouse);
// remember mouse state for button highlight
fMouseOver = true;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
void ContextButton::MouseLeave(CMouse &rMouse)
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
Control::MouseLeave(rMouse);
// mouse left
fMouseOver = false;
2009-05-08 13:28:41 +00:00
}
2010-01-25 04:00:59 +00:00
} // end of namespace
2009-05-08 13:28:41 +00:00