BTabView: implement drawing tabs on all sides.

Signed-off-by: Adrien Destugues <[email protected]>
This commit is contained in:
Kacper Kasper
2017-08-20 09:04:05 +02:00
committed by Adrien Destugues
parent 173e8195a4
commit cb13fce878
2 changed files with 290 additions and 58 deletions
+12 -2
View File
@@ -86,6 +86,13 @@ private:
class BTabView : public BView {
public:
enum tab_side {
kLeftSide = 1 << 0,
kRightSide = 1 << 1,
kTopSide = 1 << 2,
kBottomSide = 1 << 3
};
BTabView(const char* name,
button_width width = B_WIDTH_AS_USUAL,
uint32 flags = B_FULL_UPDATE_ON_RESIZE
@@ -165,6 +172,9 @@ public:
virtual void SetBorder(border_style borderStyle);
border_style Border() const;
virtual void SetTabSide(tab_side tabSide);
tab_side TabSide() const;
BView* ContainerView() const;
int32 CountTabs() const;
@@ -172,7 +182,6 @@ public:
private:
// FBC padding and forbidden methods
virtual void _ReservedTabView2();
virtual void _ReservedTabView3();
virtual void _ReservedTabView4();
virtual void _ReservedTabView5();
@@ -205,8 +214,9 @@ private:
int32 fFocus;
float fTabOffset;
border_style fBorderStyle;
tab_side fTabSide;
uint32 _reserved[10];
uint32 _reserved[9];
};
#endif // _TAB_VIEW_H
+278 -56
View File
@@ -243,20 +243,74 @@ BTab::DrawFocusMark(BView* owner, BRect frame)
owner->SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
float offset = IsSelected() ? 3 : 2;
owner->StrokeLine(BPoint((frame.left + frame.right - width) / 2.0,
frame.bottom - offset),
BPoint((frame.left + frame.right + width) / 2.0,
frame.bottom - offset));
switch (fTabView->TabSide()) {
case BTabView::kTopSide:
owner->StrokeLine(BPoint((frame.left + frame.right - width) / 2.0,
frame.bottom - offset),
BPoint((frame.left + frame.right + width) / 2.0,
frame.bottom - offset));
break;
case BTabView::kBottomSide:
owner->StrokeLine(BPoint((frame.left + frame.right - width) / 2.0,
frame.top + offset),
BPoint((frame.left + frame.right + width) / 2.0,
frame.top + offset));
break;
case BTabView::kLeftSide:
owner->StrokeLine(BPoint(frame.right - offset,
(frame.top + frame.bottom - width) / 2.0),
BPoint(frame.right - offset,
(frame.top + frame.bottom + width) / 2.0));
break;
case BTabView::kRightSide:
owner->StrokeLine(BPoint(frame.left + offset,
(frame.top + frame.bottom - width) / 2.0),
BPoint(frame.left + offset,
(frame.top + frame.bottom + width) / 2.0));
break;
}
}
void
BTab::DrawLabel(BView* owner, BRect frame)
{
float rotation;
BPoint center(frame.left + frame.Width() / 2,
frame.top + frame.Height() / 2);
switch (fTabView->TabSide()) {
case BTabView::kTopSide:
case BTabView::kBottomSide:
rotation = 0.0f;
break;
case BTabView::kLeftSide:
rotation = 270.0f;
break;
case BTabView::kRightSide:
rotation = 90.0f;
break;
}
if (rotation != 0.0f) {
// DrawLabel doesn't allow rendering rotated text
// rotate frame first and BAffineTransform will handle the rotation
// we can't give "unrotated" frame because it comes from
// BTabView::TabFrame and it is also used to handle mouse clicks
BRect originalFrame(frame);
frame.top = center.y - originalFrame.Width() / 2;
frame.bottom = center.y + originalFrame.Width() / 2;
frame.left = center.x - originalFrame.Height() / 2;
frame.right = center.x + originalFrame.Height() / 2;
}
BAffineTransform transform;
transform.RotateBy(center, rotation * M_PI / 180.0f);
owner->SetTransform(transform);
be_control_look->DrawLabel(owner, Label(), frame, frame,
ui_color(B_PANEL_BACKGROUND_COLOR),
IsEnabled() ? 0 : BPrivate::BControlLook::B_DISABLED,
BAlignment(B_ALIGN_HORIZONTAL_CENTER, B_ALIGN_VERTICAL_CENTER));
owner->SetTransform(BAffineTransform());
}
@@ -264,22 +318,47 @@ void
BTab::DrawTab(BView* owner, BRect frame, tab_position position, bool full)
{
rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR);
uint32 borders = BControlLook::B_TOP_BORDER
| BControlLook::B_BOTTOM_BORDER;
uint32 borders;
if (fTabView->TabSide() == BTabView::kTopSide
|| fTabView->TabSide() == BTabView::kBottomSide) {
borders = BControlLook::B_TOP_BORDER | BControlLook::B_BOTTOM_BORDER;
if (frame.left == owner->Bounds().left)
borders |= BControlLook::B_LEFT_BORDER;
if (frame.left == owner->Bounds().left)
borders |= BControlLook::B_LEFT_BORDER;
if (frame.right == owner->Bounds().right)
borders |= BControlLook::B_RIGHT_BORDER;
if (frame.right == owner->Bounds().right)
borders |= BControlLook::B_RIGHT_BORDER;
} else if (fTabView->TabSide() == BTabView::kLeftSide
|| fTabView->TabSide() == BTabView::kRightSide) {
borders = BControlLook::B_LEFT_BORDER | BControlLook::B_RIGHT_BORDER;
if (frame.top == owner->Bounds().top)
borders |= BControlLook::B_TOP_BORDER;
if (frame.bottom == owner->Bounds().bottom)
borders |= BControlLook::B_BOTTOM_BORDER;
}
if (position == B_TAB_FRONT) {
frame.bottom -= 1.0f;
switch (fTabView->TabSide()) {
case BTabView::kTopSide:
frame.bottom -= 1.0f;
break;
case BTabView::kBottomSide:
frame.top += 1.0f;
break;
case BTabView::kLeftSide:
frame.right -= 1.0f;
break;
case BTabView::kRightSide:
frame.left += 1.0f;
break;
}
be_control_look->DrawActiveTab(owner, frame, frame, no_tint, 0,
borders);
borders, fTabView->TabSide());
} else {
be_control_look->DrawInactiveTab(owner, frame, frame, no_tint, 0,
borders);
borders, fTabView->TabSide());
}
DrawLabel(owner, frame);
@@ -365,6 +444,9 @@ BTabView::BTabView(BMessage* archive)
if (archive->FindInt32("_border_style", (int32*)&fBorderStyle) != B_OK)
fBorderStyle = B_FANCY_BORDER;
if (archive->FindInt32("_TabSide", (int32*)&fTabSide) != B_OK)
fTabSide = kTopSide;
int32 i = 0;
BMessage tabMsg;
@@ -426,6 +508,8 @@ BTabView::Archive(BMessage* archive, bool deep) const
result = archive->AddInt32("_sel", fSelection);
if (result == B_OK && fBorderStyle != B_FANCY_BORDER)
result = archive->AddInt32("_border_style", fBorderStyle);
if (result == B_OK && fTabSide != kTopSide)
result = archive->AddInt32("_TabSide", fTabSide);
if (result == B_OK && deep) {
for (int32 i = 0; i < CountTabs(); i++) {
@@ -642,9 +726,6 @@ BTabView::KeyDown(const char* bytes, int32 numBytes)
void
BTabView::MouseDown(BPoint where)
{
if (where.y > fTabHeight)
return;
for (int32 i = 0; i < CountTabs(); i++) {
if (TabFrame(i).Contains(where)
&& i != Selection()) {
@@ -800,28 +881,64 @@ BTabView::Draw(BRect updateRect)
BRect
BTabView::DrawTabs()
{
// draw an inactive tab frame behind all tabs
BRect bounds(Bounds());
bounds.bottom = fTabHeight;
BRect tabsBounds;
uint32 borders;
rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
uint32 borders = BControlLook::B_TOP_BORDER | BControlLook::B_BOTTOM_BORDER;
if (fBorderStyle == B_NO_BORDER) {
// removes left border that is an artifact of DrawInactiveTab()
bounds.left -= 1;
} else
borders |= BControlLook::B_LEFT_BORDER | BControlLook::B_RIGHT_BORDER;
if (fTabSide == kTopSide || fTabSide == kBottomSide) {
if (fTabSide == kTopSide)
bounds.bottom = fTabHeight;
else
bounds.top = bounds.bottom - fTabHeight;
tabsBounds = bounds;
// make a copy for later
// DrawInactiveTab draws 2px border
// draw a little wider tab frame to align B_PLAIN_BORDER with it
if (fBorderStyle == B_PLAIN_BORDER) {
bounds.left -= 1;
bounds.right += 1;
// draw an inactive tab frame behind all tabs
borders = BControlLook::B_TOP_BORDER | BControlLook::B_BOTTOM_BORDER;
if (fBorderStyle == B_NO_BORDER) {
// removes left border that is an artifact of DrawInactiveTab()
bounds.left -= 1;
} else {
borders |= BControlLook::B_LEFT_BORDER
| BControlLook::B_RIGHT_BORDER;
}
// DrawInactiveTab draws 2px border
// draw a little wider tab frame to align B_PLAIN_BORDER with it
if (fBorderStyle == B_PLAIN_BORDER) {
bounds.left -= 1;
bounds.right += 1;
}
} else if (fTabSide == kLeftSide || fTabSide == kRightSide) {
if (fTabSide == kLeftSide)
bounds.right = fTabHeight;
else
bounds.left = bounds.right - fTabHeight;
tabsBounds = bounds;
// make a copy for later
// draw an inactive tab frame behind all tabs
borders = BControlLook::B_LEFT_BORDER | BControlLook::B_RIGHT_BORDER;
if (fBorderStyle == B_NO_BORDER) {
// removes top border that is an artifact of DrawInactiveTab()
bounds.top -= 1;
} else {
borders |= BControlLook::B_TOP_BORDER
| BControlLook::B_BOTTOM_BORDER;
}
// DrawInactiveTab draws 2px border
// draw a little wider tab frame to align B_PLAIN_BORDER with it
if (fBorderStyle == B_PLAIN_BORDER) {
bounds.top -= 1;
bounds.bottom += 1;
}
}
be_control_look->DrawInactiveTab(this, bounds, bounds, base, 0, borders);
be_control_look->DrawInactiveTab(this, bounds, bounds, base, 0,
borders, fTabSide);
// draw the tabs on top of the inactive tab bounds
float right = 0.0f;
BRect activeTabFrame;
int32 tabCount = CountTabs();
for (int32 i = 0; i < tabCount; i++) {
@@ -830,19 +947,29 @@ BTabView::DrawTabs()
activeTabFrame = tabFrame;
TabAt(i)->DrawTab(this, tabFrame,
i == fSelection ? B_TAB_FRONT : (i == 0) ? B_TAB_FIRST : B_TAB_ANY,
i == fSelection ? B_TAB_FRONT :
(i == 0) ? B_TAB_FIRST : B_TAB_ANY,
i + 1 != fSelection);
right = tabFrame.right;
}
if (right < bounds.right) {
float last = 0.0f;
float lastTab = 0.0f;
if (fTabSide == kTopSide || fTabSide == kBottomSide) {
lastTab = TabFrame(tabCount - 1).right;
last = bounds.right;
tabsBounds.left = tabsBounds.right = lastTab;
borders = BControlLook::B_TOP_BORDER | BControlLook::B_BOTTOM_BORDER;
} else if (fTabSide == kLeftSide || fTabSide == kRightSide) {
lastTab = TabFrame(tabCount - 1).bottom;
last = bounds.bottom;
tabsBounds.top = tabsBounds.bottom = lastTab;
borders = BControlLook::B_LEFT_BORDER | BControlLook::B_RIGHT_BORDER;
}
if (lastTab < last) {
// draw a 1px right border on the last tab
bounds = Bounds();
bounds.left = bounds.right = right;
bounds.bottom = fTabHeight;
borders = BControlLook::B_TOP_BORDER;
be_control_look->DrawInactiveTab(this, bounds, bounds, base, 0,
BControlLook::B_TOP_BORDER | BControlLook::B_BOTTOM_BORDER);
be_control_look->DrawInactiveTab(this, tabsBounds, tabsBounds, base, 0,
borders, fTabSide);
}
return fSelection < CountTabs() ? TabFrame(fSelection) : BRect();
@@ -853,15 +980,32 @@ void
BTabView::DrawBox(BRect selectedTabRect)
{
BRect rect(Bounds());
rect.top = fTabHeight;
uint32 bordersToDraw = BControlLook::B_ALL_BORDERS;
switch (fTabSide) {
case kTopSide:
bordersToDraw &= ~BControlLook::B_TOP_BORDER;
rect.top = fTabHeight;
break;
case kBottomSide:
bordersToDraw &= ~BControlLook::B_BOTTOM_BORDER;
rect.bottom -= fTabHeight;
break;
case kLeftSide:
bordersToDraw &= ~BControlLook::B_LEFT_BORDER;
rect.left = fTabHeight;
break;
case kRightSide:
bordersToDraw &= ~BControlLook::B_RIGHT_BORDER;
rect.right -= fTabHeight;
break;
}
rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
if (fBorderStyle == B_FANCY_BORDER) {
be_control_look->DrawGroupFrame(this, rect, rect, base,
BControlLook::B_ALL_BORDERS & ~BControlLook::B_TOP_BORDER);
} else if (fBorderStyle == B_PLAIN_BORDER) {
if (fBorderStyle == B_FANCY_BORDER)
be_control_look->DrawGroupFrame(this, rect, rect, base, bordersToDraw);
else if (fBorderStyle == B_PLAIN_BORDER) {
be_control_look->DrawBorder(this, rect, rect, base, B_PLAIN_BORDER,
0, BControlLook::B_ALL_BORDERS & ~BControlLook::B_TOP_BORDER);
0, bordersToDraw);
} else
; // B_NO_BORDER draws no box
}
@@ -876,6 +1020,7 @@ BTabView::TabFrame(int32 index) const
float width = 100.0f;
float height = fTabHeight;
float offset = BControlLook::ComposeSpacing(B_USE_WINDOW_SPACING);
BRect bounds(Bounds());
switch (fTabWidthSetting) {
case B_WIDTH_FROM_LABEL:
@@ -885,9 +1030,25 @@ BTabView::TabFrame(int32 index) const
x += StringWidth(TabAt(i)->Label()) + 20.0f;
}
return BRect(offset + x, 0.0f,
offset + x + StringWidth(TabAt(index)->Label()) + 20.0f,
height);
switch (fTabSide) {
case kTopSide:
return BRect(offset + x, 0.0f,
offset + x + StringWidth(TabAt(index)->Label()) + 20.0f,
height);
case kBottomSide:
return BRect(offset + x, bounds.bottom - height,
offset + x + StringWidth(TabAt(index)->Label()) + 20.0f,
bounds.bottom);
case kLeftSide:
return BRect(0.0f, offset + x, height, offset + x
+ StringWidth(TabAt(index)->Label()) + 20.0f);
case kRightSide:
return BRect(bounds.right - height, offset + x,
bounds.right, offset + x
+ StringWidth(TabAt(index)->Label()) + 20.0f);
default:
return BRect();
}
}
case B_WIDTH_FROM_WIDEST:
@@ -901,8 +1062,22 @@ BTabView::TabFrame(int32 index) const
case B_WIDTH_AS_USUAL:
default:
return BRect(offset + index * width, 0.0f,
offset + index * width + width, height);
switch (fTabSide) {
case kTopSide:
return BRect(offset + index * width, 0.0f,
offset + index * width + width, height);
case kBottomSide:
return BRect(offset + index * width, bounds.bottom - height,
offset + index * width + width, bounds.bottom);
case kLeftSide:
return BRect(0.0f, offset + index * width, height,
offset + index * width + width);
case kRightSide:
return BRect(bounds.right - height, offset + index * width,
bounds.right, offset + index * width + width);
default:
return BRect();
}
}
}
@@ -1153,6 +1328,24 @@ BTabView::Border() const
}
void
BTabView::SetTabSide(tab_side tabSide)
{
if (fTabSide == tabSide)
return;
fTabSide = tabSide;
_LayoutContainerView(Flags() & B_SUPPORTS_LAYOUT);
}
BTabView::tab_side
BTabView::TabSide() const
{
return fTabSide;
}
BView*
BTabView::ContainerView() const
{
@@ -1188,6 +1381,7 @@ BTabView::_InitObject(bool layouted, button_width width)
fFocus = -1;
fTabOffset = 0.0f;
fBorderStyle = B_FANCY_BORDER;
fTabSide = kTopSide;
SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
SetLowUIColor(B_PANEL_BACKGROUND_COLOR);
@@ -1292,13 +1486,42 @@ BTabView::_LayoutContainerView(bool layouted)
}
BGroupLayout* layout = dynamic_cast<BGroupLayout*>(GetLayout());
if (layout != NULL) {
layout->SetInsets(borderWidth, borderWidth + TabHeight()
- topBorderOffset, borderWidth, borderWidth);
float inset = borderWidth + TabHeight() - topBorderOffset;
switch (fTabSide) {
case kTopSide:
layout->SetInsets(borderWidth, inset, borderWidth,
borderWidth);
break;
case kBottomSide:
layout->SetInsets(borderWidth, borderWidth, borderWidth,
inset);
break;
case kLeftSide:
layout->SetInsets(inset, borderWidth, borderWidth,
borderWidth);
break;
case kRightSide:
layout->SetInsets(borderWidth, borderWidth, inset,
borderWidth);
break;
}
}
} else {
BRect bounds = Bounds();
bounds.top += TabHeight();
switch (fTabSide) {
case kTopSide:
bounds.top += TabHeight();
break;
case kBottomSide:
bounds.bottom -= TabHeight();
break;
case kLeftSide:
bounds.left += TabHeight();
break;
case kRightSide:
bounds.right -= TabHeight();
break;
}
bounds.InsetBy(borderWidth, borderWidth);
fContainerView->MoveTo(bounds.left, bounds.top);
@@ -1310,7 +1533,6 @@ BTabView::_LayoutContainerView(bool layouted)
// #pragma mark - FBC and forbidden
void BTabView::_ReservedTabView2() {}
void BTabView::_ReservedTabView3() {}
void BTabView::_ReservedTabView4() {}
void BTabView::_ReservedTabView5() {}