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