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 // tab control 18 19 #include "C4Include.h" 20 #include "gui/C4Gui.h" 21 22 #include "graphics/C4Draw.h" 23 #include "graphics/C4FacetEx.h" 24 #include "graphics/C4GraphicsResource.h" 25 #include "gui/C4MouseControl.h" 26 27 namespace C4GUI 28 { 29 30 31 // ---------------------------------------------------- 32 // Tabular::Sheet 33 Sheet(const char * szTitle,const C4Rect & rcBounds,int32_t icoTitle,bool fHasCloseButton,bool fTitleMarkup)34 Tabular::Sheet::Sheet(const char *szTitle, const C4Rect &rcBounds, int32_t icoTitle, bool fHasCloseButton, bool fTitleMarkup) 35 : Window(), icoTitle(icoTitle), cHotkey(0), dwCaptionClr(0u), fHasCloseButton(fHasCloseButton), fCloseButtonHighlighted(false), fTitleMarkup(fTitleMarkup) 36 { 37 // store title 38 if (szTitle) 39 { 40 sTitle.Copy(szTitle); 41 if (fTitleMarkup) ExpandHotkeyMarkup(sTitle, cHotkey); 42 } 43 // set bounds 44 SetBounds(rcBounds); 45 } 46 DrawCaption(C4TargetFacet & cgo,int32_t x,int32_t y,int32_t iMaxWdt,bool fLarge,bool fActive,bool fFocus,C4Facet * pfctClip,C4Facet * pfctIcon,CStdFont * pUseFont)47 void Tabular::Sheet::DrawCaption(C4TargetFacet &cgo, int32_t x, int32_t y, int32_t iMaxWdt, bool fLarge, bool fActive, bool fFocus, C4Facet *pfctClip, C4Facet *pfctIcon, CStdFont *pUseFont) 48 { 49 // calculations 50 int32_t iTxtHgt, iTxtWdt; 51 GetCaptionSize(&iTxtWdt, &iTxtHgt, fLarge, fActive, pfctClip, pfctIcon, pUseFont); 52 if (pfctClip) iMaxWdt = iTxtWdt; 53 CStdFont &rUseFont = pUseFont ? *pUseFont : (fLarge ? ::GraphicsResource.CaptionFont : ::GraphicsResource.TextFont); 54 if (pfctClip && pfctIcon) 55 { 56 // tab with clip gfx: Icon on top of text 57 // x and y mark topleft pos; iTxtWdt and iTxtHgt mark overall size 58 pfctClip->Draw(cgo.Surface, x, y); 59 int32_t xCenter = x + iTxtWdt/2, yCenter = y + iTxtHgt/2; 60 int32_t iLabelHgt = rUseFont.GetLineHeight(); int32_t iIconLabelSpacing = 2; 61 int32_t yTop = yCenter - (pfctIcon->Hgt+iIconLabelSpacing+iLabelHgt)/2; 62 pfctIcon->Draw(cgo.Surface, xCenter-pfctIcon->Wdt/2, yTop, icoTitle); 63 pDraw->TextOut(sTitle.getData(), rUseFont, 1.0f, cgo.Surface, xCenter, yTop + pfctIcon->Hgt+iIconLabelSpacing, fActive ? C4GUI_GfxTabCaptActiveClr : C4GUI_GfxTabCaptInactiveClr , ACenter); 64 } 65 // focus highlight 66 if (fFocus) 67 { 68 pDraw->SetBlitMode(C4GFXBLIT_ADDITIVE); 69 ::GraphicsResource.fctButtonHighlightRound.DrawX(cgo.Surface, (fLarge ? x : x - iTxtWdt/2)+5, y+3, (fLarge ? iMaxWdt : iTxtWdt)-10, iTxtHgt-6); 70 pDraw->ResetBlitMode(); 71 } 72 if (!(pfctClip && pfctIcon)) 73 { 74 // classical tab without clip 75 // icon 76 int32_t xo = x; 77 if (icoTitle>=0) 78 { 79 C4Facet cgoIcon(cgo.Surface, x, y+1, iTxtHgt-2, iTxtHgt-2); 80 if (fLarge) 81 { 82 // large caption: x parameter denotes left pos of icon 83 x += iTxtHgt + 2; 84 } 85 else 86 { 87 // small caption: x parameter denotes drawing center 88 // note that iTxtWdt includes the icon (and close button) as well 89 cgoIcon.X -= iTxtWdt / 2; 90 x += iTxtHgt / 2; 91 } 92 Icon::GetIconFacet((Icons)icoTitle).Draw(cgoIcon); 93 } 94 // text 95 if (!fLarge && fHasCloseButton) x -= iTxtHgt/2; 96 uint32_t dwClr = dwCaptionClr; 97 if (!dwClr) dwClr = fActive ? C4GUI_CaptionFontClr : C4GUI_InactCaptionFontClr; 98 pDraw->TextOut(sTitle.getData(), rUseFont, fLarge ? 1.2f : 1.0f, cgo.Surface, x, y, dwClr, fLarge ? ALeft : ACenter, fTitleMarkup); 99 // close button 100 if (fHasCloseButton) 101 { 102 xo += iTxtWdt / (2 - fLarge) - iTxtHgt + 1; 103 C4Facet cgoCloseBtn(cgo.Surface, xo, y+1, iTxtHgt-2, iTxtHgt-2); 104 if (!fCloseButtonHighlighted) pDraw->ActivateBlitModulation(0x7f7f7f); 105 Icon::GetIconFacet(Ico_Close).Draw(cgoCloseBtn); 106 if (!fCloseButtonHighlighted) pDraw->DeactivateBlitModulation(); 107 } 108 } 109 } 110 GetCaptionSize(int32_t * piWdt,int32_t * piHgt,bool fLarge,bool fActive,C4Facet * pfctClip,C4Facet * pfctIcon,CStdFont * pUseFont)111 void Tabular::Sheet::GetCaptionSize(int32_t *piWdt, int32_t *piHgt, bool fLarge, bool fActive, C4Facet *pfctClip, C4Facet *pfctIcon, CStdFont *pUseFont) 112 { 113 // caption by gfx? 114 if (pfctClip && pfctIcon) 115 { 116 if (piWdt) *piWdt = Tabular::GetLeftClipSize(pfctClip); 117 if (piHgt) *piHgt = pfctClip->Hgt; 118 return; 119 } 120 // caption by text 121 int32_t iWdt, iHgt; 122 CStdFont &rUseFont = pUseFont ? *pUseFont : (fLarge ? ::GraphicsResource.CaptionFont : ::GraphicsResource.TextFont); 123 if (!rUseFont.GetTextExtent(sTitle.getData(), iWdt, iHgt, fTitleMarkup)) 124 { 125 iWdt=70; iHgt=rUseFont.GetLineHeight(); 126 } 127 if (fLarge) { iWdt = iWdt * 6 / 5; iHgt = iHgt * 6 / 5; } 128 // add icon width 129 if (icoTitle>=0) iWdt += iHgt + fLarge*2; 130 // add close button width 131 if (fHasCloseButton) iWdt += iHgt + fLarge*2; 132 // assign output vars 133 if (piWdt) *piWdt = iWdt; 134 if (piHgt) *piHgt = iHgt; 135 } 136 IsPosOnCloseButton(int32_t x,int32_t y,int32_t iCaptWdt,int32_t iCaptHgt,bool fLarge)137 bool Tabular::Sheet::IsPosOnCloseButton(int32_t x, int32_t y, int32_t iCaptWdt, int32_t iCaptHgt, bool fLarge) 138 { 139 // close button is on right end of tab 140 return fHasCloseButton && Inside<int32_t>(x, iCaptWdt-iCaptHgt+1, iCaptWdt-2) && Inside<int32_t>(y, 1, iCaptHgt-2); 141 } 142 SetTitle(const char * szNewTitle)143 void Tabular::Sheet::SetTitle(const char *szNewTitle) 144 { 145 if (sTitle == szNewTitle) return; 146 if (szNewTitle) 147 { 148 sTitle.Copy(szNewTitle); 149 if (fTitleMarkup) ExpandHotkeyMarkup(sTitle, cHotkey); 150 } 151 else 152 { 153 sTitle.Clear(); 154 cHotkey = '\0'; 155 } 156 Tabular *pTabular = static_cast<Tabular *>(GetParent()); 157 if (pTabular) pTabular->SheetsChanged(); 158 } 159 IsActiveSheet()160 bool Tabular::Sheet::IsActiveSheet() 161 { 162 Tabular *pTabular = static_cast<Tabular *>(GetParent()); 163 if (pTabular) return pTabular->GetActiveSheet() == this; 164 return false; 165 } 166 167 // ---------------------------------------------------- 168 // Tabular 169 Tabular(C4Rect & rtBounds,TabPosition eTabPos)170 Tabular::Tabular(C4Rect &rtBounds, TabPosition eTabPos) : Control(rtBounds), pActiveSheet(nullptr), eTabPos(eTabPos), iMaxTabWidth(0), 171 iCaptionLengthTotal(0), iCaptionScrollPos(0), fScrollingLeft(false), fScrollingRight(false), fScrollingLeftDown(false), 172 fScrollingRightDown(false), iSheetMargin(4), fDrawSelf(true), pfctBack(nullptr), pfctClip(nullptr), pfctIcons(nullptr), pSheetCaptionFont(nullptr) 173 { 174 // calc client rect 175 UpdateOwnPos(); 176 // key bindings for tab selection, if this is not an invisible "blind" tabular 177 if (eTabPos != tbNone) 178 { 179 // Ctrl+(Shift-)Tab works with dialog focus only (assumes max one tabular per dialog) 180 // Arrow keys work if control is focused only 181 C4CustomKey::CodeList Keys; 182 Keys.emplace_back(K_UP); 183 if (Config.Controls.GamepadGuiControl) 184 { 185 ControllerKeys::Up(Keys); 186 } 187 pKeySelUp = new C4KeyBinding(Keys, "GUITabularSelUp", KEYSCOPE_Gui, 188 new ControlKeyCB<Tabular>(*this, &Tabular::KeySelUp), C4CustomKey::PRIO_Ctrl); 189 190 Keys.clear(); 191 Keys.emplace_back(K_DOWN); 192 if (Config.Controls.GamepadGuiControl) 193 { 194 ControllerKeys::Down(Keys); 195 } 196 pKeySelDown = new C4KeyBinding(Keys, "GUITabularSelDown", KEYSCOPE_Gui, 197 new ControlKeyCB<Tabular>(*this, &Tabular::KeySelDown), C4CustomKey::PRIO_Ctrl); 198 199 pKeySelUp2 = new C4KeyBinding(C4KeyCodeEx(K_TAB, C4KeyShiftState(KEYS_Shift | KEYS_Control)), "GUITabularSelUp2", KEYSCOPE_Gui, 200 new DlgKeyCB<Tabular>(*this, &Tabular::KeySelUp), C4CustomKey::PRIO_Ctrl); 201 pKeySelDown2 = new C4KeyBinding(C4KeyCodeEx(K_TAB, KEYS_Control), "GUITabularSelDown2", KEYSCOPE_Gui, 202 new DlgKeyCB<Tabular>(*this, &Tabular::KeySelDown), C4CustomKey::PRIO_Ctrl); 203 pKeyCloseTab = new C4KeyBinding(C4KeyCodeEx(K_F4, KEYS_Control), "GUITabularCloseTab", KEYSCOPE_Gui, 204 new DlgKeyCB<Tabular>(*this, &Tabular::KeyCloseTab), C4CustomKey::PRIO_Ctrl); 205 } 206 else 207 { 208 pKeySelUp = pKeySelDown = pKeySelUp2 = pKeySelDown2 = pKeyCloseTab = nullptr; 209 } 210 SheetsChanged(); 211 } 212 ~Tabular()213 Tabular::~Tabular() 214 { 215 if (pKeyCloseTab) delete pKeyCloseTab; 216 if (pKeySelDown2) delete pKeySelDown2; 217 if (pKeySelUp2) delete pKeySelUp2; 218 if (pKeySelDown) delete pKeySelDown; 219 if (pKeySelUp) delete pKeySelUp; 220 } 221 KeySelUp()222 bool Tabular::KeySelUp() 223 { 224 // keyboard callback: Select previous sheet 225 int32_t iNewSel = GetActiveSheetIndex() - 1; 226 if (iNewSel < 0) iNewSel = GetSheetCount() - 1; 227 if (iNewSel < 0) return false; 228 SelectSheet(iNewSel, true); 229 return true; 230 } 231 KeySelDown()232 bool Tabular::KeySelDown() 233 { 234 // keyboard callback: Select next sheet 235 int32_t iNewSel = GetActiveSheetIndex() + 1, iSheetCount = GetSheetCount(); 236 if (iNewSel >= iSheetCount) 237 { 238 if (!iSheetCount) return false; 239 else iNewSel = 0; 240 } 241 SelectSheet(iNewSel, true); 242 return true; 243 } 244 KeyCloseTab()245 bool Tabular::KeyCloseTab() 246 { 247 // keyboard callback: Close currnet sheet 248 // only for sheets that can be closed 249 Sheet *pCurrentSheet = GetActiveSheet(); 250 if (!pCurrentSheet) return false; 251 if (!pCurrentSheet->HasCloseButton()) return false; 252 pCurrentSheet->UserClose(); 253 return true; 254 } 255 SelectionChanged(bool fByUser)256 void Tabular::SelectionChanged(bool fByUser) 257 { 258 Control *pFocusCtrl = nullptr; 259 Dialog *pDlg = GetDlg(); 260 if (pDlg) pFocusCtrl = pDlg->GetFocus(); 261 // any selection? 262 if (pActiveSheet) 263 { 264 // effect 265 if (fByUser) GUISound("UI::Select"); 266 // update in sheet 267 pActiveSheet->OnShown(fByUser); 268 } 269 // make only active sheet visible 270 for (Element *pSheet = GetFirst(); pSheet; pSheet = pSheet->GetNext()) 271 { 272 pSheet->SetVisibility(pSheet == pActiveSheet); 273 } 274 // if nothing is selected now, but something was selected before, focus new default control 275 if (pFocusCtrl && !pDlg->GetFocus()) pDlg->SetFocus(pDlg->GetDefaultControl(), fByUser); 276 } 277 SheetsChanged()278 void Tabular::SheetsChanged() 279 { 280 Sheet *pSheet; 281 if (eTabPos) 282 { 283 // update iMaxTabWidth by new set of sheet labels 284 iSheetOff = 20; 285 iMaxTabWidth = 20; 286 iSheetSpacing = (eTabPos == tbLeft) ? -10 : 20; 287 int32_t iSheetNum=0, iTotalHgt=iSheetOff; 288 iCaptionLengthTotal = iSheetOff; 289 for (pSheet = (Sheet *) GetFirst(); pSheet; pSheet = (Sheet *) pSheet->GetNext()) 290 { 291 int32_t iTabWidth, iTabHeight; 292 pSheet->GetCaptionSize(&iTabWidth, &iTabHeight, HasLargeCaptions(), pSheet == pActiveSheet, pfctClip, pfctIcons, pSheetCaptionFont); 293 iTabWidth += (eTabPos == tbLeft) ? 20 : iSheetSpacing; 294 iMaxTabWidth = std::max(iTabWidth, iMaxTabWidth); 295 if (eTabPos == tbLeft) 296 { 297 iTotalHgt += iTabHeight; 298 if (iSheetNum++) iTotalHgt += iSheetSpacing; 299 } 300 else 301 { 302 iCaptionLengthTotal += iTabWidth; 303 } 304 } 305 // update sheet positioning 306 if (eTabPos == tbLeft && iTotalHgt > rcBounds.Hgt-GetMarginBottom()) 307 { 308 // sheet captions dont fit - condense them 309 iSheetSpacing -= (iTotalHgt-rcBounds.Hgt+GetMarginBottom()-iSheetOff) / iSheetNum; 310 iSheetOff = 0; 311 } 312 else if (eTabPos == tbTop) 313 { 314 } 315 } 316 // update all sheet sizes 317 UpdateSize(); 318 // update scrolling range/status 319 UpdateScrolling(); 320 } 321 UpdateScrolling()322 void Tabular::UpdateScrolling() 323 { 324 // any scrolling necessary? 325 int32_t iAvailableTabSpace = rcBounds.Wdt; 326 int32_t iScrollPinSize = GetTopSize(); 327 if (eTabPos != tbTop || iCaptionLengthTotal <= iAvailableTabSpace || iAvailableTabSpace <= iScrollPinSize*2) 328 { 329 fScrollingLeft = fScrollingRight = fScrollingLeftDown = fScrollingRightDown = false; 330 iCaptionScrollPos = 0; 331 } 332 else 333 { 334 // must scroll; update scrolling parameters 335 fScrollingLeft = !!iCaptionScrollPos; 336 if (!fScrollingLeft) 337 { 338 fScrollingRight = true; 339 fScrollingLeftDown = false; 340 } 341 else 342 { 343 iAvailableTabSpace -= iScrollPinSize; 344 fScrollingRight = (iCaptionLengthTotal - iCaptionScrollPos > iAvailableTabSpace); 345 // do not scroll past right end 346 if (!fScrollingRight) 347 { 348 iCaptionScrollPos = iCaptionLengthTotal - iAvailableTabSpace; 349 fScrollingRightDown = false; 350 } 351 } 352 } 353 } 354 DoCaptionScroll(int32_t iDir)355 void Tabular::DoCaptionScroll(int32_t iDir) 356 { 357 // store time of scrolling change 358 tLastScrollTime = C4TimeMilliseconds::Now(); 359 // change scrolling within max range 360 int32_t iAvailableTabSpace = rcBounds.Wdt; 361 int32_t iScrollPinSize = GetTopSize(); 362 iCaptionScrollPos = Clamp<int32_t>(iCaptionScrollPos + iDir*iAvailableTabSpace/2, 0, iCaptionLengthTotal - iAvailableTabSpace + iScrollPinSize); 363 UpdateScrolling(); 364 } 365 DrawElement(C4TargetFacet & cgo)366 void Tabular::DrawElement(C4TargetFacet &cgo) 367 { 368 if (!fDrawSelf) return; 369 bool fGfx = HasGfx(); 370 // execute scrolling 371 bool fCaptionScrollDelayOver = C4TimeMilliseconds::Now() - tLastScrollTime >= C4GUI_TabCaptionScrollTime; 372 if ((fScrollingLeftDown || fScrollingRightDown) && fCaptionScrollDelayOver) 373 DoCaptionScroll(fScrollingRightDown - fScrollingLeftDown); 374 // border 375 if (!fGfx) Draw3DFrame(cgo, false, 1, 0xaf, eTabPos!=tbTop, GetTopSize(), eTabPos!=tbLeft, GetLeftSize()); 376 // calc positions 377 int32_t x0 = cgo.TargetX + rcBounds.x + GetLeftSize(), 378 y0 = cgo.TargetY + rcBounds.y + GetTopSize(), 379 x1 = cgo.TargetX + rcBounds.x + rcBounds.Wdt - 1, 380 y1 = cgo.TargetY + rcBounds.y + rcBounds.Hgt - 1; 381 // main area BG 382 if (!fGfx) pDraw->DrawBoxDw(cgo.Surface, x0,y0,x1,y1, C4GUI_StandardBGColor); 383 // no tabs? 384 if (!eTabPos) 385 { 386 if (fGfx) 387 pfctBack->DrawX(cgo.Surface, x0, y0, x1-x0+1, y1-y0+1); 388 return; 389 } 390 bool fLeft = (eTabPos == tbLeft); 391 // top or left bar 392 int32_t d=(fLeft ? y0 : x0)+iSheetOff; // current tab position (leave some space to the left/top) 393 int32_t ad0=0,ad1=0, aCptTxX=0, aCptTxY=0; 394 // scrolling in captions 395 int32_t iScrollSize = GetTopSize(); 396 if (fScrollingLeft) d -= iCaptionScrollPos + iScrollSize; 397 // tabs 398 for (Sheet *pSheet = (Sheet *) GetFirst(); pSheet; pSheet = (Sheet *) pSheet->GetNext()) 399 { 400 // get tab size 401 int32_t iTabWidth, iTabHeight; 402 pSheet->GetCaptionSize(&iTabWidth, &iTabHeight, HasLargeCaptions(), pSheet == pActiveSheet, pfctClip, pfctIcons, pSheetCaptionFont); 403 // leave some space around caption 404 iTabWidth += fLeft ? 20 : iSheetSpacing; 405 iTabHeight += fLeft ? iSheetSpacing : 10; 406 // draw caption bg 407 if (!fGfx) 408 { 409 float vtx[8]; 410 if (fLeft) 411 { 412 vtx[0] = x0; vtx[1] = d; 413 vtx[2] = x0-GetLeftSize(); vtx[3] = d; 414 vtx[4] = x0-GetLeftSize(); vtx[5] = d+iTabHeight; 415 vtx[6] = x0; vtx[7] = d+iTabHeight; 416 } 417 else 418 { 419 vtx[0] = d+1; vtx[1] = y0; 420 vtx[2] = d+4+1; vtx[3] = y0-GetTopSize(); 421 vtx[4] = d+iTabWidth-4; vtx[5] = y0-GetTopSize(); 422 vtx[6] = d+iTabWidth; vtx[7] = y0; 423 } 424 DWORD dwClr = (pSheet == pActiveSheet) ? C4GUI_ActiveTabBGColor : C4GUI_StandardBGColor; 425 pDraw->DrawQuadDw(cgo.Surface, vtx, dwClr, dwClr, dwClr, dwClr, nullptr); 426 // draw caption frame 427 // TODO: Switch to PerformMultiLines 428 pDraw->DrawLineDw(cgo.Surface, (float)vtx[0]-1 , (float)vtx[1] , (float)vtx[2]-1 ,(float)vtx[3] , C4GUI_BorderColorA1); 429 pDraw->DrawLineDw(cgo.Surface, (float)vtx[2]-1 , (float)vtx[3] , (float)vtx[4]-fLeft,(float)vtx[5] , C4GUI_BorderColorA1); 430 pDraw->DrawLineDw(cgo.Surface, (float)vtx[4] , (float)vtx[5] , (float)vtx[6] ,(float)vtx[7] , C4GUI_BorderColorA1); 431 pDraw->DrawLineDw(cgo.Surface, (float)vtx[0] , (float)vtx[1]+fLeft, (float)vtx[2] ,(float)vtx[3]+fLeft , C4GUI_BorderColorA2); 432 pDraw->DrawLineDw(cgo.Surface, (float)vtx[2]-!fLeft, (float)vtx[3]+1 , (float)vtx[4] ,(float)vtx[5]+!fLeft , C4GUI_BorderColorA2); 433 pDraw->DrawLineDw(cgo.Surface, (float)vtx[4]+1 , (float)vtx[5]+fLeft, (float)vtx[6] ,(float)vtx[7]+fLeft , C4GUI_BorderColorA2); 434 } 435 // draw caption text 436 int32_t iCptTextX = fLeft ? (x0-GetLeftSize()+10) : (d+iTabWidth/2); 437 int32_t iCptTextY = fLeft ? (d+iSheetSpacing/2) : (y0-GetTopSize()+2); 438 if (pSheet == pActiveSheet) 439 { 440 // store active sheet pos for border line or later drawing 441 ad0=d; ad1=d+(fLeft ? iTabHeight : iTabWidth); 442 aCptTxX = iCptTextX; aCptTxY = iCptTextY; 443 // draw active caption 444 if (!fGfx) pSheet->DrawCaption(cgo, iCptTextX, iCptTextY, iMaxTabWidth, fLeft, true, HasDrawFocus(), nullptr, nullptr, nullptr); 445 } 446 else 447 { 448 // draw inactive caption 449 pSheet->DrawCaption(cgo, iCptTextX, iCptTextY, iMaxTabWidth, fLeft, false, false, pfctClip, pfctIcons, pSheetCaptionFont); 450 } 451 // advance position 452 d += (fLeft ? iTabHeight : iTabWidth)+2; 453 } 454 // draw tab border line across everything but active tab 455 if (!fGfx) if (ad0||ad1) 456 { 457 pDraw->DrawLineDw(cgo.Surface, (float)x0 ,(float)y0 ,(float)(fLeft ? x0 : ad0), (float)(fLeft ? ad0 : y0), C4GUI_BorderColorA1); 458 pDraw->DrawLineDw(cgo.Surface, (float)(x0+1),(float)(y0+1),(float)((fLeft ? x0 : ad0)+1), (float)((fLeft ? ad0 : y0)+1) , C4GUI_BorderColorA2); 459 pDraw->DrawLineDw(cgo.Surface, (float)(fLeft ? x0 : ad1), (float)(fLeft ? ad1 : y0), (float)(fLeft ? x0 : x1), (float)(fLeft ? y1 : y0), C4GUI_BorderColorA1); 460 pDraw->DrawLineDw(cgo.Surface, (float)((fLeft ? x0 : ad1)+1), (float)((fLeft ? ad1 : y0)+1), (float)((fLeft ? x0 : x1)+1), (float)((fLeft ? y1 : y0)+1), C4GUI_BorderColorA2); 461 } 462 // main area bg in gfx: Atop inactive tabs 463 if (fGfx) 464 { 465 pfctBack->DrawX(cgo.Surface, x0, y0, x1-x0+1, y1-y0+1); 466 // and active tab on top of that 467 if (pActiveSheet) 468 pActiveSheet->DrawCaption(cgo, aCptTxX, aCptTxY, iMaxTabWidth, fLeft, true, HasDrawFocus(), pfctClip, pfctIcons, pSheetCaptionFont); 469 } 470 // scrolling 471 if (fScrollingLeft) ::GraphicsResource.fctBigArrows.DrawX(cgo.Surface, x0+iSheetOff,y0-iScrollSize, iScrollSize,iScrollSize, fScrollingLeftDown*2); 472 if (fScrollingRight) ::GraphicsResource.fctBigArrows.DrawX(cgo.Surface, x1-iScrollSize,y0-iScrollSize, iScrollSize,iScrollSize, 1+fScrollingRightDown*2); 473 } 474 MouseInput(CMouse & rMouse,int32_t iButton,int32_t iX,int32_t iY,DWORD dwKeyParam)475 void Tabular::MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam) 476 { 477 // tabular contains controls? 478 if (eTabPos) 479 { 480 bool fLeft = (eTabPos == tbLeft); 481 bool fInCaptionArea = ((!fLeft && Inside<int32_t>(iY, 0, GetTopSize())) || (fLeft && Inside<int32_t>(iX, 0, GetLeftSize()))); 482 if (!fInCaptionArea || iButton == C4MC_Button_LeftUp) 483 { 484 MouseLeaveCaptionArea(); 485 } 486 // then check for mousedown in coloumn area 487 else if ((iButton == C4MC_Button_LeftDown || iButton == C4MC_Button_None) && fInCaptionArea) 488 { 489 int32_t d=iSheetOff; 490 // check inside scrolling buttons 491 bool fProcessed = false; 492 if (fScrollingLeft || fScrollingRight) 493 { 494 int32_t iScrollSize = GetTopSize(); 495 if (iButton == C4MC_Button_LeftDown && fScrollingRight && Inside(iX, rcBounds.Wdt-iScrollSize,rcBounds.Wdt)) 496 { 497 fProcessed = fScrollingRightDown = true; 498 GUISound("UI::Select"); 499 DoCaptionScroll(+1); 500 } 501 else if (fScrollingLeft) 502 { 503 if (iButton == C4MC_Button_LeftDown && Inside(iX, d,d+iScrollSize)) 504 { 505 fProcessed = fScrollingLeftDown = true; 506 GUISound("UI::Select"); 507 DoCaptionScroll(-1); 508 } 509 d -= iCaptionScrollPos + iScrollSize; 510 } 511 } 512 // check on sheet captions 513 if (!fProcessed) for (Sheet *pSheet = (Sheet *) GetFirst(); pSheet; pSheet = (Sheet *) pSheet->GetNext()) 514 { 515 // default: Mouse not on close button 516 pSheet->SetCloseButtonHighlight(false); 517 // get tab width 518 int32_t iCaptWidth,iCaptHeight,iTabWidth,iTabHeight; 519 pSheet->GetCaptionSize(&iCaptWidth, &iCaptHeight, HasLargeCaptions(), pSheet == pActiveSheet, pfctClip, pfctIcons, pSheetCaptionFont); 520 iTabWidth = iCaptWidth + (fLeft ? 20 : iSheetSpacing); 521 iTabHeight = iCaptHeight + (fLeft ? iSheetSpacing : 10); 522 // check containment in this tab (check rect only, may catch some side-clicks...) 523 if ((!fLeft && Inside(iX, d, d+iTabWidth)) || (fLeft && Inside(iY, d, d+iTabHeight))) 524 { 525 // close button 526 if (pSheet->IsPosOnCloseButton(iX-d*!fLeft-(iTabWidth-iCaptWidth)/2, iY-d*fLeft, iCaptWidth, iCaptHeight, HasLargeCaptions())) 527 { 528 if (iButton == C4MC_Button_LeftDown) 529 { 530 // Closing: Callback to sheet 531 pSheet->UserClose(); 532 } 533 else 534 // just moving :Highlight 535 pSheet->SetCloseButtonHighlight(true); 536 } 537 // mouse press outside close button area: Switch sheet 538 else if (iButton == C4MC_Button_LeftDown) 539 { 540 if (pSheet != pActiveSheet) 541 { 542 pActiveSheet = pSheet; 543 SelectionChanged(true); 544 } 545 } 546 break; 547 } 548 // next tab 549 d += (fLeft ? iTabHeight : iTabWidth)+2; 550 } 551 } 552 } 553 // inherited 554 Control::MouseInput(rMouse, iButton, iX, iY, dwKeyParam); 555 } 556 MouseLeaveCaptionArea()557 void Tabular::MouseLeaveCaptionArea() 558 { 559 // no more close buttons or scroll buttons highlighted 560 for (Sheet *pSheet = (Sheet *) GetFirst(); pSheet; pSheet = (Sheet *) pSheet->GetNext()) 561 { 562 // default: Mouse not on close button 563 pSheet->SetCloseButtonHighlight(false); 564 } 565 if (fScrollingLeftDown || fScrollingRightDown) 566 { 567 // stop scrolling 568 GUISound("UI::Select"); 569 fScrollingLeftDown = fScrollingRightDown = false; 570 } 571 } 572 MouseLeave(CMouse & rMouse)573 void Tabular::MouseLeave(CMouse &rMouse) 574 { 575 MouseLeaveCaptionArea(); 576 // inherited 577 Control::MouseLeave(rMouse); 578 } 579 OnGetFocus(bool fByMouse)580 void Tabular::OnGetFocus(bool fByMouse) 581 { 582 // inherited (tooltip) 583 Control::OnGetFocus(fByMouse); 584 } 585 RemoveElement(Element * pChild)586 void Tabular::RemoveElement(Element *pChild) 587 { 588 // inherited 589 Control::RemoveElement(pChild); 590 // clear selection var 591 if (pChild == pActiveSheet) 592 { 593 // select new active sheet 594 pActiveSheet = (Sheet *) GetFirst(); 595 SelectionChanged(false); 596 } 597 // update sheet labels 598 SheetsChanged(); 599 } 600 AddSheet(const char * szTitle,int32_t icoTitle)601 Tabular::Sheet *Tabular::AddSheet(const char *szTitle, int32_t icoTitle) 602 { 603 // create new sheet in client area 604 Sheet *pNewSheet = new Sheet(szTitle, GetContainedClientRect(), icoTitle); 605 AddCustomSheet(pNewSheet); 606 // k, new sheet ready! 607 return pNewSheet; 608 } 609 AddCustomSheet(Sheet * pAddSheet)610 void Tabular::AddCustomSheet(Sheet *pAddSheet) 611 { 612 AddElement(pAddSheet); 613 // select it if it's first 614 pAddSheet->SetVisibility(!pActiveSheet); 615 if (!pActiveSheet) pActiveSheet = pAddSheet; 616 // update sheet labels 617 SheetsChanged(); 618 } 619 ClearSheets()620 void Tabular::ClearSheets() 621 { 622 // del all sheets 623 Sheet *pSheet; 624 while ((pSheet = GetSheet(0))) delete pSheet; 625 SheetsChanged(); 626 } 627 SelectSheet(int32_t iIndex,bool fByUser)628 void Tabular::SelectSheet(int32_t iIndex, bool fByUser) 629 { 630 pActiveSheet = GetSheet(iIndex); 631 SelectionChanged(fByUser); 632 } 633 SelectSheet(Sheet * pSelSheet,bool fByUser)634 void Tabular::SelectSheet(Sheet *pSelSheet, bool fByUser) 635 { 636 pActiveSheet = pSelSheet; 637 SelectionChanged(fByUser); 638 } 639 GetActiveSheetIndex()640 int32_t Tabular::GetActiveSheetIndex() 641 { 642 int32_t i=-1; 643 Sheet *pSheet; 644 while ((pSheet = GetSheet(++i))) if (pSheet == pActiveSheet) return i; 645 return -1; 646 } 647 SetGfx(C4Facet * pafctBack,C4Facet * pafctClip,C4Facet * pafctIcons,CStdFont * paSheetCaptionFont,bool fResizeByAspect)648 void Tabular::SetGfx(C4Facet *pafctBack, C4Facet *pafctClip, C4Facet *pafctIcons, CStdFont *paSheetCaptionFont, bool fResizeByAspect) 649 { 650 // set gfx files 651 pfctBack=pafctBack; 652 pfctClip=pafctClip; 653 pfctIcons=pafctIcons; 654 pSheetCaptionFont=paSheetCaptionFont; 655 // make sure aspect of background is used correctly 656 if (pfctBack && fResizeByAspect) 657 { 658 int32_t iEffWdt = rcBounds.Wdt - GetLeftSize(), iEffHgt = rcBounds.Hgt - GetTopSize(); 659 if (iEffWdt * pfctBack->Hgt > pfctBack->Wdt * iEffHgt) 660 { 661 // control is too wide: center it 662 int32_t iOversize = iEffWdt - pfctBack->Wdt * iEffHgt / pfctBack->Hgt; 663 C4Rect rtBounds = GetBounds(); 664 rtBounds.x += iOversize/2; 665 rtBounds.Wdt -= iOversize; 666 SetBounds(rtBounds); 667 } 668 else 669 { 670 // control is too tall: cap at bottom 671 int32_t iOversize = iEffHgt - pfctBack->Hgt * iEffWdt / pfctBack->Wdt; 672 C4Rect rtBounds = GetBounds(); 673 rtBounds.y += iOversize; 674 rtBounds.Hgt -= iOversize; 675 SetBounds(rtBounds); 676 } 677 } 678 SheetsChanged(); 679 } 680 UpdateSize()681 void Tabular::UpdateSize() 682 { 683 Control::UpdateSize(); 684 // update all sheets 685 for (Sheet *pSheet = static_cast<Sheet *>(GetFirst()); pSheet; pSheet = static_cast<Sheet *>(pSheet->GetNext())) 686 pSheet->SetBounds(GetContainedClientRect()); 687 } 688 689 690 } // end of namespace 691 692