forked from Mirrors/openclonk
251 lines
7.6 KiB
C++
251 lines
7.6 KiB
C++
/*
|
|
* OpenClonk, http://www.openclonk.org
|
|
*
|
|
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
|
|
* Copyright (c) 2009-2016, The OpenClonk Team and contributors
|
|
*
|
|
* Distributed under the terms of the ISC license; see accompanying file
|
|
* "COPYING" for details.
|
|
*
|
|
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
|
|
* See accompanying file "TRADEMARK" for details.
|
|
*
|
|
* To redistribute this file separately, substitute the full license texts
|
|
* for the above references.
|
|
*/
|
|
// generic user interface
|
|
// dropdown box
|
|
// implemented via 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"
|
|
|
|
namespace C4GUI
|
|
{
|
|
|
|
// ----------------------------------------------------
|
|
// ComboBox_FillCB
|
|
|
|
void ComboBox_FillCB::AddEntry(const char *szText, int32_t id)
|
|
{
|
|
if (!szText) szText = "";
|
|
typedef C4GUI::CBMenuHandlerEx<ComboBox, ComboBox::ComboMenuCBStruct> Handler;
|
|
Handler *pHandler = new Handler(pCombo, &ComboBox::OnCtxComboSelect);
|
|
pHandler->SetExtra(ComboBox::ComboMenuCBStruct(szText, id));
|
|
pDrop->AddItem(szText, FormatString(LoadResStr("IDS_MSG_SELECT"), szText).getData(), Ico_Empty, pHandler);
|
|
}
|
|
|
|
bool ComboBox_FillCB::FindEntry(const char *szText)
|
|
{
|
|
// check for entry with same name
|
|
ContextMenu::Entry *pEntry; int32_t idx=0;
|
|
while ((pEntry = pDrop->GetIndexedEntry(idx++))) if (SEqual(pEntry->GetText(), szText)) return true;
|
|
return false;
|
|
}
|
|
|
|
void ComboBox_FillCB::SelectEntry(int32_t iEntry)
|
|
{
|
|
pDrop->SelectItem(iEntry);
|
|
}
|
|
|
|
void ComboBox_FillCB::ClearEntries()
|
|
{
|
|
pDrop->Clear();
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------
|
|
// ComboBox
|
|
|
|
ComboBox::ComboBox(const C4Rect &rtBounds) :
|
|
Control(rtBounds), iOpenMenu(0), pFillCallback(nullptr), fReadOnly(false), fSimple(false), fMouseOver(false),
|
|
pUseFont(nullptr), dwFontClr(C4GUI_ComboFontClr), dwBGClr(C4GUI_StandardBGColor), dwBorderClr(0), pFctSideArrow(nullptr)
|
|
{
|
|
*Text=0;
|
|
// key callbacks - lots of possibilities to get the dropdown
|
|
C4CustomKey::CodeList cbKeys;
|
|
cbKeys.emplace_back(K_DOWN);
|
|
cbKeys.emplace_back(K_SPACE);
|
|
cbKeys.emplace_back(K_DOWN, KEYS_Alt);
|
|
cbKeys.emplace_back(K_SPACE, KEYS_Alt);
|
|
if (Config.Controls.GamepadGuiControl)
|
|
{
|
|
ControllerKeys::Ok(cbKeys);
|
|
ControllerKeys::Down(cbKeys);
|
|
}
|
|
pKeyOpenCombo = new C4KeyBinding(cbKeys, "GUIComboOpen", KEYSCOPE_Gui,
|
|
new ControlKeyCB<ComboBox>(*this, &ComboBox::KeyDropDown), C4CustomKey::PRIO_Ctrl);
|
|
cbKeys.clear();
|
|
cbKeys.emplace_back(K_ESCAPE);
|
|
if (Config.Controls.GamepadGuiControl)
|
|
{
|
|
ControllerKeys::Cancel(cbKeys);
|
|
}
|
|
pKeyCloseCombo = new C4KeyBinding(cbKeys, "GUIComboClose", KEYSCOPE_Gui,
|
|
new ControlKeyCB<ComboBox>(*this, &ComboBox::KeyAbortDropDown), C4CustomKey::PRIO_Ctrl);
|
|
}
|
|
|
|
ComboBox::~ComboBox()
|
|
{
|
|
delete pKeyCloseCombo;
|
|
delete pKeyOpenCombo;
|
|
if (pFillCallback) delete pFillCallback;
|
|
}
|
|
|
|
void ComboBox::SetComboCB(ComboBox_FillCB *pNewFillCallback)
|
|
{
|
|
if (pFillCallback) delete pFillCallback;
|
|
pFillCallback = pNewFillCallback;
|
|
}
|
|
|
|
bool ComboBox::DoDropdown()
|
|
{
|
|
// not if readonly
|
|
if (fReadOnly) return false;
|
|
// get dropdown pos
|
|
int32_t iX = 0;
|
|
int32_t iY = rcBounds.Hgt;
|
|
// do dropdown
|
|
Screen *pScreen = GetScreen();
|
|
if (!pScreen) return false;
|
|
// item list as context menu
|
|
if (!pFillCallback) return false;
|
|
ContextMenu *pNewMenu = new C4GUI::ContextMenu();
|
|
// init with minimum size
|
|
pNewMenu->GetBounds().Wdt = std::max(rcBounds.Wdt, pNewMenu->GetBounds().Wdt);
|
|
// fill with items
|
|
pFillCallback->FillDropDown(this, pNewMenu);
|
|
// open it on screen
|
|
pScreen->DoContext(pNewMenu, this, iX, iY);
|
|
// store menu
|
|
iOpenMenu = pNewMenu->GetMenuIndex();
|
|
// done, success
|
|
return true;
|
|
}
|
|
|
|
bool ComboBox::AbortDropdown(bool fByUser)
|
|
{
|
|
// recheck open menu
|
|
Screen *pScr = GetScreen();
|
|
if (!pScr || (iOpenMenu != pScr->GetLastContextMenuIndex())) iOpenMenu = 0;
|
|
if (!iOpenMenu) return false;
|
|
// abort it
|
|
pScr->AbortContext(fByUser);
|
|
return true;
|
|
}
|
|
|
|
void ComboBox::DrawElement(C4TargetFacet &cgo)
|
|
{
|
|
CStdFont *pUseFont = this->pUseFont ? this->pUseFont : &(::GraphicsResource.TextFont);
|
|
// 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;
|
|
int32_t iRightTextEnd = x0 + rcBounds.Wdt - ::GraphicsResource.fctContext.Wdt - 1;
|
|
if (!fReadOnly && !fSimple)
|
|
{
|
|
// draw background
|
|
pDraw->DrawBoxDw(cgo.Surface, x0,y0,x0+rcBounds.Wdt-1,y0+rcBounds.Hgt-1,dwBGClr);
|
|
// draw frame
|
|
if (dwBorderClr)
|
|
{
|
|
int32_t x1=cgo.TargetX+rcBounds.x,y1=cgo.TargetY+rcBounds.y,x2=x1+rcBounds.Wdt,y2=y1+rcBounds.Hgt;
|
|
pDraw->DrawFrameDw(cgo.Surface, x1, y1, x2, y2-1, dwBorderClr);
|
|
pDraw->DrawFrameDw(cgo.Surface, x1+1, y1+1, x2-1, y2-2, dwBorderClr);
|
|
}
|
|
else
|
|
// default frame color
|
|
Draw3DFrame(cgo);
|
|
// draw button; down (phase 1) if combo is down
|
|
(pFctSideArrow ? pFctSideArrow : &(::GraphicsResource.fctContext))->Draw(cgo.Surface, iRightTextEnd, y0 + (rcBounds.Hgt-::GraphicsResource.fctContext.Hgt)/2, iOpenMenu ? 1 : 0);
|
|
}
|
|
else if (!fReadOnly)
|
|
{
|
|
// draw button in simple mode: Left of text
|
|
(pFctSideArrow ? pFctSideArrow : &(::GraphicsResource.fctContext))->Draw(cgo.Surface, x0, y0 + (rcBounds.Hgt-::GraphicsResource.fctContext.Hgt)/2, iOpenMenu ? 1 : 0);
|
|
}
|
|
// draw text
|
|
if (*Text)
|
|
{
|
|
pDraw->StorePrimaryClipper();
|
|
pDraw->SubPrimaryClipper(x0,y0,iRightTextEnd-1,y0+rcBounds.Hgt-1);
|
|
pDraw->TextOut(Text, *pUseFont, 1.0f, cgo.Surface, x0 + ::GraphicsResource.fctContext.Wdt + 2, y0 + (rcBounds.Hgt-pUseFont->GetLineHeight())/2, dwFontClr, ALeft);
|
|
pDraw->RestorePrimaryClipper();
|
|
}
|
|
// draw selection highlight
|
|
if ((HasDrawFocus() || iOpenMenu || fMouseOver) && !fReadOnly)
|
|
{
|
|
pDraw->SetBlitMode(C4GFXBLIT_ADDITIVE);
|
|
::GraphicsResource.fctButtonHighlightRound.DrawX(cgo.Surface, x0, y0, rcBounds.Wdt, rcBounds.Hgt);
|
|
pDraw->ResetBlitMode();
|
|
}
|
|
}
|
|
|
|
void ComboBox::MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam)
|
|
{
|
|
// left-click activates menu
|
|
if (!fReadOnly) if (iButton == C4MC_Button_LeftDown)
|
|
{
|
|
// recheck open menu
|
|
Screen *pScr = GetScreen();
|
|
if (!pScr || (iOpenMenu != pScr->GetLastContextMenuIndex())) iOpenMenu = 0;
|
|
if (iOpenMenu)
|
|
// left-click with combo down: abort has been done by screen; ignore
|
|
return;
|
|
else
|
|
// otherwise, open it
|
|
if (DoDropdown()) return;
|
|
}
|
|
// inherited
|
|
Control::MouseInput(rMouse, iButton, iX, iY, dwKeyParam);
|
|
}
|
|
|
|
void ComboBox::MouseEnter(CMouse &rMouse)
|
|
{
|
|
fMouseOver = true;
|
|
Control::MouseEnter(rMouse);
|
|
}
|
|
|
|
void ComboBox::MouseLeave(CMouse &rMouse)
|
|
{
|
|
fMouseOver = false;
|
|
Control::MouseLeave(rMouse);
|
|
}
|
|
|
|
int32_t ComboBox::GetDefaultHeight()
|
|
{
|
|
return ::GraphicsResource.TextFont.GetLineHeight() + 4;
|
|
}
|
|
|
|
void ComboBox::SetText(const char *szToText)
|
|
{
|
|
// set text without accelerator keys
|
|
if (szToText)
|
|
{
|
|
StdStrBuf sTxt(szToText);
|
|
sTxt.Replace("&", "");
|
|
SCopy(sTxt.getData(), Text, C4MaxTitle);
|
|
}
|
|
else
|
|
*Text=0;
|
|
}
|
|
|
|
void ComboBox::OnCtxComboSelect(C4GUI::Element *pListItem, const ComboMenuCBStruct &rNewSel)
|
|
{
|
|
// ignore in readonly
|
|
if (fReadOnly) return;
|
|
// do callback
|
|
if (!pFillCallback || !pFillCallback->OnComboSelChange(this, rNewSel.id))
|
|
// callback didn't process: default behaviour
|
|
SetText(rNewSel.sText.getData());
|
|
// don't do anything else, because this might be deleted
|
|
}
|
|
|
|
} // namespace C4GUI
|