1/*
2 * Copyright 2001-2020 Haiku, Inc.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Stephan A��mus, superstippi@gmx.de
7 *		DarkWyrm, bpmagic@columbus.rr.com
8 *		Ryan Leavengood, leavengood@gmail.com
9 *		Philippe Saint-Pierre, stpere@gmail.com
10 *		John Scipione, jscipione@gmail.com
11 *		Ingo Weinhold, ingo_weinhold@gmx.de
12 *		Clemens Zeidler, haiku@clemens-zeidler.de
13 *		Joseph Groover <looncraz@looncraz.net>
14 *		Nahuel Tello <nhtello@unarix.com.ar>
15 */
16
17
18/*!	Flat decorator for dark mode theme */
19
20
21#include "FlatDecorator.h"
22
23#include <algorithm>
24#include <cmath>
25#include <new>
26#include <stdio.h>
27
28#include <Autolock.h>
29#include <Debug.h>
30#include <GradientLinear.h>
31#include <Rect.h>
32#include <Region.h>
33#include <View.h>
34
35#include <WindowPrivate.h>
36
37#include "BitmapDrawingEngine.h"
38#include "DesktopSettings.h"
39#include "DrawingEngine.h"
40#include "DrawState.h"
41#include "FontManager.h"
42#include "PatternHandler.h"
43#include "ServerBitmap.h"
44
45
46//#define DEBUG_DECORATOR
47#ifdef DEBUG_DECORATOR
48#	define STRACE(x) printf x
49#else
50#	define STRACE(x) ;
51#endif
52
53
54static const float kBorderResizeLength = 22.0;
55
56
57FlatDecorAddOn::FlatDecorAddOn(image_id id, const char* name)
58	:
59	DecorAddOn(id, name)
60{
61}
62
63
64Decorator*
65FlatDecorAddOn::_AllocateDecorator(DesktopSettings& settings, BRect rect,
66	Desktop* desktop)
67{
68	return new (std::nothrow)FlatDecorator(settings, rect, desktop);
69}
70
71
72
73static inline uint8
74blend_color_value(uint8 a, uint8 b, float position)
75{
76	int16 delta = (int16)b - a;
77	int32 value = a + (int32)(position * delta);
78	if (value > 255)
79		return 255;
80	if (value < 0)
81		return 0;
82
83	return (uint8)value;
84}
85
86
87//	#pragma mark -
88
89
90// TODO: get rid of DesktopSettings here, and introduce private accessor
91//	methods to the Decorator base class
92FlatDecorator::FlatDecorator(DesktopSettings& settings, BRect rect,
93	Desktop* desktop)
94	:
95	TabDecorator(settings, rect, desktop)
96{
97	// TODO: If the decorator was created with a frame too small, it should
98	// resize itself!
99
100	STRACE(("FlatDecorator:\n"));
101	STRACE(("\tFrame (%.1f,%.1f,%.1f,%.1f)\n",
102		rect.left, rect.top, rect.right, rect.bottom));
103}
104
105
106FlatDecorator::~FlatDecorator()
107{
108	STRACE(("FlatDecorator: ~FlatDecorator()\n"));
109}
110
111
112// #pragma mark - Public methods
113
114
115/*!	Returns the frame colors for the specified decorator component.
116
117	The meaning of the color array elements depends on the specified component.
118	For some components some array elements are unused.
119
120	\param component The component for which to return the frame colors.
121	\param highlight The highlight set for the component.
122	\param colors An array of colors to be initialized by the function.
123*/
124void
125FlatDecorator::GetComponentColors(Component component, uint8 highlight,
126	ComponentColors _colors, Decorator::Tab* _tab)
127{
128	Decorator::Tab* tab = static_cast<Decorator::Tab*>(_tab);
129	switch (component) {
130		case COMPONENT_TAB:
131			if (highlight != 0) {
132				_colors[COLOR_TAB_FRAME_LIGHT]
133					= tint_color(fFocusTabColor, 1.0);
134				_colors[COLOR_TAB_FRAME_DARK]
135					= tint_color(fFocusTabColor, 1.2);
136				_colors[COLOR_TAB] = tint_color(fFocusTabColor, 0.95);
137				_colors[COLOR_TAB_LIGHT] = fFocusTabColorLight;
138				_colors[COLOR_TAB_BEVEL] = fFocusTabColorBevel;
139				_colors[COLOR_TAB_SHADOW] = fFocusTabColorShadow;
140				_colors[COLOR_TAB_TEXT] = tint_color(fFocusTextColor, 0.5);
141			}
142			else if (tab && tab->buttonFocus) {
143				_colors[COLOR_TAB_FRAME_LIGHT]
144					= tint_color(fFocusTabColor, 1.0);
145				_colors[COLOR_TAB_FRAME_DARK]
146					= tint_color(fFocusTabColor, 1.2);
147				_colors[COLOR_TAB] = fFocusTabColor;
148				_colors[COLOR_TAB_LIGHT] = fFocusTabColorLight;
149				_colors[COLOR_TAB_BEVEL] = fFocusTabColorBevel;
150				_colors[COLOR_TAB_SHADOW] = fFocusTabColorShadow;
151				_colors[COLOR_TAB_TEXT] = fFocusTextColor;
152			} else {
153				_colors[COLOR_TAB_FRAME_LIGHT]
154					= tint_color(fNonFocusTabColor, 1.0);
155				_colors[COLOR_TAB_FRAME_DARK]
156					= tint_color(fNonFocusTabColor, 1.2);
157				_colors[COLOR_TAB] = fNonFocusTabColor;
158				_colors[COLOR_TAB_LIGHT] = fNonFocusTabColorLight;
159				_colors[COLOR_TAB_BEVEL] = fNonFocusTabColorBevel;
160				_colors[COLOR_TAB_SHADOW] = fNonFocusTabColorShadow;
161				_colors[COLOR_TAB_TEXT] = fNonFocusTextColor;
162			}
163			break;
164
165		case COMPONENT_CLOSE_BUTTON:
166		case COMPONENT_ZOOM_BUTTON:
167			if (tab && tab->buttonFocus) {
168				_colors[COLOR_BUTTON] = fFocusTabColor;
169				_colors[COLOR_BUTTON_LIGHT] = fFocusTabColorLight;
170			} else {
171				_colors[COLOR_BUTTON] = fNonFocusTabColor;
172				_colors[COLOR_BUTTON_LIGHT] = fNonFocusTabColorLight;
173			}
174			break;
175
176		case COMPONENT_TOP_BORDER:
177			if (tab && tab->buttonFocus) {
178				_colors[0] = tint_color(fFocusTabColor, 1.2); // borde exterior
179				_colors[1] = tint_color(fFocusTabColor, 1.0); // borde top
180				_colors[2] = tint_color(fFocusTabColor, 1.0); // borde top
181				_colors[3] = tint_color(fFocusTabColor, 1.0); // borde top
182				_colors[4] = tint_color(fFocusFrameColor, 1.1); // borde interior
183				_colors[5] = tint_color(fFocusFrameColor, 1.1); // borde menu 1
184			} else {
185				_colors[0] = tint_color(fNonFocusTabColor, 1.2); // borde exterior
186				_colors[1] = tint_color(fNonFocusTabColor, B_NO_TINT);
187				_colors[2] = tint_color(fNonFocusTabColor, B_NO_TINT);
188				_colors[3] = tint_color(fNonFocusTabColor, B_NO_TINT);
189				_colors[4] = tint_color(fNonFocusFrameColor, 1.1); // borde interior
190				_colors[5] = tint_color(fNonFocusFrameColor, 1.1); // borde menu 1
191			}
192			break;
193		case COMPONENT_RESIZE_CORNER:
194			if (tab && tab->buttonFocus) {
195				_colors[0] = tint_color(fFocusFrameColor, 1.25); // borde exterior
196				_colors[1] = tint_color(fFocusFrameColor, 1.0); // borde top
197				_colors[2] = tint_color(fFocusFrameColor, 1.0); // borde top
198				_colors[3] = tint_color(fFocusTabColor, 1.0); // borde top
199				_colors[4] = tint_color(fFocusFrameColor, 1.1); // borde interior
200				_colors[5] = tint_color(fFocusFrameColor, 1.1); // borde menu 1
201			} else {
202				_colors[0] = tint_color(fNonFocusFrameColor, 1.25); // borde exterior
203				_colors[1] = tint_color(fNonFocusFrameColor, B_NO_TINT);
204				_colors[2] = tint_color(fNonFocusFrameColor, B_NO_TINT);
205				_colors[3] = tint_color(fNonFocusFrameColor, B_NO_TINT);
206				_colors[4] = tint_color(fNonFocusFrameColor, 1.1); // borde interior
207				_colors[5] = tint_color(fNonFocusFrameColor, 1.1); // borde menu 1
208			}
209			break;
210		case COMPONENT_LEFT_BORDER:
211		case COMPONENT_RIGHT_BORDER:
212			if (tab && tab->buttonFocus) {
213				_colors[0] = tint_color(fFocusFrameColor, 1.25); // borde exterior
214				_colors[1] = tint_color(fFocusFrameColor, B_NO_TINT);
215				_colors[2] = tint_color(fFocusFrameColor, B_NO_TINT);
216				_colors[3] = tint_color(fFocusFrameColor, B_NO_TINT);
217				_colors[4] = tint_color(fFocusFrameColor, 1.05); // borde interior
218				_colors[5] = tint_color(fFocusFrameColor, 1.1); // borde menu 1
219				_colors[6] = tint_color(fFocusTabColor, 1.2); // border tab to be part
220			} else {
221				_colors[0] = tint_color(fNonFocusFrameColor, 1.25); // borde exterior
222				_colors[1] = tint_color(fNonFocusFrameColor, B_NO_TINT);
223				_colors[2] = tint_color(fNonFocusFrameColor, B_NO_TINT);
224				_colors[3] = tint_color(fNonFocusFrameColor, B_NO_TINT);
225				_colors[4] = tint_color(fNonFocusFrameColor, 1.05); // borde interior
226				_colors[5] = tint_color(fNonFocusFrameColor, 1.0); // borde menu 1
227				_colors[6] = tint_color(fNonFocusTabColor, 1.2); // border tab to be part
228			}
229			break;
230		case COMPONENT_BOTTOM_BORDER:
231		default:
232			if (tab && tab->buttonFocus) {
233				_colors[0] = tint_color(fFocusFrameColor, 1.25); // borde exterior
234				_colors[1] = tint_color(fFocusFrameColor, B_NO_TINT);
235				_colors[2] = tint_color(fFocusFrameColor, B_NO_TINT);
236				_colors[3] = tint_color(fFocusFrameColor, B_NO_TINT);
237				_colors[4] = tint_color(fFocusFrameColor, 1.1); // borde interior
238				_colors[5] = tint_color(fFocusFrameColor, 1.1); // borde menu 1
239			} else {
240				_colors[0] = tint_color(fNonFocusFrameColor, 1.25); // borde exterior
241				_colors[1] = tint_color(fNonFocusFrameColor, B_NO_TINT);
242				_colors[2] = tint_color(fNonFocusFrameColor, B_NO_TINT);
243				_colors[3] = tint_color(fNonFocusFrameColor, B_NO_TINT);
244				_colors[4] = tint_color(fNonFocusFrameColor, 1.1); // borde interior
245				_colors[5] = tint_color(fNonFocusFrameColor, 1.1); // borde menu 1
246			}
247
248			// for the resize-border highlight dye everything bluish.
249			if (highlight == HIGHLIGHT_RESIZE_BORDER) {
250				for (int32 i = 0; i < 6; i++) {
251					_colors[i].red = std::max((int)_colors[i].red - 80, 0);
252					_colors[i].green = std::max((int)_colors[i].green - 80, 0);
253					_colors[i].blue = 255;
254				}
255			}
256			break;
257	}
258}
259
260
261void
262FlatDecorator::UpdateColors(DesktopSettings& settings)
263{
264	TabDecorator::UpdateColors(settings);
265}
266
267
268// #pragma mark - Protected methods
269
270
271void
272FlatDecorator::_DrawFrame(BRect rect)
273{
274	STRACE(("_DrawFrame(%f,%f,%f,%f)\n", rect.left, rect.top,
275		rect.right, rect.bottom));
276
277	// NOTE: the DrawingEngine needs to be locked for the entire
278	// time for the clipping to stay valid for this decorator
279
280	if (fTopTab->look == B_NO_BORDER_WINDOW_LOOK)
281		return;
282
283	if (fBorderWidth <= 0)
284		return;
285
286	// Draw the border frame
287	BRect r = BRect(fTopBorder.LeftTop(), fBottomBorder.RightBottom());
288	switch ((int)fTopTab->look) {
289		case B_TITLED_WINDOW_LOOK:
290		case B_DOCUMENT_WINDOW_LOOK:
291		case B_MODAL_WINDOW_LOOK:
292		{
293			// left
294			if (rect.Intersects(fLeftBorder.InsetByCopy(0, -fBorderWidth))) {
295				ComponentColors colors;
296				_GetComponentColors(COMPONENT_LEFT_BORDER, colors, fTopTab);
297
298				for (int8 i = 0; i < 5; i++) {
299					fDrawingEngine->StrokeLine(BPoint(r.left + i, r.top + i),
300						BPoint(r.left + i, r.bottom - i), colors[i]);
301				}
302				// redraw line to be part of tab title
303				fDrawingEngine->StrokeLine(BPoint(r.left, r.top),
304					BPoint(r.left, r.top + 4), colors[6]);
305
306			}
307			// bottom
308			if (rect.Intersects(fBottomBorder)) {
309				ComponentColors colors;
310				_GetComponentColors(COMPONENT_BOTTOM_BORDER, colors, fTopTab);
311
312				for (int8 i = 0; i < 5; i++) {
313					fDrawingEngine->StrokeLine(BPoint(r.left + i, r.bottom - i),
314						BPoint(r.right - i, r.bottom - i),
315						colors[i]);
316				}
317			}
318			// right
319			if (rect.Intersects(fRightBorder.InsetByCopy(0, -fBorderWidth))) {
320				ComponentColors colors;
321				_GetComponentColors(COMPONENT_RIGHT_BORDER, colors, fTopTab);
322
323				for (int8 i = 0; i < 5; i++) {
324						fDrawingEngine->StrokeLine(BPoint(r.right - i, r.top + i),
325							BPoint(r.right - i, r.bottom - i),
326							colors[i]);
327				}
328				// redraw line to be part of tab title
329				fDrawingEngine->StrokeLine(BPoint(r.right, r.top),
330					BPoint(r.right, r.top + 4),
331					colors[6]);
332			}
333			// top
334			if (rect.Intersects(fTopBorder)) {
335				ComponentColors colors;
336				_GetComponentColors(COMPONENT_TOP_BORDER, colors, fTopTab);
337
338				for (int8 i = 0; i < 5; i++) {
339					if (i<4)
340					{
341						fDrawingEngine->StrokeLine(BPoint(r.left + 1, r.top + i),
342							BPoint(r.right - 1, r.top + i), tint_color(colors[i], (i*0.01+1)));
343					}
344					else
345					{
346						fDrawingEngine->StrokeLine(BPoint(r.left + 1, r.top + i),
347							BPoint(r.right - 1, r.top + i), tint_color(colors[3], 1.1));
348					}
349				}
350			}
351			break;
352		}
353
354		case B_FLOATING_WINDOW_LOOK:
355		case kLeftTitledWindowLook:
356		{
357			// top
358			if (rect.Intersects(fTopBorder)) {
359				ComponentColors colors;
360				_GetComponentColors(COMPONENT_TOP_BORDER, colors, fTopTab);
361
362				for (int8 i = 0; i < 3; i++) {
363					fDrawingEngine->StrokeLine(BPoint(r.left + i, r.top + i),
364						BPoint(r.right - i, r.top + i), tint_color(colors[1], 0.95));
365				}
366				if (fTitleBarRect.IsValid() && fTopTab->look != kLeftTitledWindowLook) {
367					// grey along the bottom of the tab
368					// (overwrites "white" from frame)
369					fDrawingEngine->StrokeLine(
370						BPoint(fTitleBarRect.left + 2,
371							fTitleBarRect.bottom + 1),
372						BPoint(fTitleBarRect.right - 2,
373							fTitleBarRect.bottom + 1), colors[2]);
374				}
375			}
376			// left
377			if (rect.Intersects(fLeftBorder.InsetByCopy(0, -fBorderWidth))) {
378				ComponentColors colors;
379				_GetComponentColors(COMPONENT_LEFT_BORDER, colors, fTopTab);
380
381				for (int8 i = 0; i < 3; i++) {
382					fDrawingEngine->StrokeLine(BPoint(r.left + i, r.top + i),
383						BPoint(r.left + i, r.bottom - i), colors[i * 2]);
384				}
385				if (fTopTab->look == kLeftTitledWindowLook
386					&& fTitleBarRect.IsValid()) {
387					// grey along the right side of the tab
388					// (overwrites "white" from frame)
389					fDrawingEngine->StrokeLine(
390						BPoint(fTitleBarRect.right + 1,
391							fTitleBarRect.top + 2),
392						BPoint(fTitleBarRect.right + 1,
393							fTitleBarRect.bottom - 2), colors[2]);
394				}
395			}
396			// bottom
397			if (rect.Intersects(fBottomBorder)) {
398				ComponentColors colors;
399				_GetComponentColors(COMPONENT_BOTTOM_BORDER, colors, fTopTab);
400
401				for (int8 i = 0; i < 3; i++) {
402					fDrawingEngine->StrokeLine(BPoint(r.left + i, r.bottom - i),
403						BPoint(r.right - i, r.bottom - i),
404						colors[(2 - i) == 2 ? 5 : (2 - i) * 2]);
405				}
406			}
407			// right
408			if (rect.Intersects(fRightBorder.InsetByCopy(0, -fBorderWidth))) {
409				ComponentColors colors;
410				_GetComponentColors(COMPONENT_RIGHT_BORDER, colors, fTopTab);
411
412				for (int8 i = 0; i < 3; i++) {
413					fDrawingEngine->StrokeLine(BPoint(r.right - i, r.top + i),
414						BPoint(r.right - i, r.bottom - i),
415						colors[(2 - i) == 2 ? 5 : (2 - i) * 2]);
416				}
417			}
418			break;
419		}
420
421		case B_BORDERED_WINDOW_LOOK:
422		{
423			// TODO: Draw the borders individually!
424			ComponentColors colors;
425			_GetComponentColors(COMPONENT_LEFT_BORDER, colors, fTopTab);
426
427			fDrawingEngine->StrokeRect(r, colors[5]);
428			break;
429		}
430
431		default:
432			// don't draw a border frame
433			break;
434	}
435
436	// Draw the resize knob if we're supposed to
437	if (!(fTopTab->flags & B_NOT_RESIZABLE)) {
438		r = fResizeRect;
439
440		ComponentColors colors;
441		_GetComponentColors(COMPONENT_RESIZE_CORNER, colors, fTopTab);
442
443		switch ((int)fTopTab->look) {
444			case B_DOCUMENT_WINDOW_LOOK:
445			{
446				if (!rect.Intersects(r))
447					break;
448
449				float x = r.right - 3;
450				float y = r.bottom - 3;
451
452				BRect bg(x - 15, y - 15, x, y);
453
454				BGradientLinear gradient;
455				gradient.SetStart(bg.LeftTop());
456				gradient.SetEnd(bg.RightBottom());
457				gradient.AddColor(tint_color(colors[1], 1.05), 0);
458				gradient.AddColor(tint_color(colors[1], 1.0), 255);
459
460				fDrawingEngine->FillRect(bg, gradient);
461
462				fDrawingEngine->StrokeLine(BPoint(x - 15, y - 15),
463					BPoint(x - 15, y - 1), colors[4]);
464				fDrawingEngine->StrokeLine(BPoint(x - 15, y - 15),
465					BPoint(x - 1, y - 15), colors[4]);
466
467				if (fTopTab && !IsFocus(fTopTab))
468					break;
469
470				for (int8 i = 1; i <= 4; i++) {
471					for (int8 j = 1; j <= i; j++) {
472						BPoint pt1(x - (3 * j) + 1, y - (3 * (5 - i)) + 1);
473						BPoint pt2(x - (3 * j) + 2, y - (3 * (5 - i)) + 2);
474						fDrawingEngine->StrokePoint(pt1, tint_color(colors[1], 1.5));
475						fDrawingEngine->StrokePoint(pt2, tint_color(colors[1], 0.75));
476					}
477				}
478				break;
479			}
480
481			case B_TITLED_WINDOW_LOOK:
482			case B_FLOATING_WINDOW_LOOK:
483			case B_MODAL_WINDOW_LOOK:
484			case kLeftTitledWindowLook:
485			{
486				if (!rect.Intersects(BRect(fRightBorder.right - kBorderResizeLength,
487					fBottomBorder.bottom - kBorderResizeLength, fRightBorder.right - 1,
488					fBottomBorder.bottom - 1)))
489					break;
490
491				fDrawingEngine->StrokeLine(
492					BPoint(fRightBorder.left, fBottomBorder.bottom - kBorderResizeLength),
493					BPoint(fRightBorder.right - 1, fBottomBorder.bottom - kBorderResizeLength),
494					tint_color(colors[1], 1.2));
495				fDrawingEngine->StrokeLine(
496					BPoint(fRightBorder.right - kBorderResizeLength, fBottomBorder.top),
497					BPoint(fRightBorder.right - kBorderResizeLength, fBottomBorder.bottom - 1),
498					tint_color(colors[1], 1.2));
499
500				// Try to draw line in yellow to the resize place
501				for (int8 i = 1; i < 4; i++) {
502					fDrawingEngine->StrokeLine(
503						BPoint(fRightBorder.left+i, fBottomBorder.bottom - kBorderResizeLength + 1),
504						BPoint(fRightBorder.left+i, fBottomBorder.bottom - 1),
505						tint_color(colors[3], (i * 0.06) + 1));
506				}
507				int rez[] = {4,3,2,1};
508				for (int8 i = 1; i < 4; i++) {
509					fDrawingEngine->StrokeLine(
510						BPoint(fRightBorder.right - kBorderResizeLength + 1, fBottomBorder.bottom - i),
511						BPoint(fRightBorder.right - i, fBottomBorder.bottom - i ),
512						tint_color(colors[3], (rez[i] * 0.06) + 1));
513				}
514
515				break;
516			}
517
518			default:
519				// don't draw resize corner
520				break;
521		}
522	}
523}
524
525
526/*!	\brief Actually draws the tab
527
528	This function is called when the tab itself needs drawn. Other items,
529	like the window title or buttons, should not be drawn here.
530
531	\param tab The \a tab to update.
532	\param rect The area of the \a tab to update.
533*/
534void
535FlatDecorator::_DrawTab(Decorator::Tab* tab, BRect invalid)
536{
537	STRACE(("_DrawTab(%.1f,%.1f,%.1f,%.1f)\n",
538		invalid.left, invalid.top, invalid.right, invalid.bottom));
539	const BRect& tabRect = tab->tabRect;
540	// If a window has a tab, this will draw it and any buttons which are
541	// in it.
542	if (!tabRect.IsValid() || !invalid.Intersects(tabRect))
543		return;
544
545	ComponentColors colors;
546	_GetComponentColors(COMPONENT_TAB, colors, tab);
547
548	if (tab && tab->buttonFocus) {
549		// outer frame
550		fDrawingEngine->StrokeLine(tabRect.LeftTop(), tabRect.LeftBottom(),
551			colors[COLOR_TAB_FRAME_DARK]);
552		fDrawingEngine->StrokeLine(tabRect.LeftTop(), tabRect.RightTop(),
553			colors[COLOR_TAB_FRAME_DARK]);
554		if (tab->look != kLeftTitledWindowLook) {
555			fDrawingEngine->StrokeLine(tabRect.RightTop(), tabRect.RightBottom(),
556				colors[COLOR_TAB_FRAME_DARK]);
557		} else {
558			fDrawingEngine->StrokeLine(tabRect.LeftBottom(),
559				tabRect.RightBottom(), fFocusFrameColor);
560		}
561	} else {
562		// outer frame
563		fDrawingEngine->StrokeLine(tabRect.LeftTop(), tabRect.LeftBottom(),
564			colors[COLOR_TAB_FRAME_DARK]);
565		fDrawingEngine->StrokeLine(tabRect.LeftTop(), tabRect.RightTop(),
566			colors[COLOR_TAB_FRAME_DARK]);
567		if (tab->look != kLeftTitledWindowLook) {
568			fDrawingEngine->StrokeLine(tabRect.RightTop(), tabRect.RightBottom(),
569				colors[COLOR_TAB_FRAME_DARK]);
570		} else {
571			fDrawingEngine->StrokeLine(tabRect.LeftBottom(),
572				tabRect.RightBottom(), fFocusFrameColor);
573		}
574	}
575
576	float tabBotton = tabRect.bottom;
577	if (fTopTab != tab)
578		tabBotton -= 1;
579
580	// bevel
581	fDrawingEngine->StrokeLine(BPoint(tabRect.left + 1, tabRect.top + 1),
582		BPoint(tabRect.left + 1,
583			tabBotton - (tab->look == kLeftTitledWindowLook ? 1 : 0)),
584		colors[COLOR_TAB]);
585	fDrawingEngine->StrokeLine(BPoint(tabRect.left + 1, tabRect.top + 1),
586		BPoint(tabRect.right - (tab->look == kLeftTitledWindowLook ? 0 : 1),
587			tabRect.top + 1),
588		tint_color(colors[COLOR_TAB], 0.9));
589
590	if (tab->look != kLeftTitledWindowLook) {
591		fDrawingEngine->StrokeLine(BPoint(tabRect.right - 1, tabRect.top + 2),
592			BPoint(tabRect.right - 1, tabBotton),
593			colors[COLOR_TAB]);
594	} else {
595		fDrawingEngine->StrokeLine(
596			BPoint(tabRect.left + 2, tabRect.bottom - 1),
597			BPoint(tabRect.right, tabRect.bottom - 1),
598			colors[COLOR_TAB]);
599	}
600
601	// fill
602	BGradientLinear gradient;
603	gradient.SetStart(tabRect.LeftTop());
604	if (tab && tab->buttonFocus) {
605		gradient.AddColor(tint_color(colors[COLOR_TAB], 0.6), 0);
606		gradient.AddColor(tint_color(colors[COLOR_TAB], 1.0), 200);
607	} else {
608		gradient.AddColor(tint_color(colors[COLOR_TAB], 0.9), 0);
609		gradient.AddColor(tint_color(colors[COLOR_TAB], 1.0), 150);
610	}
611
612	if (tab->look != kLeftTitledWindowLook) {
613		gradient.SetEnd(tabRect.LeftBottom());
614		fDrawingEngine->FillRect(BRect(tabRect.left + 2, tabRect.top + 2,
615			tabRect.right - 2, tabBotton), gradient);
616	} else {
617		gradient.SetEnd(tabRect.RightTop());
618		fDrawingEngine->FillRect(BRect(tabRect.left + 2, tabRect.top + 2,
619			tabRect.right, tabRect.bottom - 2), gradient);
620	}
621
622	_DrawTitle(tab, tabRect);
623
624	_DrawButtons(tab, invalid);
625}
626
627
628/*!	\brief Actually draws the title
629
630	The main tasks for this function are to ensure that the decorator draws
631	the title only in its own area and drawing the title itself.
632	Using B_OP_COPY for drawing the title is recommended because of the marked
633	performance hit of the other drawing modes, but it is not a requirement.
634
635	\param tab The \a tab to update.
636	\param rect area of the title to update.
637*/
638void
639FlatDecorator::_DrawTitle(Decorator::Tab* _tab, BRect rect)
640{
641	STRACE(("_DrawTitle(%f,%f,%f,%f)\n", rect.left, rect.top, rect.right,
642		rect.bottom));
643
644	Decorator::Tab* tab = static_cast<Decorator::Tab*>(_tab);
645
646	const BRect& tabRect = tab->tabRect;
647	const BRect& closeRect = tab->closeRect;
648	const BRect& zoomRect = tab->zoomRect;
649
650	ComponentColors colors;
651	_GetComponentColors(COMPONENT_TAB, colors, tab);
652
653	fDrawingEngine->SetDrawingMode(B_OP_OVER);
654	fDrawingEngine->SetHighColor(colors[COLOR_TAB_TEXT]);
655	fDrawingEngine->SetFont(fDrawState.Font());
656
657	// figure out position of text
658	font_height fontHeight;
659	fDrawState.Font().GetHeight(fontHeight);
660
661	BPoint titlePos;
662	if (tab->look != kLeftTitledWindowLook) {
663		titlePos.x = closeRect.IsValid() ? closeRect.right + tab->textOffset
664			: tabRect.left + tab->textOffset;
665		titlePos.y = floorf(((tabRect.top + 2.0) + tabRect.bottom
666			+ fontHeight.ascent + fontHeight.descent) / 2.0
667			- fontHeight.descent + 0.5);
668	} else {
669		titlePos.x = floorf(((tabRect.left + 2.0) + tabRect.right
670			+ fontHeight.ascent + fontHeight.descent) / 2.0
671			- fontHeight.descent + 0.5);
672		titlePos.y = zoomRect.IsValid() ? zoomRect.top - tab->textOffset
673			: tabRect.bottom - tab->textOffset;
674	}
675
676	fDrawingEngine->DrawString(tab->truncatedTitle, tab->truncatedTitleLength,
677		titlePos);
678
679	fDrawingEngine->SetDrawingMode(B_OP_COPY);
680}
681
682
683/*!	\brief Actually draws the close button
684
685	Unless a subclass has a particularly large button, it is probably
686	unnecessary to check the update rectangle.
687
688	\param _tab The \a tab to update.
689	\param direct Draw without double buffering.
690	\param rect The area of the button to update.
691*/
692void
693FlatDecorator::_DrawClose(Decorator::Tab* _tab, bool direct, BRect rect)
694{
695	STRACE(("_DrawClose(%f,%f,%f,%f)\n", rect.left, rect.top, rect.right,
696		rect.bottom));
697
698	Decorator::Tab* tab = static_cast<Decorator::Tab*>(_tab);
699
700	int32 index = (tab->buttonFocus ? 0 : 1) + (tab->closePressed ? 0 : 2);
701	ServerBitmap* bitmap = tab->closeBitmaps[index];
702	if (bitmap == NULL) {
703		bitmap = _GetBitmapForButton(tab, COMPONENT_CLOSE_BUTTON,
704			tab->closePressed, rect.IntegerWidth(), rect.IntegerHeight());
705		tab->closeBitmaps[index] = bitmap;
706	}
707
708	_DrawButtonBitmap(bitmap, direct, rect);
709}
710
711
712/*!	\brief Actually draws the zoom button
713
714	Unless a subclass has a particularly large button, it is probably
715	unnecessary to check the update rectangle.
716
717	\param _tab The \a tab to update.
718	\param direct Draw without double buffering.
719	\param rect The area of the button to update.
720*/
721void
722FlatDecorator::_DrawZoom(Decorator::Tab* _tab, bool direct, BRect rect)
723{
724	STRACE(("_DrawZoom(%f,%f,%f,%f)\n", rect.left, rect.top, rect.right,
725		rect.bottom));
726
727	if (rect.IntegerWidth() < 1)
728		return;
729
730	Decorator::Tab* tab = static_cast<Decorator::Tab*>(_tab);
731	int32 index = (tab->buttonFocus ? 0 : 1) + (tab->zoomPressed ? 0 : 2);
732	ServerBitmap* bitmap = tab->zoomBitmaps[index];
733	if (bitmap == NULL) {
734		bitmap = _GetBitmapForButton(tab, COMPONENT_ZOOM_BUTTON,
735			tab->zoomPressed, rect.IntegerWidth(), rect.IntegerHeight());
736		tab->zoomBitmaps[index] = bitmap;
737	}
738
739	_DrawButtonBitmap(bitmap, direct, rect);
740}
741
742
743void
744FlatDecorator::_DrawMinimize(Decorator::Tab* tab, bool direct, BRect rect)
745{
746	// This decorator doesn't have this button
747}
748
749
750// #pragma mark - Private methods
751
752
753void
754FlatDecorator::_DrawButtonBitmap(ServerBitmap* bitmap, bool direct,
755	BRect rect)
756{
757	if (bitmap == NULL)
758		return;
759
760	bool copyToFrontEnabled = fDrawingEngine->CopyToFrontEnabled();
761	fDrawingEngine->SetCopyToFrontEnabled(direct);
762	drawing_mode oldMode;
763	fDrawingEngine->SetDrawingMode(B_OP_OVER, oldMode);
764	fDrawingEngine->DrawBitmap(bitmap, rect.OffsetToCopy(0, 0), rect);
765	fDrawingEngine->SetDrawingMode(oldMode);
766	fDrawingEngine->SetCopyToFrontEnabled(copyToFrontEnabled);
767}
768
769
770/*!	\brief Draws a framed rectangle with a gradient.
771	\param engine The drawing engine to use.
772	\param rect The rectangular area to draw in.
773	\param down The rectangle should be drawn recessed or not.
774	\param colors A button color array of the colors to be used.
775*/
776void
777FlatDecorator::_DrawBlendedRect(DrawingEngine* engine, const BRect rect,
778	bool down, const ComponentColors& colors)
779{
780	engine->FillRect(rect, B_TRANSPARENT_COLOR);
781
782	// figure out which colors to use
783	rgb_color startColor, endColor;
784	if (down) {
785		startColor = tint_color(colors[COLOR_BUTTON], 1.0);
786		endColor = tint_color(colors[COLOR_BUTTON], 0.9);
787	} else {
788		startColor = tint_color(colors[COLOR_BUTTON], 0.95);
789		endColor = tint_color(colors[COLOR_BUTTON], 0.5);
790	}
791
792	// fill
793	BRect fillRect(rect.InsetByCopy(1.0f, 1.0f));
794
795	BGradientLinear gradient;
796	gradient.SetStart(fillRect.LeftBottom());
797	gradient.SetEnd(fillRect.LeftTop());
798	gradient.AddColor(startColor, 0);
799	gradient.AddColor(endColor, 250);
800
801	engine->FillRect(fillRect, gradient);
802
803	// outline
804	engine->StrokeRect(rect, tint_color(colors[COLOR_BUTTON], 1.25));
805
806}
807
808
809ServerBitmap*
810FlatDecorator::_GetBitmapForButton(Decorator::Tab* tab, Component item,
811	bool down, int32 width, int32 height)
812{
813	// TODO: the list of shared bitmaps is never freed
814	struct decorator_bitmap {
815		Component			item;
816		bool				down;
817		int32				width;
818		int32				height;
819		rgb_color			baseColor;
820		rgb_color			lightColor;
821		UtilityBitmap*		bitmap;
822		decorator_bitmap*	next;
823	};
824
825	static BLocker sBitmapListLock("decorator lock", true);
826	static decorator_bitmap* sBitmapList = NULL;
827
828	ComponentColors colors;
829	_GetComponentColors(item, colors, tab);
830
831	BAutolock locker(sBitmapListLock);
832
833	// search our list for a matching bitmap
834	// TODO: use a hash map instead?
835	decorator_bitmap* current = sBitmapList;
836	while (current) {
837		if (current->item == item && current->down == down
838			&& current->width == width && current->height == height
839			&& current->baseColor == colors[COLOR_BUTTON]
840			&& current->lightColor == colors[COLOR_BUTTON_LIGHT]) {
841			return current->bitmap;
842		}
843
844		current = current->next;
845	}
846
847	static BitmapDrawingEngine* sBitmapDrawingEngine = NULL;
848
849	// didn't find any bitmap, create a new one
850	if (sBitmapDrawingEngine == NULL)
851		sBitmapDrawingEngine = new(std::nothrow) BitmapDrawingEngine();
852	if (sBitmapDrawingEngine == NULL
853		|| sBitmapDrawingEngine->SetSize(width, height) != B_OK)
854		return NULL;
855
856	BRect rect(0, 0, width - 1, height - 1);
857
858	STRACE(("FlatDecorator creating bitmap for %s %s at size %ldx%ld\n",
859		item == COMPONENT_CLOSE_BUTTON ? "close" : "zoom",
860		down ? "down" : "up", width, height));
861	switch (item) {
862		case COMPONENT_CLOSE_BUTTON:
863			if (tab && tab->buttonFocus)
864				_DrawBlendedRect(sBitmapDrawingEngine, rect, down, colors);
865			else
866				_DrawBlendedRect(sBitmapDrawingEngine, rect, true, colors);
867			break;
868
869		case COMPONENT_ZOOM_BUTTON:
870		{
871			sBitmapDrawingEngine->FillRect(rect, B_TRANSPARENT_COLOR);
872				// init the background
873
874			float inset = floorf(width / 4.0);
875			BRect zoomRect(rect);
876			zoomRect.left += inset;
877			zoomRect.top += inset;
878			if (tab && tab->buttonFocus)
879				_DrawBlendedRect(sBitmapDrawingEngine, zoomRect, down, colors);
880			else
881				_DrawBlendedRect(sBitmapDrawingEngine, zoomRect, true, colors);
882
883			inset = floorf(width / 2.1);
884			zoomRect = rect;
885			zoomRect.right -= inset;
886			zoomRect.bottom -= inset;
887			if (tab && tab->buttonFocus)
888				_DrawBlendedRect(sBitmapDrawingEngine, zoomRect, down, colors);
889			else
890				_DrawBlendedRect(sBitmapDrawingEngine, zoomRect, true, colors);
891			break;
892		}
893
894		default:
895			break;
896	}
897
898	UtilityBitmap* bitmap = sBitmapDrawingEngine->ExportToBitmap(width, height,
899		B_RGB32);
900	if (bitmap == NULL)
901		return NULL;
902
903	// bitmap ready, put it into the list
904	decorator_bitmap* entry = new(std::nothrow) decorator_bitmap;
905	if (entry == NULL) {
906		delete bitmap;
907		return NULL;
908	}
909
910	entry->item = item;
911	entry->down = down;
912	entry->width = width;
913	entry->height = height;
914	entry->bitmap = bitmap;
915	entry->baseColor = colors[COLOR_BUTTON];
916	entry->lightColor = colors[COLOR_BUTTON_LIGHT];
917	entry->next = sBitmapList;
918	sBitmapList = entry;
919
920	return bitmap;
921}
922
923
924void
925FlatDecorator::_GetComponentColors(Component component,
926	ComponentColors _colors, Decorator::Tab* tab)
927{
928	// get the highlight for our component
929	Region region = REGION_NONE;
930	switch (component) {
931		case COMPONENT_TAB:
932			region = REGION_TAB;
933			break;
934		case COMPONENT_CLOSE_BUTTON:
935			region = REGION_CLOSE_BUTTON;
936			break;
937		case COMPONENT_ZOOM_BUTTON:
938			region = REGION_ZOOM_BUTTON;
939			break;
940		case COMPONENT_LEFT_BORDER:
941			region = REGION_LEFT_BORDER;
942			break;
943		case COMPONENT_RIGHT_BORDER:
944			region = REGION_RIGHT_BORDER;
945			break;
946		case COMPONENT_TOP_BORDER:
947			region = REGION_TOP_BORDER;
948			break;
949		case COMPONENT_BOTTOM_BORDER:
950			region = REGION_BOTTOM_BORDER;
951			break;
952		case COMPONENT_RESIZE_CORNER:
953			region = REGION_RIGHT_BOTTOM_CORNER;
954			break;
955	}
956
957	return GetComponentColors(component, RegionHighlight(region), _colors, tab);
958}
959
960
961extern "C" DecorAddOn* (instantiate_decor_addon)(image_id id, const char* name)
962{
963	return new (std::nothrow)FlatDecorAddOn(id, name);
964}
965