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