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 // all generic classes that do not fit into other C4Gui*-files 18 19 #include "C4Include.h" 20 #include "gui/C4Gui.h" 21 22 #include "game/C4Application.h" 23 #include "game/C4FullScreen.h" 24 #include "game/C4GraphicsSystem.h" 25 #include "game/C4Viewport.h" 26 #include "graphics/C4Draw.h" 27 #include "graphics/C4GraphicsResource.h" 28 #include "gui/C4LoaderScreen.h" 29 #include "gui/C4MouseControl.h" 30 #include "platform/C4GamePadCon.h" 31 32 namespace C4GUI 33 { 34 35 // -------------------------------------------------- 36 // Generic helpers 37 ExpandHotkeyMarkup(StdStrBuf & sText,uint32_t & rcHotkey,bool for_tooltip)38 bool ExpandHotkeyMarkup(StdStrBuf &sText, uint32_t &rcHotkey, bool for_tooltip) 39 { 40 const char *HotkeyMarkup = (for_tooltip ? "<c ff800000>%s</c>" : "<c ffffff7f>%s</c>"); 41 42 StdStrBuf output; 43 44 const char *input = sText.getData(); 45 rcHotkey = 0; 46 47 // Iterate over all input characters 48 while (input && *input) 49 { 50 if (*input != '&') 51 { 52 // This will correctly copy UTF-8 chars too 53 output.AppendChar(*input++); 54 } 55 else 56 { 57 ++input; 58 if (*input == '\0' || *input == '&') 59 { 60 // If the ampersand is followed by another ampersand, or it is the last character, copy it verbatimly 61 // Note: This means you can't use an ampersand as an accelerator key. 62 output.AppendChar(*input); 63 } 64 else 65 { 66 // Store the start of the hotkey so we can copy it later 67 const char *accel_start = input; 68 rcHotkey = GetNextCharacter(&input); 69 // Using std::string because StdStrBuf doesn't have a ctor from two iterators 70 std::string accel(accel_start, input); 71 output.AppendFormat(HotkeyMarkup, accel.c_str()); 72 73 // Converting a char code to upper case isn't trivial for unicode. (This should really just use ICU.) 74 if (Inside(rcHotkey, static_cast<uint32_t>('a'), static_cast<uint32_t>('z'))) 75 { 76 rcHotkey += static_cast<uint32_t>('A') - 'a'; 77 } 78 else if (!Inside(rcHotkey, static_cast<uint32_t>('A'), static_cast<uint32_t>('Z'))) 79 { 80 // Warn about accelerator keys outside the basic latin alphabet. 81 LogF(LoadResStr("IDS_ERR_UNSUPPORTED_ACCELERATOR"), accel.c_str(), sText.getData()); 82 } 83 } 84 } 85 } 86 87 if (rcHotkey == 0) 88 { 89 // No accelerator found 90 return false; 91 } 92 93 sText.Take(output); 94 // done, success 95 return true; 96 } 97 MakeColorReadableOnBlack(DWORD & rdwClr)98 DWORD MakeColorReadableOnBlack(DWORD &rdwClr) 99 { 100 // max alpha 101 DWORD dwAlpha = std::max<DWORD>(rdwClr>>24&255, 0xff)<<24; 102 rdwClr &= 0xffffff; 103 // determine brightness 104 // 50% red, 87% green, 27% blue (max 164 * 255) 105 DWORD r=(rdwClr>>16&255), g=(rdwClr>>8&255), b=(rdwClr&255); 106 int32_t iLightness = r*50 + g*87 + b*27; 107 // above 65/164 (*255) is OK 108 if (iLightness < 16575) 109 { 110 int32_t iInc = (16575-iLightness) / 164; 111 // otherwise, lighten 112 rdwClr = (std::min<DWORD>(r+iInc, 255)<<16) | (std::min<DWORD>(g+iInc, 255)<<8) | std::min<DWORD>(b+iInc, 255); 113 } 114 // return color and alpha 115 rdwClr |= dwAlpha; 116 return rdwClr; 117 } 118 SetHorizontal(C4Surface & rBySfc,int iHeight,int iBorderWidth)119 void DynBarFacet::SetHorizontal(C4Surface &rBySfc, int iHeight, int iBorderWidth) 120 { 121 if (!iHeight) iHeight = rBySfc.Hgt; 122 if (!iBorderWidth) iBorderWidth = iHeight; 123 fctBegin.Set(&rBySfc,0,0,iBorderWidth,iHeight); 124 fctMiddle.Set(&rBySfc,iBorderWidth,0,rBySfc.Wdt-2*iBorderWidth,iHeight); 125 fctEnd.Set(&rBySfc,rBySfc.Wdt-iBorderWidth,0,iBorderWidth,iHeight); 126 } 127 SetHorizontal(C4Facet & rByFct,int32_t iBorderWidth)128 void DynBarFacet::SetHorizontal(C4Facet &rByFct, int32_t iBorderWidth) 129 { 130 if (!iBorderWidth) iBorderWidth = rByFct.Hgt; 131 fctBegin.Set(rByFct.Surface,rByFct.X,rByFct.Y,iBorderWidth,rByFct.Hgt); 132 fctMiddle.Set(rByFct.Surface,rByFct.Hgt,rByFct.X,rByFct.Y+rByFct.Wdt-2*iBorderWidth,rByFct.Hgt); 133 fctEnd.Set(rByFct.Surface,rByFct.X+rByFct.Wdt-iBorderWidth,rByFct.Y,iBorderWidth,rByFct.Hgt); 134 } 135 Set(const C4Facet & rByFct,int32_t iPinIndex)136 void ScrollBarFacets::Set(const C4Facet &rByFct, int32_t iPinIndex) 137 { 138 // set by hardcoded size 139 barScroll.fctBegin.Set(rByFct.Surface,0,0,16,16); 140 barScroll.fctMiddle.Set(rByFct.Surface,0,16,16,16); 141 barScroll.fctEnd.Set(rByFct.Surface,0,32,16,16); 142 fctScrollDTop.Set(rByFct.Surface,16,0,16,16); 143 if (iPinIndex) 144 fctScrollPin.Set(rByFct.Surface,32,16*(iPinIndex-1),16,16); 145 else 146 fctScrollPin.Set(rByFct.Surface,16,16,16,16); 147 fctScrollDBottom.Set(rByFct.Surface,16,32,16,16); 148 } 149 150 // -------------------------------------------------- 151 // Element 152 Element()153 Element::Element() 154 { 155 // pParent=nullptr invalidates pPrev/pNext 156 // fDragging=false invalidates iDragX/Y 157 // zero fields 158 rcBounds.Set(0,0,0,0); 159 } 160 ~Element()161 Element::~Element() 162 { 163 // delete context handler 164 if (pContextHandler) { pContextHandler->DeRef(); pContextHandler=nullptr; } 165 // remove from any container 166 if (pParent) 167 pParent->RemoveElement(this); 168 else if (this != Screen::GetScreenS() && Screen::GetScreenS()) 169 // always ensure removal from screen! 170 Screen::GetScreenS()->RemoveElement(this); 171 } 172 RemoveElement(Element * pChild)173 void Element::RemoveElement(Element *pChild) 174 { 175 // child removed: forward to parent 176 if (pParent) 177 pParent->RemoveElement(pChild); 178 else if (this != Screen::GetScreenS()) 179 // always ensure removal from screen! 180 // but not if this is the context menu, to avoid endless flip-flop! 181 if (!IsMenu()) 182 Screen::GetScreenS()->RemoveElement(pChild); 183 } 184 UpdateSize()185 void Element::UpdateSize() 186 { 187 // update own fields 188 UpdateOwnPos(); 189 // notify container 190 if (pParent) pParent->ElementSizeChanged(this); 191 } 192 UpdatePos()193 void Element::UpdatePos() 194 { 195 // update own fields 196 UpdateOwnPos(); 197 // notify container 198 if (pParent) pParent->ElementPosChanged(this); 199 } 200 IsVisible()201 bool Element::IsVisible() 202 { 203 // self and parent must be visible 204 return fVisible && (!pParent || pParent->IsVisible()); 205 } 206 SetVisibility(bool fToValue)207 void Element::SetVisibility(bool fToValue) 208 { 209 fVisible = fToValue; 210 // stop mouseover for invisible 211 if (!fVisible) 212 { 213 Screen *pScreen = GetScreen(); 214 if (pScreen) pScreen->Mouse.OnElementGetsInvisible(this); 215 } 216 } 217 ScreenPos2ClientPos(int32_t & riX,int32_t & riY)218 void Element::ScreenPos2ClientPos(int32_t &riX, int32_t &riY) 219 { 220 // apply all parent offsets 221 Container *pCont = pParent; 222 while (pCont) 223 { 224 pCont->ApplyElementOffset(riX, riY); 225 pCont = pCont->GetParent(); 226 } 227 // apply own offset 228 riX -= rcBounds.x; riY -= rcBounds.y; 229 } 230 ClientPos2ScreenPos(int32_t & riX,int32_t & riY)231 void Element::ClientPos2ScreenPos(int32_t &riX, int32_t &riY) 232 { 233 // apply all parent offsets 234 Container *pCont = pParent; 235 while (pCont) 236 { 237 pCont->ApplyInvElementOffset(riX, riY); 238 pCont = pCont->GetParent(); 239 } 240 // apply own offset 241 riX += rcBounds.x; riY += rcBounds.y; 242 } 243 MouseInput(CMouse & rMouse,int32_t iButton,int32_t iX,int32_t iY,DWORD dwKeyParam)244 void Element::MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam) 245 { 246 // store self as mouse-over-component 247 rMouse.pMouseOverElement = this; 248 // evaluate dragging 249 if (pDragTarget && iButton == C4MC_Button_LeftDown && !rMouse.pDragElement) 250 StartDragging(rMouse, iX, iY, dwKeyParam); 251 // right button down: open context menu 252 if (iButton == C4MC_Button_RightDown) 253 { 254 ContextHandler *pCtx = GetContextHandler(); 255 if (pCtx) pCtx->OnContext(this, iX, iY); 256 } 257 } 258 StartDragging(CMouse & rMouse,int32_t iX,int32_t iY,DWORD dwKeyParam)259 void Element::StartDragging(CMouse &rMouse, int32_t iX, int32_t iY, DWORD dwKeyParam) 260 { 261 // set flag 262 fDragging = true; 263 // set drag start pos 264 iDragX = iX; iDragY = iY; 265 // mark drag in mouse 266 rMouse.pDragElement = this; 267 } 268 DoDragging(CMouse & rMouse,int32_t iX,int32_t iY,DWORD dwKeyParam)269 void Element::DoDragging(CMouse &rMouse, int32_t iX, int32_t iY, DWORD dwKeyParam) 270 { 271 // check if anything moved 272 if (pDragTarget && (iX != iDragX || iY != iDragY)) 273 { 274 // move position, then 275 pDragTarget->rcBounds.x += iX-iDragX; 276 pDragTarget->rcBounds.y += iY-iDragY; 277 // drag X/Y is up-to-date if this is a child element of the drag target 278 pDragTarget->UpdatePos(); 279 } 280 } 281 StopDragging(CMouse & rMouse,int32_t iX,int32_t iY,DWORD dwKeyParam)282 void Element::StopDragging(CMouse &rMouse, int32_t iX, int32_t iY, DWORD dwKeyParam) 283 { 284 // move element pos 285 DoDragging(rMouse, iX, iY, dwKeyParam); 286 } 287 GetDlg()288 Dialog *Element::GetDlg () { if (pParent) return pParent->GetDlg (); return nullptr; } GetScreen()289 Screen *Element::GetScreen() { if (pParent) return pParent->GetScreen(); return nullptr; } 290 Draw3DFrame(C4TargetFacet & cgo,bool fUp,int32_t iIndent,BYTE byAlpha,bool fDrawTop,int32_t iTopOff,bool fDrawLeft,int32_t iLeftOff)291 void Element::Draw3DFrame(C4TargetFacet &cgo, bool fUp, int32_t iIndent, BYTE byAlpha, bool fDrawTop, int32_t iTopOff, bool fDrawLeft, int32_t iLeftOff) 292 { 293 DWORD dwAlpha = byAlpha<<24; 294 int32_t x0 = cgo.TargetX + rcBounds.x + iLeftOff, 295 y0 = cgo.TargetY + rcBounds.y + iTopOff, 296 x1 = cgo.TargetX + rcBounds.x + rcBounds.Wdt - 1, 297 y1 = cgo.TargetY + rcBounds.y + rcBounds.Hgt - 1; 298 if (fDrawTop) pDraw->DrawLineDw(cgo.Surface, (float)x0,(float)y0,(float)x1,(float)y0, C4GUI_BorderColor1 | dwAlpha); 299 if (fDrawLeft) pDraw->DrawLineDw(cgo.Surface, (float)x0,(float)y0,(float)x0,(float)y1, C4GUI_BorderColor1 | dwAlpha); 300 if (fDrawTop) pDraw->DrawLineDw(cgo.Surface, (float)(x0+1),(float)(y0+1),(float)(x1-1),(float)(y0+1), C4GUI_BorderColor2 | dwAlpha); 301 if (fDrawLeft) pDraw->DrawLineDw(cgo.Surface, (float)(x0+1),(float)(y0+1),(float)(x0+1),(float)(y1-1), C4GUI_BorderColor2 | dwAlpha); 302 pDraw->DrawLineDw(cgo.Surface, (float)x0,(float)y1,(float)x1,(float)y1, C4GUI_BorderColor3 | dwAlpha); 303 pDraw->DrawLineDw(cgo.Surface, (float)x1,(float)y0,(float)x1,(float)y1, C4GUI_BorderColor3 | dwAlpha); 304 pDraw->DrawLineDw(cgo.Surface, (float)(x0+1),(float)(y1-1),(float)(x1-1),(float)(y1-1), C4GUI_BorderColor1 | dwAlpha); 305 pDraw->DrawLineDw(cgo.Surface, (float)(x1-1),(float)(y0+1),(float)(x1-1),(float)(y1-1), C4GUI_BorderColor1 | dwAlpha); 306 } 307 DrawBar(C4TargetFacet & cgo,DynBarFacet & rFacets)308 void Element::DrawBar(C4TargetFacet &cgo, DynBarFacet &rFacets) 309 { 310 if (rcBounds.Hgt == rFacets.fctMiddle.Hgt) 311 { 312 // exact bar 313 int32_t x0=cgo.TargetX+rcBounds.x, y0=cgo.TargetY+rcBounds.y; 314 int32_t iX = rFacets.fctBegin.Wdt, w=rFacets.fctMiddle.Wdt, wLeft=rFacets.fctBegin.Wdt, wRight=rFacets.fctEnd.Wdt; 315 int32_t iRightShowLength = wRight/3; 316 bool fOverflow = (wLeft > rcBounds.Wdt); 317 if (fOverflow) rFacets.fctBegin.Wdt = rcBounds.Wdt; 318 rFacets.fctBegin.Draw(cgo.Surface, x0,y0); 319 if (fOverflow) rFacets.fctBegin.Wdt = wLeft; 320 while (iX < rcBounds.Wdt-iRightShowLength) 321 { 322 int32_t w2=std::min(w, rcBounds.Wdt-iRightShowLength-iX); rFacets.fctMiddle.Wdt=w2; 323 rFacets.fctMiddle.Draw(cgo.Surface, x0+iX, y0); 324 iX += w; 325 } 326 rFacets.fctMiddle.Wdt=w; 327 fOverflow = (wRight > rcBounds.Wdt); 328 if (fOverflow) 329 { 330 rFacets.fctEnd.X += wRight - rcBounds.Wdt; 331 rFacets.fctEnd.Wdt = rcBounds.Wdt; 332 } 333 rFacets.fctEnd.Draw(cgo.Surface, x0+rcBounds.Wdt-rFacets.fctEnd.Wdt, y0); 334 if (fOverflow) 335 { 336 rFacets.fctEnd.X -= wRight - rcBounds.Wdt; 337 rFacets.fctEnd.Wdt = wRight; 338 } 339 } 340 else 341 { 342 // zoomed bar 343 float fZoom = (float) rcBounds.Hgt / rFacets.fctMiddle.Hgt; 344 int32_t x0=cgo.TargetX+rcBounds.x, y0=cgo.TargetY+rcBounds.y; 345 int32_t iX = int32_t(fZoom*rFacets.fctBegin.Wdt), w=int32_t(fZoom*rFacets.fctMiddle.Wdt), wOld=rFacets.fctMiddle.Wdt; 346 int32_t iRightShowLength = rFacets.fctEnd.Wdt/3; 347 rFacets.fctBegin.DrawX(cgo.Surface, x0,y0,int32_t(fZoom*rFacets.fctBegin.Wdt),rcBounds.Hgt); 348 while (iX < rcBounds.Wdt-(fZoom*iRightShowLength)) 349 { 350 int32_t w2=std::min<int32_t>(w, rcBounds.Wdt-int32_t(fZoom*iRightShowLength)-iX); rFacets.fctMiddle.Wdt=long(float(w2)/fZoom); 351 rFacets.fctMiddle.DrawX(cgo.Surface, x0+iX, y0, w2,rcBounds.Hgt); 352 iX += w; 353 } 354 rFacets.fctMiddle.Wdt=wOld; 355 rFacets.fctEnd.DrawX(cgo.Surface, x0+rcBounds.Wdt-int32_t(fZoom*rFacets.fctEnd.Wdt), y0,int32_t(fZoom*rFacets.fctEnd.Wdt),rcBounds.Hgt); 356 } 357 } 358 DrawVBar(C4TargetFacet & cgo,DynBarFacet & rFacets)359 void Element::DrawVBar(C4TargetFacet &cgo, DynBarFacet &rFacets) 360 { 361 C4DrawTransform trf(1); 362 DrawHVBar(cgo, rFacets, trf, rcBounds.Hgt); 363 } 364 DrawHBarByVGfx(C4TargetFacet & cgo,DynBarFacet & rFacets)365 void Element::DrawHBarByVGfx(C4TargetFacet &cgo, DynBarFacet &rFacets) 366 { 367 C4DrawTransform trf; 368 float fOffX = cgo.TargetX + rcBounds.x + rcBounds.Hgt/2; 369 float fOffY = cgo.TargetY + rcBounds.y + rcBounds.Hgt/2; 370 trf.SetRotate(-90.0f, fOffX, fOffY); 371 372 DrawHVBar(cgo, rFacets, trf, rcBounds.Wdt); 373 } 374 DrawHVBar(C4TargetFacet & cgo,DynBarFacet & rFacets,C4DrawTransform & trf,int32_t iMiddleLength)375 void Element::DrawHVBar(C4TargetFacet &cgo, DynBarFacet &rFacets, C4DrawTransform &trf, int32_t iMiddleLength) 376 { 377 int32_t y0 = cgo.TargetY + rcBounds.y; 378 int32_t x0 = cgo.TargetX + rcBounds.x; 379 380 // draw up arrow 381 rFacets.fctBegin.DrawT(cgo.Surface, x0, y0, 0, 0, &trf); 382 383 // draw middle part 384 int32_t h = rFacets.fctMiddle.Hgt; 385 int32_t barHeight = iMiddleLength - (rFacets.fctBegin.Hgt + rFacets.fctEnd.Hgt); 386 387 for (int32_t iY = 0; iY <= barHeight; iY += h) 388 { 389 int32_t h2 = std::min(h, barHeight - iY); 390 rFacets.fctMiddle.Hgt = h2; 391 rFacets.fctMiddle.DrawT(cgo.Surface, x0, y0 + rFacets.fctBegin.Hgt + iY, 0, 0, &trf); 392 } 393 rFacets.fctMiddle.Hgt = h; 394 395 // draw lower arrow 396 rFacets.fctEnd.DrawT(cgo.Surface, x0, y0 + iMiddleLength - rFacets.fctEnd.Hgt, 0, 0, &trf); 397 } 398 GetToprightCornerRect(int32_t iWidth,int32_t iHeight,int32_t iHIndent,int32_t iVIndent,int32_t iIndexX)399 C4Rect Element::GetToprightCornerRect(int32_t iWidth, int32_t iHeight, int32_t iHIndent, int32_t iVIndent, int32_t iIndexX) 400 { 401 // bounds by topright corner of element 402 C4Rect rtBounds = (GetContainer() != this) ? GetClientRect() : GetContainedClientRect(); 403 rtBounds.x += rtBounds.Wdt - (iWidth + iHIndent) * (iIndexX + 1); 404 rtBounds.y += iVIndent; 405 rtBounds.Wdt = rtBounds.Hgt = iHeight; 406 return rtBounds; 407 } 408 SetToolTip(const char * szNewTooltip,bool is_immediate)409 void Element::SetToolTip(const char *szNewTooltip, bool is_immediate) 410 { 411 // store tooltip 412 if (szNewTooltip) ToolTip.Copy(szNewTooltip); else ToolTip.Clear(); 413 // store immediate flag 414 is_immediate_tooltip = is_immediate; 415 } 416 DoContext()417 bool Element::DoContext() 418 { 419 if (!pContextHandler) return false; 420 return pContextHandler->OnContext(this, rcBounds.Wdt/2, rcBounds.Hgt/2); 421 } 422 GetToolTip()423 const char *Element::GetToolTip() 424 { 425 // fallback to parent tooltip, if own is not assigned 426 return (!pParent || !ToolTip.isNull()) ? ToolTip.getData() : pParent->GetToolTip(); 427 } 428 GetContextHandler()429 ContextHandler *Element::GetContextHandler() 430 { 431 // fallback to parent context, if own is not assigned 432 return (!pParent || pContextHandler) ? pContextHandler : pParent->GetContextHandler(); 433 } 434 IsInActiveDlg(bool fForKeyboard)435 bool Element::IsInActiveDlg(bool fForKeyboard) 436 { 437 // get dlg 438 Dialog *pDlg=GetDlg(); 439 if (!pDlg) return false; 440 // check if dlg is active 441 return pDlg->IsActive(fForKeyboard); 442 } 443 444 445 // -------------------------------------------------- 446 // CMouse 447 CMouse(int32_t iX,int32_t iY)448 CMouse::CMouse(int32_t iX, int32_t iY) : fActive(true), fActiveInput(false) 449 { 450 // set pos 451 x=iX; y=iY; 452 // reset fields 453 LDown=MDown=RDown=false; 454 dwKeys=0; 455 pMouseOverElement = pPrevMouseOverElement = nullptr; 456 pDragElement = nullptr; 457 ResetToolTipTime(); 458 // LDownX/Y initialized upon need 459 } 460 461 CMouse::~CMouse() = default; 462 Input(int32_t iButton,int32_t iX,int32_t iY,DWORD dwKeyParam)463 void CMouse::Input(int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam) 464 { 465 // pos changed or click issued? 466 if (iButton || iX!=x || iY!=y) 467 { 468 // then hide tooltips for a while 469 ResetToolTipTime(); 470 // and mark as active input device 471 fActiveInput = true; 472 } 473 // copy fields 474 x=iX; y=iY; dwKeys=dwKeyParam; 475 // update buttons 476 switch (iButton) 477 { 478 case C4MC_Button_LeftDown: LDown=true; LDownX=x; LDownY=y; break; 479 case C4MC_Button_LeftUp: LDown=false; break; 480 case C4MC_Button_RightDown: RDown=true; break; 481 case C4MC_Button_RightUp: RDown=false; break; 482 } 483 } 484 Draw(C4TargetFacet & cgo,TooltipShowState draw_tool_tips)485 void CMouse::Draw(C4TargetFacet &cgo, TooltipShowState draw_tool_tips) 486 { 487 // only if owned 488 if (!fActive) return; 489 490 // Make sure to draw the cursor without zoom. 491 ZoomData GuiZoom; 492 pDraw->GetZoom(&GuiZoom); 493 const float oldZoom = GuiZoom.Zoom; 494 GuiZoom.Zoom = 1.0; 495 pDraw->SetZoom(GuiZoom); 496 497 int32_t iOffsetX = -GfxR->fctMouseCursor.Wdt/2; 498 int32_t iOffsetY = -GfxR->fctMouseCursor.Hgt/2; 499 GfxR->fctMouseCursor.Draw(cgo.Surface,x+iOffsetX,y+iOffsetY,0); 500 // ToolTip 501 if (pMouseOverElement && draw_tool_tips != TTST_None) 502 { 503 if (draw_tool_tips == TTST_All || pMouseOverElement->IsImmediateToolTip()) 504 { 505 const char *szTip = pMouseOverElement->GetToolTip(); 506 if (szTip && *szTip) 507 { 508 C4TargetFacet cgoTip; cgoTip.Set(cgo.Surface, cgo.X, cgo.Y, cgo.Wdt, cgo.Hgt); 509 Screen::DrawToolTip(szTip, cgoTip, x, y); 510 } 511 } 512 } 513 514 // And restore old zoom settings. 515 GuiZoom.Zoom = oldZoom; 516 pDraw->SetZoom(GuiZoom); 517 } 518 ReleaseElements()519 void CMouse::ReleaseElements() 520 { 521 // release MouseOver 522 if (pMouseOverElement) pMouseOverElement->MouseLeave(*this); 523 // release drag 524 if (pDragElement) 525 { 526 int32_t iX, iY; DWORD dwKeys; 527 GetLastXY(iX, iY, dwKeys); 528 pDragElement->ScreenPos2ClientPos(iX, iY); 529 pDragElement->StopDragging(*this, iX, iY, dwKeys); 530 } 531 pPrevMouseOverElement = pMouseOverElement = pDragElement = nullptr; 532 } 533 RemoveElement(Element * pChild)534 void CMouse::RemoveElement(Element *pChild) 535 { 536 // clear ptr 537 if (pMouseOverElement == pChild) 538 { 539 pMouseOverElement->MouseLeave(*this); // do leave callback so any tooltip is cleared! 540 pMouseOverElement = nullptr; 541 } 542 if (pPrevMouseOverElement == pChild) pPrevMouseOverElement = nullptr; 543 if (pDragElement == pChild) pDragElement = nullptr; 544 } 545 OnElementGetsInvisible(Element * pChild)546 void CMouse::OnElementGetsInvisible(Element *pChild) 547 { 548 // clear ptr 549 RemoveElement(pChild); 550 } 551 552 553 // -------------------------------------------------- 554 // Screen 555 RemoveElement(Element * pChild)556 void Screen::RemoveElement(Element *pChild) 557 { 558 // inherited 559 Window::RemoveElement(pChild); 560 // clear ptrs 561 if (pActiveDlg == pChild) { pActiveDlg = nullptr; Mouse.ResetElements(); } 562 Mouse.RemoveElement(pChild); 563 if (pContext) 564 { 565 if (pContext == pChild) pContext=nullptr; 566 else pContext->RemoveElement(pChild); 567 } 568 } 569 Screen()570 Screen::Screen() : Window(), Mouse(0, 0) 571 { 572 // no dialog active 573 pActiveDlg = nullptr; 574 // set static var 575 pScreen = this; 576 } 577 Init(int32_t tx,int32_t ty,int32_t twdt,int32_t thgt)578 void Screen::Init(int32_t tx, int32_t ty, int32_t twdt, int32_t thgt) 579 { 580 Mouse.x = tx+twdt/2; 581 Mouse.y = ty+thgt/2; 582 fZoom = 1.0f; 583 // set size - calcs client area as well 584 SetBounds(C4Rect(tx,ty,twdt,thgt)); 585 SetPreferredDlgRect(C4Rect(0,0,twdt,thgt)); 586 } 587 Clear()588 void Screen::Clear() 589 { 590 Container::Clear(); 591 // dtor: Close context menu 592 AbortContext(false); 593 // fields reset 594 fExclusive = true; 595 fZoom = 1.0f; 596 } 597 ~Screen()598 Screen::~Screen() 599 { 600 // clear singleton 601 if (this == pScreen) pScreen = nullptr; 602 } 603 ElementPosChanged(Element * pOfElement)604 void Screen::ElementPosChanged(Element *pOfElement) 605 { 606 // redraw fullscreen BG if dlgs are dragged around in shared mode 607 if (!IsExclusive()) 608 ::GraphicsSystem.InvalidateBg(); 609 } 610 ShowDialog(Dialog * pDlg,bool fFade)611 void Screen::ShowDialog(Dialog *pDlg, bool fFade) 612 { 613 assert(pDlg); 614 // do place console mode dialogs 615 if (!Application.isEditor || pDlg->IsViewportDialog()) 616 // exclusive or free dlg: center pos 617 // evaluate own placement proc first 618 if (!pDlg->DoPlacement(this, PreferredDlgRect)) 619 { 620 if (pDlg->IsFreePlaceDialog()) 621 pDlg->SetPos((GetWidth() - pDlg->GetWidth()) / 2, (GetHeight() - pDlg->GetHeight()) / 2 + pDlg->IsBottomPlacementDialog()*GetHeight()/3); 622 else if (IsExclusive()) 623 pDlg->SetPos((GetWidth() - pDlg->GetWidth()) / 2, (GetHeight() - pDlg->GetHeight()) / 2); 624 else 625 // non-exclusive mode at preferred viewport pos 626 pDlg->SetPos(PreferredDlgRect.x+30, PreferredDlgRect.y+30); 627 } 628 // add to local component list at correct ordering 629 int32_t iNewZ = pDlg->GetZOrdering(); Element *pEl; Dialog *pOtherDlg; 630 for (pEl = GetFirst(); pEl; pEl = pEl->GetNext()) 631 if ((pOtherDlg = pEl->GetDlg())) 632 if (pOtherDlg->GetZOrdering() > iNewZ) 633 break; 634 InsertElement(pDlg, pEl); 635 // set as active, if not fading and on top 636 if (!fFade && !pEl) 637 // but not viewport dialogs! 638 if (!pDlg->IsExternalDrawDialog()) 639 pActiveDlg = pDlg; 640 // show it 641 pDlg->fOK = false; 642 pDlg->fShow = true; 643 // mouse focus might have changed 644 UpdateMouseFocus(); 645 } 646 ActivateDialog(Dialog * pDlg)647 void Screen::ActivateDialog(Dialog *pDlg) 648 { 649 // no change? 650 if (pActiveDlg == pDlg) return; 651 // in single-mode: release any MouseOver/Drag of previous dlg 652 if (IsExclusive()) 653 Mouse.ReleaseElements(); 654 // close any context menu 655 AbortContext(false); 656 // set as active dlg 657 pActiveDlg = pDlg; 658 // ensure it's last in the list, if it's not a specially ordered dlg 659 if (!pDlg->GetZOrdering() && pDlg->GetNext()) 660 MakeLastElement(pDlg); 661 } 662 CloseDialog(Dialog * pDlg,bool fFade)663 void Screen::CloseDialog(Dialog *pDlg, bool fFade) 664 { 665 // hide dlg 666 if (!fFade) pDlg->fShow = false; 667 // kill from active 668 if (pActiveDlg == pDlg) 669 { 670 // release any MouseOver/Drag of previous dlg 671 Mouse.ReleaseElements(); 672 // close context menu: probably belonging to closed dlg anyway 673 AbortContext(false); 674 // set new active dlg 675 pActiveDlg = GetTopDialog(); 676 // do not set yet if it's fading 677 if (pActiveDlg && pActiveDlg->IsFading()) pActiveDlg = nullptr; 678 } 679 // redraw background; clip update 680 ::GraphicsSystem.InvalidateBg(); UpdateMouseFocus(); 681 } 682 RecheckActiveDialog()683 void Screen::RecheckActiveDialog() 684 { 685 Dialog *pNewTop = GetTopDialog(); 686 if (pActiveDlg == pNewTop) return; 687 Mouse.ReleaseElements(); 688 // do not set yet if it's fading 689 if (pActiveDlg && pActiveDlg->IsFading()) pActiveDlg = nullptr; 690 } 691 GetTopDialog()692 Dialog *Screen::GetTopDialog() 693 { 694 // search backwards in component list 695 Dialog *pDlg; 696 for (Element *pEl = pLast; pEl; pEl = pEl->GetPrev()) 697 if ((pDlg = pEl->GetDlg())) 698 if (pDlg->IsShown()) 699 return pDlg; 700 // no dlg found 701 return nullptr; 702 } 703 CloseAllDialogs(bool fWithOK)704 void Screen::CloseAllDialogs(bool fWithOK) 705 { 706 while (pActiveDlg) pActiveDlg->Close(fWithOK); 707 } 708 #ifdef USE_WIN32_WINDOWS GetDialog(HWND hWindow)709 Dialog *Screen::GetDialog(HWND hWindow) 710 { 711 // get dialog with matching handle 712 Dialog *pDlg; 713 for (Element *pEl = pLast; pEl; pEl = pEl->GetPrev()) 714 if ((pDlg = pEl->GetDlg())) 715 if (pDlg->pWindow && pDlg->pWindow->hWindow == hWindow) 716 return pDlg; 717 return nullptr; 718 } 719 #endif GetDialog(C4Window * pWindow)720 Dialog *Screen::GetDialog(C4Window * pWindow) 721 { 722 // get dialog with matching window 723 Dialog *pDlg; 724 for (Element *pEl = pLast; pEl; pEl = pEl->GetPrev()) 725 if ( (pDlg = pEl->GetDlg()) != nullptr) 726 if (pDlg->pWindow == pWindow) 727 return pDlg; 728 return nullptr; 729 } Render(bool fDoBG)730 void Screen::Render(bool fDoBG) 731 { 732 // get output cgo 733 C4TargetFacet cgo; 734 cgo.Set(FullScreen.pSurface, rcBounds); 735 // draw to it 736 Draw(cgo, fDoBG); 737 } 738 RenderMouse(C4TargetFacet & cgo)739 void Screen::RenderMouse(C4TargetFacet &cgo) 740 { 741 // draw mouse cursor 742 // All tool tips hidden during keyboard input. Immediate tooltips hidden if mouse was moving recently. 743 Mouse.Draw(cgo, Mouse.IsActiveInput() ? Mouse.IsMouseStill() ? CMouse::TTST_All : CMouse::TTST_Immediate : CMouse::TTST_None); 744 } 745 Draw(C4TargetFacet & cgo,bool fDoBG)746 void Screen::Draw(C4TargetFacet &cgo, bool fDoBG) 747 { 748 // draw bg, if this won't be done by a fullscreen dialog 749 if (fDoBG) 750 { 751 Dialog *pFSDlg = GetFullscreenDialog(false); 752 if (!pFSDlg || !pFSDlg->HasBackground()) 753 { 754 if (::GraphicsSystem.pLoaderScreen) 755 ::GraphicsSystem.pLoaderScreen->Draw(cgo, C4LoaderScreen::Flag::BACKGROUND); 756 else 757 // loader not yet loaded: black BG 758 pDraw->DrawBoxDw(cgo.Surface, 0,0, cgo.Wdt+1, cgo.Hgt+1, 0x00000000); 759 } 760 } 761 // draw contents (if GUI-gfx are loaded, which is assumed in GUI-drawing-functions) 762 if (IsVisible() && ::GraphicsResource.IsInitialized()) 763 { 764 Window::Draw(cgo); 765 if (pContext) pContext->Draw(cgo); 766 } 767 // draw mouse cursor 768 if (!Application.isEditor) RenderMouse(cgo); 769 } 770 KeyAny()771 bool Screen::KeyAny() 772 { 773 // mark keystroke in mouse 774 Mouse.ResetActiveInput(); 775 // key not yet processed 776 return false; 777 } 778 CharIn(const char * c)779 bool Screen::CharIn(const char * c) 780 { 781 // Special: Tab chars are ignored, because they are always handled as focus advance 782 if (c[0] == 0x09) return false; 783 // mark in mouse 784 Mouse.ResetActiveInput(); 785 // no processing if focus is not set 786 if (!HasKeyboardFocus()) return false; 787 // always return true in exclusive mode (which means: key processed) 788 bool fResult = IsExclusive(); 789 // context menu: forward to context 790 if (pContext) return pContext->CharIn(c) || fResult; 791 // no active dlg? 792 if (!pActiveDlg || !pActiveDlg->IsVisible()) return fResult; 793 // forward to dialog 794 return pActiveDlg->CharIn(c) || fResult; 795 } 796 MouseMove(int32_t iButton,int32_t iX,int32_t iY,DWORD dwKeyParam,class C4Viewport * pVP)797 void Screen::MouseMove(int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam, class C4Viewport *pVP) 798 { 799 // Special: Pass to MouseControl if dragging and button is not upped 800 if (IsActive() && !::MouseControl.IsDragging()) 801 { 802 bool fResult = MouseInput(iButton, iX, iY, dwKeyParam, nullptr, pVP); 803 if (HasMouseFocus()) { SetMouseInGUI(true, true); return; } 804 // non-exclusive GUI: inform mouse-control about GUI-result 805 SetMouseInGUI(fResult, true); 806 // abort if GUI processed it 807 if (fResult) return; 808 } 809 else 810 // no GUI: mouse is not in GUI 811 SetMouseInGUI(false, true); 812 // mouse control enabled? 813 if (!::MouseControl.IsActive()) 814 { 815 // enable mouse in GUI, if a mouse-only-dlg is displayed 816 if (GetMouseControlledDialogCount()) 817 SetMouseInGUI(true, true); 818 return; 819 } 820 // Pass on to mouse controlled viewport 821 ::Viewports.MouseMoveToViewport(iButton, iX, iY, dwKeyParam); 822 } 823 SetMouseInGUI(bool fInGUI,bool fByMouse)824 void Screen::SetMouseInGUI(bool fInGUI, bool fByMouse) 825 { 826 // inform mouse control and GUI 827 Mouse.SetOwnedMouse(fInGUI); 828 // initial movement to ensure mouse control pos is correct 829 if (!::MouseControl.IsMouseOwned() && !fInGUI && !fByMouse) 830 { 831 ::MouseControl.SetOwnedMouse(true); 832 ::Viewports.MouseMoveToViewport(C4MC_Button_None, int32_t(::pGUI->Mouse.x*C4GUI::GetZoom()), int32_t(::pGUI->Mouse.y*C4GUI::GetZoom()), ::pGUI->Mouse.dwKeys); 833 } 834 ::MouseControl.SetOwnedMouse(!fInGUI); 835 } 836 MouseInput(int32_t iButton,int32_t iPxX,int32_t iPxY,DWORD dwKeyParam,Dialog * pForDlg,class C4Viewport * pForVP)837 bool Screen::MouseInput(int32_t iButton, int32_t iPxX, int32_t iPxY, DWORD dwKeyParam, Dialog *pForDlg, class C4Viewport *pForVP) 838 { 839 // convert from screen pixel coordinates to GUI coordinates 840 float fZoom = pForDlg ? 1.0f : GetZoom(); // Developer mode dialogs are currently drawn unzoomed 841 float fX = float(iPxX) / fZoom; 842 float fY = float(iPxY) / fZoom; 843 // forward to mouse 844 Mouse.Input(iButton, fX, fY, dwKeyParam); 845 846 // dragging 847 if (Mouse.pDragElement) 848 { 849 int32_t iX2=fX, iY2=fY; 850 Mouse.pDragElement->ScreenPos2ClientPos(iX2, iY2); 851 if (!Mouse.IsLDown()) 852 { 853 // stop dragging 854 Mouse.pDragElement->StopDragging(Mouse, iX2, iY2, dwKeyParam); 855 Mouse.pDragElement = nullptr; 856 } 857 else 858 { 859 // continue dragging 860 Mouse.pDragElement->DoDragging(Mouse, iX2, iY2, dwKeyParam); 861 } 862 } 863 // backup previous MouseOver-element 864 Mouse.pPrevMouseOverElement = Mouse.pMouseOverElement; 865 Mouse.pMouseOverElement = nullptr; 866 bool fProcessed = false; 867 // active context menu? 868 if (!pForVP && pContext && pContext->CtxMouseInput(Mouse, iButton, fX, fY, dwKeyParam)) 869 { 870 // processed by context menu: OK! 871 } 872 // otherwise: active dlg and inside screen? (or direct forward to specific dlg/viewport dlg) 873 else if (rcBounds.Contains(fX, fY) || pForDlg || pForVP) 874 { 875 // context menu open but mouse down command issued? close context then 876 if (pContext && (iButton == C4MC_Button_LeftDown || iButton == C4MC_Button_RightDown)) 877 AbortContext(true); 878 // get client pos 879 if (!pForDlg && !pForVP) 880 { 881 C4Rect &rcClientArea = GetClientRect(); 882 fX -= rcClientArea.x; fY -= rcClientArea.y; 883 } 884 // exclusive mode: process active dialog only 885 if (IsExclusive() && !pForDlg && !pForVP) 886 { 887 if (pActiveDlg && pActiveDlg->IsVisible() && !pActiveDlg->IsFading()) 888 { 889 // bounds check to dlg: only if not dragging 890 C4Rect &rcDlgBounds = pActiveDlg->GetBounds(); 891 if (Mouse.IsLDown() || rcDlgBounds.Contains(fX, fY)) 892 // forward to active dialog 893 pActiveDlg->MouseInput(Mouse, iButton, fX - rcDlgBounds.x, fY - rcDlgBounds.y, dwKeyParam); 894 else 895 Mouse.pMouseOverElement = nullptr; 896 } 897 else 898 // outside dialog: own handling (for screen context menu) 899 Window::MouseInput(Mouse, iButton, fX, fY, dwKeyParam); 900 } 901 else 902 { 903 // non-exclusive mode: process all dialogs; make them active on left-click 904 Dialog *pDlg; 905 for (Element *pEl = pLast; pEl; pEl = pEl->GetPrev()) 906 if ((pDlg = pEl->GetDlg())) 907 if (pDlg->IsShown()) 908 { 909 // if specified: process specified dlg only 910 if (pForDlg && pDlg != pForDlg) continue; 911 // if specified: process specified viewport only 912 bool fIsExternalDrawDialog = pDlg->IsExternalDrawDialog(); 913 C4Viewport *pVP = fIsExternalDrawDialog ? pDlg->GetViewport() : nullptr; 914 if (pForVP && pForVP != pVP) continue; 915 // calc offset 916 C4Rect &rcDlgBounds = pDlg->GetBounds(); 917 int32_t iOffX=0, iOffY=0; 918 // special handling for viewport dialogs 919 if (fIsExternalDrawDialog) 920 { 921 // ignore external drawing dialogs without a viepwort assigned 922 if (!pVP) continue; 923 // always clip to viewport bounds 924 C4Rect rcOut(pVP->GetOutputRect()); 925 if (!rcOut.Contains(fX + rcBounds.x, fY + rcBounds.y)) continue; 926 // viewport dialogs: Offset determined by viewport position 927 iOffX = rcOut.x; iOffY = rcOut.y; 928 } 929 // hit test; or special: dragging possible outside active dialog 930 if (rcDlgBounds.Contains(fX-iOffX, fY-iOffY) || (pDlg == pActiveDlg && Mouse.pDragElement && Mouse.pDragElement->GetDlg() == pDlg)) 931 { 932 // Okay; do input 933 pDlg->MouseInput(Mouse, iButton, fX - rcDlgBounds.x - iOffX, fY - rcDlgBounds.y - iOffY, dwKeyParam); 934 // CAUTION: pDlg may be invalid now! 935 // set processed-flag manually 936 fProcessed = true; 937 // inactive dialogs get activated by clicks 938 if (Mouse.IsLDown() && pDlg != pActiveDlg) 939 // but not viewport dialogs! 940 if (!pDlg->IsExternalDrawDialog()) 941 ActivateDialog(pDlg); 942 // one dlg only; break loop here 943 break; 944 } 945 } 946 } 947 } 948 949 // check if MouseOver has changed 950 if (Mouse.pPrevMouseOverElement != Mouse.pMouseOverElement) 951 { 952 // send events 953 if (Mouse.pPrevMouseOverElement) Mouse.pPrevMouseOverElement->MouseLeave(Mouse); 954 if (Mouse.pMouseOverElement) Mouse.pMouseOverElement->MouseEnter(Mouse); 955 } 956 // return whether anything processed it 957 return fProcessed || Mouse.pDragElement || (Mouse.pMouseOverElement && Mouse.pMouseOverElement!=this) || pContext; 958 } 959 RecheckMouseInput()960 bool Screen::RecheckMouseInput() 961 { 962 return MouseInput(C4MC_Button_None, Mouse.x, Mouse.y, Mouse.dwKeys, nullptr, nullptr); 963 } 964 UpdateMouseFocus()965 void Screen::UpdateMouseFocus() 966 { 967 // when exclusive mode has changed: Make sure mouse clip is correct 968 ::MouseControl.UpdateClip(); 969 } 970 DoContext(ContextMenu * pNewCtx,Element * pAtElement,int32_t iX,int32_t iY)971 void Screen::DoContext(ContextMenu *pNewCtx, Element *pAtElement, int32_t iX, int32_t iY) 972 { 973 assert(pNewCtx); assert(pNewCtx != pContext); 974 // close previous context menu 975 AbortContext(false); 976 // element offset 977 if (pAtElement) pAtElement->ClientPos2ScreenPos(iX, iY); 978 // usually open bottom right 979 // check bottom bounds 980 if (iY + pNewCtx->GetBounds().Hgt >= GetBounds().Hgt) 981 { 982 // bottom too narrow: open to top, if height is sufficient 983 // otherwise, open to top from bottom screen pos 984 if (iY < pNewCtx->GetBounds().Hgt) iY = GetBounds().Hgt; 985 iY -= pNewCtx->GetBounds().Hgt; 986 } 987 // check right bounds likewise 988 if (iX + pNewCtx->GetBounds().Wdt >= GetBounds().Wdt) 989 { 990 // bottom too narrow: open to top, if height is sufficient 991 // otherwise, open to top from bottom screen pos 992 if (iX < pNewCtx->GetBounds().Wdt) iX = GetBounds().Wdt; 993 iX -= pNewCtx->GetBounds().Wdt; 994 } 995 // open new 996 (pContext = pNewCtx)->Open(pAtElement, iX, iY); 997 } 998 GetMouseControlledDialogCount()999 int32_t Screen::GetMouseControlledDialogCount() 1000 { 1001 Dialog *pDlg; int32_t iResult=0; 1002 for (Element *pEl = GetFirst(); pEl; pEl = pEl->GetNext()) 1003 if ((pDlg = pEl->GetDlg())) 1004 if (pDlg->IsShown() && pDlg->IsMouseControlled()) 1005 ++iResult; 1006 return iResult; 1007 } 1008 DrawToolTip(const char * szTip,C4TargetFacet & cgo,float x,float y)1009 void Screen::DrawToolTip(const char *szTip, C4TargetFacet &cgo, float x, float y) 1010 { 1011 CStdFont *pUseFont = &(::GraphicsResource.TooltipFont); 1012 StdStrBuf sText; 1013 pUseFont->BreakMessage(szTip, std::min<int32_t>(C4GUI_MaxToolTipWdt, std::max<int32_t>(cgo.Wdt, 50)), &sText, true); 1014 // get tooltip rect 1015 int32_t tWdt,tHgt; 1016 if (pUseFont->GetTextExtent(sText.getData(), tWdt, tHgt, true)) 1017 { 1018 tWdt+=6; tHgt+=4; 1019 int32_t tX, tY; 1020 if (y < cgo.Y+cgo.TargetY+tHgt+5) tY = std::min<int32_t>(y+5, cgo.TargetY+cgo.Hgt-tHgt); else tY = y-tHgt-5; 1021 tX = Clamp<int32_t>(x-tWdt/2, cgo.TargetX+cgo.X, cgo.TargetX+cgo.Wdt-tWdt); 1022 // draw tooltip box 1023 pDraw->DrawBoxDw(cgo.Surface, tX,tY,tX+tWdt-1,tY+tHgt-2, C4GUI_ToolTipBGColor); 1024 pDraw->DrawFrameDw(cgo.Surface, tX,tY,tX+tWdt-1,tY+tHgt-1, C4GUI_ToolTipFrameColor); 1025 // draw tooltip 1026 pDraw->TextOut(sText.getData(), *pUseFont, 1.0f, cgo.Surface, tX+3,tY+1, C4GUI_ToolTipColor, ALeft); 1027 // while there's a tooltip, redraw the bg, because it might overlap 1028 ::GraphicsSystem.InvalidateBg(); 1029 } 1030 } 1031 HasFullscreenDialog(bool fIncludeFading)1032 bool Screen::HasFullscreenDialog(bool fIncludeFading) 1033 { 1034 return !!GetFullscreenDialog(fIncludeFading); 1035 } 1036 GetFullscreenDialog(bool fIncludeFading)1037 Dialog *Screen::GetFullscreenDialog(bool fIncludeFading) 1038 { 1039 Dialog *pDlg; 1040 for (Element *pEl = GetFirst(); pEl; pEl = pEl->GetNext()) 1041 if ((pDlg = pEl->GetDlg())) 1042 if (pDlg->IsVisible()) 1043 if (pDlg->IsFullscreenDialog()) 1044 if (fIncludeFading || !pDlg->IsFading()) 1045 return pDlg; 1046 return nullptr; 1047 } 1048 UpdateGamepadGUIControlEnabled()1049 void Screen::UpdateGamepadGUIControlEnabled() 1050 { 1051 // Gamepad is always kept open now. 1052 } 1053 1054 Screen TheScreen; 1055 1056 // -------------------------------------------------- 1057 // ComponentAligner 1058 GetFromTop(int32_t iHgt,int32_t iWdt,C4Rect & rcOut)1059 bool ComponentAligner::GetFromTop(int32_t iHgt, int32_t iWdt, C4Rect &rcOut) 1060 { 1061 rcOut.x = rcClientArea.x + iMarginX; 1062 rcOut.y = rcClientArea.y + iMarginY; 1063 rcOut.Wdt = rcClientArea.Wdt - iMarginX * 2; 1064 rcOut.Hgt = iHgt; 1065 int32_t d = iHgt + iMarginY * 2; 1066 rcClientArea.y += d; rcClientArea.Hgt -= d; 1067 // get centered in width as specified 1068 if (iWdt >= 0) 1069 { 1070 rcOut.x += (rcOut.Wdt - iWdt) / 2; 1071 rcOut.Wdt = iWdt; 1072 } 1073 return rcClientArea.Hgt >= 0; 1074 } 1075 GetFromLeft(int32_t iWdt,int32_t iHgt,C4Rect & rcOut)1076 bool ComponentAligner::GetFromLeft(int32_t iWdt, int32_t iHgt, C4Rect &rcOut) 1077 { 1078 rcOut.x = rcClientArea.x + iMarginX; 1079 rcOut.y = rcClientArea.y + iMarginY; 1080 rcOut.Wdt = iWdt; 1081 rcOut.Hgt = rcClientArea.Hgt - iMarginY * 2; 1082 int32_t d = iWdt + iMarginX * 2; 1083 rcClientArea.x += d; rcClientArea.Wdt -= d; 1084 // get centered in height as specified 1085 if (iHgt >= 0) 1086 { 1087 rcOut.y += (rcOut.Hgt - iHgt) / 2; 1088 rcOut.Hgt = iHgt; 1089 } 1090 return rcClientArea.Wdt >= 0; 1091 } 1092 GetFromRight(int32_t iWdt,int32_t iHgt,C4Rect & rcOut)1093 bool ComponentAligner::GetFromRight(int32_t iWdt, int32_t iHgt, C4Rect &rcOut) 1094 { 1095 rcOut.x = rcClientArea.x + rcClientArea.Wdt - iWdt - iMarginX; 1096 rcOut.y = rcClientArea.y + iMarginY; 1097 rcOut.Wdt = iWdt; 1098 rcOut.Hgt = rcClientArea.Hgt - iMarginY * 2; 1099 rcClientArea.Wdt -= iWdt + iMarginX * 2; 1100 // get centered in height as specified 1101 if (iHgt >= 0) 1102 { 1103 rcOut.y += (rcOut.Hgt - iHgt) / 2; 1104 rcOut.Hgt = iHgt; 1105 } 1106 return rcClientArea.Wdt >= 0; 1107 } 1108 GetFromBottom(int32_t iHgt,int32_t iWdt,C4Rect & rcOut)1109 bool ComponentAligner::GetFromBottom(int32_t iHgt, int32_t iWdt, C4Rect &rcOut) 1110 { 1111 rcOut.x = rcClientArea.x + iMarginX; 1112 rcOut.y = rcClientArea.y + rcClientArea.Hgt - iHgt - iMarginY; 1113 rcOut.Wdt = rcClientArea.Wdt - iMarginX * 2; 1114 rcOut.Hgt = iHgt; 1115 rcClientArea.Hgt -= iHgt + iMarginY * 2; 1116 // get centered in width as specified 1117 if (iWdt >= 0) 1118 { 1119 rcOut.x += (rcOut.Wdt - iWdt) / 2; 1120 rcOut.Wdt = iWdt; 1121 } 1122 return rcClientArea.Hgt >= 0; 1123 } 1124 GetAll(C4Rect & rcOut)1125 void ComponentAligner::GetAll(C4Rect &rcOut) 1126 { 1127 rcOut.x = rcClientArea.x + iMarginX; 1128 rcOut.y = rcClientArea.y + iMarginY; 1129 rcOut.Wdt = rcClientArea.Wdt - iMarginX * 2; 1130 rcOut.Hgt = rcClientArea.Hgt - iMarginY * 2; 1131 } 1132 GetCentered(int32_t iWdt,int32_t iHgt,C4Rect & rcOut)1133 bool ComponentAligner::GetCentered(int32_t iWdt, int32_t iHgt, C4Rect &rcOut) 1134 { 1135 rcOut.x = rcClientArea.GetMiddleX() - iWdt/2; 1136 rcOut.y = rcClientArea.GetMiddleY() - iHgt/2; 1137 rcOut.Wdt = iWdt; 1138 rcOut.Hgt = iHgt; 1139 // range check 1140 return rcOut.Wdt+iMarginX*2 <= rcClientArea.Wdt && rcOut.Hgt+iMarginY*2 <= rcClientArea.Hgt; 1141 } 1142 LogIt(const char * szName)1143 void ComponentAligner::LogIt(const char *szName) 1144 { 1145 LogF("ComponentAligner %s: (%d,%d)+(%d,%d), Margin (%d,%d)", szName, rcClientArea.x, rcClientArea.y, rcClientArea.Wdt, rcClientArea.Hgt, iMarginX, iMarginY); 1146 } 1147 GetGridCell(int32_t iSectX,int32_t iSectXMax,int32_t iSectY,int32_t iSectYMax,int32_t iSectSizeX,int32_t iSectSizeY,bool fCenterPos,int32_t iSectNumX,int32_t iSectNumY)1148 C4Rect &ComponentAligner::GetGridCell(int32_t iSectX, int32_t iSectXMax, int32_t iSectY, int32_t iSectYMax, int32_t iSectSizeX, int32_t iSectSizeY, bool fCenterPos, int32_t iSectNumX, int32_t iSectNumY) 1149 { 1150 int32_t iSectSizeXO = iSectSizeX, iSectSizeYO = iSectSizeY; 1151 int32_t iSectSizeXMax = (rcClientArea.Wdt-iMarginX) / iSectXMax - iMarginX; 1152 int32_t iSectSizeYMax = (rcClientArea.Hgt-iMarginY) / iSectYMax - iMarginY; 1153 if (iSectSizeX<0 || fCenterPos) iSectSizeX=iSectSizeXMax; else iSectSizeX=std::min<int32_t>(iSectSizeX, iSectSizeXMax); 1154 if (iSectSizeY<0 || fCenterPos) iSectSizeY=iSectSizeYMax; else iSectSizeY=std::min<int32_t>(iSectSizeY, iSectSizeYMax); 1155 rcTemp.x = iSectX * (iSectSizeX+iMarginX) + iMarginX + rcClientArea.x; 1156 rcTemp.y = iSectY * (iSectSizeY+iMarginY) + iMarginY + rcClientArea.y; 1157 rcTemp.Wdt = iSectSizeX * iSectNumX + iMarginX*(iSectNumX-1); rcTemp.Hgt = iSectSizeY * iSectNumY + iMarginY*(iSectNumY-1); 1158 if (iSectSizeXO>=0 && fCenterPos) 1159 { 1160 rcTemp.x += (iSectSizeX - iSectSizeXO)/2; 1161 rcTemp.Wdt = iSectSizeXO; 1162 } 1163 if (iSectSizeYO>=0 && fCenterPos) 1164 { 1165 rcTemp.y += (iSectSizeY - iSectSizeYO)/2; 1166 rcTemp.Hgt = iSectSizeYO; 1167 } 1168 return rcTemp; 1169 } 1170 1171 1172 // -------------------------------------------------- 1173 // Global stuff 1174 GUISound(const char * szSound)1175 void GUISound(const char *szSound) 1176 { 1177 if (Config.Sound.FESamples) 1178 StartSoundEffect(szSound); 1179 } 1180 1181 1182 // -------------------------------------------------- 1183 // Static vars 1184 1185 C4Rect ComponentAligner::rcTemp; 1186 Screen *Screen::pScreen; 1187 1188 1189 } // end of namespace 1190 1191 C4GUIScreen *pGUI = &C4GUI::TheScreen; 1192