1 /* 2 * OpenClonk, http://www.openclonk.org 3 * 4 * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/ 5 * Copyright (c) 2009-2016, The OpenClonk Team and contributors 6 * 7 * Distributed under the terms of the ISC license; see accompanying file 8 * "COPYING" for details. 9 * 10 * "Clonk" is a registered trademark of Matthes Bender, used with permission. 11 * See accompanying file "TRADEMARK" for details. 12 * 13 * To redistribute this file separately, substitute the full license texts 14 * for the above references. 15 */ 16 // generic user interface 17 // dropdown box 18 // implemented via context menu 19 20 #include "C4Include.h" 21 #include "gui/C4Gui.h" 22 23 #include "graphics/C4Draw.h" 24 #include "graphics/C4FacetEx.h" 25 #include "graphics/C4GraphicsResource.h" 26 #include "gui/C4MouseControl.h" 27 28 namespace C4GUI 29 { 30 31 // ---------------------------------------------------- 32 // ComboBox_FillCB 33 AddEntry(const char * szText,int32_t id)34 void ComboBox_FillCB::AddEntry(const char *szText, int32_t id) 35 { 36 if (!szText) szText = ""; 37 typedef C4GUI::CBMenuHandlerEx<ComboBox, ComboBox::ComboMenuCBStruct> Handler; 38 Handler *pHandler = new Handler(pCombo, &ComboBox::OnCtxComboSelect); 39 pHandler->SetExtra(ComboBox::ComboMenuCBStruct(szText, id)); 40 pDrop->AddItem(szText, FormatString(LoadResStr("IDS_MSG_SELECT"), szText).getData(), Ico_Empty, pHandler); 41 } 42 FindEntry(const char * szText)43 bool ComboBox_FillCB::FindEntry(const char *szText) 44 { 45 // check for entry with same name 46 ContextMenu::Entry *pEntry; int32_t idx=0; 47 while ((pEntry = pDrop->GetIndexedEntry(idx++))) if (SEqual(pEntry->GetText(), szText)) return true; 48 return false; 49 } 50 SelectEntry(int32_t iEntry)51 void ComboBox_FillCB::SelectEntry(int32_t iEntry) 52 { 53 pDrop->SelectItem(iEntry); 54 } 55 ClearEntries()56 void ComboBox_FillCB::ClearEntries() 57 { 58 pDrop->Clear(); 59 } 60 61 62 // ---------------------------------------------------- 63 // ComboBox 64 ComboBox(const C4Rect & rtBounds)65 ComboBox::ComboBox(const C4Rect &rtBounds) : 66 Control(rtBounds), iOpenMenu(0), pFillCallback(nullptr), fReadOnly(false), fSimple(false), fMouseOver(false), 67 pUseFont(nullptr), dwFontClr(C4GUI_ComboFontClr), dwBGClr(C4GUI_StandardBGColor), dwBorderClr(0), pFctSideArrow(nullptr) 68 { 69 *Text=0; 70 // key callbacks - lots of possibilities to get the dropdown 71 C4CustomKey::CodeList cbKeys; 72 cbKeys.emplace_back(K_DOWN); 73 cbKeys.emplace_back(K_SPACE); 74 cbKeys.emplace_back(K_DOWN, KEYS_Alt); 75 cbKeys.emplace_back(K_SPACE, KEYS_Alt); 76 if (Config.Controls.GamepadGuiControl) 77 { 78 ControllerKeys::Ok(cbKeys); 79 ControllerKeys::Down(cbKeys); 80 } 81 pKeyOpenCombo = new C4KeyBinding(cbKeys, "GUIComboOpen", KEYSCOPE_Gui, 82 new ControlKeyCB<ComboBox>(*this, &ComboBox::KeyDropDown), C4CustomKey::PRIO_Ctrl); 83 cbKeys.clear(); 84 cbKeys.emplace_back(K_ESCAPE); 85 if (Config.Controls.GamepadGuiControl) 86 { 87 ControllerKeys::Cancel(cbKeys); 88 } 89 pKeyCloseCombo = new C4KeyBinding(cbKeys, "GUIComboClose", KEYSCOPE_Gui, 90 new ControlKeyCB<ComboBox>(*this, &ComboBox::KeyAbortDropDown), C4CustomKey::PRIO_Ctrl); 91 } 92 ~ComboBox()93 ComboBox::~ComboBox() 94 { 95 delete pKeyCloseCombo; 96 delete pKeyOpenCombo; 97 if (pFillCallback) delete pFillCallback; 98 } 99 SetComboCB(ComboBox_FillCB * pNewFillCallback)100 void ComboBox::SetComboCB(ComboBox_FillCB *pNewFillCallback) 101 { 102 if (pFillCallback) delete pFillCallback; 103 pFillCallback = pNewFillCallback; 104 } 105 DoDropdown()106 bool ComboBox::DoDropdown() 107 { 108 // not if readonly 109 if (fReadOnly) return false; 110 // get dropdown pos 111 int32_t iX = 0; 112 int32_t iY = rcBounds.Hgt; 113 // do dropdown 114 Screen *pScreen = GetScreen(); 115 if (!pScreen) return false; 116 // item list as context menu 117 if (!pFillCallback) return false; 118 ContextMenu *pNewMenu = new C4GUI::ContextMenu(); 119 // init with minimum size 120 pNewMenu->GetBounds().Wdt = std::max(rcBounds.Wdt, pNewMenu->GetBounds().Wdt); 121 // fill with items 122 pFillCallback->FillDropDown(this, pNewMenu); 123 // open it on screen 124 pScreen->DoContext(pNewMenu, this, iX, iY); 125 // store menu 126 iOpenMenu = pNewMenu->GetMenuIndex(); 127 // done, success 128 return true; 129 } 130 AbortDropdown(bool fByUser)131 bool ComboBox::AbortDropdown(bool fByUser) 132 { 133 // recheck open menu 134 Screen *pScr = GetScreen(); 135 if (!pScr || (iOpenMenu != pScr->GetLastContextMenuIndex())) iOpenMenu = 0; 136 if (!iOpenMenu) return false; 137 // abort it 138 pScr->AbortContext(fByUser); 139 return true; 140 } 141 DrawElement(C4TargetFacet & cgo)142 void ComboBox::DrawElement(C4TargetFacet &cgo) 143 { 144 CStdFont *pUseFont = this->pUseFont ? this->pUseFont : &(::GraphicsResource.TextFont); 145 // recheck open menu 146 Screen *pScr = GetScreen(); 147 if (!pScr || (iOpenMenu != pScr->GetContextMenuIndex())) iOpenMenu = 0; 148 // calc drawing bounds 149 int32_t x0 = cgo.TargetX + rcBounds.x, y0 = cgo.TargetY + rcBounds.y; 150 int32_t iRightTextEnd = x0 + rcBounds.Wdt - ::GraphicsResource.fctContext.Wdt - 1; 151 if (!fReadOnly && !fSimple) 152 { 153 // draw background 154 pDraw->DrawBoxDw(cgo.Surface, x0,y0,x0+rcBounds.Wdt-1,y0+rcBounds.Hgt-1,dwBGClr); 155 // draw frame 156 if (dwBorderClr) 157 { 158 int32_t x1=cgo.TargetX+rcBounds.x,y1=cgo.TargetY+rcBounds.y,x2=x1+rcBounds.Wdt,y2=y1+rcBounds.Hgt; 159 pDraw->DrawFrameDw(cgo.Surface, x1, y1, x2, y2-1, dwBorderClr); 160 pDraw->DrawFrameDw(cgo.Surface, x1+1, y1+1, x2-1, y2-2, dwBorderClr); 161 } 162 else 163 // default frame color 164 Draw3DFrame(cgo); 165 // draw button; down (phase 1) if combo is down 166 (pFctSideArrow ? pFctSideArrow : &(::GraphicsResource.fctContext))->Draw(cgo.Surface, iRightTextEnd, y0 + (rcBounds.Hgt-::GraphicsResource.fctContext.Hgt)/2, iOpenMenu ? 1 : 0); 167 } 168 else if (!fReadOnly) 169 { 170 // draw button in simple mode: Left of text 171 (pFctSideArrow ? pFctSideArrow : &(::GraphicsResource.fctContext))->Draw(cgo.Surface, x0, y0 + (rcBounds.Hgt-::GraphicsResource.fctContext.Hgt)/2, iOpenMenu ? 1 : 0); 172 } 173 // draw text 174 if (*Text) 175 { 176 pDraw->StorePrimaryClipper(); 177 pDraw->SubPrimaryClipper(x0,y0,iRightTextEnd-1,y0+rcBounds.Hgt-1); 178 pDraw->TextOut(Text, *pUseFont, 1.0f, cgo.Surface, x0 + ::GraphicsResource.fctContext.Wdt + 2, y0 + (rcBounds.Hgt-pUseFont->GetLineHeight())/2, dwFontClr, ALeft); 179 pDraw->RestorePrimaryClipper(); 180 } 181 // draw selection highlight 182 if ((HasDrawFocus() || iOpenMenu || fMouseOver) && !fReadOnly) 183 { 184 pDraw->SetBlitMode(C4GFXBLIT_ADDITIVE); 185 ::GraphicsResource.fctButtonHighlightRound.DrawX(cgo.Surface, x0, y0, rcBounds.Wdt, rcBounds.Hgt); 186 pDraw->ResetBlitMode(); 187 } 188 } 189 MouseInput(CMouse & rMouse,int32_t iButton,int32_t iX,int32_t iY,DWORD dwKeyParam)190 void ComboBox::MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam) 191 { 192 // left-click activates menu 193 if (!fReadOnly) if (iButton == C4MC_Button_LeftDown) 194 { 195 // recheck open menu 196 Screen *pScr = GetScreen(); 197 if (!pScr || (iOpenMenu != pScr->GetLastContextMenuIndex())) iOpenMenu = 0; 198 if (iOpenMenu) 199 // left-click with combo down: abort has been done by screen; ignore 200 return; 201 else 202 // otherwise, open it 203 if (DoDropdown()) return; 204 } 205 // inherited 206 Control::MouseInput(rMouse, iButton, iX, iY, dwKeyParam); 207 } 208 MouseEnter(CMouse & rMouse)209 void ComboBox::MouseEnter(CMouse &rMouse) 210 { 211 fMouseOver = true; 212 Control::MouseEnter(rMouse); 213 } 214 MouseLeave(CMouse & rMouse)215 void ComboBox::MouseLeave(CMouse &rMouse) 216 { 217 fMouseOver = false; 218 Control::MouseLeave(rMouse); 219 } 220 GetDefaultHeight()221 int32_t ComboBox::GetDefaultHeight() 222 { 223 return ::GraphicsResource.TextFont.GetLineHeight() + 4; 224 } 225 SetText(const char * szToText)226 void ComboBox::SetText(const char *szToText) 227 { 228 // set text without accelerator keys 229 if (szToText) 230 { 231 StdStrBuf sTxt(szToText); 232 sTxt.Replace("&", ""); 233 SCopy(sTxt.getData(), Text, C4MaxTitle); 234 } 235 else 236 *Text=0; 237 } 238 OnCtxComboSelect(C4GUI::Element * pListItem,const ComboMenuCBStruct & rNewSel)239 void ComboBox::OnCtxComboSelect(C4GUI::Element *pListItem, const ComboMenuCBStruct &rNewSel) 240 { 241 // ignore in readonly 242 if (fReadOnly) return; 243 // do callback 244 if (!pFillCallback || !pFillCallback->OnComboSelChange(this, rNewSel.id)) 245 // callback didn't process: default behaviour 246 SetText(rNewSel.sText.getData()); 247 // don't do anything else, because this might be deleted 248 } 249 250 } // namespace C4GUI 251