/* * Copyright 2001-2020 Haiku, Inc. * Distributed under the terms of the MIT License. * * Authors: * Stephan Aßmus, superstippi@gmx.de * DarkWyrm, bpmagic@columbus.rr.com * Ryan Leavengood, leavengood@gmail.com * Philippe Saint-Pierre, stpere@gmail.com * John Scipione, jscipione@gmail.com * Ingo Weinhold, ingo_weinhold@gmx.de * Clemens Zeidler, haiku@clemens-zeidler.de * Joseph Groover, looncraz@looncraz.net * Tri-Edge AI * Jacob Secunda, secundja@gmail.com */ /*! Default and fallback decorator for the app_server - the yellow tabs */ #include "DefaultDecorator.h" #include #include #include #include #include #include #include #include #include #include #include #include "BitmapDrawingEngine.h" #include "DesktopSettings.h" #include "DrawingEngine.h" #include "DrawState.h" #include "FontManager.h" #include "PatternHandler.h" #include "ServerBitmap.h" //#define DEBUG_DECORATOR #ifdef DEBUG_DECORATOR # define STRACE(x) printf x #else # define STRACE(x) ; #endif static inline uint8 blend_color_value(uint8 a, uint8 b, float position) { int16 delta = (int16)b - a; int32 value = a + (int32)(position * delta); if (value > 255) return 255; if (value < 0) return 0; return (uint8)value; } // #pragma mark - // TODO: get rid of DesktopSettings here, and introduce private accessor // methods to the Decorator base class DefaultDecorator::DefaultDecorator(DesktopSettings& settings, BRect rect, Desktop* desktop) : TabDecorator(settings, rect, desktop) { // TODO: If the decorator was created with a frame too small, it should // resize itself! STRACE(("DefaultDecorator:\n")); STRACE(("\tFrame (%.1f,%.1f,%.1f,%.1f)\n", rect.left, rect.top, rect.right, rect.bottom)); } DefaultDecorator::~DefaultDecorator() { STRACE(("DefaultDecorator: ~DefaultDecorator()\n")); } // #pragma mark - Public methods /*! Returns the frame colors for the specified decorator component. The meaning of the color array elements depends on the specified component. For some components some array elements are unused. \param component The component for which to return the frame colors. \param highlight The highlight set for the component. \param colors An array of colors to be initialized by the function. */ void DefaultDecorator::GetComponentColors(Component component, uint8 highlight, ComponentColors _colors, Decorator::Tab* _tab) { Decorator::Tab* tab = static_cast(_tab); switch (component) { case COMPONENT_TAB: if (tab && tab->buttonFocus) { _colors[COLOR_TAB_FRAME_LIGHT] = tint_color(fFocusFrameColor, B_DARKEN_2_TINT); _colors[COLOR_TAB_FRAME_DARK] = tint_color(fFocusFrameColor, B_DARKEN_3_TINT); _colors[COLOR_TAB] = fFocusTabColor; _colors[COLOR_TAB_LIGHT] = fFocusTabColorLight; _colors[COLOR_TAB_BEVEL] = fFocusTabColorBevel; _colors[COLOR_TAB_SHADOW] = fFocusTabColorShadow; _colors[COLOR_TAB_TEXT] = fFocusTextColor; } else { _colors[COLOR_TAB_FRAME_LIGHT] = tint_color(fNonFocusFrameColor, B_DARKEN_2_TINT); _colors[COLOR_TAB_FRAME_DARK] = tint_color(fNonFocusFrameColor, B_DARKEN_3_TINT); _colors[COLOR_TAB] = fNonFocusTabColor; _colors[COLOR_TAB_LIGHT] = fNonFocusTabColorLight; _colors[COLOR_TAB_BEVEL] = fNonFocusTabColorBevel; _colors[COLOR_TAB_SHADOW] = fNonFocusTabColorShadow; _colors[COLOR_TAB_TEXT] = fNonFocusTextColor; } break; case COMPONENT_CLOSE_BUTTON: case COMPONENT_ZOOM_BUTTON: if (tab && tab->buttonFocus) { _colors[COLOR_BUTTON] = fFocusTabColor; _colors[COLOR_BUTTON_LIGHT] = fFocusTabColorLight; } else { _colors[COLOR_BUTTON] = fNonFocusTabColor; _colors[COLOR_BUTTON_LIGHT] = fNonFocusTabColorLight; } break; case COMPONENT_LEFT_BORDER: case COMPONENT_RIGHT_BORDER: case COMPONENT_TOP_BORDER: case COMPONENT_BOTTOM_BORDER: case COMPONENT_RESIZE_CORNER: default: if (tab && tab->buttonFocus) { _colors[0] = tint_color(fFocusFrameColor, B_DARKEN_2_TINT); _colors[1] = tint_color(fFocusFrameColor, B_LIGHTEN_2_TINT); _colors[2] = fFocusFrameColor; _colors[3] = tint_color(fFocusFrameColor, (B_DARKEN_1_TINT + B_NO_TINT) / 2); _colors[4] = tint_color(fFocusFrameColor, B_DARKEN_2_TINT); _colors[5] = tint_color(fFocusFrameColor, B_DARKEN_3_TINT); } else { _colors[0] = tint_color(fNonFocusFrameColor, B_DARKEN_2_TINT); _colors[1] = tint_color(fNonFocusFrameColor, B_LIGHTEN_2_TINT); _colors[2] = fNonFocusFrameColor; _colors[3] = tint_color(fNonFocusFrameColor, (B_DARKEN_1_TINT + B_NO_TINT) / 2); _colors[4] = tint_color(fNonFocusFrameColor, B_DARKEN_2_TINT); _colors[5] = tint_color(fNonFocusFrameColor, B_DARKEN_3_TINT); } // for the resize-border highlight dye everything bluish. if (highlight == HIGHLIGHT_RESIZE_BORDER) { for (int32 i = 0; i < 6; i++) { _colors[i].red = std::max((int)_colors[i].red - 80, 0); _colors[i].green = std::max((int)_colors[i].green - 80, 0); _colors[i].blue = 255; } } break; } } void DefaultDecorator::UpdateColors(DesktopSettings& settings) { TabDecorator::UpdateColors(settings); } // #pragma mark - Protected methods void DefaultDecorator::_DrawFrame(BRect rect) { STRACE(("_DrawFrame(%f,%f,%f,%f)\n", rect.left, rect.top, rect.right, rect.bottom)); // NOTE: the DrawingEngine needs to be locked for the entire // time for the clipping to stay valid for this decorator if (fTopTab->look == B_NO_BORDER_WINDOW_LOOK) return; if (fBorderWidth <= 0) return; // TODO: While this works, it does not look so crisp at higher resolutions. #define COLORS_INDEX(i, borderWidth, nominalLimit) int32((float(i) / float(borderWidth)) * nominalLimit) // Draw the border frame BRect border = BRect(fTopBorder.LeftTop(), fBottomBorder.RightBottom()); switch ((int)fTopTab->look) { case B_TITLED_WINDOW_LOOK: case B_DOCUMENT_WINDOW_LOOK: case B_MODAL_WINDOW_LOOK: { // top if (rect.Intersects(fTopBorder)) { ComponentColors colors; _GetComponentColors(COMPONENT_TOP_BORDER, colors, fTopTab); for (int8 i = 0; i < fBorderWidth; i++) { const int8 colorsIndex = COLORS_INDEX(i, fBorderWidth, 5); fDrawingEngine->StrokeLine( BPoint(border.left + i, border.top + i), BPoint(border.right - i, border.top + i), colors[colorsIndex]); } if (fTitleBarRect.IsValid()) { // grey along the bottom of the tab // (overwrites "white" from frame) const int overdraw = (int)ceilf(fBorderWidth / 5.0f); for (int i = 1; i <= overdraw; i++) { fDrawingEngine->StrokeLine( BPoint(fTitleBarRect.left + 2, fTitleBarRect.bottom + i), BPoint(fTitleBarRect.right - 2, fTitleBarRect.bottom + i), colors[2]); } } } // left if (rect.Intersects(fLeftBorder.InsetByCopy(0, -fBorderWidth))) { ComponentColors colors; _GetComponentColors(COMPONENT_LEFT_BORDER, colors, fTopTab); for (int8 i = 0; i < fBorderWidth; i++) { const int8 colorsIndex = COLORS_INDEX(i, fBorderWidth, 5); fDrawingEngine->StrokeLine( BPoint(border.left + i, border.top + i), BPoint(border.left + i, border.bottom - i), colors[colorsIndex]); } } // bottom if (rect.Intersects(fBottomBorder)) { ComponentColors colors; _GetComponentColors(COMPONENT_BOTTOM_BORDER, colors, fTopTab); for (int8 i = 0; i < fBorderWidth; i++) { const int8 colorsIndex = COLORS_INDEX(i, fBorderWidth, 5); fDrawingEngine->StrokeLine( BPoint(border.left + i, border.bottom - i), BPoint(border.right - i, border.bottom - i), colors[(4 - colorsIndex) == 4 ? 5 : (4 - colorsIndex)]); } } // right if (rect.Intersects(fRightBorder.InsetByCopy(0, -fBorderWidth))) { ComponentColors colors; _GetComponentColors(COMPONENT_RIGHT_BORDER, colors, fTopTab); for (int8 i = 0; i < fBorderWidth; i++) { const int8 colorsIndex = COLORS_INDEX(i, fBorderWidth, 5); fDrawingEngine->StrokeLine( BPoint(border.right - i, border.top + i), BPoint(border.right - i, border.bottom - i), colors[(4 - colorsIndex) == 4 ? 5 : (4 - colorsIndex)]); } } break; } case B_FLOATING_WINDOW_LOOK: case kLeftTitledWindowLook: { // top if (rect.Intersects(fTopBorder)) { ComponentColors colors; _GetComponentColors(COMPONENT_TOP_BORDER, colors, fTopTab); for (int8 i = 0; i < fBorderWidth; i++) { const int8 colorsIndex = COLORS_INDEX(i, fBorderWidth, 3); fDrawingEngine->StrokeLine( BPoint(border.left + i, border.top + i), BPoint(border.right - i, border.top + i), colors[colorsIndex * 2]); } if (fTitleBarRect.IsValid() && fTopTab->look != kLeftTitledWindowLook) { // grey along the bottom of the tab // (overwrites "white" from frame) const int overdraw = (int)ceilf(fBorderWidth / 5.0f); for (int i = 1; i <= overdraw; i++) { fDrawingEngine->StrokeLine( BPoint(fTitleBarRect.left + 2, fTitleBarRect.bottom + i), BPoint(fTitleBarRect.right - 2, fTitleBarRect.bottom + i), colors[2]); } } } // left if (rect.Intersects(fLeftBorder.InsetByCopy(0, -fBorderWidth))) { ComponentColors colors; _GetComponentColors(COMPONENT_LEFT_BORDER, colors, fTopTab); for (int8 i = 0; i < fBorderWidth; i++) { const int8 colorsIndex = COLORS_INDEX(i, fBorderWidth, 3); fDrawingEngine->StrokeLine( BPoint(border.left + i, border.top + i), BPoint(border.left + i, border.bottom - i), colors[colorsIndex * 2]); } if (fTopTab->look == kLeftTitledWindowLook && fTitleBarRect.IsValid()) { // grey along the right side of the tab // (overwrites "white" from frame) fDrawingEngine->StrokeLine( BPoint(fTitleBarRect.right + 1, fTitleBarRect.top + 2), BPoint(fTitleBarRect.right + 1, fTitleBarRect.bottom - 2), colors[2]); } } // bottom if (rect.Intersects(fBottomBorder)) { ComponentColors colors; _GetComponentColors(COMPONENT_BOTTOM_BORDER, colors, fTopTab); for (int8 i = 0; i < fBorderWidth; i++) { const int8 colorsIndex = COLORS_INDEX(i, fBorderWidth, 3); fDrawingEngine->StrokeLine( BPoint(border.left + i, border.bottom - i), BPoint(border.right - i, border.bottom - i), colors[(2 - colorsIndex) == 2 ? 5 : (2 - colorsIndex) * 2]); } } // right if (rect.Intersects(fRightBorder.InsetByCopy(0, -fBorderWidth))) { ComponentColors colors; _GetComponentColors(COMPONENT_RIGHT_BORDER, colors, fTopTab); for (int8 i = 0; i < fBorderWidth; i++) { const int8 colorsIndex = COLORS_INDEX(i, fBorderWidth, 3); fDrawingEngine->StrokeLine( BPoint(border.right - i, border.top + i), BPoint(border.right - i, border.bottom - i), colors[(2 - colorsIndex) == 2 ? 5 : (2 - colorsIndex) * 2]); } } break; } case B_BORDERED_WINDOW_LOOK: { // TODO: Draw the borders individually! ComponentColors colors; _GetComponentColors(COMPONENT_LEFT_BORDER, colors, fTopTab); fDrawingEngine->StrokeRect(border, colors[5]); break; } default: // don't draw a border frame break; } // Draw the resize knob if we're supposed to if (!(fTopTab->flags & B_NOT_RESIZABLE)) { ComponentColors colors; _GetComponentColors(COMPONENT_RESIZE_CORNER, colors, fTopTab); switch ((int)fTopTab->look) { case B_DOCUMENT_WINDOW_LOOK: { if (fOutlinesDelta.x != 0 || fOutlinesDelta.y != 0) { border.Set(fFrame.right - 13, fFrame.bottom - 13, fFrame.right + 3, fFrame.bottom + 3); if (rect.Intersects(border)) _DrawResizeKnob(border, false, colors); } if (rect.Intersects(fResizeRect)) { _DrawResizeKnob(fResizeRect, fTopTab && IsFocus(fTopTab), colors); } break; } case B_TITLED_WINDOW_LOOK: case B_FLOATING_WINDOW_LOOK: case B_MODAL_WINDOW_LOOK: case kLeftTitledWindowLook: { if (!rect.Intersects(BRect( fRightBorder.right - fBorderResizeLength, fBottomBorder.bottom - fBorderResizeLength, fRightBorder.right - 1, fBottomBorder.bottom - 1))) break; fDrawingEngine->StrokeLine( BPoint(fRightBorder.left, fBottomBorder.bottom - fBorderResizeLength), BPoint(fRightBorder.right - 1, fBottomBorder.bottom - fBorderResizeLength), colors[0]); fDrawingEngine->StrokeLine( BPoint(fRightBorder.right - fBorderResizeLength, fBottomBorder.top), BPoint(fRightBorder.right - fBorderResizeLength, fBottomBorder.bottom - 1), colors[0]); break; } default: // don't draw resize corner break; } } } void DefaultDecorator::_DrawResizeKnob(BRect rect, bool full, const ComponentColors& colors) { float x = rect.right -= 3; float y = rect.bottom -= 3; BGradientLinear gradient; gradient.SetStart(rect.LeftTop()); gradient.SetEnd(rect.RightBottom()); gradient.AddColor(colors[1], 0); gradient.AddColor(colors[2], 255); fDrawingEngine->FillRect(rect, gradient); BPoint offset1(rect.Width(), rect.Height()), offset2(rect.Width() - 1, rect.Height() - 1); fDrawingEngine->StrokeLine(BPoint(x, y) - offset1, BPoint(x - offset1.x, y - 2), colors[0]); fDrawingEngine->StrokeLine(BPoint(x, y) - offset2, BPoint(x - offset2.x, y - 1), colors[1]); fDrawingEngine->StrokeLine(BPoint(x, y) - offset1, BPoint(x - 2, y - offset1.y), colors[0]); fDrawingEngine->StrokeLine(BPoint(x, y) - offset2, BPoint(x - 1, y - offset2.y), colors[1]); if (!full) return; static const rgb_color kWhite = (rgb_color){ 255, 255, 255, 255 }; for (int8 i = 1; i <= 4; i++) { for (int8 j = 1; j <= i; j++) { BPoint pt1(x - (3 * j) + 1, y - (3 * (5 - i)) + 1); BPoint pt2(x - (3 * j) + 2, y - (3 * (5 - i)) + 2); fDrawingEngine->StrokePoint(pt1, colors[0]); fDrawingEngine->StrokePoint(pt2, kWhite); } } } /*! \brief Actually draws the tab This function is called when the tab itself needs drawn. Other items, like the window title or buttons, should not be drawn here. \param tab The \a tab to update. \param rect The area of the \a tab to update. */ void DefaultDecorator::_DrawTab(Decorator::Tab* tab, BRect invalid) { STRACE(("_DrawTab(%.1f,%.1f,%.1f,%.1f)\n", invalid.left, invalid.top, invalid.right, invalid.bottom)); const BRect& tabRect = tab->tabRect; // If a window has a tab, this will draw it and any buttons which are // in it. if (!tabRect.IsValid() || !invalid.Intersects(tabRect)) return; ComponentColors colors; _GetComponentColors(COMPONENT_TAB, colors, tab); // outer frame fDrawingEngine->StrokeLine(tabRect.LeftTop(), tabRect.LeftBottom(), colors[COLOR_TAB_FRAME_LIGHT]); fDrawingEngine->StrokeLine(tabRect.LeftTop(), tabRect.RightTop(), colors[COLOR_TAB_FRAME_LIGHT]); if (tab->look != kLeftTitledWindowLook) { fDrawingEngine->StrokeLine(tabRect.RightTop(), tabRect.RightBottom(), colors[COLOR_TAB_FRAME_DARK]); } else { fDrawingEngine->StrokeLine(tabRect.LeftBottom(), tabRect.RightBottom(), colors[COLOR_TAB_FRAME_DARK]); } float tabBotton = tabRect.bottom; if (fTopTab != tab) tabBotton -= 1; // bevel fDrawingEngine->StrokeLine(BPoint(tabRect.left + 1, tabRect.top + 1), BPoint(tabRect.left + 1, tabBotton - (tab->look == kLeftTitledWindowLook ? 1 : 0)), colors[COLOR_TAB_BEVEL]); fDrawingEngine->StrokeLine(BPoint(tabRect.left + 1, tabRect.top + 1), BPoint(tabRect.right - (tab->look == kLeftTitledWindowLook ? 0 : 1), tabRect.top + 1), colors[COLOR_TAB_BEVEL]); if (tab->look != kLeftTitledWindowLook) { fDrawingEngine->StrokeLine(BPoint(tabRect.right - 1, tabRect.top + 2), BPoint(tabRect.right - 1, tabBotton), colors[COLOR_TAB_SHADOW]); } else { fDrawingEngine->StrokeLine( BPoint(tabRect.left + 2, tabRect.bottom - 1), BPoint(tabRect.right, tabRect.bottom - 1), colors[COLOR_TAB_SHADOW]); } // fill BGradientLinear gradient; gradient.SetStart(tabRect.LeftTop()); gradient.AddColor(colors[COLOR_TAB_LIGHT], 0); gradient.AddColor(colors[COLOR_TAB], 255); if (tab->look != kLeftTitledWindowLook) { gradient.SetEnd(tabRect.LeftBottom()); fDrawingEngine->FillRect(BRect(tabRect.left + 2, tabRect.top + 2, tabRect.right - 2, tabBotton), gradient); } else { gradient.SetEnd(tabRect.RightTop()); fDrawingEngine->FillRect(BRect(tabRect.left + 2, tabRect.top + 2, tabRect.right, tabRect.bottom - 2), gradient); } _DrawTitle(tab, tabRect); _DrawButtons(tab, invalid); } /*! \brief Actually draws the title The main tasks for this function are to ensure that the decorator draws the title only in its own area and drawing the title itself. Using B_OP_COPY for drawing the title is recommended because of the marked performance hit of the other drawing modes, but it is not a requirement. \param tab The \a tab to update. \param rect area of the title to update. */ void DefaultDecorator::_DrawTitle(Decorator::Tab* _tab, BRect rect) { STRACE(("_DrawTitle(%f,%f,%f,%f)\n", rect.left, rect.top, rect.right, rect.bottom)); Decorator::Tab* tab = static_cast(_tab); const BRect& tabRect = tab->tabRect; const BRect& closeRect = tab->closeRect; const BRect& zoomRect = tab->zoomRect; ComponentColors colors; _GetComponentColors(COMPONENT_TAB, colors, tab); fDrawingEngine->SetDrawingMode(B_OP_OVER); fDrawingEngine->SetHighColor(colors[COLOR_TAB_TEXT]); fDrawingEngine->SetFont(fDrawState.Font()); // figure out position of text font_height fontHeight; fDrawState.Font().GetHeight(fontHeight); BPoint titlePos; if (tab->look != kLeftTitledWindowLook) { titlePos.x = closeRect.IsValid() ? closeRect.right + tab->textOffset : tabRect.left + tab->textOffset; titlePos.y = floorf(((tabRect.top + 2.0) + tabRect.bottom + fontHeight.ascent + fontHeight.descent) / 2.0 - fontHeight.descent + 0.5); } else { titlePos.x = floorf(((tabRect.left + 2.0) + tabRect.right + fontHeight.ascent + fontHeight.descent) / 2.0 - fontHeight.descent + 0.5); titlePos.y = zoomRect.IsValid() ? zoomRect.top - tab->textOffset : tabRect.bottom - tab->textOffset; } fDrawingEngine->DrawString(tab->truncatedTitle, tab->truncatedTitleLength, titlePos); fDrawingEngine->SetDrawingMode(B_OP_COPY); } /*! \brief Actually draws the close button Unless a subclass has a particularly large button, it is probably unnecessary to check the update rectangle. \param _tab The \a tab to update. \param direct Draw without double buffering. \param rect The area of the button to update. */ void DefaultDecorator::_DrawClose(Decorator::Tab* _tab, bool direct, BRect rect) { STRACE(("_DrawClose(%f,%f,%f,%f)\n", rect.left, rect.top, rect.right, rect.bottom)); Decorator::Tab* tab = static_cast(_tab); int32 index = (tab->buttonFocus ? 0 : 1) + (tab->closePressed ? 0 : 2); ServerBitmap* bitmap = tab->closeBitmaps[index]; if (bitmap == NULL) { bitmap = _GetBitmapForButton(tab, COMPONENT_CLOSE_BUTTON, tab->closePressed, rect.IntegerWidth(), rect.IntegerHeight()); tab->closeBitmaps[index] = bitmap; } _DrawButtonBitmap(bitmap, direct, rect); } /*! \brief Actually draws the zoom button Unless a subclass has a particularly large button, it is probably unnecessary to check the update rectangle. \param _tab The \a tab to update. \param direct Draw without double buffering. \param rect The area of the button to update. */ void DefaultDecorator::_DrawZoom(Decorator::Tab* _tab, bool direct, BRect rect) { STRACE(("_DrawZoom(%f,%f,%f,%f)\n", rect.left, rect.top, rect.right, rect.bottom)); if (rect.IntegerWidth() < 1) return; Decorator::Tab* tab = static_cast(_tab); int32 index = (tab->buttonFocus ? 0 : 1) + (tab->zoomPressed ? 0 : 2); ServerBitmap* bitmap = tab->zoomBitmaps[index]; if (bitmap == NULL) { bitmap = _GetBitmapForButton(tab, COMPONENT_ZOOM_BUTTON, tab->zoomPressed, rect.IntegerWidth(), rect.IntegerHeight()); tab->zoomBitmaps[index] = bitmap; } _DrawButtonBitmap(bitmap, direct, rect); } void DefaultDecorator::_DrawMinimize(Decorator::Tab* tab, bool direct, BRect rect) { // This decorator doesn't have this button } // #pragma mark - Private methods void DefaultDecorator::_DrawButtonBitmap(ServerBitmap* bitmap, bool direct, BRect rect) { if (bitmap == NULL) return; bool copyToFrontEnabled = fDrawingEngine->CopyToFrontEnabled(); fDrawingEngine->SetCopyToFrontEnabled(direct); drawing_mode oldMode; fDrawingEngine->SetDrawingMode(B_OP_OVER, oldMode); fDrawingEngine->DrawBitmap(bitmap, rect.OffsetToCopy(0, 0), rect); fDrawingEngine->SetDrawingMode(oldMode); fDrawingEngine->SetCopyToFrontEnabled(copyToFrontEnabled); } /*! \brief Draws a framed rectangle with a gradient. \param engine The drawing engine to use. \param rect The rectangular area to draw in. \param down The rectangle should be drawn recessed or not. \param colors A button color array of the colors to be used. */ void DefaultDecorator::_DrawBlendedRect(DrawingEngine* engine, const BRect rect, bool down, const ComponentColors& colors) { // figure out which colors to use rgb_color startColor, endColor; if (down) { startColor = tint_color(colors[COLOR_BUTTON], B_DARKEN_1_TINT); endColor = colors[COLOR_BUTTON_LIGHT]; } else { startColor = tint_color(colors[COLOR_BUTTON], B_LIGHTEN_MAX_TINT); endColor = colors[COLOR_BUTTON]; } // fill BRect fillRect(rect.InsetByCopy(1.0f, 1.0f)); BGradientLinear gradient; gradient.SetStart(fillRect.LeftTop()); gradient.SetEnd(fillRect.RightBottom()); gradient.AddColor(startColor, 0); gradient.AddColor(endColor, 255); engine->FillRect(fillRect, gradient); // outline engine->StrokeRect(rect, tint_color(colors[COLOR_BUTTON], B_DARKEN_2_TINT)); } ServerBitmap* DefaultDecorator::_GetBitmapForButton(Decorator::Tab* tab, Component item, bool down, int32 width, int32 height) { // TODO: the list of shared bitmaps is never freed struct decorator_bitmap { Component item; bool down; int32 width; int32 height; rgb_color baseColor; rgb_color lightColor; UtilityBitmap* bitmap; decorator_bitmap* next; }; static BLocker sBitmapListLock("decorator lock", true); static decorator_bitmap* sBitmapList = NULL; ComponentColors colors; _GetComponentColors(item, colors, tab); BAutolock locker(sBitmapListLock); // search our list for a matching bitmap // TODO: use a hash map instead? decorator_bitmap* current = sBitmapList; while (current) { if (current->item == item && current->down == down && current->width == width && current->height == height && current->baseColor == colors[COLOR_BUTTON] && current->lightColor == colors[COLOR_BUTTON_LIGHT]) { return current->bitmap; } current = current->next; } static BitmapDrawingEngine* sBitmapDrawingEngine = NULL; // didn't find any bitmap, create a new one if (sBitmapDrawingEngine == NULL) sBitmapDrawingEngine = new(std::nothrow) BitmapDrawingEngine(); if (sBitmapDrawingEngine == NULL || sBitmapDrawingEngine->SetSize(width, height) != B_OK) return NULL; BRect rect(0, 0, width - 1, height - 1); STRACE(("DefaultDecorator creating bitmap for %s %s at size %ldx%ld\n", item == COMPONENT_CLOSE_BUTTON ? "close" : "zoom", down ? "down" : "up", width, height)); switch (item) { case COMPONENT_CLOSE_BUTTON: _DrawBlendedRect(sBitmapDrawingEngine, rect, down, colors); break; case COMPONENT_ZOOM_BUTTON: { sBitmapDrawingEngine->FillRect(rect, B_TRANSPARENT_COLOR); // init the background float inset = floorf(width / 4.0); BRect zoomRect(rect); zoomRect.left += inset; zoomRect.top += inset; _DrawBlendedRect(sBitmapDrawingEngine, zoomRect, down, colors); inset = floorf(width / 2.1); zoomRect = rect; zoomRect.right -= inset; zoomRect.bottom -= inset; _DrawBlendedRect(sBitmapDrawingEngine, zoomRect, down, colors); break; } default: break; } UtilityBitmap* bitmap = sBitmapDrawingEngine->ExportToBitmap(width, height, B_RGB32); if (bitmap == NULL) return NULL; // bitmap ready, put it into the list decorator_bitmap* entry = new(std::nothrow) decorator_bitmap; if (entry == NULL) { delete bitmap; return NULL; } entry->item = item; entry->down = down; entry->width = width; entry->height = height; entry->bitmap = bitmap; entry->baseColor = colors[COLOR_BUTTON]; entry->lightColor = colors[COLOR_BUTTON_LIGHT]; entry->next = sBitmapList; sBitmapList = entry; return bitmap; } void DefaultDecorator::_GetComponentColors(Component component, ComponentColors _colors, Decorator::Tab* tab) { // get the highlight for our component Region region = REGION_NONE; switch (component) { case COMPONENT_TAB: region = REGION_TAB; break; case COMPONENT_CLOSE_BUTTON: region = REGION_CLOSE_BUTTON; break; case COMPONENT_ZOOM_BUTTON: region = REGION_ZOOM_BUTTON; break; case COMPONENT_LEFT_BORDER: region = REGION_LEFT_BORDER; break; case COMPONENT_RIGHT_BORDER: region = REGION_RIGHT_BORDER; break; case COMPONENT_TOP_BORDER: region = REGION_TOP_BORDER; break; case COMPONENT_BOTTOM_BORDER: region = REGION_BOTTOM_BORDER; break; case COMPONENT_RESIZE_CORNER: region = REGION_RIGHT_BOTTOM_CORNER; break; } return GetComponentColors(component, RegionHighlight(region), _colors, tab); }