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.
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.
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 fromDecorAddOn
. - The constructor initializes
DecorAddOn
using an initialization list. - It provides an implementation for the
_AllocateDecorator
method to allocate and return a newFlatDecorator
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 aBRect
.
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 aBRect
.
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
andzoomRect
). - 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
). TheDrawString
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 aBRect
.
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'scloseBitmaps
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 thedirect
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 aBRect
.
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'szoomBitmaps
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 thedirect
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 theServerBitmap
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 isNULL
, 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 thedirect
parameter istrue
, 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.
- Copy to Front Setting: The function checks the current state of the
- 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.