* 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
* Jacob Secunda, secundaja@gmail.com
*/
#include "TabDecorator.h"
#include <algorithm>
#include <cmath>
#include <new>
#include <stdio.h>
#include <Autolock.h>
#include <Debug.h>
#include <GradientLinear.h>
#include <Rect.h>
#include <View.h>
#include <WindowPrivate.h>
#include "BitmapDrawingEngine.h"
#include "DesktopSettings.h"
#include "DrawingEngine.h"
#include "DrawState.h"
#include "FontManager.h"
#include "PatternHandler.h"
#ifdef DEBUG_DECORATOR
# define STRACE(x) printf x
#else
# define STRACE(x) ;
#endif
static bool
int_equal(float x, float y)
{
return abs(x - y) <= 1;
}
static const float kBorderResizeLength = 22.0;
static const float kResizeKnobSize = 18.0;
TabDecorator::TabDecorator(DesktopSettings& settings, BRect frame,
Desktop* desktop)
:
Decorator(settings, frame, desktop),
fOldMovingTab(0, 0, -1, -1)
{
STRACE(("TabDecorator:\n"));
STRACE(("\tFrame (%.1f,%.1f,%.1f,%.1f)\n",
frame.left, frame.top, frame.right, frame.bottom));
}
TabDecorator::~TabDecorator()
{
STRACE(("TabDecorator: ~TabDecorator()\n"));
}
Updates all areas which intersect the frame and tab.
\param updateRect The rectangular area to update.
*/
void
TabDecorator::Draw(BRect updateRect)
{
STRACE(("TabDecorator::Draw(BRect "
"updateRect(l:%.1f, t:%.1f, r:%.1f, b:%.1f))\n",
updateRect.left, updateRect.top, updateRect.right, updateRect.bottom));
fDrawingEngine->SetDrawState(&fDrawState);
_DrawFrame(updateRect & fBorderRect);
if (IsOutlineResizing())
_DrawOutlineFrame(updateRect & fOutlineBorderRect);
_DrawTabs(updateRect & fTitleBarRect);
}
void
TabDecorator::Draw()
{
STRACE(("TabDecorator: Draw()"));
fDrawingEngine->SetDrawState(&fDrawState);
_DrawFrame(fBorderRect);
if (IsOutlineResizing())
_DrawOutlineFrame(fOutlineBorderRect);
_DrawTabs(fTitleBarRect);
}
Decorator::Region
TabDecorator::RegionAt(BPoint where, int32& tab) const
{
Region region = Decorator::RegionAt(where, tab);
if (region != REGION_NONE)
return region;
if (fTopTab->look == B_DOCUMENT_WINDOW_LOOK && fResizeRect.Contains(where))
return REGION_RIGHT_BOTTOM_CORNER;
if (fLeftBorder.Contains(where))
return REGION_LEFT_BORDER;
if (fTopBorder.Contains(where))
return REGION_TOP_BORDER;
if (fRightBorder.Contains(where))
region = REGION_RIGHT_BORDER;
else if (fBottomBorder.Contains(where))
region = REGION_BOTTOM_BORDER;
else
return REGION_NONE;
if ((fTopTab->flags & B_NOT_RESIZABLE) == 0
&& (fTopTab->look == B_TITLED_WINDOW_LOOK
|| fTopTab->look == B_FLOATING_WINDOW_LOOK
|| fTopTab->look == B_MODAL_WINDOW_LOOK
|| fTopTab->look == kLeftTitledWindowLook)) {
BRect resizeRect(BPoint(fBottomBorder.right - fBorderResizeLength,
fBottomBorder.bottom - fBorderResizeLength),
fBottomBorder.RightBottom());
if (resizeRect.Contains(where))
return REGION_RIGHT_BOTTOM_CORNER;
}
return region;
}
bool
TabDecorator::SetRegionHighlight(Region region, uint8 highlight,
BRegion* dirty, int32 tabIndex)
{
Decorator::Tab* tab
= static_cast<Decorator::Tab*>(_TabAt(tabIndex));
if (tab != NULL) {
tab->isHighlighted = highlight != 0;
switch (region) {
case REGION_CLOSE_BUTTON:
if (highlight != RegionHighlight(region))
memset(&tab->closeBitmaps, 0, sizeof(tab->closeBitmaps));
break;
case REGION_ZOOM_BUTTON:
if (highlight != RegionHighlight(region))
memset(&tab->zoomBitmaps, 0, sizeof(tab->zoomBitmaps));
break;
default:
break;
}
}
return Decorator::SetRegionHighlight(region, highlight, dirty, tabIndex);
}
void
TabDecorator::UpdateColors(DesktopSettings& settings)
{
fFocusFrameColor = settings.UIColor(B_WINDOW_BORDER_COLOR);
fFocusTabColor = settings.UIColor(B_WINDOW_TAB_COLOR);
fFocusTabColorLight = tint_color(fFocusTabColor,
(B_LIGHTEN_MAX_TINT + B_LIGHTEN_2_TINT) / 2);
fFocusTabColorBevel = tint_color(fFocusTabColor, B_LIGHTEN_2_TINT);
fFocusTabColorShadow = tint_color(fFocusTabColor,
(B_DARKEN_1_TINT + B_NO_TINT) / 2);
fFocusTextColor = settings.UIColor(B_WINDOW_TEXT_COLOR);
fNonFocusFrameColor = settings.UIColor(B_WINDOW_INACTIVE_BORDER_COLOR);
fNonFocusTabColor = settings.UIColor(B_WINDOW_INACTIVE_TAB_COLOR);
fNonFocusTabColorLight = tint_color(fNonFocusTabColor,
(B_LIGHTEN_MAX_TINT + B_LIGHTEN_2_TINT) / 2);
fNonFocusTabColorBevel = tint_color(fNonFocusTabColor, B_LIGHTEN_2_TINT);
fNonFocusTabColorShadow = tint_color(fNonFocusTabColor,
(B_DARKEN_1_TINT + B_NO_TINT) / 2);
fNonFocusTextColor = settings.UIColor(B_WINDOW_INACTIVE_TEXT_COLOR);
}
void
TabDecorator::_DoLayout()
{
STRACE(("TabDecorator: Do Layout\n"));
bool hasTab = false;
const float scaleFactor = max_c(fDrawState.Font().Size() / 12.0f, 1.0f);
switch ((int)fTopTab->look) {
case B_MODAL_WINDOW_LOOK:
fBorderWidth = 5;
break;
case B_TITLED_WINDOW_LOOK:
case B_DOCUMENT_WINDOW_LOOK:
hasTab = true;
fBorderWidth = 5;
break;
case B_FLOATING_WINDOW_LOOK:
case kLeftTitledWindowLook:
hasTab = true;
fBorderWidth = 3;
break;
case B_BORDERED_WINDOW_LOOK:
fBorderWidth = 1;
break;
default:
fBorderWidth = 0;
}
fBorderWidth = int32(fBorderWidth * scaleFactor);
fResizeKnobSize = kResizeKnobSize * scaleFactor;
fBorderResizeLength = kBorderResizeLength * scaleFactor;
if (fBorderWidth > 0) {
fLeftBorder.Set(fFrame.left - fBorderWidth, fFrame.top,
fFrame.left - 1, fFrame.bottom);
fRightBorder.Set(fFrame.right + 1, fFrame.top ,
fFrame.right + fBorderWidth, fFrame.bottom);
fTopBorder.Set(fFrame.left - fBorderWidth, fFrame.top - fBorderWidth,
fFrame.right + fBorderWidth, fFrame.top - 1);
fBottomBorder.Set(fFrame.left - fBorderWidth, fFrame.bottom + 1,
fFrame.right + fBorderWidth, fFrame.bottom + fBorderWidth);
} else {
fLeftBorder.Set(0.0, 0.0, -1.0, -1.0);
fRightBorder.Set(0.0, 0.0, -1.0, -1.0);
fTopBorder.Set(0.0, 0.0, -1.0, -1.0);
fBottomBorder.Set(0.0, 0.0, -1.0, -1.0);
}
fBorderRect = BRect(fTopBorder.LeftTop(), fBottomBorder.RightBottom());
if (fBorderWidth > 1) {
fResizeRect.Set(fBottomBorder.right - fResizeKnobSize,
fBottomBorder.bottom - fResizeKnobSize, fBottomBorder.right,
fBottomBorder.bottom);
} else {
fResizeRect.Set(0, 0, -1, -1);
}
if (hasTab) {
_DoTabLayout();
return;
} else {
for (int32 i = 0; i < fTabList.CountItems(); i++) {
Decorator::Tab* tab = fTabList.ItemAt(i);
tab->tabRect.Set(0.0, 0.0, -1.0, -1.0);
}
fTabsRegion.MakeEmpty();
fTitleBarRect.Set(0.0, 0.0, -1.0, -1.0);
}
}
void
TabDecorator::_DoOutlineLayout()
{
fOutlineBorderWidth = 1;
fLeftOutlineBorder.Set(fFrame.left - fOutlineBorderWidth, fFrame.top,
fFrame.left - 1, fFrame.bottom);
fRightOutlineBorder.Set(fFrame.right + 1, fFrame.top ,
fFrame.right + fOutlineBorderWidth, fFrame.bottom);
fTopOutlineBorder.Set(fFrame.left - fOutlineBorderWidth,
fFrame.top - fOutlineBorderWidth,
fFrame.right + fOutlineBorderWidth, fFrame.top - 1);
fBottomOutlineBorder.Set(fFrame.left - fOutlineBorderWidth,
fFrame.bottom + 1,
fFrame.right + fOutlineBorderWidth,
fFrame.bottom + fOutlineBorderWidth);
fOutlineBorderRect = BRect(fTopOutlineBorder.LeftTop(),
fBottomOutlineBorder.RightBottom());
}
void
TabDecorator::_DoTabLayout()
{
float tabOffset = 0;
if (fTabList.CountItems() == 1) {
float tabSize;
tabOffset = _SingleTabOffsetAndSize(tabSize);
}
float sumTabWidth = 0;
for (int32 i = 0; i < fTabList.CountItems(); i++) {
Decorator::Tab* tab = _TabAt(i);
BRect& tabRect = tab->tabRect;
tab->textOffset = _DefaultTextOffset();
font_height fontHeight;
fDrawState.Font().GetHeight(fontHeight);
if (tab->look != kLeftTitledWindowLook) {
const float spacing = fBorderWidth * 1.4f;
tabRect.Set(fFrame.left - fBorderWidth,
fFrame.top - fBorderWidth
- ceilf(fontHeight.ascent + fontHeight.descent + spacing),
((fFrame.right - fFrame.left) < (spacing * 5) ?
fFrame.left + (spacing * 5) : fFrame.right) + fBorderWidth,
fFrame.top - fBorderWidth);
} else {
tabRect.Set(fFrame.left - fBorderWidth
- ceilf(fontHeight.ascent + fontHeight.descent + fBorderWidth),
fFrame.top - fBorderWidth, fFrame.left - fBorderWidth,
fFrame.bottom + fBorderWidth);
}
if (tab->look == B_FLOATING_WINDOW_LOOK) {
tabRect.InsetBy(0, 2);
tabRect.OffsetBy(0, 2);
}
float offset;
float size;
float inset;
_GetButtonSizeAndOffset(tabRect, &offset, &size, &inset);
tab->minTabSize = inset * 2 + tab->textOffset;
if ((tab->flags & B_NOT_CLOSABLE) == 0)
tab->minTabSize += offset + size;
if ((tab->flags & B_NOT_ZOOMABLE) == 0)
tab->minTabSize += offset + size;
tab->maxTabSize = fDrawingEngine
? ceilf(fDrawingEngine->StringWidth(Title(tab), strlen(Title(tab)),
fDrawState.Font())) : 0.0;
if (tab->maxTabSize > 0.0)
tab->maxTabSize += tab->textOffset;
tab->maxTabSize += tab->minTabSize;
float tabSize = (tab->look != kLeftTitledWindowLook
? fFrame.Width() : fFrame.Height()) + fBorderWidth * 2;
if (tabSize < tab->minTabSize)
tabSize = tab->minTabSize;
if (tabSize > tab->maxTabSize)
tabSize = tab->maxTabSize;
if (tab->look != kLeftTitledWindowLook)
tabRect.right = tabRect.left + tabSize;
else
tabRect.bottom = tabRect.top + tabSize;
tab->tabOffset = (uint32)tabOffset;
if (tab->tabLocation != 0.0 && fTabList.CountItems() == 1
&& tab->tabOffset > (fRightBorder.right - fLeftBorder.left
- tabRect.Width())) {
tab->tabOffset = uint32(fRightBorder.right - fLeftBorder.left
- tabRect.Width());
}
tabRect.OffsetBy(tab->tabOffset, 0);
tabOffset += tabRect.Width();
sumTabWidth += tabRect.Width();
}
float windowWidth = fFrame.Width() + 2 * fBorderWidth;
if (CountTabs() > 1 && sumTabWidth > windowWidth)
_DistributeTabSize(sumTabWidth - windowWidth);
for (int32 i = 0; i < fTabList.CountItems(); i++) {
Decorator::Tab* tab = fTabList.ItemAt(i);
if (i == 0)
fTitleBarRect = tab->tabRect;
else
fTitleBarRect = fTitleBarRect | tab->tabRect;
_LayoutTabItems(tab, tab->tabRect);
}
fTabsRegion = fTitleBarRect;
}
void
TabDecorator::_DistributeTabSize(float delta)
{
int32 tabCount = fTabList.CountItems();
ASSERT(tabCount > 1);
float maxTabSize = 0;
float secMaxTabSize = 0;
int32 nTabsWithMaxSize = 0;
for (int32 i = 0; i < tabCount; i++) {
Decorator::Tab* tab = fTabList.ItemAt(i);
if (tab == NULL)
continue;
float tabWidth = tab->tabRect.Width();
if (int_equal(maxTabSize, tabWidth)) {
nTabsWithMaxSize++;
continue;
}
if (maxTabSize < tabWidth) {
secMaxTabSize = maxTabSize;
maxTabSize = tabWidth;
nTabsWithMaxSize = 1;
} else if (secMaxTabSize <= tabWidth)
secMaxTabSize = tabWidth;
}
float minus = ceilf(std::min(maxTabSize - secMaxTabSize, delta));
if (minus < 1.0)
return;
delta -= minus;
minus /= nTabsWithMaxSize;
Decorator::Tab* previousTab = NULL;
for (int32 i = 0; i < tabCount; i++) {
Decorator::Tab* tab = fTabList.ItemAt(i);
if (tab == NULL)
continue;
if (int_equal(maxTabSize, tab->tabRect.Width()))
tab->tabRect.right -= minus;
if (previousTab != NULL) {
float offsetX = previousTab->tabRect.right - tab->tabRect.left;
tab->tabRect.OffsetBy(offsetX, 0);
}
previousTab = tab;
}
if (delta > 0) {
_DistributeTabSize(delta);
return;
}
if (previousTab != NULL)
previousTab->tabRect.right = floorf(fFrame.right + fBorderWidth);
for (int32 i = 0; i < tabCount; i++) {
Decorator::Tab* tab = fTabList.ItemAt(i);
if (tab == NULL)
continue;
tab->tabOffset = uint32(tab->tabRect.left - fLeftBorder.left);
}
}
void
TabDecorator::_DrawOutlineFrame(BRect rect)
{
drawing_mode oldMode;
fDrawingEngine->SetDrawingMode(B_OP_ALPHA, oldMode);
fDrawingEngine->SetPattern(B_MIXED_COLORS);
fDrawingEngine->StrokeRect(rect);
fDrawingEngine->SetDrawingMode(oldMode);
}
void
TabDecorator::_SetTitle(Decorator::Tab* tab, const char* string,
BRegion* updateRegion)
{
BRect rect = TabRect((int32) 0) | TabRect(CountTabs() - 1);
_DoLayout();
_DoOutlineLayout();
if (updateRegion == NULL)
return;
rect = rect | TabRect(CountTabs() - 1);
rect.bottom++;
updateRegion->Include(rect);
}
void
TabDecorator::_MoveBy(BPoint offset)
{
STRACE(("TabDecorator: Move By (%.1f, %.1f)\n", offset.x, offset.y));
for (int32 i = 0; i < fTabList.CountItems(); i++) {
Decorator::Tab* tab = fTabList.ItemAt(i);
tab->zoomRect.OffsetBy(offset);
tab->closeRect.OffsetBy(offset);
tab->tabRect.OffsetBy(offset);
}
fFrame.OffsetBy(offset);
fTitleBarRect.OffsetBy(offset);
fTabsRegion.OffsetBy(offset);
fResizeRect.OffsetBy(offset);
fBorderRect.OffsetBy(offset);
fLeftBorder.OffsetBy(offset);
fRightBorder.OffsetBy(offset);
fTopBorder.OffsetBy(offset);
fBottomBorder.OffsetBy(offset);
}
void
TabDecorator::_ResizeBy(BPoint offset, BRegion* dirty)
{
STRACE(("TabDecorator: Resize By (%.1f, %.1f)\n", offset.x, offset.y));
fFrame.right += offset.x;
fFrame.bottom += offset.y;
if (dirty != NULL && !(fTopTab->flags & B_NOT_RESIZABLE)) {
BRect realResizeRect;
switch ((int)fTopTab->look) {
case B_DOCUMENT_WINDOW_LOOK:
realResizeRect = fResizeRect;
dirty->Include(realResizeRect);
realResizeRect.OffsetBy(offset);
dirty->Include(realResizeRect);
break;
case B_TITLED_WINDOW_LOOK:
case B_FLOATING_WINDOW_LOOK:
case B_MODAL_WINDOW_LOOK:
case kLeftTitledWindowLook:
realResizeRect.Set(fRightBorder.right - fBorderResizeLength,
fBottomBorder.top,
fRightBorder.right - fBorderResizeLength,
fBottomBorder.bottom - 1);
dirty->Include(realResizeRect);
realResizeRect.OffsetBy(offset);
dirty->Include(realResizeRect);
realResizeRect.Set(fRightBorder.left,
fBottomBorder.bottom - fBorderResizeLength,
fRightBorder.right - 1,
fBottomBorder.bottom - fBorderResizeLength);
dirty->Include(realResizeRect);
realResizeRect.OffsetBy(offset);
dirty->Include(realResizeRect);
break;
default:
break;
}
}
fResizeRect.OffsetBy(offset);
fBorderRect.right += offset.x;
fBorderRect.bottom += offset.y;
fLeftBorder.bottom += offset.y;
fTopBorder.right += offset.x;
fRightBorder.OffsetBy(offset.x, 0.0);
fRightBorder.bottom += offset.y;
fBottomBorder.OffsetBy(0.0, offset.y);
fBottomBorder.right += offset.x;
if (dirty) {
if (offset.x > 0.0) {
BRect t(fRightBorder.left - offset.x, fTopBorder.top,
fRightBorder.right, fTopBorder.bottom);
dirty->Include(t);
t.Set(fRightBorder.left - offset.x, fBottomBorder.top,
fRightBorder.right, fBottomBorder.bottom);
dirty->Include(t);
dirty->Include(fRightBorder);
} else if (offset.x < 0.0) {
dirty->Include(BRect(fRightBorder.left, fTopBorder.top,
fRightBorder.right, fBottomBorder.bottom));
}
if (offset.y > 0.0) {
BRect t(fLeftBorder.left, fLeftBorder.bottom - offset.y,
fLeftBorder.right, fLeftBorder.bottom);
dirty->Include(t);
t.Set(fRightBorder.left, fRightBorder.bottom - offset.y,
fRightBorder.right, fRightBorder.bottom);
dirty->Include(t);
dirty->Include(fBottomBorder);
} else if (offset.y < 0.0) {
dirty->Include(fBottomBorder);
}
}
if (fTitleBarRect.IsValid()) {
if (fTabList.CountItems() > 1) {
_DoTabLayout();
if (dirty != NULL)
dirty->Include(fTitleBarRect);
return;
}
Decorator::Tab* tab = _TabAt(0);
BRect& tabRect = tab->tabRect;
BRect oldTabRect(tabRect);
float tabSize;
float tabOffset = _SingleTabOffsetAndSize(tabSize);
float delta = tabOffset - tab->tabOffset;
tab->tabOffset = (uint32)tabOffset;
if (fTopTab->look != kLeftTitledWindowLook)
tabRect.OffsetBy(delta, 0.0);
else
tabRect.OffsetBy(0.0, delta);
if (tabSize < tab->minTabSize)
tabSize = tab->minTabSize;
if (tabSize > tab->maxTabSize)
tabSize = tab->maxTabSize;
if (fTopTab->look != kLeftTitledWindowLook
&& tabSize != tabRect.Width()) {
tabRect.right = tabRect.left + tabSize;
} else if (fTopTab->look == kLeftTitledWindowLook
&& tabSize != tabRect.Height()) {
tabRect.bottom = tabRect.top + tabSize;
}
if (oldTabRect != tabRect) {
_LayoutTabItems(tab, tabRect);
if (dirty) {
BRect redraw(tabRect);
if (delta != 0.0) {
redraw = redraw | oldTabRect;
if (fTopTab->look != kLeftTitledWindowLook)
redraw.bottom++;
else
redraw.right++;
}
dirty->Include(redraw);
}
}
fTitleBarRect = tabRect;
fTabsRegion = fTitleBarRect;
}
}
void
TabDecorator::_SetFocus(Decorator::Tab* tab)
{
Decorator::Tab* decoratorTab = static_cast<Decorator::Tab*>(tab);
decoratorTab->buttonFocus = IsFocus(tab)
|| ((decoratorTab->look == B_FLOATING_WINDOW_LOOK
|| decoratorTab->look == kLeftTitledWindowLook)
&& (decoratorTab->flags & B_AVOID_FOCUS) != 0);
if (CountTabs() > 1)
_LayoutTabItems(decoratorTab, decoratorTab->tabRect);
}
bool
TabDecorator::_SetTabLocation(Decorator::Tab* _tab, float location,
bool isShifting, BRegion* updateRegion)
{
STRACE(("TabDecorator: Set Tab Location(%.1f)\n", location));
if (CountTabs() > 1) {
if (isShifting == false) {
_DoTabLayout();
if (updateRegion != NULL)
updateRegion->Include(fTitleBarRect);
fOldMovingTab = BRect(0, 0, -1, -1);
return true;
} else {
if (fOldMovingTab.IsValid() == false)
fOldMovingTab = _tab->tabRect;
}
}
Decorator::Tab* tab = static_cast<Decorator::Tab*>(_tab);
BRect& tabRect = tab->tabRect;
if (tabRect.IsValid() == false)
return false;
if (location < 0)
location = 0;
float maxLocation
= fRightBorder.right - fLeftBorder.left - tabRect.Width();
if (CountTabs() > 1)
maxLocation = fTitleBarRect.right - fLeftBorder.left - tabRect.Width();
if (location > maxLocation)
location = maxLocation;
float delta = floor(location - tab->tabOffset);
if (delta == 0.0)
return false;
BRect rect(tabRect);
rect.bottom++;
if (updateRegion != NULL)
updateRegion->Include(rect);
tabRect.OffsetBy(delta, 0);
tab->tabOffset = (int32)location;
_LayoutTabItems(_tab, tabRect);
tab->tabLocation = maxLocation > 0.0 ? tab->tabOffset / maxLocation : 0.0;
if (fTabList.CountItems() == 1)
fTitleBarRect = tabRect;
_CalculateTabsRegion();
rect = tabRect;
rect.bottom++;
if (updateRegion != NULL)
updateRegion->Include(rect);
return true;
}
bool
TabDecorator::_SetSettings(const BMessage& settings, BRegion* updateRegion)
{
float tabLocation;
bool modified = false;
for (int32 i = 0; i < fTabList.CountItems(); i++) {
if (settings.FindFloat("tab location", i, &tabLocation) != B_OK)
return false;
modified |= SetTabLocation(i, tabLocation, updateRegion);
}
return modified;
}
bool
TabDecorator::_AddTab(DesktopSettings& settings, int32 index,
BRegion* updateRegion)
{
_UpdateFont(settings);
_DoLayout();
_DoOutlineLayout();
if (updateRegion != NULL)
updateRegion->Include(fTitleBarRect);
return true;
}
bool
TabDecorator::_RemoveTab(int32 index, BRegion* updateRegion)
{
BRect oldRect = TabRect(index) | TabRect(CountTabs() - 1);
_DoLayout();
_DoOutlineLayout();
if (updateRegion != NULL) {
updateRegion->Include(oldRect);
updateRegion->Include(fTitleBarRect);
}
return true;
}
bool
TabDecorator::_MoveTab(int32 from, int32 to, bool isMoving,
BRegion* updateRegion)
{
Decorator::Tab* toTab = _TabAt(to);
if (toTab == NULL)
return false;
if (from < to) {
fOldMovingTab.OffsetBy(toTab->tabRect.Width(), 0);
toTab->tabRect.OffsetBy(-fOldMovingTab.Width(), 0);
} else {
fOldMovingTab.OffsetBy(-toTab->tabRect.Width(), 0);
toTab->tabRect.OffsetBy(fOldMovingTab.Width(), 0);
}
toTab->tabOffset = uint32(toTab->tabRect.left - fLeftBorder.left);
_LayoutTabItems(toTab, toTab->tabRect);
_CalculateTabsRegion();
if (updateRegion != NULL)
updateRegion->Include(fTitleBarRect);
return true;
}
void
TabDecorator::_GetFootprint(BRegion *region)
{
STRACE(("TabDecorator: GetFootprint\n"));
if (region == NULL)
return;
if (fTopTab->look == B_NO_BORDER_WINDOW_LOOK)
return;
region->Include(fTopBorder);
region->Include(fLeftBorder);
region->Include(fRightBorder);
region->Include(fBottomBorder);
if (fTopTab->look == B_BORDERED_WINDOW_LOOK)
return;
region->Include(&fTabsRegion);
if (fTopTab->look == B_DOCUMENT_WINDOW_LOOK) {
float knobSize = fResizeKnobSize - fBorderWidth;
region->Include(BRect(fFrame.right - knobSize, fFrame.bottom - knobSize,
fFrame.right, fFrame.bottom));
}
}
void
TabDecorator::_DrawButtons(Decorator::Tab* tab, const BRect& invalid)
{
STRACE(("TabDecorator: _DrawButtons\n"));
if (!(tab->flags & B_NOT_CLOSABLE) && invalid.Intersects(tab->closeRect))
_DrawClose(tab, false, tab->closeRect);
if (!(tab->flags & B_NOT_ZOOMABLE) && invalid.Intersects(tab->zoomRect))
_DrawZoom(tab, false, tab->zoomRect);
}
void
TabDecorator::_UpdateFont(DesktopSettings& settings)
{
ServerFont font;
if (fTopTab->look == B_FLOATING_WINDOW_LOOK
|| fTopTab->look == kLeftTitledWindowLook) {
settings.GetDefaultPlainFont(font);
if (fTopTab->look == kLeftTitledWindowLook)
font.SetRotation(90.0f);
} else
settings.GetDefaultBoldFont(font);
font.SetFlags(B_FORCE_ANTIALIASING);
font.SetSpacing(B_STRING_SPACING);
fDrawState.SetFont(font);
}
void
TabDecorator::_GetButtonSizeAndOffset(const BRect& tabRect, float* _offset,
float* _size, float* _inset) const
{
float tabSize = fTopTab->look == kLeftTitledWindowLook ?
tabRect.Width() : tabRect.Height();
bool smallTab = fTopTab->look == B_FLOATING_WINDOW_LOOK
|| fTopTab->look == kLeftTitledWindowLook;
*_offset = smallTab ? floorf(fDrawState.Font().Size() / 2.6)
: floorf(fDrawState.Font().Size() / 2.3);
*_inset = smallTab ? floorf(fDrawState.Font().Size() / 5.0)
: floorf(fDrawState.Font().Size() / 6.0);
*_size = tabSize - 2 * *_offset + *_inset;
}
void
TabDecorator::_LayoutTabItems(Decorator::Tab* _tab, const BRect& tabRect)
{
Decorator::Tab* tab = static_cast<Decorator::Tab*>(_tab);
float offset;
float size;
float inset;
_GetButtonSizeAndOffset(tabRect, &offset, &size, &inset);
tab->textOffset = _DefaultTextOffset();
BRect& closeRect = tab->closeRect;
BRect& zoomRect = tab->zoomRect;
if (tab->look != kLeftTitledWindowLook) {
closeRect.Set(tabRect.left + offset, tabRect.top + offset,
tabRect.left + offset + size, tabRect.top + offset + size);
zoomRect.Set(tabRect.right - offset - size, tabRect.top + offset,
tabRect.right - offset, tabRect.top + offset + size);
if ((tab->flags & B_NOT_CLOSABLE) != 0)
closeRect.right = closeRect.left - offset;
if ((tab->flags & B_NOT_ZOOMABLE) != 0)
zoomRect.left = zoomRect.right + offset;
} else {
closeRect.Set(tabRect.left + offset, tabRect.top + offset,
tabRect.left + offset + size, tabRect.top + offset + size);
zoomRect.Set(tabRect.left + offset, tabRect.bottom - offset - size,
tabRect.left + size + offset, tabRect.bottom - offset);
if ((tab->flags & B_NOT_CLOSABLE) != 0)
closeRect.bottom = closeRect.top - offset;
if ((tab->flags & B_NOT_ZOOMABLE) != 0)
zoomRect.top = zoomRect.bottom + offset;
}
if (tab->look != kLeftTitledWindowLook)
size = (zoomRect.left - closeRect.right) - tab->textOffset * 2 + inset;
else
size = (zoomRect.top - closeRect.bottom) - tab->textOffset * 2 + inset;
bool stackMode = fTabList.CountItems() > 1;
if (stackMode && IsFocus(tab) == false) {
zoomRect.Set(0, 0, 0, 0);
size = (tab->tabRect.right - closeRect.right) - tab->textOffset * 2
+ inset;
}
uint8 truncateMode = B_TRUNCATE_MIDDLE;
if (stackMode) {
if (tab->tabRect.Width() < 100)
truncateMode = B_TRUNCATE_END;
float titleWidth = fDrawState.Font().StringWidth(Title(tab),
BString(Title(tab)).Length());
if (size < titleWidth) {
float oldTextOffset = tab->textOffset;
tab->textOffset -= (titleWidth - size) / 2;
const float kMinTextOffset = 5.;
if (tab->textOffset < kMinTextOffset)
tab->textOffset = kMinTextOffset;
size += oldTextOffset * 2;
size -= tab->textOffset * 2;
}
}
tab->truncatedTitle = Title(tab);
fDrawState.Font().TruncateString(&tab->truncatedTitle, truncateMode, size);
tab->truncatedTitleLength = tab->truncatedTitle.Length();
}
float
TabDecorator::_DefaultTextOffset() const
{
if (fTopTab->look == B_FLOATING_WINDOW_LOOK
|| fTopTab->look == kLeftTitledWindowLook)
return int32(fBorderWidth * 3.4f);
return int32(fBorderWidth * 3.6f);
}
float
TabDecorator::_SingleTabOffsetAndSize(float& tabSize)
{
float maxLocation;
if (fTopTab->look != kLeftTitledWindowLook) {
tabSize = fRightBorder.right - fLeftBorder.left;
} else {
tabSize = fBottomBorder.bottom - fTopBorder.top;
}
Decorator::Tab* tab = _TabAt(0);
maxLocation = tabSize - tab->maxTabSize;
if (maxLocation < 0)
maxLocation = 0;
return floorf(tab->tabLocation * maxLocation);
}
void
TabDecorator::_CalculateTabsRegion()
{
fTabsRegion.MakeEmpty();
for (int32 i = 0; i < fTabList.CountItems(); i++)
fTabsRegion.Include(fTabList.ItemAt(i)->tabRect);
}