1/*
2 * LegacyClonk
3 *
4 * Copyright (c) RedWolf Design
5 * Copyright (c) 2001, Sven2
6 * Copyright (c) 2017-2020, The LegacyClonk Team and contributors
7 *
8 * Distributed under the terms of the ISC license; see accompanying file
9 * "COPYING" for details.
10 *
11 * "Clonk" is a registered trademark of Matthes Bender, used with permission.
12 * See accompanying file "TRADEMARK" for details.
13 *
14 * To redistribute this file separately, substitute the full license texts
15 * for the above references.
16 */
17
18// generic user interface
19// eye candy
20
21#include <C4Include.h>
22#include <C4Gui.h>
23#include <C4FullScreen.h>
24#include <C4LoaderScreen.h>
25#include "C4GuiResource.h"
26#include "C4OpenURL.h"
27#include <C4Application.h>
28
29namespace C4GUI
30{
31
32// Label
33
34void Label::DrawElement(C4FacetEx &cgo)
35{
36 // print out
37 lpDDraw->TextOut(szText: sText.getData(), rFont&: *pFont, fZoom: 1.0f, sfcDest: cgo.Surface, iTx: x0 + cgo.TargetX, iTy: rcBounds.y + cgo.TargetY, dwFCol: dwFgClr, byForm: iAlign, fDoMarkup: fMarkup);
38 if (sHyperlink.getData())
39 {
40 int32_t iLinkWdt = 10, iLinkHgt = 10;
41 pFont->GetTextExtent(szText: sText.getData(), rsx&: iLinkWdt, rsy&: iLinkHgt, fCheckMarkup: fMarkup);
42 lpDDraw->DrawLineDw(sfcTarget: cgo.Surface,
43 x1: static_cast<float>(rcBounds.x + cgo.TargetX),
44 y1: static_cast<float>(rcBounds.y + iLinkHgt - 2 + cgo.TargetY),
45 x2: static_cast<float>(rcBounds.x + iLinkWdt + cgo.TargetX),
46 y2: static_cast<float>(rcBounds.y + iLinkHgt - 2 + cgo.TargetY),
47 C4GUI_HyperlinkFontClr & 0xffffff
48 );
49 }
50}
51
52Label::Label(std::string_view lblText, int32_t iX0, int32_t iTop, int32_t iAlign, uint32_t dwFClr, CStdFont *pFont, bool fMakeReadableOnBlack, bool fMarkup)
53 : Element(), dwFgClr(dwFClr), x0(iX0), iAlign(iAlign), pFont(pFont), cHotkey(0), pClickFocusControl(nullptr), fAutosize(true), fMarkup(fMarkup)
54{
55 // make color readable
56 if (fMakeReadableOnBlack) MakeColorReadableOnBlack(rdwClr&: dwFgClr);
57 // default font
58 if (!this->pFont) this->pFont = &GetRes()->TextFont;
59 // set top
60 rcBounds.y = iTop;
61 // update text
62 SetText(toText: lblText);
63}
64
65Label::Label(std::string_view lblText, const C4Rect &rcBounds, int32_t iAlign, uint32_t dwFClr, CStdFont *pFont, bool fMakeReadableOnBlack, bool fAutosize, bool fMarkup)
66 : Element(), dwFgClr(dwFClr), iAlign(iAlign), pFont(pFont), cHotkey(0), pClickFocusControl(nullptr), fAutosize(fAutosize), fMarkup(fMarkup)
67{
68 // make color readable
69 if (fMakeReadableOnBlack) MakeColorReadableOnBlack(rdwClr&: dwFgClr);
70 this->rcBounds = rcBounds;
71 // default font
72 if (!this->pFont) this->pFont = &GetRes()->TextFont;
73 // set x0
74 UpdateOwnPos();
75 // update text
76 SetText(toText: lblText);
77}
78
79void Label::MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, uint32_t dwKeyParam)
80{
81 // left-click changes focus
82 if (iButton == C4MC_Button_LeftDown)
83 {
84 if (pClickFocusControl) GetDlg()->SetFocus(pCtrl: pClickFocusControl, fByMouse: true);
85 // left click opens URL
86 if (sHyperlink.getData()) OpenURL(szURL: sHyperlink.getData());
87 }
88 // inherited
89 Element::MouseInput(rMouse, iButton, iX, iY, dwKeyParam);
90}
91
92void Label::SetText(const char *szText, bool fAllowHotkey)
93{
94 return SetText(toText: szText ? std::string_view{szText} : std::string_view{}, fAllowHotkey);
95}
96
97void Label::SetText(std::string_view toText, bool fAllowHotkey)
98{
99 // set new text
100 if (!toText.empty())
101 {
102 sText.Copy(pnData: toText.data(), iChars: toText.size());
103 // expand hotkey markup
104 if (fAllowHotkey && fMarkup) ExpandHotkeyMarkup(sText, rcHotkey&: cHotkey);
105 }
106 else
107 {
108 sText = "";
109 cHotkey = 0;
110 }
111 // update according to text only if autosize label (not wooden)
112 if (!fAutosize) return;
113 // get text extents
114 pFont->GetTextExtent(szText: sText.getData(), rsx&: rcBounds.Wdt, rsy&: rcBounds.Hgt, fCheckMarkup: fMarkup);
115 // update pos
116 SetX0(x0);
117}
118
119void Label::UpdateOwnPos()
120{
121 // update text drawing pos
122 switch (iAlign)
123 {
124 case ALeft: x0 = rcBounds.x + GetLeftIndent(); break;
125 case ACenter: x0 = rcBounds.x + rcBounds.Wdt / 2; break;
126 case ARight: x0 = rcBounds.x + rcBounds.Wdt; break;
127 }
128}
129
130bool Label::OnHotkey(char cHotkey)
131{
132 // if hotkey matches and focus control is assigned, set focus
133 if (this->cHotkey == cHotkey && pClickFocusControl)
134 {
135 GetDlg()->SetFocus(pCtrl: pClickFocusControl, fByMouse: false);
136 return true;
137 }
138 else return false;
139}
140
141void Label::SetX0(int32_t iToX0)
142{
143 x0 = iToX0;
144 // update x-startpos
145 switch (iAlign)
146 {
147 case ALeft: rcBounds.x = x0; break;
148 case ACenter: rcBounds.x = x0 - rcBounds.Wdt / 2; break;
149 case ARight: rcBounds.x = x0 - rcBounds.Wdt; break;
150 }
151 // size might have changed
152 UpdateSize();
153}
154
155void Label::SetHyperlink(const char *szURL)
156{
157 if (szURL && *szURL)
158 {
159 sHyperlink.Copy(pnData: szURL);
160 SetColor(C4GUI_HyperlinkFontClr, fMakeReadableOnBlack: false);
161 }
162 else
163 sHyperlink.Clear();
164}
165
166// WoodenLabel
167
168void WoodenLabel::DrawElement(C4FacetEx &cgo)
169{
170 // draw wood
171 DrawBar(cgo, rFacets&: GetRes()->barCaption);
172 // draw symbol
173 if (fctIcon.Surface)
174 {
175 C4Facet cgoSymbol(cgo.Surface, cgo.TargetX + rcBounds.x + 1, cgo.TargetY + rcBounds.y + 1, rcBounds.Hgt - 2, rcBounds.Hgt - 2);
176 fctIcon.Draw(cgo&: cgoSymbol);
177 }
178 // calculations for automatic scrolling
179 int32_t iXOff = 0;
180 if (iAlign == ALeft) iXOff += 5;
181 if (tAutoScrollDelay)
182 {
183 time_t tNow = timeGetTime();
184 if (!tLastChangeTime)
185 tLastChangeTime = tNow;
186 else if (tNow - tLastChangeTime >= tAutoScrollDelay)
187 {
188 if (!iScrollDir) iScrollDir = 1;
189 int32_t iMaxScroll = std::max<int32_t>(a: pFont->GetTextWidth(szText: sText.getData(), fCheckMarkup: true) + (x0 - rcBounds.x) + iXOff + GetRightIndent() - rcBounds.Wdt, b: 0);
190 if (iMaxScroll)
191 {
192 iScrollPos += iScrollDir;
193 if (iScrollPos >= iMaxScroll || iScrollPos < 0)
194 {
195 iScrollDir = -iScrollDir;
196 iScrollPos += iScrollDir;
197 tLastChangeTime = tNow;
198 }
199 }
200 }
201 iXOff -= iScrollPos;
202 }
203 // print out text; clipped
204 int ClipX1, ClipY1, ClipX2, ClipY2;
205 lpDDraw->GetPrimaryClipper(rX1&: ClipX1, rY1&: ClipY1, rX2&: ClipX2, rY2&: ClipY2);
206 lpDDraw->SetPrimaryClipper(iX1: rcBounds.x + GetLeftIndent() + cgo.TargetX, iY1: rcBounds.y + cgo.TargetY, iX2: rcBounds.x + rcBounds.Wdt - GetRightIndent() + cgo.TargetX, iY2: rcBounds.y + rcBounds.Hgt + cgo.TargetY);
207 lpDDraw->TextOut(szText: sText.getData(), rFont&: *pFont, fZoom: 1.0f, sfcDest: cgo.Surface, iTx: x0 + cgo.TargetX + iXOff, iTy: rcBounds.y + cgo.TargetY + (rcBounds.Hgt - pFont->GetLineHeight()) / 2 - 1, dwFCol: dwFgClr, byForm: iAlign);
208 lpDDraw->SetPrimaryClipper(iX1: ClipX1, iY1: ClipY1, iX2: ClipX2, iY2: ClipY2);
209}
210
211int32_t WoodenLabel::GetDefaultHeight(CStdFont *pUseFont)
212{
213 if (!pUseFont) pUseFont = &(GetRes()->TextFont);
214 return std::max<int32_t>(a: pUseFont->GetLineHeight(), C4GUI_MinWoodBarHgt);
215}
216
217void WoodenLabel::SetIcon(const C4Facet &rfctIcon)
218{
219 // set icon
220 fctIcon = rfctIcon;
221 // realign text to left for set icons
222 if (fctIcon.Surface)
223 iAlign = ALeft;
224 else
225 iAlign = ACenter;
226 UpdateOwnPos();
227}
228
229// MultilineLabel
230
231MultilineLabel::MultilineLabel(const C4Rect &rcBounds, int32_t iMaxLines, int32_t iMaxBuf, const char *szIndentChars, bool fAutoGrow, bool fMarkup)
232 : Element(), Lines(iMaxBuf, iMaxLines, rcBounds.Wdt, szIndentChars, fAutoGrow, fMarkup), fMarkup(fMarkup)
233{
234 // set bounds
235 this->rcBounds = rcBounds;
236 // update height (min height)
237 UpdateOwnPos();
238}
239
240void MultilineLabel::DrawElement(C4FacetEx &cgo)
241{
242 // get clipping
243 int iClipX, iClipY, iClipX2, iClipY2;
244 lpDDraw->GetPrimaryClipper(rX1&: iClipX, rY1&: iClipY, rX2&: iClipX2, rY2&: iClipY2);
245 // draw all lines
246 int32_t iIndex = 0; const char *szLine;
247 int32_t iY = rcBounds.y + cgo.TargetY;
248 CStdFont *pLineFont; uint32_t dwLineClr; bool fNewParagraph;
249 while (szLine = Lines.GetLine(iLineIndex: iIndex, ppFont: &pLineFont, pdwClr: &dwLineClr, pNewParagraph: &fNewParagraph))
250 {
251 int32_t iFontLineHeight = pLineFont->GetLineHeight();
252 // indents between paragraphs
253 if (fNewParagraph && iIndex) iY += iFontLineHeight / 3;
254 // clip
255 if (iY > iClipY2) break;
256 if (iY >= iClipY - iFontLineHeight)
257 {
258 // draw line
259 lpDDraw->TextOut(szText: szLine, rFont&: *pLineFont, fZoom: 1.0f, sfcDest: cgo.Surface, iTx: rcBounds.x + cgo.TargetX, iTy: iY, dwFCol: dwLineClr, byForm: ALeft, fDoMarkup: fMarkup);
260 }
261 // advance line
262 iY += iFontLineHeight;
263 ++iIndex;
264 }
265}
266
267void MultilineLabel::UpdateSize()
268{
269 // forward change to line buffer
270 Lines.SetLBWidth(rcBounds.Wdt);
271 UpdateHeight();
272}
273
274void MultilineLabel::UpdateHeight()
275{
276 // size by line count
277 int32_t iIndex = 0; const char *szLine; int32_t iHgt = 0;
278 CStdFont *pLineFont; bool fNewPar;
279 while (szLine = Lines.GetLine(iLineIndex: iIndex, ppFont: &pLineFont, pdwClr: nullptr, pNewParagraph: &fNewPar))
280 {
281 int32_t iFontLineHeight = pLineFont->GetLineHeight();
282 // indents between separate messages
283 if (fNewPar && iIndex) iHgt += iFontLineHeight / 3;
284 // text line height
285 iHgt += iFontLineHeight;
286 ++iIndex;
287 }
288 rcBounds.Hgt = std::max<int32_t>(a: iHgt, b: 5);
289 // update parent container
290 Element::UpdateSize();
291}
292
293void MultilineLabel::AddLine(const char *szLine, CStdFont *pFont, uint32_t dwClr, bool fDoUpdate, bool fMakeReadableOnBlack, CStdFont *pCaptionFont)
294{
295 // make color readable
296 if (fMakeReadableOnBlack) MakeColorReadableOnBlack(rdwClr&: dwClr);
297 // forward to line buffer
298 if (szLine) Lines.AppendLines(szLine, pFont, dwClr, pFirstLineFont: pCaptionFont);
299 // adjust height
300 if (fDoUpdate) UpdateSize();
301}
302
303void MultilineLabel::Clear(bool fDoUpdate)
304{
305 // forward to line buffer
306 Lines.Clear();
307 // adjust height
308 if (fDoUpdate) UpdateSize();
309}
310
311// HorizontalLine
312
313void HorizontalLine::DrawElement(C4FacetEx &cgo)
314{
315 // draw horizontal line
316 int32_t iX1 = rcBounds.x + cgo.TargetX, iX2 = iX1 + rcBounds.Wdt,
317 iY = rcBounds.y + cgo.TargetY;
318 lpDDraw->DrawLineDw(sfcTarget: cgo.Surface, x1: static_cast<float>(iX1 + 1), y1: static_cast<float>(iY + 1), x2: static_cast<float>(iX2 - 1), y2: static_cast<float>(iY + 1), dwClr: dwShadowClr);
319 lpDDraw->DrawLineDw(sfcTarget: cgo.Surface, x1: static_cast<float>(iX1), y1: static_cast<float>(iY), x2: static_cast<float>(iX2 - 2), y2: static_cast<float>(iY), dwClr);
320}
321
322// ProgressBar
323
324void ProgressBar::DrawElement(C4FacetEx &cgo)
325{
326 // do not draw in negative progress
327 if (iProgress < 0) return;
328 CStdFont &rFont = GetRes()->TextFont;
329 // draw border
330 Draw3DFrame(cgo);
331 // calc progress width
332 int32_t iProgressWdt = (rcBounds.Wdt - 4) * iProgress / iMax;
333 // draw progress
334 GetRes()->fctProgressBar.DrawX(sfcTarget: cgo.Surface, iX: cgo.TargetX + rcBounds.x + 2, iY: cgo.TargetY + rcBounds.y + 2, iWdt: iProgressWdt, iHgt: rcBounds.Hgt - 2);
335 // print out progress text
336 lpDDraw->TextOut(szText: (std::to_string(val: 100 * iProgress / iMax) + "%").c_str(), rFont, fZoom: 1.0f, sfcDest: cgo.Surface, iTx: cgo.TargetX + rcBounds.GetMiddleX(), iTy: rcBounds.y + cgo.TargetY + (rcBounds.Hgt - rFont.GetLineHeight()) / 2 - 1, C4GUI_ProgressBarFontClr, byForm: ACenter);
337}
338
339// Picture
340
341Picture::Picture(const C4Rect &rcBounds, bool fAspect) : fCustomDrawClr(false), fAnimate(false)
342{
343 // set values
344 this->fAspect = fAspect;
345 this->rcBounds = rcBounds;
346 // no facet yet
347}
348
349void Picture::DrawElement(C4FacetEx &cgo)
350{
351 // animation?
352 C4Facet *pDrawFacet, DrawFacet;
353 if (fAnimate)
354 {
355 if (++iPhaseTime > iDelay)
356 {
357 int32_t iPhasesX = 1, iPhasesY = 1;
358 Facet.GetPhaseNum(rX&: iPhasesX, rY&: iPhasesY);
359 if (++iAnimationPhase >= iPhasesX) iAnimationPhase = 0;
360 iPhaseTime = 0;
361 }
362 DrawFacet = Facet.GetPhase(iPhaseX: iAnimationPhase);
363 pDrawFacet = &DrawFacet;
364 }
365 else
366 pDrawFacet = &Facet;
367 // draw the facet
368 C4Facet cgo2 = cgo;
369 cgo2.X = rcBounds.x + cgo.TargetX;
370 cgo2.Y = rcBounds.y + cgo.TargetY;
371 cgo2.Wdt = rcBounds.Wdt;
372 cgo2.Hgt = rcBounds.Hgt;
373 if (fCustomDrawClr)
374 {
375 pDrawFacet->DrawClr(cgo&: cgo2, fAspect, dwClr: dwDrawClr);
376 }
377 else
378 pDrawFacet->Draw(cgo&: cgo2, fAspect);
379}
380
381bool Picture::EnsureOwnSurface()
382{
383 // no surface?
384 if (!Facet.Surface) return false;
385 // equals face already?
386 if (Facet.Surface == &Facet.GetFace()) return true;
387 // then create as a copy
388 C4Facet cgo = Facet;
389 if (!Facet.Create(iWdt: cgo.Wdt, iHgt: cgo.Hgt)) return false;
390 cgo.Draw(cgo&: Facet);
391 return true;
392}
393
394void Picture::SetAnimated(bool fEnabled, int iDelay)
395{
396 if (fAnimate = fEnabled)
397 {
398 // starts cycling through all phases of the specified facet
399 iAnimationPhase = iPhaseTime = 0;
400 this->iDelay = iDelay;
401 }
402}
403
404// OverlayPicture
405
406OverlayPicture::OverlayPicture(const C4Rect &rcBounds, bool fAspect, const C4Facet &rOverlayImage, int iBorderSize)
407 : Picture(rcBounds, fAspect), iBorderSize(iBorderSize), OverlayImage(rOverlayImage) {}
408
409void OverlayPicture::DrawElement(C4FacetEx &cgo)
410{
411 // draw inner image
412 C4Facet cgo2 = cgo;
413 cgo2.X = rcBounds.x + cgo.TargetX + iBorderSize * rcBounds.Wdt / std::max<int>(a: OverlayImage.Wdt, b: 1);
414 cgo2.Y = rcBounds.y + cgo.TargetY + iBorderSize * rcBounds.Hgt / std::max<int>(a: OverlayImage.Hgt, b: 1);
415 cgo2.Wdt = rcBounds.Wdt - 2 * iBorderSize * rcBounds.Wdt / std::max<int>(a: OverlayImage.Wdt, b: 1);
416 cgo2.Hgt = rcBounds.Hgt - 2 * iBorderSize * rcBounds.Hgt / std::max<int>(a: OverlayImage.Hgt, b: 1);
417 Facet.Draw(cgo&: cgo2, fAspect);
418 // draw outer image
419 cgo2.X = rcBounds.x + cgo.TargetX;
420 cgo2.Y = rcBounds.y + cgo.TargetY;
421 cgo2.Wdt = rcBounds.Wdt;
422 cgo2.Hgt = rcBounds.Hgt;
423 OverlayImage.Draw(cgo&: cgo2, fAspect);
424}
425
426// Icon
427
428Icon::Icon(const C4Rect &rcBounds, Icons icoIconIndex)
429 : Picture(rcBounds, true)
430{
431 // set icon facet
432 SetIcon(icoIconIndex);
433}
434
435void Icon::SetIcon(Icons icoNewIconIndex)
436{
437 // load icon
438 SetFacet(GetIconFacet(icoIconIndex: icoNewIconIndex));
439}
440
441C4FacetEx Icon::GetIconFacet(Icons icoIconIndex)
442{
443 if (icoIconIndex == Ico_None) return C4FacetEx();
444 C4FacetEx &rFacet = (icoIconIndex & Ico_Extended) ? GetRes()->fctIconsEx : GetRes()->fctIcons;
445 icoIconIndex = Icons(icoIconIndex & (Ico_Extended - 1));
446 int32_t iXMax, iYMax;
447 rFacet.GetPhaseNum(rX&: iXMax, rY&: iYMax);
448 if (!iXMax) iXMax = 6;
449 return rFacet.GetPhase(iPhaseX: icoIconIndex % iXMax, iPhaseY: icoIconIndex / iXMax);
450}
451
452// TextWindow
453
454TextWindow::TextWindow(const C4Rect &rtBounds, size_t iPicWdt, size_t iPicHgt, size_t iPicPadding, size_t iMaxLines, size_t iMaxTextLen, const char *szIndentChars, bool fAutoGrow, const C4Facet *pOverlayPic, int iOverlayBorder, bool fMarkup)
455 : Control(rtBounds), fDrawBackground(true), fDrawFrame(true), iPicPadding(iPicPadding), pLogBuffer(nullptr)
456{
457 // calc client rect
458 UpdateOwnPos();
459 // create content scroll window
460 pClientWindow = new ScrollWindow(this);
461 pClientWindow->SetBounds(GetContainedClientRect());
462 // create content multiline label
463 pLogBuffer = new MultilineLabel(pClientWindow->GetContainedClientRect(), iMaxLines, iMaxTextLen, szIndentChars, fAutoGrow, fMarkup);
464 // add to scroll window
465 pClientWindow->AddElement(pChild: pLogBuffer);
466 // update scrolling (for empty buffer)
467 pClientWindow->SetClientHeight(1);
468 // create content picture, if desired
469 C4Rect rcContentSize = pClientWindow->GetClientRect();
470 if (iPicWdt && iPicHgt)
471 {
472 C4Rect rcImage;
473 rcImage.x = std::max<int32_t>(a: rcContentSize.GetMiddleX() - iPicWdt / 2, b: 0);
474 rcImage.y = 0;
475 rcImage.Wdt = std::min<size_t>(a: iPicWdt, b: rcContentSize.Wdt);
476 rcImage.Hgt = iPicHgt * rcImage.Wdt / iPicWdt;
477 rcContentSize.y += rcImage.Hgt + iPicPadding;
478 if (pOverlayPic)
479 pTitlePicture = new OverlayPicture(rcImage, false, *pOverlayPic, iOverlayBorder);
480 else
481 pTitlePicture = new Picture(rcImage, false);
482 pClientWindow->AddElement(pChild: pTitlePicture);
483 }
484 else pTitlePicture = nullptr;
485
486 // update size
487 UpdateSize();
488}
489
490void TextWindow::UpdateSize()
491{
492 Control::UpdateSize();
493 pClientWindow->SetBounds(GetContainedClientRect());
494 // resize log buffer pos to horizontal extents
495 C4Rect rcChildBounds = pLogBuffer->GetBounds();
496 rcChildBounds.x = 0;
497 rcChildBounds.y = pTitlePicture ? pTitlePicture->GetBounds().Hgt + iPicPadding : 0;
498 rcChildBounds.Wdt = pClientWindow->GetClientRect().Wdt;
499 pLogBuffer->SetBounds(rcChildBounds);
500}
501
502void TextWindow::DrawElement(C4FacetEx &cgo)
503{
504 // draw background
505 if (fDrawBackground) lpDDraw->DrawBoxDw(sfcDest: cgo.Surface, iX1: cgo.TargetX + rcBounds.x, iY1: cgo.TargetY + rcBounds.y, iX2: rcBounds.x + rcBounds.Wdt - 1 + cgo.TargetX, iY2: rcBounds.y + rcBounds.Hgt - 1 + cgo.TargetY, dwClr: 0x7f000000);
506 // draw frame
507 if (fDrawFrame) Draw3DFrame(cgo);
508}
509
510void TextWindow::ElementSizeChanged(Element *pOfElement)
511{
512 // inherited
513 if (pOfElement->GetParent() == this)
514 Control::ElementSizeChanged(pOfElement);
515 // update size of scroll control
516 if (pClientWindow && pLogBuffer)
517 pClientWindow->SetClientHeight(pLogBuffer->GetBounds().y + pLogBuffer->GetBounds().Hgt);
518}
519
520void TextWindow::ElementPosChanged(Element *pOfElement)
521{
522 // inherited
523 if (pOfElement->GetParent() == this)
524 Control::ElementSizeChanged(pOfElement);
525 // update size of scroll control
526 if (pClientWindow && pLogBuffer)
527 pClientWindow->SetClientHeight(pLogBuffer->GetBounds().y + pLogBuffer->GetBounds().Hgt);
528}
529
530void TextWindow::SetPicture(const C4Facet &rNewPic)
531{
532 // update picture
533 if (!pTitlePicture) return;
534 pTitlePicture->SetFacet(rNewPic);
535 // reposition multiline label below picture if any is assigned
536 pLogBuffer->GetBounds().y = rNewPic.Surface ? pTitlePicture->GetBounds().Hgt + iPicPadding : 0;
537 pLogBuffer->UpdateOwnPos();
538 pTitlePicture->SetVisibility(!!rNewPic.Surface);
539}
540
541} // end of namespace
542