Decorator and ControlLook

Decorator and ControlLook, stand out as the architects of the user interface These components plays a pivotal role in shaping the visual aesthetics and functional nuances of the operating system.

Understanding the Decorator:

What is a Decorator?

In Haiku OS, a Decorator is responsible for framing and decorating the windows of applications, giving them a distinct appearance. Think of it as the visual wrapper around application windows, determining the window's shape, borders, title bar, and controls.

image-44.png

Understanding ControlLook:

What is ControlLook?

In Haiku OS, ControlLook refers to the uniform visual style applied to user interface controls such as buttons, sliders, and text fields. It complements Decorator by defining the appearance and behavior of user interface elements or controls within an application window.

image-45.png

Developing the Decorator

The DecorAddon class serves as the foundation for creating custom decorators in Haiku OS. It provides a framework for defining windows decorations, handling user interactions, and managing the visual presentation of windows. By inheriting from the DecorAddon class, developers can leverage its functionality to implement their custom decorator logic.

DecorAddon Class Declaration:

Create an class that represents our add-on which will inherits from the DecorAddOn class. It should provide a constructor and a protected virtual method _AllocateDecorator() for allocating a decorating object.

Let us consider FlatDecorAddon as the class name for our example:

So, we created and header file for our class declaration as follows:

// FlatDecorator.h

class FlatDecorAddOn : public DecorAddOn {
public:
    FlatDecorAddOn(image_id id, const char* name);

protected:
    virtual Decorator* _AllocateDecorator(DesktopSettings& settings, BRect rect, Desktop* desktop);
};

Inheriting From TabDecorator

Create an class which inherits from the TabDecorator class. It should provide a constructor and a destructor. It should override several virtual methods for drawing frames, tabs, titles, buttons, and handling colors.

Write it down in the same header file as above:

class FlatDecorator : public TabDecorator {
public:
    FlatDecorator(DesktopSettings& settings, BRect frame, Desktop* desktop);
    virtual ~FlatDecorator();

    virtual void GetComponentColors(Component component, uint8 highlight, ComponentColors _colors, Decorator::Tab* tab = NULL);
    virtual void UpdateColors(DesktopSettings& settings);

protected:
    virtual void _DrawFrame(BRect rect);
    virtual void _DrawTab(Decorator::Tab* tab, BRect r);
    virtual void _DrawTitle(Decorator::Tab* tab, BRect r);
    virtual void _DrawClose(Decorator::Tab* tab, bool direct, BRect rect);
    virtual void _DrawZoom(Decorator::Tab* tab, bool direct, BRect rect);
    virtual void _DrawMinimize(Decorator::Tab* tab, bool direct, BRect rect);

private:
    void _DrawButtonBitmap(ServerBitmap* bitmap, bool direct, BRect rect);
    void _DrawBlendedRect(DrawingEngine *engine, const BRect rect, bool down, const ComponentColors& colors);
    ServerBitmap* _GetBitmapForButton(Decorator::Tab* tab, Component item, bool down, int32 width, int32 height);
    void _GetComponentColors(Component component, ComponentColors _colors, Decorator::Tab* tab = NULL);
};

Class FlatDecorAddon:

FlatDecorAddOn::FlatDecorAddOn(image_id id, const char* name)
    : DecorAddOn(id, name)
{
}

Decorator* FlatDecorAddOn::_AllocateDecorator(DesktopSettings& settings, BRect rect, Desktop* desktop)
{
    return new (std::nothrow) FlatDecorator(settings, rect, desktop);
}
  • It defines a class FlatDecorAddon which inherits from DecorAddOn.
  • The constructor initializes DecorAddOn using an initialization list.
  • It provides an implementation for the _AllocateDecorator method to allocate and return a new FlatDecorator object.

Class FlatDecorator:

Constructor

FlatDecorator::FlatDecorator(DesktopSettings& settings, BRect rect, Desktop* desktop)
    : TabDecorator(settings, rect, desktop)
{
    STRACE(("FlatDecorator:\n"));
    STRACE(("\tFrame (%.1f,%.1f,%.1f,%.1f)\n", rect.left, rect.top, rect.right, rect.bottom));
}
  • Constructor initializes FlatDecorator using an initialization list, passing settings, rectangle, and desktop objects.
  • It prints debug information about the frame coordinates.
  • This class overrides methods for drawing various window elements like tabs, buttons, frames, etc.

Component Color Handling:

The GetComponentColors method is responsible for determining the colors of different components of the window decoration based on the window's state (focused or not focused).

/*!	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
FlatDecorator::GetComponentColors(Component component, uint8 highlight,
	ComponentColors _colors, Decorator::Tab* _tab)
{
	Decorator::Tab* tab = static_cast<Decorator::Tab*>(_tab);
	switch (component) {
		case COMPONENT_TAB:
			if (highlight != 0) {
				_colors[COLOR_TAB_FRAME_LIGHT]
					= tint_color(fFocusTabColor, 1.0);
				_colors[COLOR_TAB_FRAME_DARK]
					= tint_color(fFocusTabColor, 1.2);
				_colors[COLOR_TAB] = tint_color(fFocusTabColor, 0.95);
				_colors[COLOR_TAB_LIGHT] = fFocusTabColorLight; 
				_colors[COLOR_TAB_BEVEL] = fFocusTabColorBevel;
				_colors[COLOR_TAB_SHADOW] = fFocusTabColorShadow;
				_colors[COLOR_TAB_TEXT] = tint_color(fFocusTextColor, 0.5);
			} 
			else if (tab && tab->buttonFocus) {
				_colors[COLOR_TAB_FRAME_LIGHT]
					= tint_color(fFocusTabColor, 1.0);
				_colors[COLOR_TAB_FRAME_DARK]
					= tint_color(fFocusTabColor, 1.2);
				_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(fNonFocusTabColor, 1.0);
				_colors[COLOR_TAB_FRAME_DARK]
					= tint_color(fNonFocusTabColor, 1.2);
				_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_TOP_BORDER:
			if (tab && tab->buttonFocus) {
				_colors[0] = tint_color(fFocusTabColor, 1.2); // borde exterior
				_colors[1] = tint_color(fFocusTabColor, 1.0); // borde top
				_colors[2] = tint_color(fFocusTabColor, 1.0); // borde top
				_colors[3] = tint_color(fFocusTabColor, 1.0); // borde top
				_colors[4] = tint_color(fFocusFrameColor, 1.1); // borde interior
				_colors[5] = tint_color(fFocusFrameColor, 1.1); // borde menu 1
			} else {
				_colors[0] = tint_color(fNonFocusTabColor, 1.2); // borde exterior
				_colors[1] = tint_color(fNonFocusTabColor, B_NO_TINT);
				_colors[2] = tint_color(fNonFocusTabColor, B_NO_TINT);
				_colors[3] = tint_color(fNonFocusTabColor, B_NO_TINT);
				_colors[4] = tint_color(fNonFocusFrameColor, 1.1); // borde interior
				_colors[5] = tint_color(fNonFocusFrameColor, 1.1); // borde menu 1
			}
			break;
		case COMPONENT_RESIZE_CORNER:
			if (tab && tab->buttonFocus) {
				_colors[0] = tint_color(fFocusFrameColor, 1.25); // borde exterior
				_colors[1] = tint_color(fFocusFrameColor, 1.0); // borde top
				_colors[2] = tint_color(fFocusFrameColor, 1.0); // borde top
				_colors[3] = tint_color(fFocusTabColor, 1.0); // borde top
				_colors[4] = tint_color(fFocusFrameColor, 1.1); // borde interior
				_colors[5] = tint_color(fFocusFrameColor, 1.1); // borde menu 1
			} else {
				_colors[0] = tint_color(fNonFocusFrameColor, 1.25); // borde exterior
				_colors[1] = tint_color(fNonFocusFrameColor, B_NO_TINT);
				_colors[2] = tint_color(fNonFocusFrameColor, B_NO_TINT);
				_colors[3] = tint_color(fNonFocusFrameColor, B_NO_TINT);
				_colors[4] = tint_color(fNonFocusFrameColor, 1.1); // borde interior
				_colors[5] = tint_color(fNonFocusFrameColor, 1.1); // borde menu 1
			}
			break;
		case COMPONENT_LEFT_BORDER:
		case COMPONENT_RIGHT_BORDER:
			if (tab && tab->buttonFocus) {
				_colors[0] = tint_color(fFocusFrameColor, 1.25); // borde exterior
				_colors[1] = tint_color(fFocusFrameColor, B_NO_TINT);
				_colors[2] = tint_color(fFocusFrameColor, B_NO_TINT);
				_colors[3] = tint_color(fFocusFrameColor, B_NO_TINT);
				_colors[4] = tint_color(fFocusFrameColor, 1.05); // borde interior
				_colors[5] = tint_color(fFocusFrameColor, 1.1); // borde menu 1
				_colors[6] = tint_color(fFocusTabColor, 1.2); // border tab to be part
			} else {
				_colors[0] = tint_color(fNonFocusFrameColor, 1.25); // borde exterior
				_colors[1] = tint_color(fNonFocusFrameColor, B_NO_TINT);
				_colors[2] = tint_color(fNonFocusFrameColor, B_NO_TINT);
				_colors[3] = tint_color(fNonFocusFrameColor, B_NO_TINT);
				_colors[4] = tint_color(fNonFocusFrameColor, 1.05); // borde interior
				_colors[5] = tint_color(fNonFocusFrameColor, 1.0); // borde menu 1
				_colors[6] = tint_color(fNonFocusTabColor, 1.2); // border tab to be part
			}
			break;
		case COMPONENT_BOTTOM_BORDER:
		default:
			if (tab && tab->buttonFocus) {
				_colors[0] = tint_color(fFocusFrameColor, 1.25); // borde exterior
				_colors[1] = tint_color(fFocusFrameColor, B_NO_TINT);
				_colors[2] = tint_color(fFocusFrameColor, B_NO_TINT);
				_colors[3] = tint_color(fFocusFrameColor, B_NO_TINT);
				_colors[4] = tint_color(fFocusFrameColor, 1.1); // borde interior
				_colors[5] = tint_color(fFocusFrameColor, 1.1); // borde menu 1
			} else {
				_colors[0] = tint_color(fNonFocusFrameColor, 1.25); // borde exterior
				_colors[1] = tint_color(fNonFocusFrameColor, B_NO_TINT);
				_colors[2] = tint_color(fNonFocusFrameColor, B_NO_TINT);
				_colors[3] = tint_color(fNonFocusFrameColor, B_NO_TINT);
				_colors[4] = tint_color(fNonFocusFrameColor, 1.1); // borde interior
				_colors[5] = tint_color(fNonFocusFrameColor, 1.1); // borde menu 1
			}

			// 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;
	}
}

Update Colors

void
FlatDecorator::UpdateColors(DesktopSettings& settings)
{
	TabDecorator::UpdateColors(settings);
}


The UpdateColors() function in the FlatDecorator class is responsible for updating the colors used within the decorator based on the provided DesktopSettings. It inherits this functionality from its parent class TabDecorator by invoking TabDecorator::UpdateColors(settings). This function ensures that the decorator adapts dynamically to changes in desktop settings, maintaining visual consistency across the application.

_DrawFrame(BRect rect)

// #pragma mark - Protected methods


void
FlatDecorator::_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;

	// Draw the border frame
	BRect r = BRect(fTopBorder.LeftTop(), fBottomBorder.RightBottom());
	switch ((int)fTopTab->look) {
		case B_TITLED_WINDOW_LOOK:
		case B_DOCUMENT_WINDOW_LOOK:
		case B_MODAL_WINDOW_LOOK:
		{
			// left
			if (rect.Intersects(fLeftBorder.InsetByCopy(0, -fBorderWidth))) {
				ComponentColors colors;
				_GetComponentColors(COMPONENT_LEFT_BORDER, colors, fTopTab);

				for (int8 i = 0; i < 5; i++) {
					fDrawingEngine->StrokeLine(BPoint(r.left + i, r.top + i),
						BPoint(r.left + i, r.bottom - i), colors[i]);
				}
				// redraw line to be part of tab title
				fDrawingEngine->StrokeLine(BPoint(r.left, r.top),
					BPoint(r.left, r.top + 4), colors[6]);
				
			}
			// bottom
			if (rect.Intersects(fBottomBorder)) {
				ComponentColors colors;
				_GetComponentColors(COMPONENT_BOTTOM_BORDER, colors, fTopTab);

				for (int8 i = 0; i < 5; i++) {
					fDrawingEngine->StrokeLine(BPoint(r.left + i, r.bottom - i),
						BPoint(r.right - i, r.bottom - i),
						colors[i]);
				}
			}
			// right
			if (rect.Intersects(fRightBorder.InsetByCopy(0, -fBorderWidth))) {
				ComponentColors colors;
				_GetComponentColors(COMPONENT_RIGHT_BORDER, colors, fTopTab);

				for (int8 i = 0; i < 5; i++) {
						fDrawingEngine->StrokeLine(BPoint(r.right - i, r.top + i),
							BPoint(r.right - i, r.bottom - i),
							colors[i]);
				}
				// redraw line to be part of tab title
				fDrawingEngine->StrokeLine(BPoint(r.right, r.top),
					BPoint(r.right, r.top + 4),
					colors[6]);
			}
			// top
			if (rect.Intersects(fTopBorder)) {
				ComponentColors colors;
				_GetComponentColors(COMPONENT_TOP_BORDER, colors, fTopTab);

				for (int8 i = 0; i < 5; i++) {
					if (i<4)
					{
						fDrawingEngine->StrokeLine(BPoint(r.left + 1, r.top + i),
							BPoint(r.right - 1, r.top + i), tint_color(colors[i], (i*0.01+1)));
					}
					else
					{
						fDrawingEngine->StrokeLine(BPoint(r.left + 1, r.top + i),
							BPoint(r.right - 1, r.top + i), tint_color(colors[3], 1.1));
					}
				}
			}
			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 < 3; i++) {
					fDrawingEngine->StrokeLine(BPoint(r.left + i, r.top + i),
						BPoint(r.right - i, r.top + i), tint_color(colors[1], 0.95));
				}
				if (fTitleBarRect.IsValid() && fTopTab->look != kLeftTitledWindowLook) {
					// grey along the bottom of the tab
					// (overwrites "white" from frame)
					fDrawingEngine->StrokeLine(
						BPoint(fTitleBarRect.left + 2,
							fTitleBarRect.bottom + 1),
						BPoint(fTitleBarRect.right - 2,
							fTitleBarRect.bottom + 1), colors[2]);
				}
			}
			// left
			if (rect.Intersects(fLeftBorder.InsetByCopy(0, -fBorderWidth))) {
				ComponentColors colors;
				_GetComponentColors(COMPONENT_LEFT_BORDER, colors, fTopTab);

				for (int8 i = 0; i < 3; i++) {
					fDrawingEngine->StrokeLine(BPoint(r.left + i, r.top + i),
						BPoint(r.left + i, r.bottom - i), colors[i * 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 < 3; i++) {
					fDrawingEngine->StrokeLine(BPoint(r.left + i, r.bottom - i),
						BPoint(r.right - i, r.bottom - i),
						colors[(2 - i) == 2 ? 5 : (2 - i) * 2]);
				}
			}
			// right
			if (rect.Intersects(fRightBorder.InsetByCopy(0, -fBorderWidth))) {
				ComponentColors colors;
				_GetComponentColors(COMPONENT_RIGHT_BORDER, colors, fTopTab);

				for (int8 i = 0; i < 3; i++) {
					fDrawingEngine->StrokeLine(BPoint(r.right - i, r.top + i),
						BPoint(r.right - i, r.bottom - i),
						colors[(2 - i) == 2 ? 5 : (2 - i) * 2]);
				}
			}
			break;
		}

		case B_BORDERED_WINDOW_LOOK:
		{
			// TODO: Draw the borders individually!
			ComponentColors colors;
			_GetComponentColors(COMPONENT_LEFT_BORDER, colors, fTopTab);

			fDrawingEngine->StrokeRect(r, 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)) {
		r = fResizeRect;

		ComponentColors colors;
		_GetComponentColors(COMPONENT_RESIZE_CORNER, colors, fTopTab);

		switch ((int)fTopTab->look) {
			case B_DOCUMENT_WINDOW_LOOK:
			{
				if (!rect.Intersects(r))
					break;

				float x = r.right - 3;
				float y = r.bottom - 3;

				BRect bg(x - 15, y - 15, x, y);

				BGradientLinear gradient;
				gradient.SetStart(bg.LeftTop());
				gradient.SetEnd(bg.RightBottom());
				gradient.AddColor(tint_color(colors[1], 1.05), 0);
				gradient.AddColor(tint_color(colors[1], 1.0), 255);

				fDrawingEngine->FillRect(bg, gradient);

				fDrawingEngine->StrokeLine(BPoint(x - 15, y - 15),
					BPoint(x - 15, y - 1), colors[4]);
				fDrawingEngine->StrokeLine(BPoint(x - 15, y - 15),
					BPoint(x - 1, y - 15), colors[4]);

				if (fTopTab && !IsFocus(fTopTab))
					break;

				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, tint_color(colors[1], 1.5));
						fDrawingEngine->StrokePoint(pt2, tint_color(colors[1], 0.75));
					}
				}
				break;
			}

			case B_TITLED_WINDOW_LOOK:
			case B_FLOATING_WINDOW_LOOK:
			case B_MODAL_WINDOW_LOOK:
			case kLeftTitledWindowLook:
			{
				if (!rect.Intersects(BRect(fRightBorder.right - kBorderResizeLength,
					fBottomBorder.bottom - kBorderResizeLength, fRightBorder.right - 1,
					fBottomBorder.bottom - 1)))
					break;

				fDrawingEngine->StrokeLine(
					BPoint(fRightBorder.left, fBottomBorder.bottom - kBorderResizeLength),
					BPoint(fRightBorder.right - 1, fBottomBorder.bottom - kBorderResizeLength),
					tint_color(colors[1], 1.2));
				fDrawingEngine->StrokeLine(
					BPoint(fRightBorder.right - kBorderResizeLength, fBottomBorder.top),
					BPoint(fRightBorder.right - kBorderResizeLength, fBottomBorder.bottom - 1),
					tint_color(colors[1], 1.2));

				// Try to draw line in yellow to the resize place
				for (int8 i = 1; i < 4; i++) {
					fDrawingEngine->StrokeLine(
						BPoint(fRightBorder.left+i, fBottomBorder.bottom - kBorderResizeLength + 1),
						BPoint(fRightBorder.left+i, fBottomBorder.bottom - 1),
						tint_color(colors[3], (i * 0.06) + 1));
				}
				int rez[] = {4,3,2,1};
				for (int8 i = 1; i < 4; i++) {
					fDrawingEngine->StrokeLine(
						BPoint(fRightBorder.right - kBorderResizeLength + 1, fBottomBorder.bottom - i),
						BPoint(fRightBorder.right - i, fBottomBorder.bottom - i ),
						tint_color(colors[3], (rez[i] * 0.06) + 1));
				}

				break;
			}

			default:
				// don't draw resize corner
				break;
		}
	}
}

The _DrawFrame() function is a protected method within the FlatDecorator class that handles the drawing of the frame, or border, around the decorated GUI element. Let's break down its functionality:

  • Frame Drawing Logic: This method draws the border frame around the GUI element based on its position and dimensions specified by the BRect rect parameter.
  • Border Styles: Depending on the window look ()
  • Drawing Borders: Borders are drawn individually for each side (top, bottom, left, right) of the frame, with varying thickness and color gradients.
  • Resize Know: If the GUI element is resizable, a resize know is drawn at the bottom-right corner, allowing users to resize the window interactively. The appearance of the know varies based on the window look and focus state.

_DrawTab

/*!	\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
FlatDecorator::_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);

	if (tab && tab->buttonFocus) {
		// outer frame
		fDrawingEngine->StrokeLine(tabRect.LeftTop(), tabRect.LeftBottom(),
			colors[COLOR_TAB_FRAME_DARK]);
		fDrawingEngine->StrokeLine(tabRect.LeftTop(), tabRect.RightTop(),
			colors[COLOR_TAB_FRAME_DARK]);
		if (tab->look != kLeftTitledWindowLook) {
			fDrawingEngine->StrokeLine(tabRect.RightTop(), tabRect.RightBottom(),
				colors[COLOR_TAB_FRAME_DARK]);
		} else {
			fDrawingEngine->StrokeLine(tabRect.LeftBottom(),
				tabRect.RightBottom(), fFocusFrameColor);
		}
	} else {
		// outer frame
		fDrawingEngine->StrokeLine(tabRect.LeftTop(), tabRect.LeftBottom(),
			colors[COLOR_TAB_FRAME_DARK]);
		fDrawingEngine->StrokeLine(tabRect.LeftTop(), tabRect.RightTop(),
			colors[COLOR_TAB_FRAME_DARK]);
		if (tab->look != kLeftTitledWindowLook) {
			fDrawingEngine->StrokeLine(tabRect.RightTop(), tabRect.RightBottom(),
				colors[COLOR_TAB_FRAME_DARK]);
		} else {
			fDrawingEngine->StrokeLine(tabRect.LeftBottom(),
				tabRect.RightBottom(), fFocusFrameColor);
		}
	}

	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]);
	fDrawingEngine->StrokeLine(BPoint(tabRect.left + 1, tabRect.top + 1),
		BPoint(tabRect.right - (tab->look == kLeftTitledWindowLook ? 0 : 1),
			tabRect.top + 1),
		tint_color(colors[COLOR_TAB], 0.9));

	if (tab->look != kLeftTitledWindowLook) {
		fDrawingEngine->StrokeLine(BPoint(tabRect.right - 1, tabRect.top + 2),
			BPoint(tabRect.right - 1, tabBotton),
			colors[COLOR_TAB]);
	} else {
		fDrawingEngine->StrokeLine(
			BPoint(tabRect.left + 2, tabRect.bottom - 1),
			BPoint(tabRect.right, tabRect.bottom - 1),
			colors[COLOR_TAB]);
	}

	// fill
	BGradientLinear gradient;
	gradient.SetStart(tabRect.LeftTop());
	if (tab && tab->buttonFocus) {
		gradient.AddColor(tint_color(colors[COLOR_TAB], 0.6), 0);
		gradient.AddColor(tint_color(colors[COLOR_TAB], 1.0), 200);
	} else {
		gradient.AddColor(tint_color(colors[COLOR_TAB], 0.9), 0);
		gradient.AddColor(tint_color(colors[COLOR_TAB], 1.0), 150);
	}

	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);
}

The _DrawTab function in the FlatDecorator class is responsible for drawing the tab itself, along with any associated buttons.

The DrawTab function is called when the tab itself needs to be drawn.

Parameters:

  • tab: The tab to update.
  • invalid: The area of the tab to update, specified as a BRect.

Drawing the Tab:

  • Validation: Before drawing, the function checks if the tab rectangle (tabRect) is valid and if the update area intersects with it. If not, the function returns early to avoid unnecessary drawing operations.
  • Outer Frame: The function draws the outer frame of the tab using stroke lines, applying different colors depending on whether the tab has focus (tab->buttonFocus).
  • Bevel: A bevel effect is applied to the tab using stroke lines, giving it a raised appearance.
  • Fill: The tab is filled with a linear gradient, creating a visually appealing background effect. The gradient colors vary based on whether the tab has focus.
  • Orientation: Depending on the window look (tab->look), the orientation of the tab and associated elements may differ.

Drawing Buttons

  • After drawing the tab, the function calls _DrawButtons to draw any associated buttons within the tab area.

_DrawTitle

/*!	\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
FlatDecorator::_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<Decorator::Tab*>(_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);
}

The _DrawTitle function within the FlatDecorator class is responsible for rendering the title of tabs in GUI. This function handles the drawing of the title text within the designated area of the tab, ensuring proper alignment and appearance.

Purpose and Parameters

  • Purpose: The primary objective of _DrawTitle is to draw the title text of a tab within its specified area.
  • Parameters:
    • tab: The tab for which the title is being drawn.
    • rect: The area of the title to update, specified as a BRect.

Drawing the Title

  • Validation: Before drawing the title, the function validates the tab's attributes, including its rectangle (tabRect) and the rectangles for close and zoom buttons (closeRect and zoomRect).
  • Color and Font: The function sets the drawing color and font based on the component colors retrieved using _GetComponentColors. This ensures that the title text aligns with the overall theme and appearance defined by the decorator.
  • Text Positioning: The position for drawing the title text (titlePos) is calculated based on the tab's look (tab->look) and the presence of close or zoom buttons. The position is adjusted to center the text vertically within the tab area.
  • Drawing Text: Finally, the function draws the truncated title text (tab->truncatedTitle) using the calculated position (titlePos). The DrawString method is employed to render the text onto the screen.

Drawing Mode

  • Optimization: The function utilizes the drawing mode B_OP_COPY for rendering the title text. This mode is recommended for its performance benefits, ensuring efficient drawing operations.

_DrawClose

The _DrawClose function is an integral part of the FlatDecorator class responsible for rendering the close button associated with tabs in GUI. This function facilitates the drawing of the close button within the specified area of the tab.

Purpose and Parameters

  • Purpose: The main objective of _DrawClose is to draw the close button of a tab within the designated area.
  • Parameters:
    • _tab: The tab for which the close button is being drawn.
    • direct: A boolean flag indicating whether to draw without double buffering.
    • rect: The area of the close button to update, specified as a BRect.

Drawing the Close Button

  • Bitmap Retrieval: The function first determines the appropriate bitmap to use for the close button based on the button's state, including whether it has focus (tab->buttonFocus) and whether it is currently pressed (tab->closePressed). The bitmap is retrieved from the tab's closeBitmaps array, which caches previously generated to optimize performance.
  • Bitmap Generation: If the required bitmap is not found in the cache, _GetBitmapForButton is called to generate a new bitmap for the close button. This method fetches the bitmap resource for the button from the decorator and creates the bitmap based on the button's current state and dimensions.
  • Drawing: Once the appropriate bitmap is obtained, _DrawButtonBitmap is invoked to draw the bitmap onto the screen within the specified rectangle (rect). This function handles the actual rendering of the bitmap, taking into account the need for double buffering if specified by the direct parameter.

_DrawZoom

Purpose and Parameters

  • Purpose: The primary objective of _DrawZoom is to draw the zoom button of a tab within the designated area.
  • Parameters:
    • _tab: The tab for which the zoom button is being drawn.
    • direct: A boolean flag indicating whether to draw without double buffering.
    • rect: The area of the zoom button to update, specified as a BRect.

Drawing the Zoom Button

  • Rectangle Width Check: The function begins by checking if the width of the update rectangle is less than 1. If so, it returns immediately, as drawing a button with no width would be unnecessary.
  • Bitmap Retrieval: Next, the function determines the appropriate bitmap to use for the zoom button based on the button's state, including whether it has focus (tab->buttonFocus) and whether it is currently pressed (tab->zoomPressed). The bitmap is retrieved from the tab's zoomBitmaps array, which caches previously generated bitmaps to optimize performance.
  • Bitmap Generation: If the required bitmap is not found in the cache, _GetBitmapForButton is called to generate a new bitmap for the zoom button. This method fetches the bitmap resource for the button from the decorator and creates the bitmap based on the button's current state and dimensions.
  • Drawing: Once the appropriate bitmap is obtained, _DrawButtonBitmap is invoked to draw the bitmap onto the screen within the specified rectangle (rect). This function handles the actual rendering of the bitmap, considering the need for double buffering if specified by the direct parameter.

_DrawButtonBitmap

void
FlatDecorator::_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);
}

The _DrawButtonBitmap function is a utility method responsible for drawing a bitmap representation of a button onto the screen within a specified rectangle.

Purpose and Parameters

  • Purpose: The main purpose of _DrawButtonBitmap is to render a bitmap onto the screen within the given rectangle, typically representing a button such as close or zoom button.
  • Parameters:
    • bitmap: A pointer to the ServerBitmap object representing the bitmap image to be drawn.
    • direct: A boolean flag indicating whether to draw without double buffering.
    • rect: The bounding rectangle specifying the area where the bitmap should be drawn.

Drawing the Bitmap

  • Bitmap Validation: The function begins by checking if the provided bitmap is valid (i.e., not NULL). If the bitmap is NULL, indicating that no valid image is available, the function returns without further action.
  • Drawing Setup:
    • Copy to Front Setting: The function checks the current state of the Copy to Front setting (copyToFrontEnabled) in the drawing engine and stores it for later restoration. If the direct parameter is true, indicating that drawing should occur without double buffering, the "Copy to Front” setting is temporarily disabled to ensure that drawing occurs directly on the screen.
    • Drawing Mode: The drawing mode is temporarily set to B_OP_OVER, indicating that the bitmap should be drawn over the existing content in the target rectangle.
  • Bitmap Drawing: The bitmap is drawn onto the screen using the DrawBitmap method of the drawing engine. The bitmap is positioned within the specified rectangle (rect), which may involve offsetting the rectangle to ensure correct placement of the bitmap.
  • Drawing Mode and Copy to Front Restoration: After drawing the bitmap, the original drawing mode and “Copy to Front” setting are restored to their previous states to maintain consistency with the drawing environment.