diff --git a/src/apps/webpositive/LauncherWindow.cpp b/src/apps/webpositive/LauncherWindow.cpp index af877d99ca..908c4a1582 100644 --- a/src/apps/webpositive/LauncherWindow.cpp +++ b/src/apps/webpositive/LauncherWindow.cpp @@ -77,8 +77,6 @@ enum { TEXT_HIDE_FIND_GROUP = 'hfnd', TEXT_FIND_NEXT = 'fndn', TEXT_FIND_PREVIOUS = 'fndp', - - CLOSE_TAB = 'ctab' }; using namespace WebCore; @@ -97,8 +95,7 @@ LauncherWindow::LauncherWindow(BRect frame, const BMessenger& downloadListener, B_AUTO_UPDATE_SIZE_LIMITS | B_ASYNCHRONOUS_CONTROLS) , m_downloadListener(downloadListener) { - m_tabView = new WebTabView("tabview", BMessenger(this)); - m_tabView->setTarget(BMessenger(this)); + m_tabManager = new TabManager(BMessenger(this)); if (toolbarPolicy == HaveToolbar) { // Menu @@ -196,6 +193,7 @@ LauncherWindow::LauncherWindow(BRect frame, const BMessenger& downloadListener, // Layout AddChild(BGroupLayoutBuilder(B_VERTICAL) .Add(m_menuBar) + .Add(m_tabManager->TabGroup()) .Add(BGridLayoutBuilder(kElementSpacing, kElementSpacing) .Add(m_BackButton, 0, 0) .Add(m_ForwardButton, 1, 0) @@ -204,8 +202,8 @@ LauncherWindow::LauncherWindow(BRect frame, const BMessenger& downloadListener, .Add(button, 4, 0) .SetInsets(kInsetSpacing, kInsetSpacing, kInsetSpacing, kInsetSpacing) ) -// .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER)) - .Add(m_tabView) + .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER)) + .Add(m_tabManager->ContainerView()) .Add(findGroup) .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER)) .Add(BGroupLayoutBuilder(B_HORIZONTAL, kElementSpacing) @@ -219,6 +217,7 @@ LauncherWindow::LauncherWindow(BRect frame, const BMessenger& downloadListener, m_url->MakeFocus(true); m_findGroup = layoutItemFor(findGroup); + m_tabGroup = layoutItemFor(m_tabManager->TabGroup()); } else { m_BackButton = 0; m_ForwardButton = 0; @@ -344,23 +343,28 @@ void LauncherWindow::MessageReceived(BMessage* message) be_app->PostMessage(message); break; - case CLOSE_TAB: { - if (m_tabView->CountTabs() > 1) - delete m_tabView->RemoveTab(m_tabView->Selection()); - else + case CLOSE_TAB: + if (m_tabManager->CountTabs() > 1) { + int32 index; + if (message->FindInt32("tab index", &index) != B_OK) + index = m_tabManager->SelectedTabIndex(); + delete m_tabManager->RemoveTab(index); + updateTabGroupVisibility(); + } else PostMessage(B_QUIT_REQUESTED); break; - } case TAB_CHANGED: { // This message may be received also when the last tab closed, i.e. with index == -1. - int32 index = -1; - message->FindInt32("index", &index); - BWebView* webView = dynamic_cast(m_tabView->ViewForTab(index)); + int32 index; + if (message->FindInt32("tab index", &index) != B_OK) + index = -1; + BWebView* webView = dynamic_cast(m_tabManager->ViewForTab(index)); + if (webView == CurrentWebView()) + break; SetCurrentWebView(webView); - BTab* tab = m_tabView->TabAt(index); - if (tab) - updateTitle(tab->Label()); + if (webView) + updateTitle(webView->MainFrameTitle()); else updateTitle(""); if (webView) { @@ -384,8 +388,8 @@ bool LauncherWindow::QuitRequested() // Iterate over all tabs to delete all BWebViews. // Do this here, so WebKit tear down happens earlier. - while (m_tabView->CountTabs() > 0) - delete m_tabView->RemoveTab(0L); + while (m_tabManager->CountTabs() > 0) + delete m_tabManager->RemoveTab(0L); SetCurrentWebView(0); BMessage message(WINDOW_CLOSED); @@ -432,21 +436,22 @@ void LauncherWindow::newTab(const BString& url, bool select, BWebView* webView) webView = new BWebView("web view"); webView->WebPage()->SetDownloadListener(m_downloadListener); - m_tabView->AddTab(webView); - m_tabView->TabAt(m_tabView->CountTabs() - 1)->SetLabel("New tab"); - // TODO: Remove when BTabView is fixed... - m_tabView->Invalidate(); + m_tabManager->AddTab(webView, "New tab"); if (url.Length()) webView->LoadURL(url.String()); if (select) { - m_tabView->Select(m_tabView->CountTabs() - 1); + m_tabManager->SelectTab(m_tabManager->CountTabs() - 1); SetCurrentWebView(webView); NavigationCapabilitiesChanged(false, false, false, webView); - if (m_url) + if (m_url) { + m_url->SetText(url.String()); m_url->MakeFocus(true); + } } + + updateTabGroupVisibility(); } // #pragma mark - Notification API @@ -577,10 +582,9 @@ void LauncherWindow::SetResizable(bool flag, BWebView* view) void LauncherWindow::TitleChanged(const BString& title, BWebView* view) { - for (int32 i = 0; i < m_tabView->CountTabs(); i++) { - if (m_tabView->ViewForTab(i) == view) { - m_tabView->TabAt(i)->SetLabel(title); - m_tabView->DrawTabs(); + for (int32 i = 0; i < m_tabManager->CountTabs(); i++) { + if (m_tabManager->ViewForTab(i) == view) { + m_tabManager->SetTabLabel(i, title); break; } } @@ -653,3 +657,11 @@ void LauncherWindow::updateTitle(const BString& title) windowTitle << "HaikuLauncher"; SetTitle(windowTitle.String()); } + +void LauncherWindow::updateTabGroupVisibility() +{ + if (Lock()) { + m_tabGroup->SetVisible(m_tabManager->CountTabs() > 1); + Unlock(); + } +} diff --git a/src/apps/webpositive/LauncherWindow.h b/src/apps/webpositive/LauncherWindow.h index 97b28f1123..e06837b48f 100644 --- a/src/apps/webpositive/LauncherWindow.h +++ b/src/apps/webpositive/LauncherWindow.h @@ -42,7 +42,7 @@ class BStatusBar; class BStringView; class BTextControl; class IconButton; -class WebTabView; +class TabManager; class BWebView; enum ToolbarPolicy { @@ -93,6 +93,7 @@ private: virtual void AuthenticationChallenge(BMessage* challenge); void updateTitle(const BString &title); + void updateTabGroupVisibility(); private: BMessenger m_downloadListener; @@ -106,9 +107,10 @@ private: BStringView* m_statusText; BStatusBar* m_loadingProgressBar; BLayoutItem* m_findGroup; + BLayoutItem* m_tabGroup; BTextControl* m_findTextControl; BCheckBox* m_findCaseSensitiveCheckBox; - WebTabView* m_tabView; + TabManager* m_tabManager; }; #endif // LauncherWindow_h diff --git a/src/apps/webpositive/WebTabView.cpp b/src/apps/webpositive/WebTabView.cpp index b4ead81c6c..cb93ada686 100644 --- a/src/apps/webpositive/WebTabView.cpp +++ b/src/apps/webpositive/WebTabView.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2010 Rene Gollent + * Copyright (C) 2010 Stephan Aßmus * * All rights reserved. * @@ -27,34 +28,990 @@ #include "WebTabView.h" -#include +#include "WebView.h" +#include +#include +#include +#include +#include +#include +#include -WebTabView::WebTabView(const char *name, const BMessenger& target) - : BTabView(name) - , m_target(target) + +class TabView; +class TabContainerView; + + +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; + +private: + TabView* fParent; + BRect fFrame; +}; + + +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); + 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 TabContainerView : public BGroupView { +public: + class Controller { + public: + virtual void TabSelected(int32 tabIndex) = 0; + virtual bool HasFrames() = 0; + virtual TabView* CreateTabView() = 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); + + void AddTab(const char* label, int32 index = -1); + void AddTab(TabView* tab, int32 index = -1); + TabView* RemoveTab(int32 index); + + 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; + +private: + TabView* fLastMouseEventTab; + bool fMouseDown; + TabView* fSelectedTab; + Controller* fController; +}; + + +// #pragma mark - TabContainerView + + +static const float kLeftTabInset = 4; + + +TabContainerView::TabContainerView(Controller* controller) + : + BGroupView(B_HORIZONTAL), + fLastMouseEventTab(NULL), + fMouseDown(false), + fSelectedTab(NULL), + fController(controller) { - SetBorder(B_NO_BORDER); - SetFlags(Flags() & ~B_FULL_UPDATE_ON_RESIZE); + SetFlags(Flags() | B_WILL_DRAW); + SetViewColor(B_TRANSPARENT_COLOR); + GroupLayout()->SetInsets(kLeftTabInset, 0, 0, 1); + GroupLayout()->AddItem(BSpaceLayoutItem::CreateGlue(), 0.0f); } -WebTabView::~WebTabView(void) + +TabContainerView::~TabContainerView() +{ + // TODO: Not so nice, that we take ownership of the controller. + delete fController; +} + + +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; + } + + // Draw empty area after last tab. + be_control_look->DrawInactiveTab(this, frame, updateRect, base, 0, borders); +} + + +void +TabContainerView::MouseDown(BPoint where) +{ + fMouseDown = true; + SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS); + if (fLastMouseEventTab) + fLastMouseEventTab->MouseDown(where); +} + + +void +TabContainerView::MouseUp(BPoint where) +{ + fMouseDown = false; + if (fLastMouseEventTab) + fLastMouseEventTab->MouseUp(where); +} + + +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::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); + } +} + +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); + + return removedTab; +} + + +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; +} + + +// #pragma mark - TabView + + +TabView::TabView() + : fContainerView(NULL) + , fLayoutItem(new TabLayoutItem(this)) + , fLabel() { } -void WebTabView::Select(int32 tab) +TabView::~TabView() { - BTabView::Select(tab); + // 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) +{ + 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 + + +class TabManagerController : public TabContainerView::Controller { +public: + TabManagerController(TabManager* manager); + + virtual void TabSelected(int32 index) + { + fManager->SelectTab(index); + } + + virtual bool HasFrames() + { + return false; + } + + virtual TabView* CreateTabView(); + + void CloseTab(int32 index); + +private: + TabManager* fManager; +}; + + +// #pragma mark - WebTabView + + +class WebTabView : public TabView { +public: + WebTabView(TabManagerController* controller); + + virtual BSize MaxSize(); + + virtual void DrawContents(BView* owner, BRect frame, const BRect& updateRect, + bool isFirst, bool isLast, bool isFront); + + virtual void MouseDown(BPoint where); + virtual void MouseUp(BPoint where); + virtual void MouseMoved(BPoint where, uint32 transit, + const BMessage* dragMessage); + +private: + BRect _CloseRectFrame(BRect frame) const; + +private: + TabManagerController* fController; + bool fOverCloseRect; + bool fClicked; +}; + + +WebTabView::WebTabView(TabManagerController* controller) + : + TabView(), + fController(controller), + fOverCloseRect(false), + fClicked(false) +{ +} + + +BSize +WebTabView::MaxSize() +{ + // Account for close button. + BSize size(TabView::MaxSize()); + size.width += size.height; + return size; +} + + +void +WebTabView::DrawContents(BView* owner, BRect frame, const BRect& updateRect, + bool isFirst, bool isLast, bool isFront) +{ + BRect closeRect = _CloseRectFrame(frame); + frame.right = closeRect.left - be_control_look->DefaultLabelSpacing(); + + closeRect.left = (closeRect.left + closeRect.right) / 2 - 3; + closeRect.right = closeRect.left + 6; + closeRect.top = (closeRect.top + closeRect.bottom) / 2 - 3; + closeRect.bottom = closeRect.top + 6; + + rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); + float tint = B_DARKEN_1_TINT; + if (!IsFront()) + tint *= 1.02; + + base = tint_color(base, tint); + + if (fOverCloseRect) + tint *= 1.2; + + if (fClicked && fOverCloseRect) { + BRect buttonRect(closeRect.InsetByCopy(-4, -4)); + be_control_look->DrawButtonFrame(owner, buttonRect, updateRect, + base, base, + BControlLook::B_ACTIVATED | BControlLook::B_BLEND_FRAME); + be_control_look->DrawButtonBackground(owner, buttonRect, updateRect, + base, BControlLook::B_ACTIVATED); + tint *= 1.2; + closeRect.OffsetBy(1, 1); + } + + base = tint_color(base, tint); + owner->SetHighColor(base); + owner->SetPenSize(2); + owner->StrokeLine(closeRect.LeftTop(), closeRect.RightBottom()); + owner->StrokeLine(closeRect.LeftBottom(), closeRect.RightTop()); + owner->SetPenSize(1); + + TabView::DrawContents(owner, frame, updateRect, isFirst, isLast, isFront); +} + + +void +WebTabView::MouseDown(BPoint where) +{ + BRect closeRect = _CloseRectFrame(Frame()); + if (!closeRect.Contains(where)) { + TabView::MouseDown(where); + return; + } + + fClicked = true; + ContainerView()->Invalidate(closeRect); +} + + +void +WebTabView::MouseUp(BPoint where) +{ + if (!fClicked) { + TabView::MouseUp(where); + return; + } + + fClicked = false; + + if (_CloseRectFrame(Frame()).Contains(where)) + fController->CloseTab(ContainerView()->IndexOf(this)); +} + + +void +WebTabView::MouseMoved(BPoint where, uint32 transit, + const BMessage* dragMessage) +{ + BRect closeRect = _CloseRectFrame(Frame()); + bool overCloseRect = closeRect.Contains(where); + if (overCloseRect != fOverCloseRect) { + fOverCloseRect = overCloseRect; + ContainerView()->Invalidate(closeRect); + } + + TabView::MouseMoved(where, transit, dragMessage); +} + + +BRect +WebTabView::_CloseRectFrame(BRect frame) const +{ + frame.left = frame.right - frame.Height(); + return frame; +} + + +// #pragma mark - TabManagerController + + +TabManagerController::TabManagerController(TabManager* manager) + : + fManager(manager) +{ +} + + +TabView* +TabManagerController::CreateTabView() +{ + return new WebTabView(this); +} + + +void +TabManagerController::CloseTab(int32 index) +{ + fManager->CloseTab(index); +} + + +// #pragma mark - TabManager + + + +TabManager::TabManager(const BMessenger& target) + : + fTarget(target) +{ + fContainerView = new BView("web view container", 0); + fCardLayout = new BCardLayout(); + fContainerView->SetLayout(fCardLayout); + + fTabContainerView = new TabContainerView(new TabManagerController(this)); + fTabContainerGroup = new BGroupView(B_HORIZONTAL); + fTabContainerGroup->GroupLayout()->SetInsets(0, 5, 0, 0); + fTabContainerGroup->GroupLayout()->AddView(fTabContainerView); +} + + +TabManager::~TabManager() +{ +} + + +void +TabManager::SetTarget(const BMessenger& target) +{ + fTarget = target; +} + + +const BMessenger& +TabManager::Target() const +{ + return fTarget; +} + + +BView* +TabManager::TabGroup() const +{ + return fTabContainerGroup; +} + + +BView* +TabManager::ContainerView() const +{ + return fContainerView; +} + + +BView* +TabManager::ViewForTab(int32 tabIndex) const +{ + BLayoutItem* item = fCardLayout->ItemAt(tabIndex); + if (item != NULL) + return item->View(); + return NULL; +} + + +void +TabManager::SelectTab(int32 tabIndex) +{ + fCardLayout->SetVisibleItem(tabIndex); + fTabContainerView->SelectTab(tabIndex); + BMessage message(TAB_CHANGED); - message.AddInt32("index", tab); - m_target.SendMessage(&message); + message.AddInt32("tab index", tabIndex); + fTarget.SendMessage(&message); } -const BMessenger& WebTabView::target() const + +int32 +TabManager::SelectedTabIndex() const { - return m_target; + return fCardLayout->VisibleIndex(); } -void WebTabView::setTarget(const BMessenger& target) + +void +TabManager::CloseTab(int32 tabIndex) { - m_target = target; + BMessage message(CLOSE_TAB); + message.AddInt32("tab index", tabIndex); + fTarget.SendMessage(&message); } + + +void +TabManager::AddTab(BView* view, const char* label, int32 index) +{ + fTabContainerView->AddTab(label, index); + fCardLayout->AddView(index, view); +} + + +BView* +TabManager::RemoveTab(int32 index) +{ + // It's important to remove the view first, since + // removing the tab will preliminary mess with the selected tab + // and then item count of card layout and tab container will not + // match yet. + BLayoutItem* item = fCardLayout->RemoveItem(index); + if (item == NULL) + return NULL; + + TabView* tab = fTabContainerView->RemoveTab(index); + delete tab; + + BView* view = item->View(); + delete item; + return view; +} + + +int32 +TabManager::CountTabs() const +{ + return fCardLayout->CountItems(); +} + + +void +TabManager::SetTabLabel(int32 tabIndex, const char* label) +{ + fTabContainerView->SetTabLabel(tabIndex, label); +} + diff --git a/src/apps/webpositive/WebTabView.h b/src/apps/webpositive/WebTabView.h index ac85a460c1..c97c899c7b 100644 --- a/src/apps/webpositive/WebTabView.h +++ b/src/apps/webpositive/WebTabView.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Rene Gollent + * Copyright (C) 2010 Stephan Aßmus * * All rights reserved. * @@ -25,28 +25,52 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef WebTabView_h -#define WebTabView_h +#ifndef TAB_MANAGER_H +#define TAB_MANAGER_H #include #include enum { - TAB_CHANGED = 'tcha' + TAB_CHANGED = 'tcha', + CLOSE_TAB = 'cltb' }; -class WebTabView : public BTabView { -public: - WebTabView(const char *name, const BMessenger& target); - virtual ~WebTabView(void); - virtual void Select(int32 tab); +class BCardLayout; +class BGroupView; +class TabContainerView; - const BMessenger& target() const; - void setTarget(const BMessenger& target); +class TabManager { +public: + TabManager(const BMessenger& target); + virtual ~TabManager(); + + void SetTarget(const BMessenger& target); + const BMessenger& Target() const; + + BView* TabGroup() const; + BView* ContainerView() const; + + BView* ViewForTab(int32 tabIndex) const; + + void SelectTab(int32 tabIndex); + int32 SelectedTabIndex() const; + void CloseTab(int32 tabIndex); + + void AddTab(BView* view, const char* label, + int32 index = -1); + BView* RemoveTab(int32 index); + int32 CountTabs() const; + + void SetTabLabel(int32 tabIndex, const char* label); private: - BMessenger m_target; + BGroupView* fTabContainerGroup; + TabContainerView* fTabContainerView; + BView* fContainerView; + BCardLayout* fCardLayout; + + BMessenger fTarget; }; -#define WebTabView_h -#endif // WebTabView_h +#endif // TAB_MANAGER_H