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