From 029353b44c63fa39a9135f36f225bfac5daed34b Mon Sep 17 00:00:00 2001 From: stippi Date: Thu, 15 Apr 2010 12:19:16 +0000 Subject: [PATCH] * Reworded defines in Jamfiles to make them more readable. * Split WebTabView into several files in a new sub-folder "tabview". * Implemented scrolling the tab view left/right when there are more tabs than fit into the view. * Fixed graphic glitches in the TabContainerView when the window is resized, the space behind the last tab was not managed properly. git-svn-id: http://svn.haiku-os.org/webpositive/webkit/trunk@416 94f232f2-1747-11df-bad5-a5bfde151594 --- .../webpositive/tabview/TabContainerView.cpp | 520 ++++++++ .../webpositive/tabview/TabContainerView.h | 102 ++ .../TabManager.cpp} | 1052 ++++------------- .../{WebTabView.h => tabview/TabManager.h} | 3 +- src/apps/webpositive/tabview/TabView.cpp | 366 ++++++ src/apps/webpositive/tabview/TabView.h | 122 ++ 6 files changed, 1330 insertions(+), 835 deletions(-) create mode 100644 src/apps/webpositive/tabview/TabContainerView.cpp create mode 100644 src/apps/webpositive/tabview/TabContainerView.h rename src/apps/webpositive/{WebTabView.cpp => tabview/TabManager.cpp} (50%) rename src/apps/webpositive/{WebTabView.h => tabview/TabManager.h} (97%) create mode 100644 src/apps/webpositive/tabview/TabView.cpp create mode 100644 src/apps/webpositive/tabview/TabView.h diff --git a/src/apps/webpositive/tabview/TabContainerView.cpp b/src/apps/webpositive/tabview/TabContainerView.cpp new file mode 100644 index 0000000000..a4c26ddb2d --- /dev/null +++ b/src/apps/webpositive/tabview/TabContainerView.cpp @@ -0,0 +1,520 @@ +/* + * Copyright (C) 2010 Rene Gollent + * Copyright (C) 2010 Stephan Aßmus + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "TabContainerView.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "TabView.h" + + +static const float kLeftTabInset = 4; + + +TabContainerView::TabContainerView(Controller* controller) + : + BGroupView(B_HORIZONTAL), + fLastMouseEventTab(NULL), + fMouseDown(false), + fClickCount(0), + fSelectedTab(NULL), + fController(controller), + fFirstVisibleTabIndex(0) +{ + SetFlags(Flags() | B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE); + SetViewColor(B_TRANSPARENT_COLOR); + GroupLayout()->SetInsets(kLeftTabInset, 0, 0, 1); + GroupLayout()->AddItem(BSpaceLayoutItem::CreateGlue(), 0.0f); +} + + +TabContainerView::~TabContainerView() +{ +} + + +BSize +TabContainerView::MinSize() +{ + // Eventually, we want to be scrolling if the tabs don't fit. + BSize size(BGroupView::MinSize()); + size.width = 300; + return size; +} + + +void +TabContainerView::MessageReceived(BMessage* message) +{ + switch (message->what) { + default: + BGroupView::MessageReceived(message); + } +} + + +void +TabContainerView::Draw(BRect updateRect) +{ + // Stroke separator line at bottom. + rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); + BRect frame(Bounds()); + SetHighColor(tint_color(base, B_DARKEN_2_TINT)); + StrokeLine(frame.LeftBottom(), frame.RightBottom()); + frame.bottom--; + + // Draw empty area before first tab. + uint32 borders = BControlLook::B_TOP_BORDER | BControlLook::B_BOTTOM_BORDER; + BRect leftFrame(frame.left, frame.top, kLeftTabInset, frame.bottom); + be_control_look->DrawInactiveTab(this, leftFrame, updateRect, base, 0, + borders); + + // Draw all tabs, keeping track of where they end. + BGroupLayout* layout = GroupLayout(); + int32 count = layout->CountItems() - 1; + for (int32 i = 0; i < count; i++) { + TabLayoutItem* item = dynamic_cast( + layout->ItemAt(i)); + if (!item || !item->IsVisible()) + continue; + item->Parent()->Draw(updateRect); + frame.left = item->Frame().right + 1; + } + + // Draw empty area after last tab. + be_control_look->DrawInactiveTab(this, frame, updateRect, base, 0, borders); +} + + +void +TabContainerView::MouseDown(BPoint where) +{ + uint32 buttons; + if (Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons) != B_OK) + buttons = B_PRIMARY_MOUSE_BUTTON; + uint32 clicks; + if (Window()->CurrentMessage()->FindInt32("clicks", (int32*)&clicks) != B_OK) + clicks = 1; + fMouseDown = true; + SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS); + if (fLastMouseEventTab) + fLastMouseEventTab->MouseDown(where, buttons); + else { + if (clicks > 1) + fClickCount++; + else + fClickCount = 1; + } +} + + +void +TabContainerView::MouseUp(BPoint where) +{ + fMouseDown = false; + if (fLastMouseEventTab) + fLastMouseEventTab->MouseUp(where); + else if (fClickCount > 1) { + fClickCount = 0; + fController->DoubleClickOutsideTabs(); + } +} + + +void +TabContainerView::MouseMoved(BPoint where, uint32 transit, + const BMessage* dragMessage) +{ + _MouseMoved(where, transit, dragMessage); +} + + +void +TabContainerView::DoLayout() +{ + BGroupView::DoLayout(); + + _ValidateTabVisibility(); + _SendFakeMouseMoved(); +} + +void +TabContainerView::AddTab(const char* label, int32 index) +{ + TabView* tab; + if (fController) + tab = fController->CreateTabView(); + else + tab = new TabView(); + tab->SetLabel(label); + AddTab(tab, index); +} + + +void +TabContainerView::AddTab(TabView* tab, int32 index) +{ + tab->SetContainerView(this); + + if (index == -1) + index = GroupLayout()->CountItems() - 1; + + bool hasFrames = fController != NULL && fController->HasFrames(); + bool isFirst = index == 0 && hasFrames; + bool isLast = index == GroupLayout()->CountItems() - 1 && hasFrames; + bool isFront = fSelectedTab == NULL; + tab->Update(isFirst, isLast, isFront); + + GroupLayout()->AddItem(index, tab->LayoutItem()); + + if (isFront) + SelectTab(tab); + if (isLast) { + TabLayoutItem* item + = dynamic_cast(GroupLayout()->ItemAt(index - 1)); + if (item) + item->Parent()->SetIsLast(false); + } + + SetFirstVisibleTabIndex(MaxFirstVisibleTabIndex()); + _ValidateTabVisibility(); +} + +TabView* +TabContainerView::RemoveTab(int32 index) +{ + TabLayoutItem* item + = dynamic_cast(GroupLayout()->RemoveItem(index)); + + if (!item) + return NULL; + + BRect dirty(Bounds()); + dirty.left = item->Frame().left; + TabView* removedTab = item->Parent(); + removedTab->SetContainerView(NULL); + + if (removedTab == fLastMouseEventTab) + fLastMouseEventTab = NULL; + + // Update tabs after or before the removed tab. + bool hasFrames = fController != NULL && fController->HasFrames(); + item = dynamic_cast(GroupLayout()->ItemAt(index)); + if (item) { + // This tab is behind the removed tab. + TabView* tab = item->Parent(); + tab->Update(index == 0 && hasFrames, + index == GroupLayout()->CountItems() - 2 && hasFrames, + tab == fSelectedTab); + if (removedTab == fSelectedTab) { + fSelectedTab = NULL; + SelectTab(tab); + } else if (fController && tab == fSelectedTab) + fController->TabSelected(index); + } else { + // The removed tab was the last tab. + item = dynamic_cast(GroupLayout()->ItemAt(index - 1)); + if (item) { + TabView* tab = item->Parent(); + tab->Update(index == 0 && hasFrames, + index == GroupLayout()->CountItems() - 2 && hasFrames, + tab == fSelectedTab); + if (removedTab == fSelectedTab) { + fSelectedTab = NULL; + SelectTab(tab); + } + } + } + + Invalidate(dirty); + _ValidateTabVisibility(); + + return removedTab; +} + + +TabView* +TabContainerView::TabAt(int32 index) const +{ + TabLayoutItem* item = dynamic_cast( + GroupLayout()->ItemAt(index)); + if (item) + return item->Parent(); + return NULL; +} + + +int32 +TabContainerView::IndexOf(TabView* tab) const +{ + return GroupLayout()->IndexOfItem(tab->LayoutItem()); +} + + +void +TabContainerView::SelectTab(int32 index) +{ + TabView* tab = NULL; + TabLayoutItem* item = dynamic_cast( + GroupLayout()->ItemAt(index)); + if (item) + tab = item->Parent(); + + SelectTab(tab); +} + + +void +TabContainerView::SelectTab(TabView* tab) +{ + if (tab == fSelectedTab) + return; + + if (fSelectedTab) + fSelectedTab->SetIsFront(false); + + fSelectedTab = tab; + + if (fSelectedTab) + fSelectedTab->SetIsFront(true); + + if (fController != NULL) { + int32 index = -1; + if (fSelectedTab != NULL) + index = GroupLayout()->IndexOfItem(tab->LayoutItem()); + + fController->TabSelected(index); + } +} + + +void +TabContainerView::SetTabLabel(int32 tabIndex, const char* label) +{ + TabLayoutItem* item = dynamic_cast( + GroupLayout()->ItemAt(tabIndex)); + if (item == NULL) + return; + + item->Parent()->SetLabel(label); +} + + +void +TabContainerView::SetFirstVisibleTabIndex(int32 index) +{ + if (index < 0) + index = 0; + if (index > MaxFirstVisibleTabIndex()) + index = MaxFirstVisibleTabIndex(); + if (fFirstVisibleTabIndex == index) + return; + + fFirstVisibleTabIndex = index; + + _UpdateTabVisibility(); +} + + +int32 +TabContainerView::FirstVisibleTabIndex() const +{ + return fFirstVisibleTabIndex; +} + + +int32 +TabContainerView::MaxFirstVisibleTabIndex() const +{ + float availableWidth = _AvailableWidthForTabs(); + if (availableWidth < 0) + return 0; + float visibleTabsWidth = 0; + + BGroupLayout* layout = GroupLayout(); + int32 i = layout->CountItems() - 2; + for (; i >= 0; i--) { + TabLayoutItem* item = dynamic_cast( + layout->ItemAt(i)); + if (item == NULL) + continue; + + float itemWidth = item->MinSize().width; + if (availableWidth >= visibleTabsWidth + itemWidth) + visibleTabsWidth += itemWidth; + else { + // The tab before this tab is the last one that can be visible. + return i + 1; + } + } + + return 0; +} + + +bool +TabContainerView::CanScrollLeft() const +{ + return fFirstVisibleTabIndex < MaxFirstVisibleTabIndex(); +} + + +bool +TabContainerView::CanScrollRight() const +{ + BGroupLayout* layout = GroupLayout(); + int32 count = layout->CountItems() - 1; + if (count > 0) { + TabLayoutItem* item = dynamic_cast( + layout->ItemAt(count - 1)); + return !item->IsVisible(); + } + return false; +} + + +// #pragma mark - + + +TabView* +TabContainerView::_TabAt(const BPoint& where) const +{ + BGroupLayout* layout = GroupLayout(); + int32 count = layout->CountItems() - 1; + for (int32 i = 0; i < count; i++) { + TabLayoutItem* item = dynamic_cast( + layout->ItemAt(i)); + if (item && item->IsVisible() && item->Frame().Contains(where)) + return item->Parent(); + } + return NULL; +} + + +void +TabContainerView::_MouseMoved(BPoint where, uint32 _transit, + const BMessage* dragMessage) +{ + TabView* tab = _TabAt(where); + if (fMouseDown) { + uint32 transit = tab == fLastMouseEventTab + ? B_INSIDE_VIEW : B_OUTSIDE_VIEW; + if (fLastMouseEventTab) + fLastMouseEventTab->MouseMoved(where, transit, dragMessage); + return; + } + + if (fLastMouseEventTab && fLastMouseEventTab == tab) + fLastMouseEventTab->MouseMoved(where, B_INSIDE_VIEW, dragMessage); + else { + if (fLastMouseEventTab) + fLastMouseEventTab->MouseMoved(where, B_EXITED_VIEW, dragMessage); + fLastMouseEventTab = tab; + if (fLastMouseEventTab) + fLastMouseEventTab->MouseMoved(where, B_ENTERED_VIEW, dragMessage); + } +} + + +void +TabContainerView::_ValidateTabVisibility() +{ + if (fFirstVisibleTabIndex > MaxFirstVisibleTabIndex()) + SetFirstVisibleTabIndex(MaxFirstVisibleTabIndex()); + else + _UpdateTabVisibility(); +} + + +void +TabContainerView::_UpdateTabVisibility() +{ + float availableWidth = _AvailableWidthForTabs(); + if (availableWidth < 0) + return; + float visibleTabsWidth = 0; + + bool canScrollTabsLeft = fFirstVisibleTabIndex > 0; + bool canScrollTabsRight = false; + + BGroupLayout* layout = GroupLayout(); + int32 count = layout->CountItems() - 1; + for (int32 i = 0; i < count; i++) { + TabLayoutItem* item = dynamic_cast( + layout->ItemAt(i)); + if (i < fFirstVisibleTabIndex) + item->SetVisible(false); + else { + float itemWidth = item->MinSize().width; + bool visible = availableWidth >= visibleTabsWidth + itemWidth; + item->SetVisible(visible && !canScrollTabsRight); + visibleTabsWidth += itemWidth; + if (!visible) + canScrollTabsRight = true; + } + } + fController->UpdateTabScrollability(canScrollTabsLeft, canScrollTabsRight); +} + + +float +TabContainerView::_AvailableWidthForTabs() const +{ + float width = Bounds().Width() - 10; + // TODO: Don't really know why -10 is needed above. + + float left; + float right; + GroupLayout()->GetInsets(&left, NULL, &right, NULL); + width -= left + right; + + return width; +} + + +void +TabContainerView::_SendFakeMouseMoved() +{ + BPoint where; + uint32 buttons; + GetMouse(&where, &buttons, false); + if (Bounds().Contains(where)) + _MouseMoved(where, B_INSIDE_VIEW, NULL); +} + diff --git a/src/apps/webpositive/tabview/TabContainerView.h b/src/apps/webpositive/tabview/TabContainerView.h new file mode 100644 index 0000000000..13815d1592 --- /dev/null +++ b/src/apps/webpositive/tabview/TabContainerView.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2010 Stephan Aßmus + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef TAB_CONTAINER_VIEW_H +#define TAB_CONTAINER_VIEW_H + +#include + + +class TabView; + + +class TabContainerView : public BGroupView { +public: + class Controller { + public: + virtual void TabSelected(int32 tabIndex) = 0; + virtual bool HasFrames() = 0; + virtual TabView* CreateTabView() = 0; + virtual void DoubleClickOutsideTabs() = 0; + virtual void UpdateTabScrollability(bool canScrollLeft, + bool canScrollRight) = 0; + }; + +public: + TabContainerView(Controller* controller); + virtual ~TabContainerView(); + + virtual BSize MinSize(); + + virtual void MessageReceived(BMessage*); + + virtual void Draw(BRect updateRect); + + virtual void MouseDown(BPoint where); + virtual void MouseUp(BPoint where); + virtual void MouseMoved(BPoint where, uint32 transit, + const BMessage* dragMessage); + + virtual void DoLayout(); + + void AddTab(const char* label, int32 index = -1); + void AddTab(TabView* tab, int32 index = -1); + TabView* RemoveTab(int32 index); + TabView* TabAt(int32 index) const; + + int32 IndexOf(TabView* tab) const; + + void SelectTab(int32 tabIndex); + void SelectTab(TabView* tab); + + void SetTabLabel(int32 tabIndex, const char* label); + + void SetFirstVisibleTabIndex(int32 index); + int32 FirstVisibleTabIndex() const; + int32 MaxFirstVisibleTabIndex() const; + + bool CanScrollLeft() const; + bool CanScrollRight() const; + +private: + TabView* _TabAt(const BPoint& where) const; + void _MouseMoved(BPoint where, uint32 transit, + const BMessage* dragMessage); + void _ValidateTabVisibility(); + void _UpdateTabVisibility(); + float _AvailableWidthForTabs() const; + void _SendFakeMouseMoved(); + +private: + TabView* fLastMouseEventTab; + bool fMouseDown; + uint32 fClickCount; + TabView* fSelectedTab; + Controller* fController; + int32 fFirstVisibleTabIndex; +}; + +#endif // TAB_CONTAINER_VIEW_H diff --git a/src/apps/webpositive/WebTabView.cpp b/src/apps/webpositive/tabview/TabManager.cpp similarity index 50% rename from src/apps/webpositive/WebTabView.cpp rename to src/apps/webpositive/tabview/TabManager.cpp index d953d2ee99..814496e582 100644 --- a/src/apps/webpositive/WebTabView.cpp +++ b/src/apps/webpositive/tabview/TabManager.cpp @@ -26,9 +26,10 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "WebTabView.h" +#include "TabManager.h" + +#include -#include "WebView.h" #include #include #include @@ -39,716 +40,245 @@ #include #include #include -#include + +#include "TabContainerView.h" +#include "TabView.h" -class TabView; -class TabContainerView; +// #pragma mark - Helper classes -class TabLayoutItem : public BAbstractLayoutItem { +class TabButton : public BButton { public: - TabLayoutItem(TabView* parent); + TabButton(BMessage* message) + : BButton("", message) + { + } - virtual bool IsVisible(); - virtual void SetVisible(bool visible); + virtual BSize MinSize() + { + return BSize(12, 12); + } - virtual BRect Frame(); - virtual void SetFrame(BRect frame); + virtual BSize MaxSize() + { + return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED); + } - virtual BView* View(); + virtual BSize PreferredSize() + { + return MinSize(); + } - virtual BSize BaseMinSize(); - virtual BSize BaseMaxSize(); - virtual BSize BasePreferredSize(); - virtual BAlignment BaseAlignment(); + virtual void Draw(BRect updateRect) + { + BRect bounds(Bounds()); + rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); + SetHighColor(tint_color(base, B_DARKEN_2_TINT)); + StrokeLine(bounds.LeftBottom(), bounds.RightBottom()); + bounds.bottom--; + uint32 flags = be_control_look->Flags(this); + uint32 borders = BControlLook::B_TOP_BORDER + | BControlLook::B_BOTTOM_BORDER; + be_control_look->DrawInactiveTab(this, bounds, updateRect, base, + 0, borders); + if (IsEnabled()) { + rgb_color button = tint_color(base, 1.07); + be_control_look->DrawButtonBackground(this, bounds, updateRect, + button, flags, 0); + } - TabView* Parent() const; + bounds.left = (bounds.left + bounds.right) / 2 - 6; + bounds.top = (bounds.top + bounds.bottom) / 2 - 6; + bounds.right = bounds.left + 12; + bounds.bottom = bounds.top + 12; + DrawSymbol(bounds, updateRect, base); + } -private: - TabView* fParent; - BRect fFrame; + virtual void DrawSymbol(BRect frame, const BRect& updateRect, + const rgb_color& base) + { + } }; -class TabView { +class ScrollLeftTabButton : public TabButton { public: - TabView(); - virtual ~TabView(); + ScrollLeftTabButton(BMessage* message) + : TabButton(message) + { + } - virtual BSize MinSize(); - virtual BSize PreferredSize(); - virtual BSize MaxSize(); - - void Draw(BRect updateRect); - virtual void DrawBackground(BView* owner, BRect frame, - const BRect& updateRect, bool isFirst, bool isLast, bool isFront); - virtual void DrawContents(BView* owner, BRect frame, - const BRect& updateRect, bool isFirst, bool isLast, bool isFront); - - virtual void MouseDown(BPoint where, uint32 buttons); - virtual void MouseUp(BPoint where); - virtual void MouseMoved(BPoint where, uint32 transit, - const BMessage* dragMessage); - - void SetIsFront(bool isFront); - bool IsFront() const; - void SetIsLast(bool isLast); - virtual void Update(bool isFirst, bool isLast, bool isFront); - - BLayoutItem* LayoutItem() const; - void SetContainerView(TabContainerView* containerView); - TabContainerView* ContainerView() const; - - void SetLabel(const char* label); - const BString& Label() const; - - BRect Frame() const; - -private: - float _LabelHeight() const; - -private: - TabContainerView* fContainerView; - TabLayoutItem* fLayoutItem; - - BString fLabel; - - bool fIsFirst; - bool fIsLast; - bool fIsFront; + virtual void DrawSymbol(BRect frame, const BRect& updateRect, + const rgb_color& base) + { + float tint = IsEnabled() ? B_DARKEN_4_TINT : B_DARKEN_1_TINT; + be_control_look->DrawArrowShape(this, frame, updateRect, + base, BControlLook::B_LEFT_ARROW, 0, tint); + } }; -class TabContainerView : public BGroupView { +class ScrollRightTabButton : public TabButton { public: - class Controller { - public: - virtual void TabSelected(int32 tabIndex) = 0; - virtual bool HasFrames() = 0; - virtual TabView* CreateTabView() = 0; - virtual void DoubleClickOutsideTabs() = 0; - }; + ScrollRightTabButton(BMessage* message) + : TabButton(message) + { + } -public: - TabContainerView(Controller* controller); - virtual ~TabContainerView(); - - virtual BSize MinSize(); - - virtual void MessageReceived(BMessage*); - - virtual void Draw(BRect updateRect); - - virtual void MouseDown(BPoint where); - virtual void MouseUp(BPoint where); - virtual void MouseMoved(BPoint where, uint32 transit, - const BMessage* dragMessage); - - virtual void DoLayout(); - - void AddTab(const char* label, int32 index = -1); - void AddTab(TabView* tab, int32 index = -1); - TabView* RemoveTab(int32 index); - TabView* TabAt(int32 index) const; - - int32 IndexOf(TabView* tab) const; - - void SelectTab(int32 tabIndex); - void SelectTab(TabView* tab); - - void SetTabLabel(int32 tabIndex, const char* label); - -private: - TabView* _TabAt(const BPoint& where) const; - void _MouseMoved(BPoint where, uint32 transit, - const BMessage* dragMessage); - -private: - TabView* fLastMouseEventTab; - bool fMouseDown; - uint32 fClickCount; - TabView* fSelectedTab; - Controller* fController; + virtual void DrawSymbol(BRect frame, const BRect& updateRect, + const rgb_color& base) + { + frame.OffsetBy(1, 0); + float tint = IsEnabled() ? B_DARKEN_4_TINT : B_DARKEN_1_TINT; + be_control_look->DrawArrowShape(this, frame, updateRect, + base, BControlLook::B_RIGHT_ARROW, 0, tint); + } }; -// #pragma mark - TabContainerView - - -static const float kLeftTabInset = 4; - - -TabContainerView::TabContainerView(Controller* controller) - : - BGroupView(B_HORIZONTAL), - fLastMouseEventTab(NULL), - fMouseDown(false), - fClickCount(0), - fSelectedTab(NULL), - fController(controller) -{ - SetFlags(Flags() | B_WILL_DRAW); - SetViewColor(B_TRANSPARENT_COLOR); - GroupLayout()->SetInsets(kLeftTabInset, 0, 0, 1); - GroupLayout()->AddItem(BSpaceLayoutItem::CreateGlue(), 0.0f); -} - - -TabContainerView::~TabContainerView() -{ -} - - -BSize -TabContainerView::MinSize() -{ - // Eventually, we want to be scrolling if the tabs don't fit. - BSize size(BGroupView::MinSize()); - size.width = 300; - return size; -} - - -void -TabContainerView::MessageReceived(BMessage* message) -{ - switch (message->what) { - default: - BGroupView::MessageReceived(message); - } -} - - -void -TabContainerView::Draw(BRect updateRect) -{ - // Stroke separator line at bottom. - rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); - BRect frame(Bounds()); - SetHighColor(tint_color(base, B_DARKEN_2_TINT)); - StrokeLine(frame.LeftBottom(), frame.RightBottom()); - frame.bottom--; - - // Draw empty area before first tab. - uint32 borders = BControlLook::B_TOP_BORDER | BControlLook::B_BOTTOM_BORDER; - BRect leftFrame(frame.left, frame.top, kLeftTabInset, frame.bottom); - be_control_look->DrawInactiveTab(this, leftFrame, updateRect, base, 0, - borders); - - // Draw all tabs, keeping track of where they end. - BGroupLayout* layout = GroupLayout(); - int32 count = layout->CountItems() - 1; - for (int32 i = 0; i < count; i++) { - TabLayoutItem* item = dynamic_cast( - layout->ItemAt(i)); - if (!item) - continue; - item->Parent()->Draw(updateRect); - frame.left = item->Frame().right + 1; +class NewTabButton : public TabButton { +public: + NewTabButton(BMessage* message) + : TabButton(message) + { + SetToolTip("New tab (Cmd-T)"); } - // Draw empty area after last tab. - be_control_look->DrawInactiveTab(this, frame, updateRect, base, 0, borders); -} - - -void -TabContainerView::MouseDown(BPoint where) -{ - uint32 buttons; - if (Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons) != B_OK) - buttons = B_PRIMARY_MOUSE_BUTTON; - uint32 clicks; - if (Window()->CurrentMessage()->FindInt32("clicks", (int32*)&clicks) != B_OK) - clicks = 1; - fMouseDown = true; - SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS); - if (fLastMouseEventTab) - fLastMouseEventTab->MouseDown(where, buttons); - else { - if (clicks > 1) - fClickCount++; - else - fClickCount = 1; + virtual BSize MinSize() + { + return BSize(18, 12); } -} - -void -TabContainerView::MouseUp(BPoint where) -{ - fMouseDown = false; - if (fLastMouseEventTab) - fLastMouseEventTab->MouseUp(where); - else if (fClickCount > 1) { - fClickCount = 0; - fController->DoubleClickOutsideTabs(); + virtual void DrawSymbol(BRect frame, const BRect& updateRect, + const rgb_color& base) + { + SetHighColor(tint_color(base, B_DARKEN_4_TINT)); + float inset = 3; + frame.InsetBy(2, 2); + frame.top++; + frame.left++; + FillRoundRect(BRect(frame.left, frame.top + inset, + frame.right, frame.bottom - inset), 1, 1); + FillRoundRect(BRect(frame.left + inset, frame.top, + frame.right - inset, frame.bottom), 1, 1); } -} +}; -void -TabContainerView::MouseMoved(BPoint where, uint32 transit, - const BMessage* dragMessage) -{ - _MouseMoved(where, transit, dragMessage); -} - - -void -TabContainerView::DoLayout() -{ - BGroupView::DoLayout(); - - BPoint where; - uint32 buttons; - GetMouse(&where, &buttons, false); - if (Bounds().Contains(where)) - _MouseMoved(where, B_INSIDE_VIEW, NULL); -} - -void -TabContainerView::AddTab(const char* label, int32 index) -{ - TabView* tab; - if (fController) - tab = fController->CreateTabView(); - else - tab = new TabView(); - tab->SetLabel(label); - AddTab(tab, index); -} - - -void -TabContainerView::AddTab(TabView* tab, int32 index) -{ - tab->SetContainerView(this); - - if (index == -1) - index = GroupLayout()->CountItems() - 1; - - bool hasFrames = fController != NULL && fController->HasFrames(); - bool isFirst = index == 0 && hasFrames; - bool isLast = index == GroupLayout()->CountItems() - 1 && hasFrames; - bool isFront = fSelectedTab == NULL; - tab->Update(isFirst, isLast, isFront); - - GroupLayout()->AddItem(index, tab->LayoutItem()); - - if (isFront) - SelectTab(tab); - if (isLast) { - TabLayoutItem* item - = dynamic_cast(GroupLayout()->ItemAt(index - 1)); - if (item) - item->Parent()->SetIsLast(false); +class TabMenuTabButton : public TabButton { +public: + TabMenuTabButton(BMessage* message) + : TabButton(message) + { } -} -TabView* -TabContainerView::RemoveTab(int32 index) -{ - TabLayoutItem* item - = dynamic_cast(GroupLayout()->RemoveItem(index)); + virtual BSize MinSize() + { + return BSize(18, 12); + } - if (!item) - return NULL; + virtual void DrawSymbol(BRect frame, const BRect& updateRect, + const rgb_color& base) + { + be_control_look->DrawArrowShape(this, frame, updateRect, + base, BControlLook::B_DOWN_ARROW, 0, B_DARKEN_4_TINT); + } +}; - BRect dirty(Bounds()); - dirty.left = item->Frame().left; - TabView* removedTab = item->Parent(); - removedTab->SetContainerView(NULL); - if (removedTab == fLastMouseEventTab) - fLastMouseEventTab = NULL; +enum { + MSG_SCROLL_TABS_LEFT = 'stlt', + MSG_SCROLL_TABS_RIGHT = 'strt' +}; - // Update tabs after or before the removed tab. - bool hasFrames = fController != NULL && fController->HasFrames(); - item = dynamic_cast(GroupLayout()->ItemAt(index)); - if (item) { - // This tab is behind the removed tab. - TabView* tab = item->Parent(); - tab->Update(index == 0 && hasFrames, - index == GroupLayout()->CountItems() - 2 && hasFrames, - tab == fSelectedTab); - if (removedTab == fSelectedTab) { - fSelectedTab = NULL; - SelectTab(tab); - } else if (fController && tab == fSelectedTab) - fController->TabSelected(index); - } else { - // The removed tab was the last tab. - item = dynamic_cast(GroupLayout()->ItemAt(index - 1)); - if (item) { - TabView* tab = item->Parent(); - tab->Update(index == 0 && hasFrames, - index == GroupLayout()->CountItems() - 2 && hasFrames, - tab == fSelectedTab); - if (removedTab == fSelectedTab) { - fSelectedTab = NULL; - SelectTab(tab); - } + +class TabContainerGroup : public BGroupView { +public: + TabContainerGroup(TabContainerView* tabContainerView) + : + BGroupView(B_HORIZONTAL), + fTabContainerView(tabContainerView), + fScrollLeftTabButton(NULL), + fScrollRightTabButton(NULL) + { + } + + virtual void AttachedToWindow() + { + if (fScrollLeftTabButton != NULL) + fScrollLeftTabButton->SetTarget(this); + if (fScrollRightTabButton != NULL) + fScrollRightTabButton->SetTarget(this); + } + + virtual void MessageReceived(BMessage* message) + { + switch (message->what) { + case MSG_SCROLL_TABS_LEFT: + fTabContainerView->SetFirstVisibleTabIndex( + fTabContainerView->FirstVisibleTabIndex() - 1); + break; + case MSG_SCROLL_TABS_RIGHT: + fTabContainerView->SetFirstVisibleTabIndex( + fTabContainerView->FirstVisibleTabIndex() + 1); + break; + default: + BGroupView::MessageReceived(message); + break; } } - Invalidate(dirty); - - return removedTab; -} - - -TabView* -TabContainerView::TabAt(int32 index) const -{ - TabLayoutItem* item = dynamic_cast( - GroupLayout()->ItemAt(index)); - if (item) - return item->Parent(); - return NULL; -} - - -int32 -TabContainerView::IndexOf(TabView* tab) const -{ - return GroupLayout()->IndexOfItem(tab->LayoutItem()); -} - - -void -TabContainerView::SelectTab(int32 index) -{ - TabView* tab = NULL; - TabLayoutItem* item = dynamic_cast( - GroupLayout()->ItemAt(index)); - if (item) - tab = item->Parent(); - - SelectTab(tab); -} - - -void -TabContainerView::SelectTab(TabView* tab) -{ - if (tab == fSelectedTab) - return; - - if (fSelectedTab) - fSelectedTab->SetIsFront(false); - - fSelectedTab = tab; - - if (fSelectedTab) - fSelectedTab->SetIsFront(true); - - if (fController != NULL) { - int32 index = -1; - if (fSelectedTab != NULL) - index = GroupLayout()->IndexOfItem(tab->LayoutItem()); - - fController->TabSelected(index); - } -} - - -void -TabContainerView::SetTabLabel(int32 tabIndex, const char* label) -{ - TabLayoutItem* item = dynamic_cast( - GroupLayout()->ItemAt(tabIndex)); - if (item == NULL) - return; - - item->Parent()->SetLabel(label); -} - - -TabView* -TabContainerView::_TabAt(const BPoint& where) const -{ - BGroupLayout* layout = GroupLayout(); - int32 count = layout->CountItems() - 1; - for (int32 i = 0; i < count; i++) { - TabLayoutItem* item = dynamic_cast( - layout->ItemAt(i)); - if (item && item->Frame().Contains(where)) - return item->Parent(); - } - return NULL; -} - - -void -TabContainerView::_MouseMoved(BPoint where, uint32 _transit, - const BMessage* dragMessage) -{ - TabView* tab = _TabAt(where); - if (fMouseDown) { - uint32 transit = tab == fLastMouseEventTab - ? B_INSIDE_VIEW : B_OUTSIDE_VIEW; - if (fLastMouseEventTab) - fLastMouseEventTab->MouseMoved(where, transit, dragMessage); - return; + void AddScrollLeftButton(TabButton* button) + { + fScrollLeftTabButton = button; + GroupLayout()->AddView(button, 0.0f); } - if (fLastMouseEventTab && fLastMouseEventTab == tab) - fLastMouseEventTab->MouseMoved(where, B_INSIDE_VIEW, dragMessage); - else { - if (fLastMouseEventTab) - fLastMouseEventTab->MouseMoved(where, B_EXITED_VIEW, dragMessage); - fLastMouseEventTab = tab; - if (fLastMouseEventTab) - fLastMouseEventTab->MouseMoved(where, B_ENTERED_VIEW, dragMessage); + void AddScrollRightButton(TabButton* button) + { + fScrollRightTabButton = button; + GroupLayout()->AddView(button, 0.0f); } -} - -// #pragma mark - TabView - - -TabView::TabView() - : fContainerView(NULL) - , fLayoutItem(new TabLayoutItem(this)) - , fLabel() -{ -} - -TabView::~TabView() -{ - // The layout item is deleted for us by the layout which contains it. - if (!fContainerView) - delete fLayoutItem; -} - -BSize TabView::MinSize() -{ - BSize size(MaxSize()); - size.width = 100.0f; - return size; -} - -BSize TabView::PreferredSize() -{ - return MaxSize(); -} - -BSize TabView::MaxSize() -{ - float extra = be_control_look->DefaultLabelSpacing(); - float labelWidth = fContainerView->StringWidth(fLabel.String()) + 2 * extra; - labelWidth = min_c(300.0f, labelWidth); - return BSize(labelWidth, _LabelHeight() + extra); -} - -void TabView::Draw(BRect updateRect) -{ - BRect frame(fLayoutItem->Frame()); - if (fIsFront) { - // Extend the front tab outward left/right in order to merge - // the frames of adjacent tabs. - if (!fIsFirst) - frame.left--; - if (!fIsLast) - frame.right++; - - frame.bottom++; + void EnableScrollButtons(bool canScrollLeft, bool canScrollRight) + { + fScrollLeftTabButton->SetEnabled(canScrollLeft); + fScrollRightTabButton->SetEnabled(canScrollRight); + if (!canScrollLeft && !canScrollRight) { + // hide scroll buttons + } else { + // show scroll buttons + } } - DrawBackground(fContainerView, frame, updateRect, fIsFirst, fIsLast, - fIsFront); - if (fIsFront) { - frame.top += 3.0f; - if (!fIsFirst) - frame.left++; - if (!fIsLast) - frame.right--; - } else - frame.top += 6.0f; - float spacing = be_control_look->DefaultLabelSpacing(); - frame.InsetBy(spacing, spacing / 2); - DrawContents(fContainerView, frame, updateRect, fIsFirst, fIsLast, - fIsFront); -} -void TabView::DrawBackground(BView* owner, BRect frame, const BRect& updateRect, - bool isFirst, bool isLast, bool isFront) -{ - rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); - uint32 borders = BControlLook::B_TOP_BORDER - | BControlLook::B_BOTTOM_BORDER; - if (isFirst) - borders |= BControlLook::B_LEFT_BORDER; - if (isLast) - borders |= BControlLook::B_RIGHT_BORDER; - if (isFront) { - be_control_look->DrawActiveTab(owner, frame, updateRect, base, - 0, borders); - } else { - be_control_look->DrawInactiveTab(owner, frame, updateRect, base, - 0, borders); +private: + TabContainerView* fTabContainerView; + TabButton* fScrollLeftTabButton; + TabButton* fScrollRightTabButton; +}; + + +class TabButtonContainer : public BGroupView { +public: + TabButtonContainer() + : + BGroupView(B_HORIZONTAL) + { + SetFlags(Flags() | B_WILL_DRAW); + SetViewColor(B_TRANSPARENT_COLOR); + SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR)); + GroupLayout()->SetInsets(0, 6, 0, 0); } -} -void TabView::DrawContents(BView* owner, BRect frame, const BRect& updateRect, - bool isFirst, bool isLast, bool isFront) -{ - rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); - be_control_look->DrawLabel(owner, fLabel.String(), frame, updateRect, - base, 0, BAlignment(B_ALIGN_LEFT, B_ALIGN_MIDDLE)); -} - -void TabView::MouseDown(BPoint where, uint32 buttons) -{ - fContainerView->SelectTab(this); -} - -void TabView::MouseUp(BPoint where) -{ -} - -void TabView::MouseMoved(BPoint where, uint32 transit, - const BMessage* dragMessage) -{ -} - -void TabView::SetIsFront(bool isFront) -{ - Update(fIsFirst, fIsLast, isFront); -} - -bool TabView::IsFront() const -{ - return fIsFront; -} - -void TabView::SetIsLast(bool isLast) -{ - Update(fIsFirst, isLast, fIsFront); -} - -void TabView::Update(bool isFirst, bool isLast, bool isFront) -{ - if (fIsFirst == isFirst && fIsLast == isLast && fIsFront == isFront) - return; - fIsFirst = isFirst; - fIsLast = isLast; - fIsFront = isFront; - BRect frame = fLayoutItem->Frame(); - frame.bottom++; - fContainerView->Invalidate(frame); -} - -void TabView::SetContainerView(TabContainerView* containerView) -{ - fContainerView = containerView; -} - -TabContainerView* TabView::ContainerView() const -{ - return fContainerView; -} - -BLayoutItem* TabView::LayoutItem() const -{ - return fLayoutItem; -} - -void TabView::SetLabel(const char* label) -{ - if (fLabel == label) - return; - fLabel = label; - fLayoutItem->InvalidateLayout(); -} - -const BString& TabView::Label() const -{ - return fLabel; -} - - -BRect -TabView::Frame() const -{ - return fLayoutItem->Frame(); -} - - -float TabView::_LabelHeight() const -{ - font_height fontHeight; - fContainerView->GetFontHeight(&fontHeight); - return ceilf(fontHeight.ascent) + ceilf(fontHeight.descent); -} - -// #pragma mark - TabLayoutItem - -TabLayoutItem::TabLayoutItem(TabView* parent) - : fParent(parent) -{ -} - -bool TabLayoutItem::IsVisible() -{ - return !fParent->ContainerView()->IsHidden(fParent->ContainerView()); -} - -void TabLayoutItem::SetVisible(bool visible) -{ - // not allowed -} - -BRect TabLayoutItem::Frame() -{ - return fFrame; -} - -void TabLayoutItem::SetFrame(BRect frame) -{ - BRect dirty = fFrame; - fFrame = frame; - dirty = dirty | fFrame; - // Invalidate more than necessary, to help the TabContainerView - // redraw the parts outside any tabs... - dirty.bottom++; - dirty.right++; - fParent->ContainerView()->Invalidate(dirty); -} - -BView* TabLayoutItem::View() -{ - return NULL; -} - -BSize TabLayoutItem::BaseMinSize() -{ - return fParent->MinSize(); -} - -BSize TabLayoutItem::BaseMaxSize() -{ - return fParent->MaxSize(); -} - -BSize TabLayoutItem::BasePreferredSize() -{ - return fParent->PreferredSize(); -} - -BAlignment TabLayoutItem::BaseAlignment() -{ - return BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT); -} - -TabView* TabLayoutItem::Parent() const -{ - return fParent; -} - - -// #pragma mark - TabManagerController + virtual void Draw(BRect updateRect) + { + BRect bounds(Bounds()); + rgb_color base = LowColor(); + be_control_look->DrawInactiveTab(this, bounds, updateRect, + base, 0, BControlLook::B_TOP_BORDER); + } +}; class TabManagerController : public TabContainerView::Controller { @@ -771,6 +301,12 @@ public: virtual void DoubleClickOutsideTabs(); + virtual void UpdateTabScrollability(bool canScrollLeft, + bool canScrollRight) + { + fTabContainerGroup->EnableScrollButtons(canScrollLeft, canScrollRight); + } + void CloseTab(int32 index); void SetCloseButtonsAvailable(bool available) @@ -786,11 +322,17 @@ public: void SetDoubleClickOutsideTabsMessage(const BMessage& message, const BMessenger& target); + void SetTabContainerGroup(TabContainerGroup* tabContainerGroup) + { + fTabContainerGroup = tabContainerGroup; + } + private: - TabManager* fManager; - bool fCloseButtonsAvailable; - BMessage* fDoubleClickOutsideTabsMessage; - BMessenger fTarget; + TabManager* fManager; + TabContainerGroup* fTabContainerGroup; + bool fCloseButtonsAvailable; + BMessage* fDoubleClickOutsideTabsMessage; + BMessenger fTarget; }; @@ -1021,6 +563,7 @@ void WebTabView::_DrawCloseButton(BView* owner, BRect& frame, TabManagerController::TabManagerController(TabManager* manager) : fManager(manager), + fTabContainerGroup(NULL), fCloseButtonsAvailable(false), fDoubleClickOutsideTabsMessage(NULL) { @@ -1064,169 +607,6 @@ TabManagerController::SetDoubleClickOutsideTabsMessage(const BMessage& message, } -// #pragma mark - TabButtonContainer - - -class TabButtonContainer : public BGroupView { -public: - TabButtonContainer() - : BGroupView(B_HORIZONTAL) - { - SetFlags(Flags() | B_WILL_DRAW); - SetViewColor(B_TRANSPARENT_COLOR); - SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR)); - GroupLayout()->SetInsets(0, 6, 0, 0); - } - - virtual void Draw(BRect updateRect) - { - BRect bounds(Bounds()); - rgb_color base = LowColor(); - be_control_look->DrawInactiveTab(this, bounds, updateRect, - base, 0, BControlLook::B_TOP_BORDER); - } -}; - - -// #pragma mark - TabButton - - -class TabButton : public BButton { -public: - TabButton(BMessage* message) - : BButton("", message) - { - } - - virtual BSize MinSize() - { - return BSize(12, 12); - } - - virtual BSize MaxSize() - { - return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED); - } - - virtual BSize PreferredSize() - { - return MinSize(); - } - - virtual void Draw(BRect updateRect) - { - BRect bounds(Bounds()); - rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); - SetHighColor(tint_color(base, B_DARKEN_2_TINT)); - StrokeLine(bounds.LeftBottom(), bounds.RightBottom()); - bounds.bottom--; - uint32 flags = be_control_look->Flags(this); - uint32 borders = BControlLook::B_TOP_BORDER - | BControlLook::B_BOTTOM_BORDER; - be_control_look->DrawInactiveTab(this, bounds, updateRect, base, - flags, borders); - if (IsEnabled()) { - rgb_color button = tint_color(base, 1.07); - be_control_look->DrawButtonBackground(this, bounds, updateRect, - button, flags, 0); - } - - bounds.left = (bounds.left + bounds.right) / 2 - 6; - bounds.top = (bounds.top + bounds.bottom) / 2 - 6; - bounds.right = bounds.left + 12; - bounds.bottom = bounds.top + 12; - DrawSymbol(bounds, updateRect, base); - } - - virtual void DrawSymbol(BRect frame, const BRect& updateRect, - const rgb_color& base) - { - } -}; - - -class ScrollLeftTabButton : public TabButton { -public: - ScrollLeftTabButton(BMessage* message) - : TabButton(message) - { - } - - virtual void DrawSymbol(BRect frame, const BRect& updateRect, - const rgb_color& base) - { - be_control_look->DrawArrowShape(this, frame, updateRect, - base, BControlLook::B_LEFT_ARROW, 0, B_DARKEN_4_TINT); - } -}; - - -class ScrollRightTabButton : public TabButton { -public: - ScrollRightTabButton(BMessage* message) - : TabButton(message) - { - } - - virtual void DrawSymbol(BRect frame, const BRect& updateRect, - const rgb_color& base) - { - be_control_look->DrawArrowShape(this, frame, updateRect, - base, BControlLook::B_RIGHT_ARROW, 0, B_DARKEN_4_TINT); - } -}; - - -class NewTabButton : public TabButton { -public: - NewTabButton(BMessage* message) - : TabButton(message) - { - SetToolTip("New tab (Cmd-T)"); - } - - virtual BSize MinSize() - { - return BSize(18, 12); - } - - virtual void DrawSymbol(BRect frame, const BRect& updateRect, - const rgb_color& base) - { - SetHighColor(tint_color(base, B_DARKEN_4_TINT)); - float inset = 3; - frame.InsetBy(2, 2); - frame.top++; - frame.left++; - FillRoundRect(BRect(frame.left, frame.top + inset, - frame.right, frame.bottom - inset), 1, 1); - FillRoundRect(BRect(frame.left + inset, frame.top, - frame.right - inset, frame.bottom), 1, 1); - } -}; - - -class TabMenuTabButton : public TabButton { -public: - TabMenuTabButton(BMessage* message) - : TabButton(message) - { - } - - virtual BSize MinSize() - { - return BSize(18, 12); - } - - virtual void DrawSymbol(BRect frame, const BRect& updateRect, - const rgb_color& base) - { - be_control_look->DrawArrowShape(this, frame, updateRect, - base, BControlLook::B_DOWN_ARROW, 0, B_DARKEN_4_TINT); - } -}; - - // #pragma mark - TabManager @@ -1243,9 +623,11 @@ TabManager::TabManager(const BMessenger& target, BMessage* newTabMessage) fContainerView->SetLayout(fCardLayout); fTabContainerView = new TabContainerView(fController); - fTabContainerGroup = new BGroupView(B_HORIZONTAL); + fTabContainerGroup = new TabContainerGroup(fTabContainerView); fTabContainerGroup->GroupLayout()->SetInsets(0, 3, 0, 0); + fController->SetTabContainerGroup(fTabContainerGroup); + #if INTEGRATE_MENU_INTO_TAB_BAR fMenu = new BMenu("Menu"); BMenuBar* menuBar = new BMenuBar("Menu bar"); @@ -1256,8 +638,10 @@ TabManager::TabManager(const BMessenger& target, BMessage* newTabMessage) #endif fTabContainerGroup->GroupLayout()->AddView(fTabContainerView); -// fTabContainerGroup->GroupLayout()->AddView(new ScrollLeftTabButton(NULL), 0.0f); -// fTabContainerGroup->GroupLayout()->AddView(new ScrollRightTabButton(NULL), 0.0f); + fTabContainerGroup->AddScrollLeftButton(new ScrollLeftTabButton( + new BMessage(MSG_SCROLL_TABS_LEFT))); + fTabContainerGroup->AddScrollRightButton(new ScrollRightTabButton( + new BMessage(MSG_SCROLL_TABS_RIGHT))); NewTabButton* newTabButton = new NewTabButton(newTabMessage); newTabButton->SetTarget(be_app); fTabContainerGroup->GroupLayout()->AddView(newTabButton, 0.0f); diff --git a/src/apps/webpositive/WebTabView.h b/src/apps/webpositive/tabview/TabManager.h similarity index 97% rename from src/apps/webpositive/WebTabView.h rename to src/apps/webpositive/tabview/TabManager.h index 23ea4cc62f..04f86062a2 100644 --- a/src/apps/webpositive/WebTabView.h +++ b/src/apps/webpositive/tabview/TabManager.h @@ -40,6 +40,7 @@ class BBitmap; class BCardLayout; class BGroupView; class BMenu; +class TabContainerGroup; class TabContainerView; class TabManagerController; @@ -81,7 +82,7 @@ private: #if INTEGRATE_MENU_INTO_TAB_BAR BMenu* fMenu; #endif - BGroupView* fTabContainerGroup; + TabContainerGroup* fTabContainerGroup; TabContainerView* fTabContainerView; BView* fContainerView; BCardLayout* fCardLayout; diff --git a/src/apps/webpositive/tabview/TabView.cpp b/src/apps/webpositive/tabview/TabView.cpp new file mode 100644 index 0000000000..bc5e2a6703 --- /dev/null +++ b/src/apps/webpositive/tabview/TabView.cpp @@ -0,0 +1,366 @@ +/* + * Copyright (C) 2010 Rene Gollent + * Copyright (C) 2010 Stephan Aßmus + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "TabView.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "TabContainerView.h" + + +// #pragma mark - TabView + + +TabView::TabView() + : + fContainerView(NULL), + fLayoutItem(new TabLayoutItem(this)), + fLabel() +{ +} + + +TabView::~TabView() +{ + // The layout item is deleted for us by the layout which contains it. + if (!fContainerView) + delete fLayoutItem; +} + + +BSize +TabView::MinSize() +{ + BSize size(MaxSize()); + size.width = 100.0f; + return size; +} + + +BSize +TabView::PreferredSize() +{ + return MaxSize(); +} + + +BSize +TabView::MaxSize() +{ + float extra = be_control_look->DefaultLabelSpacing(); + float labelWidth = fContainerView->StringWidth(fLabel.String()) + 2 * extra; + labelWidth = min_c(300.0f, labelWidth); + return BSize(labelWidth, _LabelHeight() + extra); +} + + +void +TabView::Draw(BRect updateRect) +{ + BRect frame(fLayoutItem->Frame()); + if (fIsFront) { + // Extend the front tab outward left/right in order to merge + // the frames of adjacent tabs. + if (!fIsFirst) + frame.left--; + if (!fIsLast) + frame.right++; + + frame.bottom++; + } + DrawBackground(fContainerView, frame, updateRect, fIsFirst, fIsLast, + fIsFront); + if (fIsFront) { + frame.top += 3.0f; + if (!fIsFirst) + frame.left++; + if (!fIsLast) + frame.right--; + } else + frame.top += 6.0f; + float spacing = be_control_look->DefaultLabelSpacing(); + frame.InsetBy(spacing, spacing / 2); + DrawContents(fContainerView, frame, updateRect, fIsFirst, fIsLast, + fIsFront); +} + + +void +TabView::DrawBackground(BView* owner, BRect frame, const BRect& updateRect, + bool isFirst, bool isLast, bool isFront) +{ + rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); + uint32 borders = BControlLook::B_TOP_BORDER + | BControlLook::B_BOTTOM_BORDER; + if (isFirst) + borders |= BControlLook::B_LEFT_BORDER; + if (isLast) + borders |= BControlLook::B_RIGHT_BORDER; + if (isFront) { + be_control_look->DrawActiveTab(owner, frame, updateRect, base, + 0, borders); + } else { + be_control_look->DrawInactiveTab(owner, frame, updateRect, base, + 0, borders); + } +} + + +void +TabView::DrawContents(BView* owner, BRect frame, const BRect& updateRect, + bool isFirst, bool isLast, bool isFront) +{ + rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); + be_control_look->DrawLabel(owner, fLabel.String(), frame, updateRect, + base, 0, BAlignment(B_ALIGN_LEFT, B_ALIGN_MIDDLE)); +} + + +void +TabView::MouseDown(BPoint where, uint32 buttons) +{ + fContainerView->SelectTab(this); +} + + +void +TabView::MouseUp(BPoint where) +{ +} + + +void +TabView::MouseMoved(BPoint where, uint32 transit, const BMessage* dragMessage) +{ +} + + +void +TabView::SetIsFront(bool isFront) +{ + Update(fIsFirst, fIsLast, isFront); +} + + +bool +TabView::IsFront() const +{ + return fIsFront; +} + + +void +TabView::SetIsLast(bool isLast) +{ + Update(fIsFirst, isLast, fIsFront); +} + + +void +TabView::Update(bool isFirst, bool isLast, bool isFront) +{ + if (fIsFirst == isFirst && fIsLast == isLast && fIsFront == isFront) + return; + fIsFirst = isFirst; + fIsLast = isLast; + fIsFront = isFront; + + fLayoutItem->InvalidateContainer(); +} + + +void +TabView::SetContainerView(TabContainerView* containerView) +{ + fContainerView = containerView; +} + + +TabContainerView* +TabView::ContainerView() const +{ + return fContainerView; +} + + +BLayoutItem* +TabView::LayoutItem() const +{ + return fLayoutItem; +} + + +void +TabView::SetLabel(const char* label) +{ + if (fLabel == label) + return; + fLabel = label; + fLayoutItem->InvalidateLayout(); +} + + +const BString& +TabView::Label() const +{ + return fLabel; +} + + +BRect +TabView::Frame() const +{ + return fLayoutItem->Frame(); +} + + +float +TabView::_LabelHeight() const +{ + font_height fontHeight; + fContainerView->GetFontHeight(&fontHeight); + return ceilf(fontHeight.ascent) + ceilf(fontHeight.descent); +} + + +// #pragma mark - TabLayoutItem + + +TabLayoutItem::TabLayoutItem(TabView* parent) + : + fParent(parent), + fVisible(true) +{ +} + + +bool +TabLayoutItem::IsVisible() +{ + return fVisible; +} + + +void +TabLayoutItem::SetVisible(bool visible) +{ + if (fVisible == visible) + return; + + fVisible = visible; + + InvalidateContainer(); + fParent->ContainerView()->InvalidateLayout(); +} + + +BRect +TabLayoutItem::Frame() +{ + return fFrame; +} + + +void +TabLayoutItem::SetFrame(BRect frame) +{ + BRect dirty = fFrame; + fFrame = frame; + dirty = dirty | fFrame; + InvalidateContainer(dirty); +} + + +BView* +TabLayoutItem::View() +{ + return NULL; +} + + +BSize +TabLayoutItem::BaseMinSize() +{ + return fParent->MinSize(); +} + + +BSize +TabLayoutItem::BaseMaxSize() +{ + return fParent->MaxSize(); +} + + +BSize +TabLayoutItem::BasePreferredSize() +{ + return fParent->PreferredSize(); +} + + +BAlignment +TabLayoutItem::BaseAlignment() +{ + return BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT); +} + + +TabView* +TabLayoutItem::Parent() const +{ + return fParent; +} + + +void +TabLayoutItem::InvalidateContainer() +{ + InvalidateContainer(Frame()); +} + + +void +TabLayoutItem::InvalidateContainer(BRect frame) +{ + // Invalidate more than necessary, to help the TabContainerView + // redraw the parts outside any tabs... + frame.bottom++; + frame.right++; + fParent->ContainerView()->Invalidate(frame); +} diff --git a/src/apps/webpositive/tabview/TabView.h b/src/apps/webpositive/tabview/TabView.h new file mode 100644 index 0000000000..9f73c5a994 --- /dev/null +++ b/src/apps/webpositive/tabview/TabView.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2010 Stephan Aßmus + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef TAB_VIEW_H +#define TAB_VIEW_H + +#include +#include +#include + + +class BMessage; +class BView; +class TabContainerView; +class TabLayoutItem; + + +class TabView { +public: + TabView(); + virtual ~TabView(); + + virtual BSize MinSize(); + virtual BSize PreferredSize(); + virtual BSize MaxSize(); + + void Draw(BRect updateRect); + virtual void DrawBackground(BView* owner, BRect frame, + const BRect& updateRect, bool isFirst, + bool isLast, bool isFront); + virtual void DrawContents(BView* owner, BRect frame, + const BRect& updateRect, bool isFirst, + bool isLast, bool isFront); + + virtual void MouseDown(BPoint where, uint32 buttons); + virtual void MouseUp(BPoint where); + virtual void MouseMoved(BPoint where, uint32 transit, + const BMessage* dragMessage); + + void SetIsFront(bool isFront); + bool IsFront() const; + void SetIsLast(bool isLast); + virtual void Update(bool isFirst, bool isLast, + bool isFront); + + BLayoutItem* LayoutItem() const; + void SetContainerView( + TabContainerView* containerView); + TabContainerView* ContainerView() const; + + void SetLabel(const char* label); + const BString& Label() const; + + BRect Frame() const; + +private: + float _LabelHeight() const; + +private: + TabContainerView* fContainerView; + TabLayoutItem* fLayoutItem; + + BString fLabel; + + bool fIsFirst; + bool fIsLast; + bool fIsFront; +}; + + +class TabLayoutItem : public BAbstractLayoutItem { +public: + TabLayoutItem(TabView* parent); + + virtual bool IsVisible(); + virtual void SetVisible(bool visible); + + virtual BRect Frame(); + virtual void SetFrame(BRect frame); + + virtual BView* View(); + + virtual BSize BaseMinSize(); + virtual BSize BaseMaxSize(); + virtual BSize BasePreferredSize(); + virtual BAlignment BaseAlignment(); + + TabView* Parent() const; + void InvalidateContainer(); + void InvalidateContainer(BRect frame); +private: + TabView* fParent; + BRect fFrame; + bool fVisible; +}; + + + +#endif // TAB_VIEW_H