From bb2e9b06acb1783543442464561b7811892ee7e2 Mon Sep 17 00:00:00 2001 From: Clemens Zeidler Date: Mon, 25 Jul 2011 01:09:26 +0000 Subject: [PATCH] Add multi tab support to the default decorator as discussed on the mailing list. Windows can be stacked on top of one another. All windows using the same decorator instance. This makes it easier to draw the stacked tabs and makes it possible to design more fancy looks for stacked windows. This also helps to fix some issues in S&T, e.g. when activating one window in a stacked group all windows have to be activated to ensure that all tabs are on top. This causes some flickering in tracker. * Each Window has a reference counted WindowStack class which can be shared between stacked Windows. To keep the Decorator separated from Window there is another tab list in the Decorator now. The index of the stacked Window in the window stack is the same as the index of the tab in the Decorator. Properties like title or window focus are managed on a per tab basis now. This mean when you set the title in the Decorator you also have to specify the tab id which is equal to the window position in the stack. * When drawing the decorator its important that only the top window is doing the drawing. Also the top window drawing engine should be used. Actually that is only a problem directly after a window is stacked and the other window has still a none empty dirty region. In this case we clear the dirty region of this window and stop the drawing (the top window will draw everything). * Track if shifting of a tab is still ongoing, i.e. mouse still down. * The key event filter called the DesktopListener without holding the window write lock. This probably caused #7801 and #7796. * Commented out assert's in Window::SetScreen and Window::Screen. Add TODO because I'm not sure about the screen access. This breaks all existing decorators again, sorry guys! Haven't looked into any other then the default decorator (and the SAT decorator). Will not fix the others in the near future so go for it! Since applications should be able to rely on S&T features the other decorator must be able to handle multiple tabs as well. A simple solution would be to draw all title bars in multiple rows. That probably looks quit poorly. Think the better solution would be to draw a tab interface in the title bar, e.g. like in KDE. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@42478 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/servers/app/DecorManager.cpp | 9 +- src/servers/app/Decorator.cpp | 538 ++++++++----- src/servers/app/Decorator.h | 157 ++-- src/servers/app/DefaultDecorator.cpp | 835 ++++++++++++++------- src/servers/app/DefaultDecorator.h | 93 ++- src/servers/app/DefaultWindowBehaviour.cpp | 87 ++- src/servers/app/DefaultWindowBehaviour.h | 3 +- src/servers/app/Desktop.cpp | 29 +- src/servers/app/Desktop.h | 2 +- src/servers/app/DesktopListener.cpp | 4 +- src/servers/app/DesktopListener.h | 4 +- src/servers/app/Window.cpp | 494 ++++++++++-- src/servers/app/Window.h | 69 +- src/servers/app/WorkspacesView.cpp | 2 +- 14 files changed, 1666 insertions(+), 660 deletions(-) diff --git a/src/servers/app/DecorManager.cpp b/src/servers/app/DecorManager.cpp index 9b53e060e0..c06ef07dc7 100644 --- a/src/servers/app/DecorManager.cpp +++ b/src/servers/app/DecorManager.cpp @@ -65,15 +65,16 @@ DecorAddOn::AllocateDecorator(Desktop* desktop, DrawingEngine* engine, DesktopSettings settings(desktop); Decorator* decorator; decorator = _AllocateDecorator(settings, rect, look, flags); - desktop->UnlockSingleWindow(); - if (!decorator) return NULL; - decorator->SetDrawingEngine(engine); - decorator->SetTitle(title); + if (decorator->AddTab(title) == false) { + delete decorator; + return NULL; + } + decorator->SetDrawingEngine(engine); return decorator; } diff --git a/src/servers/app/Decorator.cpp b/src/servers/app/Decorator.cpp index 92dbd6045f..837a2f9e93 100644 --- a/src/servers/app/Decorator.cpp +++ b/src/servers/app/Decorator.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2010, Haiku. + * Copyright 2001-2011, Haiku. * Distributed under the terms of the MIT License. * * Authors: @@ -22,6 +22,22 @@ #include "DrawingEngine.h" +Decorator::Tab::Tab() + : + zoomRect(), + closeRect(), + minimizeRect(), + + closePressed(false), + zoomPressed(false), + minimizePressed(false), + isFocused(false), + title("") +{ + +} + + /*! \brief Constructor Does general initialization of internal data members and creates a colorset @@ -41,19 +57,12 @@ Decorator::Decorator(DesktopSettings& settings, BRect rect, window_look look, fLook(look), fFlags(flags), - fZoomRect(), - fCloseRect(), - fMinimizeRect(), - fTabRect(), + fTitleBarRect(), fFrame(rect), fResizeRect(), fBorderRect(), - fClosePressed(false), - fZoomPressed(false), - fMinimizePressed(false), - fIsFocused(false), - fTitle(""), + fTopTab(NULL), fFootprintValid(false) { @@ -71,6 +80,89 @@ Decorator::~Decorator() } +Decorator::Tab* +Decorator::AddTab(const char* title, int32 index, BRegion* updateRegion) +{ + Decorator::Tab* tab = _AllocateNewTab(); + if (tab == NULL) + return NULL; + tab->title = title; + + bool ok = false; + if (index >= 0) { + if (fTabList.AddItem(tab, index) == true) + ok = true; + } else if (fTabList.AddItem(tab) == true) + ok = true; + + if (ok == false) { + delete tab; + return NULL; + } + + if (_AddTab(index, updateRegion) == false) { + fTabList.RemoveItem(tab); + delete tab; + return NULL; + } + + if (fTopTab == NULL) + fTopTab = tab; + + _InvalidateFootprint(); + return tab; +} + + +bool +Decorator::RemoveTab(int32 index, BRegion* updateRegion) +{ + Decorator::Tab* tab = fTabList.RemoveItemAt(index); + if (tab == NULL) + return false; + + _RemoveTab(index, updateRegion); + + delete tab; + _InvalidateFootprint(); + return true; +} + + +bool +Decorator::MoveTab(int32 from, int32 to, bool isMoving, BRegion* updateRegion) +{ + if (_MoveTab(from, to, isMoving, updateRegion) == false) + return false; + if (fTabList.MoveItem(from, to) == false) { + // move the tab back + _MoveTab(from, to, isMoving, updateRegion); + return false; + } + return true; +} + + +int32 +Decorator::TabAt(const BPoint& where) const +{ + for (int32 i = 0; i < fTabList.CountItems(); i++) { + Decorator::Tab* tab = fTabList.ItemAt(i); + if (tab->tabRect.Contains(where)) + return i; + } + + return -1; +} + + +void +Decorator::SetTopTap(int32 tab) +{ + fTopTab = fTabList.ItemAt(tab); +} + + /*! \brief Assigns a display driver to the decorator \param driver A valid DrawingEngine object */ @@ -132,71 +224,6 @@ Decorator::SetLook(DesktopSettings& settings, window_look look, } -/*! \brief Sets the close button's value. - - Note that this does not update the button's look - it just updates the - internal button value - - \param is_down Whether the button is down or not -*/ -void -Decorator::SetClose(bool pressed) -{ - if (pressed != fClosePressed) { - fClosePressed = pressed; - DrawClose(); - } -} - -/*! \brief Sets the minimize button's value. - - Note that this does not update the button's look - it just updates the - internal button value - - \param is_down Whether the button is down or not -*/ -void -Decorator::SetMinimize(bool pressed) -{ - if (pressed != fMinimizePressed) { - fMinimizePressed = pressed; - DrawMinimize(); - } -} - -/*! \brief Sets the zoom button's value. - - Note that this does not update the button's look - it just updates the - internal button value - - \param is_down Whether the button is down or not -*/ -void -Decorator::SetZoom(bool pressed) -{ - if (pressed != fZoomPressed) { - fZoomPressed = pressed; - DrawZoom(); - } -} - - -/*! \brief Updates the value of the decorator title - \param string New title value -*/ -void -Decorator::SetTitle(const char* string, BRegion* updateRegion) -{ - fTitle.SetTo(string); - _SetTitle(string, updateRegion); - - _InvalidateFootprint(); - // the border very likely changed - - // TODO: redraw? -} - - /*! \brief Returns the decorator's window look \return the decorator's window look */ @@ -217,16 +244,6 @@ Decorator::Flags() const } -/*! \brief Returns the decorator's title - \return the decorator's title -*/ -const char* -Decorator::Title() const -{ - return fTitle.String(); -} - - /*! \brief Returns the decorator's border rectangle \return the decorator's border rectangle */ @@ -237,52 +254,149 @@ Decorator::BorderRect() const } +BRect +Decorator::TitleBarRect() const +{ + return fTitleBarRect; +} + + /*! \brief Returns the decorator's tab rectangle \return the decorator's tab rectangle */ BRect -Decorator::TabRect() const +Decorator::TabRect(int32 tab) const { - return fTabRect; + Decorator::Tab* decoratorTab = fTabList.ItemAt(tab); + if (decoratorTab == NULL) + return BRect(); + return decoratorTab->tabRect; } -/*! \brief Returns the value of the close button - \return true if down, false if up +BRect +Decorator::TabRect(Decorator::Tab* tab) const +{ + return tab->tabRect; +} + + +/*! \brief Sets the close button's value. + + Note that this does not update the button's look - it just updates the + internal button value + + \param is_down Whether the button is down or not */ -bool -Decorator::GetClose() -{ - return fClosePressed; -} - - -/*! \brief Returns the value of the minimize button - \return true if down, false if up -*/ -bool -Decorator::GetMinimize() -{ - return fMinimizePressed; -} - - -/*! \brief Returns the value of the zoom button - \return true if down, false if up -*/ -bool -Decorator::GetZoom() -{ - return fZoomPressed; -} - - void -Decorator::GetSizeLimits(int32* minWidth, int32* minHeight, int32* maxWidth, - int32* maxHeight) const +Decorator::SetClose(int32 tab, bool pressed) { + Decorator::Tab* decoratorTab = fTabList.ItemAt(tab); + if (decoratorTab == NULL) + return; + + if (pressed != decoratorTab->closePressed) { + decoratorTab->closePressed = pressed; + DrawClose(tab); + } } +/*! \brief Sets the minimize button's value. + + Note that this does not update the button's look - it just updates the + internal button value + + \param is_down Whether the button is down or not +*/ +void +Decorator::SetMinimize(int32 tab, bool pressed) +{ + Decorator::Tab* decoratorTab = fTabList.ItemAt(tab); + if (decoratorTab == NULL) + return; + + if (pressed != decoratorTab->minimizePressed) { + decoratorTab->minimizePressed = pressed; + DrawMinimize(tab); + } +} + +/*! \brief Sets the zoom button's value. + + Note that this does not update the button's look - it just updates the + internal button value + + \param is_down Whether the button is down or not +*/ +void +Decorator::SetZoom(int32 tab, bool pressed) +{ + Decorator::Tab* decoratorTab = fTabList.ItemAt(tab); + if (decoratorTab == NULL) + return; + + if (pressed != decoratorTab->zoomPressed) { + decoratorTab->zoomPressed = pressed; + DrawZoom(tab); + } +} + + +/*! \brief Updates the value of the decorator title + \param string New title value +*/ +void +Decorator::SetTitle(int32 tab, const char* string, BRegion* updateRegion) +{ + Decorator::Tab* decoratorTab = fTabList.ItemAt(tab); + if (decoratorTab == NULL) + return; + + decoratorTab->title.SetTo(string); + _SetTitle(decoratorTab, string, updateRegion); + + _InvalidateFootprint(); + // the border very likely changed + + // TODO: redraw? +} + + +/*! \brief Returns the decorator's title + \return the decorator's title +*/ +const char* +Decorator::Title(int32 tab) const +{ + Decorator::Tab* decoratorTab = fTabList.ItemAt(tab); + if (decoratorTab == NULL) + return ""; + return decoratorTab->title; +} + + +const char* +Decorator::Title(Decorator::Tab* tab) const +{ + return tab->title; +} + + +bool +Decorator::SetTabLocation(int32 tab, float location, bool isShifting, + BRegion* updateRegion) +{ + Decorator::Tab* decoratorTab = fTabList.ItemAt(tab); + if (decoratorTab == NULL) + return false; + if (_SetTabLocation(decoratorTab, location, isShifting, updateRegion)) { + _InvalidateFootprint(); + return true; + } + return false; +} + + /*! \brief Changes the focus value of the decorator @@ -292,14 +406,41 @@ Decorator::GetSizeLimits(int32* minWidth, int32* minHeight, int32* maxWidth, \param active True if active, false if not */ void -Decorator::SetFocus(bool active) +Decorator::SetFocus(int32 tab, bool active) { - fIsFocused = active; - _SetFocus(); + Decorator::Tab* decoratorTab = fTabList.ItemAt(tab); + if (decoratorTab == NULL) + return; + decoratorTab->isFocused = active; + _SetFocus(decoratorTab); // TODO: maybe it would be cleaner to handle the redraw here. } +bool +Decorator::IsFocus(int32 tab) const +{ + Decorator::Tab* decoratorTab = fTabList.ItemAt(tab); + if (decoratorTab == NULL) + return false; + return decoratorTab->isFocused; +}; + + +bool +Decorator::IsFocus(Decorator::Tab* tab) const +{ + return tab->isFocused; +} + + +void +Decorator::GetSizeLimits(int32* minWidth, int32* minHeight, int32* maxWidth, + int32* maxHeight) const +{ +} + + // #pragma mark - virtual methods @@ -339,14 +480,25 @@ Decorator::GetFootprint() - \c REGION_RIGHT_BOTTOM_CORNER The right-bottom corner. */ Decorator::Region -Decorator::RegionAt(BPoint where) const +Decorator::RegionAt(BPoint where, int32& tabIndex) const { - if (fCloseRect.Contains(where)) - return REGION_CLOSE_BUTTON; - if (fZoomRect.Contains(where)) - return REGION_ZOOM_BUTTON; - if (fTabRect.Contains(where)) - return REGION_TAB; + tabIndex = -1; + + for (int32 i = 0; i < fTabList.CountItems(); i++) { + Decorator::Tab* tab = fTabList.ItemAt(i); + if (tab->closeRect.Contains(where)) { + tabIndex = i; + return REGION_CLOSE_BUTTON; + } + if (tab->zoomRect.Contains(where)) { + tabIndex = i; + return REGION_ZOOM_BUTTON; + } + if (tab->tabRect.Contains(where)) { + tabIndex = i; + return REGION_TAB; + } + } return REGION_NONE; } @@ -411,17 +563,6 @@ Decorator::ResizeBy(BPoint offset, BRegion* dirty) } -bool -Decorator::SetTabLocation(float location, BRegion* updateRegion) -{ - if (_SetTabLocation(location, updateRegion)) { - _InvalidateFootprint(); - return true; - } - return false; -} - - /*! \brief Sets a specific highlight for a decorator region. Can be overridden by derived classes, but the base class version must be @@ -434,7 +575,8 @@ Decorator::SetTabLocation(float location, BRegion* updateRegion) \return \c true, if the highlight could be applied. */ bool -Decorator::SetRegionHighlight(Region region, uint8 highlight, BRegion* dirty) +Decorator::SetRegionHighlight(Region region, uint8 highlight, BRegion* dirty, + int32 tab) { int32 index = (int32)region - 1; if (index < 0 || index >= REGION_COUNT - 1) @@ -479,7 +621,7 @@ void Decorator::Draw(BRect rect) { _DrawFrame(rect & fFrame); - _DrawTab(rect & fTabRect); + _DrawTabs(rect & fTitleBarRect); } @@ -488,15 +630,7 @@ void Decorator::Draw() { _DrawFrame(fFrame); - _DrawTab(fTabRect); -} - - -//! Draws the close button -void -Decorator::DrawClose() -{ - _DrawClose(fCloseRect); + _DrawTabs(fTitleBarRect); } @@ -508,39 +642,63 @@ Decorator::DrawFrame() } -//! draws the minimize button +//! draws the tab, title, and buttons void -Decorator::DrawMinimize() +Decorator::DrawTab(int32 tabIndex) { - _DrawTab(fMinimizeRect); + Decorator::Tab* tab = fTabList.ItemAt(tabIndex); + if (tab == NULL) + return; + + _DrawTab(tab, tab->tabRect); + _DrawZoom(tab, tab->zoomRect); + _DrawMinimize(tab, tab->minimizeRect); + _DrawTitle(tab, tab->tabRect); + _DrawClose(tab, tab->closeRect); } -//! draws the tab, title, and buttons +//! Draws the close button void -Decorator::DrawTab() +Decorator::DrawClose(int32 tab) { - _DrawTab(fTabRect); - _DrawZoom(fZoomRect); - _DrawMinimize(fMinimizeRect); - _DrawTitle(fTabRect); - _DrawClose(fCloseRect); + Decorator::Tab* decoratorTab = fTabList.ItemAt(tab); + if (decoratorTab == NULL) + return; + _DrawClose(decoratorTab, decoratorTab->closeRect); +} + + +//! draws the minimize button +void +Decorator::DrawMinimize(int32 tab) +{ + Decorator::Tab* decoratorTab = fTabList.ItemAt(tab); + if (decoratorTab == NULL) + return; + _DrawTab(decoratorTab, decoratorTab->minimizeRect); } //! draws the title void -Decorator::DrawTitle() +Decorator::DrawTitle(int32 tab) { - _DrawTitle(fTabRect); + Decorator::Tab* decoratorTab = fTabList.ItemAt(tab); + if (decoratorTab == NULL) + return; + _DrawTitle(decoratorTab, decoratorTab->tabRect); } //! draws the zoom button void -Decorator::DrawZoom() +Decorator::DrawZoom(int32 tab) { - _DrawZoom(fZoomRect); + Decorator::Tab* decoratorTab = fTabList.ItemAt(tab); + if (decoratorTab == NULL) + return; + _DrawZoom(decoratorTab, decoratorTab->zoomRect); } @@ -582,6 +740,24 @@ Decorator::_DrawFrame(BRect rect) } + +void +Decorator::_DrawTabs(BRect rect) +{ + Decorator::Tab* focusTab = NULL; + for (int32 i = 0; i < fTabList.CountItems(); i++) { + Decorator::Tab* tab = fTabList.ItemAt(i); + if (tab->isFocused) { + focusTab = tab; + continue; + } + _DrawTab(tab, rect); + } + if (focusTab != NULL) + _DrawTab(focusTab, rect); +} + + /*! \brief Actually draws the tab This function is called when the tab itself needs drawn. Other items, @@ -590,7 +766,7 @@ Decorator::_DrawFrame(BRect rect) \param rect Area of the tab to update */ void -Decorator::_DrawTab(BRect rect) +Decorator::_DrawTab(Decorator::Tab* tab, BRect rect) { } @@ -603,7 +779,7 @@ Decorator::_DrawTab(BRect rect) \param rect Area of the button to update */ void -Decorator::_DrawClose(BRect rect) +Decorator::_DrawClose(Decorator::Tab* tab, BRect rect) { } @@ -618,7 +794,7 @@ Decorator::_DrawClose(BRect rect) \param rect area of the title to update */ void -Decorator::_DrawTitle(BRect rect) +Decorator::_DrawTitle(Decorator::Tab* tab, BRect rect) { } @@ -631,7 +807,7 @@ Decorator::_DrawTitle(BRect rect) \param rect Area of the button to update */ void -Decorator::_DrawZoom(BRect rect) +Decorator::_DrawZoom(Decorator::Tab* tab, BRect rect) { } @@ -644,14 +820,22 @@ Decorator::_DrawZoom(BRect rect) \param rect Area of the button to update */ void -Decorator::_DrawMinimize(BRect rect) +Decorator::_DrawMinimize(Decorator::Tab* tab, BRect rect) { } +bool +Decorator::_SetTabLocation(Decorator::Tab* tab, float location, bool isShifting, + BRegion* /*updateRegion*/) +{ + return false; +} + + //! Hook function called when the decorator changes focus void -Decorator::_SetFocus() +Decorator::_SetFocus(Decorator::Tab* tab) { } @@ -680,11 +864,15 @@ Decorator::_SetFlags(uint32 flags, BRegion* updateRegion) void Decorator::_MoveBy(BPoint offset) { - fZoomRect.OffsetBy(offset); - fCloseRect.OffsetBy(offset); - fMinimizeRect.OffsetBy(offset); - fMinimizeRect.OffsetBy(offset); - fTabRect.OffsetBy(offset); + for (int32 i = 0; i < fTabList.CountItems(); i++) { + Decorator::Tab* tab = fTabList.ItemAt(i); + + tab->zoomRect.OffsetBy(offset); + tab->closeRect.OffsetBy(offset); + tab->minimizeRect.OffsetBy(offset); + tab->tabRect.OffsetBy(offset); + } + fTitleBarRect.OffsetBy(offset); fFrame.OffsetBy(offset); fResizeRect.OffsetBy(offset); fBorderRect.OffsetBy(offset); diff --git a/src/servers/app/Decorator.h b/src/servers/app/Decorator.h index d02f2f360d..66451ec334 100644 --- a/src/servers/app/Decorator.h +++ b/src/servers/app/Decorator.h @@ -1,5 +1,5 @@ /* - * Copyright 2001-2010, Haiku. + * Copyright 2001-2011, Haiku. * Distributed under the terms of the MIT License. * * Authors: @@ -27,6 +27,25 @@ class BRegion; class Decorator { public: + class Tab { + public: + Tab(); + virtual ~Tab() {} + + BRect zoomRect; + BRect closeRect; + BRect minimizeRect; + BRect tabRect; + + bool closePressed : 1; + bool zoomPressed : 1; + bool minimizePressed : 1; + + bool isFocused : 1; + + BString title; + }; + enum Region { REGION_NONE, @@ -61,6 +80,19 @@ public: window_look look, uint32 flags); virtual ~Decorator(); + virtual Decorator::Tab* AddTab(const char* title, int32 index = -1, + BRegion* updateRegion = NULL); + virtual bool RemoveTab(int32 index, + BRegion* updateRegion = NULL); + virtual bool MoveTab(int32 from, int32 to, bool isMoving, + BRegion* updateRegion = NULL); + virtual int32 TabAt(const BPoint& where) const; + Decorator::Tab* TabAt(int32 index) + { return fTabList.ItemAt(index); } + int32 CountTabs() const + { return fTabList.CountItems(); } + void SetTopTap(int32 tab); + void SetDrawingEngine(DrawingEngine *driver); inline DrawingEngine* GetDrawingEngine() const { return fDrawingEngine; } @@ -72,52 +104,50 @@ public: void SetFlags(uint32 flags, BRegion* updateRegion = NULL); - void SetClose(bool pressed); - void SetMinimize(bool pressed); - void SetZoom(bool pressed); - - void SetTitle(const char* string, - BRegion* updateRegion = NULL); - window_look Look() const; uint32 Flags() const; - const char* Title() const; - BRect BorderRect() const; - BRect TabRect() const; + BRect TitleBarRect() const; + BRect TabRect(int32 tab) const; + BRect TabRect(Decorator::Tab* tab) const; - bool GetClose(); - bool GetMinimize(); - bool GetZoom(); + void SetClose(int32 tab, bool pressed); + void SetMinimize(int32 tab, bool pressed); + void SetZoom(int32 tab, bool pressed); + + const char* Title(int32 tab) const; + const char* Title(Decorator::Tab* tab) const; + void SetTitle(int32 tab, const char* string, + BRegion* updateRegion = NULL); + + void SetFocus(int32 tab, bool focussed); + bool IsFocus(int32 tab) const; + bool IsFocus(Decorator::Tab* tab) const; + + /*! \return true if tab location updated, false if out of bounds + or unsupported */ + bool SetTabLocation(int32 tab, float location, + bool isShifting, BRegion* updateRegion = NULL); + virtual float TabLocation(int32 tab) const + { return 0.0; } + + virtual Region RegionAt(BPoint where, int32& tab) const; virtual void GetSizeLimits(int32* minWidth, int32* minHeight, int32* maxWidth, int32* maxHeight) const; - void SetFocus(bool focussed); - bool IsFocus() - { return fIsFocused; }; - const BRegion& GetFootprint(); - virtual Region RegionAt(BPoint where) const; - void MoveBy(float x, float y); void MoveBy(BPoint offset); void ResizeBy(float x, float y, BRegion* dirty); void ResizeBy(BPoint offset, BRegion* dirty); - /*! \return true if tab location updated, false if out of bounds - or unsupported - */ - bool SetTabLocation(float location, - BRegion* /*updateRegion*/ = NULL); - virtual float TabLocation() const - { return 0.0; } - virtual bool SetRegionHighlight(Region region, uint8 highlight, - BRegion* dirty); - inline uint8 RegionHighlight(Region region) const; + BRegion* dirty, int32 tab = -1); + inline uint8 RegionHighlight(Region region, + int32 tab = -1) const; bool SetSettings(const BMessage& settings, BRegion* updateRegion = NULL); @@ -125,30 +155,39 @@ public: virtual void Draw(BRect rect); virtual void Draw(); - virtual void DrawClose(); + virtual void DrawClose(int32 tab); + virtual void DrawMinimize(int32 tab); + virtual void DrawTab(int32 tab); + virtual void DrawTitle(int32 tab); + virtual void DrawZoom(int32 tab); virtual void DrawFrame(); - virtual void DrawMinimize(); - virtual void DrawTab(); - virtual void DrawTitle(); - virtual void DrawZoom(); rgb_color UIColor(color_which which); virtual void ExtendDirtyRegion(Region region, BRegion& dirty); protected: - int32 _TitleWidth() const - { return fTitle.CountChars(); } - virtual void _DoLayout(); virtual void _DrawFrame(BRect rect); - virtual void _DrawTab(BRect rect); - virtual void _DrawClose(BRect rect); - virtual void _DrawTitle(BRect rect); - virtual void _DrawZoom(BRect rect); - virtual void _DrawMinimize(BRect rect); + virtual void _DrawTabs(BRect rect); + virtual void _DrawTab(Decorator::Tab* tab, BRect rect); + virtual void _DrawClose(Decorator::Tab* tab, BRect rect); + virtual void _DrawTitle(Decorator::Tab* tab, BRect rect); + virtual void _DrawZoom(Decorator::Tab* tab, BRect rect); + virtual void _DrawMinimize(Decorator::Tab* tab, BRect rect); + + virtual Decorator::Tab* _AllocateNewTab() = 0; + + virtual void _SetTitle(Decorator::Tab* tab, const char* string, + BRegion* updateRegion = NULL) = 0; + int32 _TitleWidth(Decorator::Tab* tab) const + { return tab->title.CountChars(); } + + virtual bool _SetTabLocation(Decorator::Tab* tab, float location, + bool isShifting, BRegion* updateRegion = NULL); + virtual void _SetFocus(Decorator::Tab* tab); virtual void _FontsChanged(DesktopSettings& settings, BRegion* updateRegion = NULL); @@ -157,20 +196,19 @@ protected: virtual void _SetFlags(uint32 flags, BRegion* updateRegion = NULL); - virtual void _SetTitle(const char* string, - BRegion* updateRegion = NULL) = 0; - - virtual void _SetFocus(); virtual void _MoveBy(BPoint offset); virtual void _ResizeBy(BPoint offset, BRegion* dirty) = 0; - virtual bool _SetTabLocation(float location, - BRegion* /*updateRegion*/ = NULL) - { return false; } - virtual bool _SetSettings(const BMessage& settings, BRegion* updateRegion = NULL); + virtual bool _AddTab(int32 index = -1, + BRegion* updateRegion = NULL) = 0; + virtual bool _RemoveTab(int32 index, + BRegion* updateRegion = NULL) = 0; + virtual bool _MoveTab(int32 from, int32 to, bool isMoving, + BRegion* updateRegion = NULL) = 0; + virtual void _GetFootprint(BRegion *region); void _InvalidateFootprint(); @@ -180,23 +218,14 @@ protected: window_look fLook; uint32 fFlags; - BRect fZoomRect; - BRect fCloseRect; - BRect fMinimizeRect; - BRect fTabRect; + BRect fTitleBarRect; BRect fFrame; BRect fResizeRect; BRect fBorderRect; + Decorator::Tab* fTopTab; + BObjectList fTabList; private: - bool fClosePressed : 1; - bool fZoomPressed : 1; - bool fMinimizePressed : 1; - - bool fIsFocused : 1; - - BString fTitle; - BRegion fFootprint; bool fFootprintValid : 1; @@ -205,7 +234,7 @@ private: uint8 -Decorator::RegionHighlight(Region region) const +Decorator::RegionHighlight(Region region, int32 tab) const { int32 index = (int32)region - 1; return index >= 0 && index < REGION_COUNT - 1 diff --git a/src/servers/app/DefaultDecorator.cpp b/src/servers/app/DefaultDecorator.cpp index ea6840e85a..fe0fb01dad 100644 --- a/src/servers/app/DefaultDecorator.cpp +++ b/src/servers/app/DefaultDecorator.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2010, Haiku, Inc. + * Copyright 2001-2011, Haiku, Inc. * Distributed under the terms of the MIT License. * * Authors: @@ -18,10 +18,12 @@ #include "DefaultDecorator.h" #include +#include #include #include #include +#include #include #include #include @@ -45,6 +47,18 @@ #endif +DefaultDecorator::Tab::Tab() + : + tabOffset(0), + tabLocation(0.0), + isHighlighted(false) +{ + closeBitmaps[0] = closeBitmaps[1] = closeBitmaps[2] = closeBitmaps[3] + = zoomBitmaps[0] = zoomBitmaps[1] = zoomBitmaps[2] = zoomBitmaps[3] + = NULL; +} + + static const float kBorderResizeLength = 22.0; static const float kResizeKnobSize = 18.0; @@ -107,19 +121,11 @@ DefaultDecorator::DefaultDecorator(DesktopSettings& settings, BRect rect, kNonFocusTabColorShadow(tint_color(kNonFocusTabColor, (B_DARKEN_1_TINT + B_NO_TINT) / 2)), kNonFocusTextColor(settings.UIColor(B_WINDOW_INACTIVE_TEXT_COLOR)), - fTabOffset(0), - fTabLocation(0.0) + + fOldMovingTab(0, 0, -1, -1) { _UpdateFont(settings); - fCloseBitmaps[0] = fCloseBitmaps[1] = fCloseBitmaps[2] = fCloseBitmaps[3] - = fZoomBitmaps[0] = fZoomBitmaps[1] = fZoomBitmaps[2] = fZoomBitmaps[3] - = NULL; - - // Set appropriate colors based on the current focus value. In this case, - // each decorator defaults to not having the focus. - _SetFocus(); - // Do initial decorator setup _DoLayout(); @@ -138,19 +144,36 @@ DefaultDecorator::~DefaultDecorator() } +float +DefaultDecorator::TabLocation(int32 tab) const +{ + DefaultDecorator::Tab* decoratorTab = _TabAt(tab); + if (decoratorTab == NULL) + return 0.; + return (float)decoratorTab->tabOffset; +} + + bool DefaultDecorator::GetSettings(BMessage* settings) const { - if (!fTabRect.IsValid()) + if (!fTitleBarRect.IsValid()) return false; - if (settings->AddRect("tab frame", fTabRect) != B_OK) + if (settings->AddRect("tab frame", fTitleBarRect) != B_OK) return false; if (settings->AddFloat("border width", fBorderWidth) != B_OK) return false; - return settings->AddFloat("tab location", (float)fTabOffset) == B_OK; + // TODO only add the location of the tab of the window who requested the + // settings + for (int32 i = 0; i < fTabList.CountItems(); i++) { + DefaultDecorator::Tab* tab = _TabAt(i); + if (settings->AddFloat("tab location", (float)tab->tabOffset) != B_OK) + return false; + } + return true; } @@ -168,7 +191,7 @@ DefaultDecorator::Draw(BRect update) fDrawingEngine->SetDrawState(&fDrawState); _DrawFrame(update); - _DrawTab(update); + _DrawTabs(update); } @@ -180,7 +203,7 @@ DefaultDecorator::Draw() fDrawingEngine->SetDrawState(&fDrawState); _DrawFrame(BRect(fTopBorder.LeftTop(), fBottomBorder.RightBottom())); - _DrawTab(fTabRect); + _DrawTabs(fTitleBarRect); } @@ -188,9 +211,12 @@ void DefaultDecorator::GetSizeLimits(int32* minWidth, int32* minHeight, int32* maxWidth, int32* maxHeight) const { - if (fTabRect.IsValid()) { + float minTabSize = 0; + if (CountTabs() > 0) + minTabSize = _TabAt(0)->minTabSize; + if (fTitleBarRect.IsValid()) { *minWidth = (int32)roundf(max_c(*minWidth, - fMinTabSize - 2 * fBorderWidth)); + minTabSize - 2 * fBorderWidth)); } if (fResizeRect.IsValid()) { *minHeight = (int32)roundf(max_c(*minHeight, @@ -200,10 +226,10 @@ DefaultDecorator::GetSizeLimits(int32* minWidth, int32* minHeight, Decorator::Region -DefaultDecorator::RegionAt(BPoint where) const +DefaultDecorator::RegionAt(BPoint where, int32& tab) const { // Let the base class version identify hits of the buttons and the tab. - Region region = Decorator::RegionAt(where); + Region region = Decorator::RegionAt(where, tab); if (region != REGION_NONE) return region; @@ -245,24 +271,28 @@ DefaultDecorator::RegionAt(BPoint where) const bool DefaultDecorator::SetRegionHighlight(Region region, uint8 highlight, - BRegion* dirty) + BRegion* dirty, int32 tabIndex) { - // Invalidate the bitmap caches for the close/zoom button, when the - // highlight changes. - switch (region) { - case REGION_CLOSE_BUTTON: - if (highlight != RegionHighlight(region)) - memset(&fCloseBitmaps, 0, sizeof(fCloseBitmaps)); - break; - case REGION_ZOOM_BUTTON: - if (highlight != RegionHighlight(region)) - memset(&fZoomBitmaps, 0, sizeof(fZoomBitmaps)); - break; - default: - break; + DefaultDecorator::Tab* tab = _TabAt(tabIndex); + if (tab != NULL) { + tab->isHighlighted = highlight != 0; + // Invalidate the bitmap caches for the close/zoom button, when the + // highlight changes. + switch (region) { + case REGION_CLOSE_BUTTON: + if (highlight != RegionHighlight(region)) + memset(&tab->closeBitmaps, 0, sizeof(tab->closeBitmaps)); + break; + case REGION_ZOOM_BUTTON: + if (highlight != RegionHighlight(region)) + memset(&tab->zoomBitmaps, 0, sizeof(tab->zoomBitmaps)); + break; + default: + break; + } } - return Decorator::SetRegionHighlight(region, highlight, dirty); + return Decorator::SetRegionHighlight(region, highlight, dirty, tabIndex); } @@ -271,17 +301,19 @@ DefaultDecorator::ExtendDirtyRegion(Region region, BRegion& dirty) { switch (region) { case REGION_TAB: - dirty.Include(fTabRect); + dirty.Include(fTitleBarRect); break; case REGION_CLOSE_BUTTON: if ((fFlags & B_NOT_CLOSABLE) == 0) - dirty.Include(fCloseRect); + for (int32 i = 0; i < fTabList.CountItems(); i++) + dirty.Include(fTabList.ItemAt(i)->closeRect); break; case REGION_ZOOM_BUTTON: if ((fFlags & B_NOT_ZOOMABLE) == 0) - dirty.Include(fZoomRect); + for (int32 i = 0; i < fTabList.CountItems(); i++) + dirty.Include(fTabList.ItemAt(i)->zoomRect); break; case REGION_LEFT_BORDER: @@ -335,8 +367,8 @@ DefaultDecorator::BorderWidth() float DefaultDecorator::TabHeight() { - if (fTabRect.IsValid()) - return fTabRect.Height(); + if (fTitleBarRect.IsValid()) + return fTitleBarRect.Height(); return BorderWidth(); } @@ -374,77 +406,6 @@ DefaultDecorator::_DoLayout() fBorderWidth = 0; } - // calculate our tab rect - if (hasTab) { - // distance from one item of the tab bar to another. - // In this case the text and close/zoom rects - fTextOffset = (fLook == B_FLOATING_WINDOW_LOOK - || fLook == kLeftTitledWindowLook) ? 10 : 18; - - font_height fontHeight; - fDrawState.Font().GetHeight(fontHeight); - - if (fLook != kLeftTitledWindowLook) { - fTabRect.Set(fFrame.left - fBorderWidth, - fFrame.top - fBorderWidth - - ceilf(fontHeight.ascent + fontHeight.descent + 7.0), - ((fFrame.right - fFrame.left) < 35.0 ? - fFrame.left + 35.0 : fFrame.right) + fBorderWidth, - fFrame.top - fBorderWidth); - } else { - fTabRect.Set(fFrame.left - fBorderWidth - - ceilf(fontHeight.ascent + fontHeight.descent + 5.0), - fFrame.top - fBorderWidth, fFrame.left - fBorderWidth, - fFrame.bottom + fBorderWidth); - } - - // format tab rect for a floating window - make the rect smaller - if (fLook == B_FLOATING_WINDOW_LOOK) { - fTabRect.InsetBy(0, 2); - fTabRect.OffsetBy(0, 2); - } - - float offset; - float size; - float inset; - _GetButtonSizeAndOffset(fTabRect, &offset, &size, &inset); - - // fMinTabSize contains just the room for the buttons - fMinTabSize = inset * 2 + fTextOffset; - if ((fFlags & B_NOT_CLOSABLE) == 0) - fMinTabSize += offset + size; - if ((fFlags & B_NOT_ZOOMABLE) == 0) - fMinTabSize += offset + size; - - // fMaxTabSize contains fMinWidth + the width required for the title - fMaxTabSize = fDrawingEngine - ? ceilf(fDrawingEngine->StringWidth(Title(), strlen(Title()), - fDrawState.Font())) : 0.0; - if (fMaxTabSize > 0.0) - fMaxTabSize += fTextOffset; - fMaxTabSize += fMinTabSize; - - float tabSize = (fLook != kLeftTitledWindowLook - ? fFrame.Width() : fFrame.Height()) + fBorderWidth * 2; - if (tabSize < fMinTabSize) - tabSize = fMinTabSize; - if (tabSize > fMaxTabSize) - tabSize = fMaxTabSize; - - // layout buttons and truncate text - if (fLook != kLeftTitledWindowLook) - fTabRect.right = fTabRect.left + tabSize; - else - fTabRect.bottom = fTabRect.top + tabSize; - } else { - // no tab - fMinTabSize = 0.0; - fMaxTabSize = 0.0; - fTabRect.Set(0.0, 0.0, -1.0, -1.0); - fCloseRect.Set(0.0, 0.0, -1.0, -1.0); - fZoomRect.Set(0.0, 0.0, -1.0, -1.0); - } - // calculate left/top/right/bottom borders if (fBorderWidth > 0) { // NOTE: no overlapping, the left and right border rects @@ -479,21 +440,205 @@ DefaultDecorator::_DoLayout() } if (hasTab) { - // make sure fTabOffset is within limits and apply it to - // the fTabRect - if (fTabLocation != 0.0 - && fTabOffset > (fRightBorder.right - fLeftBorder.left - - fTabRect.Width())) - fTabOffset = uint32(fRightBorder.right - fLeftBorder.left - - fTabRect.Width()); - fTabRect.OffsetBy(fTabOffset, 0); - - // finally, layout the buttons and text within the tab rect - _LayoutTabItems(fTabRect); + _DoTabLayout(); + return; + } else { + // no tab + fTitleBarRect.Set(0.0, 0.0, -1.0, -1.0); } } +void +DefaultDecorator::_DoTabLayout() +{ + float tabPosition = 0; + if (fTabList.CountItems() == 1) + tabPosition = _TabAt(0)->tabOffset; + float sumTabWidth = 0; + // calculate our tab rect + for (int32 i = 0; i < fTabList.CountItems(); i++) { + DefaultDecorator::Tab* tab = _TabAt(i); + + BRect& tabRect = tab->tabRect; + // distance from one item of the tab bar to another. + // In this case the text and close/zoom rects + tab->textOffset = _DefaultTextOffset(); + + font_height fontHeight; + fDrawState.Font().GetHeight(fontHeight); + + if (fLook != kLeftTitledWindowLook) { + tabRect.Set(fFrame.left - fBorderWidth, + fFrame.top - fBorderWidth + - ceilf(fontHeight.ascent + fontHeight.descent + 7.0), + ((fFrame.right - fFrame.left) < 35.0 ? + fFrame.left + 35.0 : fFrame.right) + fBorderWidth, + fFrame.top - fBorderWidth); + } else { + tabRect.Set(fFrame.left - fBorderWidth + - ceilf(fontHeight.ascent + fontHeight.descent + 5.0), + fFrame.top - fBorderWidth, fFrame.left - fBorderWidth, + fFrame.bottom + fBorderWidth); + } + + // format tab rect for a floating window - make the rect smaller + if (fLook == B_FLOATING_WINDOW_LOOK) { + tabRect.InsetBy(0, 2); + tabRect.OffsetBy(0, 2); + } + + float offset; + float size; + float inset; + _GetButtonSizeAndOffset(tabRect, &offset, &size, &inset); + + // tab->minTabSize contains just the room for the buttons + tab->minTabSize = inset * 2 + tab->textOffset; + if ((fFlags & B_NOT_CLOSABLE) == 0) + tab->minTabSize += offset + size; + if ((fFlags & B_NOT_ZOOMABLE) == 0) + tab->minTabSize += offset + size; + + // tab->maxTabSize contains tab->minTabSize + the width required for the + // title + tab->maxTabSize = fDrawingEngine + ? ceilf(fDrawingEngine->StringWidth(Title(tab), strlen(Title(tab)), + fDrawState.Font())) : 0.0; + if (tab->maxTabSize > 0.0) + tab->maxTabSize += tab->textOffset; + tab->maxTabSize += tab->minTabSize; + + float tabSize = (fLook != kLeftTitledWindowLook + ? fFrame.Width() : fFrame.Height()) + fBorderWidth * 2; + if (tabSize < tab->minTabSize) + tabSize = tab->minTabSize; + if (tabSize > tab->maxTabSize) + tabSize = tab->maxTabSize; + + // layout buttons and truncate text + if (fLook != kLeftTitledWindowLook) + tabRect.right = tabRect.left + tabSize; + else + tabRect.bottom = tabRect.top + tabSize; + + // make sure fTabOffset is within limits and apply it to + // the tabRect + if (tab->tabLocation != 0.0 + && tab->tabOffset > (fRightBorder.right - fLeftBorder.left + - tabRect.Width())) { + tab->tabOffset = uint32(fRightBorder.right - fLeftBorder.left + - tabRect.Width()); + } + tab->tabOffset = (uint32)tabPosition; + tabRect.OffsetBy(tab->tabOffset, 0); + tabPosition += tabRect.Width(); + + sumTabWidth += tabRect.Width(); + } + + float windowWidth = fFrame.Width() + 2 * fBorderWidth; + if (CountTabs() > 1 && sumTabWidth > windowWidth) + _DistributeTabSize(sumTabWidth - windowWidth); + + // finally, layout the buttons and text within the tab rect + for (int32 i = 0; i < fTabList.CountItems(); i++) { + Decorator::Tab* tab = fTabList.ItemAt(i); + + if (i == 0) + fTitleBarRect = tab->tabRect; + else + fTitleBarRect = fTitleBarRect | tab->tabRect; + + _LayoutTabItems(tab, tab->tabRect); + } + fTabsRegion = fTitleBarRect; +} + + +static bool +int_equal(float x, float y) +{ + return abs(x - y) <= 1; +} + + +void +DefaultDecorator::_DistributeTabSize(float delta) +{ + ASSERT(CountTabs() > 1); + + float maxTabSize = 0; + float secMaxTabSize = 0; + int32 nTabsWithMaxSize = 0; + for (int32 i = 0; i < fTabList.CountItems(); i++) { + Decorator::Tab* tab = fTabList.ItemAt(i); + float tabWidth = tab->tabRect.Width(); + if (int_equal(maxTabSize, tabWidth)) { + nTabsWithMaxSize++; + continue; + } + if (maxTabSize < tabWidth) { + secMaxTabSize = maxTabSize; + maxTabSize = tabWidth; + nTabsWithMaxSize = 1; + } else if (secMaxTabSize <= tabWidth) + secMaxTabSize = tabWidth; + } + + float minus = ceil(std::min(maxTabSize - secMaxTabSize, delta)); + delta -= minus; + minus /= nTabsWithMaxSize; + + Decorator::Tab* prevTab = NULL; + for (int32 i = 0; i < fTabList.CountItems(); i++) { + Decorator::Tab* tab = fTabList.ItemAt(i); + if (int_equal(maxTabSize, tab->tabRect.Width())) + tab->tabRect.right -= minus; + + if (prevTab) { + tab->tabRect.OffsetBy(prevTab->tabRect.right - tab->tabRect.left, + 0); + } + + prevTab = tab; + } + + if (delta > 0) { + _DistributeTabSize(delta); + return; + } + + // done + prevTab->tabRect.right = floor(fFrame.right + fBorderWidth); + + for (int32 i = 0; i < fTabList.CountItems(); i++) { + DefaultDecorator::Tab* tab = _TabAt(i); + tab->tabOffset = uint32(tab->tabRect.left - fLeftBorder.left); + } +} + + +Decorator::Tab* +DefaultDecorator::_AllocateNewTab() +{ + Decorator::Tab* tab = new(std::nothrow) DefaultDecorator::Tab; + if (tab == NULL) + return NULL; + // Set appropriate colors based on the current focus value. In this case, + // each decorator defaults to not having the focus. + _SetFocus(tab); + return tab; +} + + +DefaultDecorator::Tab* +DefaultDecorator::_TabAt(int32 index) const +{ + return static_cast(fTabList.ItemAt(index)); +} + + void DefaultDecorator::_DrawFrame(BRect invalid) { @@ -525,12 +670,14 @@ DefaultDecorator::_DrawFrame(BRect invalid) fDrawingEngine->StrokeLine(BPoint(r.left + i, r.top + i), BPoint(r.right - i, r.top + i), colors[i]); } - if (fTabRect.IsValid()) { + if (fTitleBarRect.IsValid()) { // grey along the bottom of the tab // (overwrites "white" from frame) fDrawingEngine->StrokeLine( - BPoint(fTabRect.left + 2, fTabRect.bottom + 1), - BPoint(fTabRect.right - 2, fTabRect.bottom + 1), + BPoint(fTitleBarRect.left + 2, + fTitleBarRect.bottom + 1), + BPoint(fTitleBarRect.right - 2, + fTitleBarRect.bottom + 1), colors[2]); } } @@ -581,13 +728,14 @@ DefaultDecorator::_DrawFrame(BRect invalid) fDrawingEngine->StrokeLine(BPoint(r.left + i, r.top + i), BPoint(r.right - i, r.top + i), colors[i * 2]); } - if (fTabRect.IsValid() && fLook != kLeftTitledWindowLook) { + if (fTitleBarRect.IsValid() && fLook != kLeftTitledWindowLook) { // grey along the bottom of the tab // (overwrites "white" from frame) fDrawingEngine->StrokeLine( - BPoint(fTabRect.left + 2, fTabRect.bottom + 1), - BPoint(fTabRect.right - 2, fTabRect.bottom + 1), - colors[2]); + BPoint(fTitleBarRect.left + 2, + fTitleBarRect.bottom + 1), + BPoint(fTitleBarRect.right - 2, + fTitleBarRect.bottom + 1), colors[2]); } } // left @@ -599,13 +747,14 @@ DefaultDecorator::_DrawFrame(BRect invalid) fDrawingEngine->StrokeLine(BPoint(r.left + i, r.top + i), BPoint(r.left + i, r.bottom - i), colors[i * 2]); } - if (fLook == kLeftTitledWindowLook && fTabRect.IsValid()) { + if (fLook == kLeftTitledWindowLook && fTitleBarRect.IsValid()) { // grey along the right side of the tab // (overwrites "white" from frame) fDrawingEngine->StrokeLine( - BPoint(fTabRect.right + 1, fTabRect.top + 2), - BPoint(fTabRect.right + 1, fTabRect.bottom - 2), - colors[2]); + BPoint(fTitleBarRect.right + 1, + fTitleBarRect.top + 2), + BPoint(fTitleBarRect.right + 1, + fTitleBarRect.bottom - 2), colors[2]); } } // bottom @@ -683,7 +832,7 @@ DefaultDecorator::_DrawFrame(BRect invalid) fDrawingEngine->StrokeLine(BPoint(x - 14, y - 14), BPoint(x - 1, y - 14), colors[1]); - if (!IsFocus()) + if (fTopTab && !IsFocus(fTopTab)) break; static const rgb_color kWhite @@ -729,86 +878,93 @@ DefaultDecorator::_DrawFrame(BRect invalid) void -DefaultDecorator::_DrawTab(BRect invalid) +DefaultDecorator::_DrawTab(Decorator::Tab* tab, BRect invalid) { STRACE(("_DrawTab(%.1f,%.1f,%.1f,%.1f)\n", - invalid.left, invalid.top, invalid.right, invalid.bottom)); + invalid.left, invalid.top, invalid.right, invalid.bottom)); + const BRect& tabRect = tab->tabRect; // If a window has a tab, this will draw it and any buttons which are // in it. - if (!fTabRect.IsValid() || !invalid.Intersects(fTabRect)) + if (!tabRect.IsValid() || !invalid.Intersects(tabRect)) return; ComponentColors colors; - _GetComponentColors(COMPONENT_TAB, colors); + _GetComponentColors(COMPONENT_TAB, colors, tab); // outer frame - fDrawingEngine->StrokeLine(fTabRect.LeftTop(), fTabRect.LeftBottom(), + fDrawingEngine->StrokeLine(tabRect.LeftTop(), tabRect.LeftBottom(), colors[COLOR_TAB_FRAME_LIGHT]); - fDrawingEngine->StrokeLine(fTabRect.LeftTop(), fTabRect.RightTop(), + fDrawingEngine->StrokeLine(tabRect.LeftTop(), tabRect.RightTop(), colors[COLOR_TAB_FRAME_LIGHT]); if (fLook != kLeftTitledWindowLook) { - fDrawingEngine->StrokeLine(fTabRect.RightTop(), fTabRect.RightBottom(), + fDrawingEngine->StrokeLine(tabRect.RightTop(), tabRect.RightBottom(), colors[COLOR_TAB_FRAME_DARK]); } else { - fDrawingEngine->StrokeLine(fTabRect.LeftBottom(), - fTabRect.RightBottom(), colors[COLOR_TAB_FRAME_DARK]); + fDrawingEngine->StrokeLine(tabRect.LeftBottom(), + tabRect.RightBottom(), colors[COLOR_TAB_FRAME_DARK]); } + float tabBotton = tabRect.bottom; + if (fTopTab != tab) + tabBotton -= 1; + // bevel - fDrawingEngine->StrokeLine(BPoint(fTabRect.left + 1, fTabRect.top + 1), - BPoint(fTabRect.left + 1, - fTabRect.bottom - (fLook == kLeftTitledWindowLook ? 1 : 0)), + fDrawingEngine->StrokeLine(BPoint(tabRect.left + 1, tabRect.top + 1), + BPoint(tabRect.left + 1, + tabBotton - (fLook == kLeftTitledWindowLook ? 1 : 0)), colors[COLOR_TAB_BEVEL]); - fDrawingEngine->StrokeLine(BPoint(fTabRect.left + 1, fTabRect.top + 1), - BPoint(fTabRect.right - (fLook == kLeftTitledWindowLook ? 0 : 1), - fTabRect.top + 1), + fDrawingEngine->StrokeLine(BPoint(tabRect.left + 1, tabRect.top + 1), + BPoint(tabRect.right - (fLook == kLeftTitledWindowLook ? 0 : 1), + tabRect.top + 1), colors[COLOR_TAB_BEVEL]); if (fLook != kLeftTitledWindowLook) { - fDrawingEngine->StrokeLine(BPoint(fTabRect.right - 1, fTabRect.top + 2), - BPoint(fTabRect.right - 1, fTabRect.bottom), + fDrawingEngine->StrokeLine(BPoint(tabRect.right - 1, tabRect.top + 2), + BPoint(tabRect.right - 1, tabBotton), colors[COLOR_TAB_SHADOW]); } else { fDrawingEngine->StrokeLine( - BPoint(fTabRect.left + 2, fTabRect.bottom - 1), - BPoint(fTabRect.right, fTabRect.bottom - 1), + BPoint(tabRect.left + 2, tabRect.bottom - 1), + BPoint(tabRect.right, tabRect.bottom - 1), colors[COLOR_TAB_SHADOW]); } // fill BGradientLinear gradient; - gradient.SetStart(fTabRect.LeftTop()); + gradient.SetStart(tabRect.LeftTop()); gradient.AddColor(colors[COLOR_TAB_LIGHT], 0); gradient.AddColor(colors[COLOR_TAB], 255); if (fLook != kLeftTitledWindowLook) { - gradient.SetEnd(fTabRect.LeftBottom()); - fDrawingEngine->FillRect(BRect(fTabRect.left + 2, fTabRect.top + 2, - fTabRect.right - 2, fTabRect.bottom), gradient); + gradient.SetEnd(tabRect.LeftBottom()); + fDrawingEngine->FillRect(BRect(tabRect.left + 2, tabRect.top + 2, + tabRect.right - 2, tabBotton), gradient); } else { - gradient.SetEnd(fTabRect.RightTop()); - fDrawingEngine->FillRect(BRect(fTabRect.left + 2, fTabRect.top + 2, - fTabRect.right, fTabRect.bottom - 2), gradient); + gradient.SetEnd(tabRect.RightTop()); + fDrawingEngine->FillRect(BRect(tabRect.left + 2, tabRect.top + 2, + tabRect.right, tabRect.bottom - 2), gradient); } - _DrawTitle(fTabRect); + _DrawTitle(tab, tabRect); - DrawButtons(invalid); + DrawButtons(tab, invalid); } void -DefaultDecorator::_DrawClose(BRect rect) +DefaultDecorator::_DrawClose(Decorator::Tab* _tab, BRect rect) { STRACE(("_DrawClose(%f,%f,%f,%f)\n", rect.left, rect.top, rect.right, rect.bottom)); - int32 index = (fButtonFocus ? 0 : 1) + (GetClose() ? 0 : 2); - ServerBitmap* bitmap = fCloseBitmaps[index]; + DefaultDecorator::Tab* tab = static_cast(_tab); + + int32 index = (tab->buttonFocus ? 0 : 1) + (tab->closePressed ? 0 : 2); + ServerBitmap* bitmap = tab->closeBitmaps[index]; if (bitmap == NULL) { - bitmap = _GetBitmapForButton(COMPONENT_CLOSE_BUTTON, GetClose(), - rect.IntegerWidth(), rect.IntegerHeight()); - fCloseBitmaps[index] = bitmap; + bitmap = _GetBitmapForButton(tab, COMPONENT_CLOSE_BUTTON, + tab->closePressed, rect.IntegerWidth(), rect.IntegerHeight()); + tab->closeBitmaps[index] = bitmap; } _DrawButtonBitmap(bitmap, rect); @@ -816,12 +972,18 @@ DefaultDecorator::_DrawClose(BRect rect) void -DefaultDecorator::_DrawTitle(BRect r) +DefaultDecorator::_DrawTitle(Decorator::Tab* _tab, BRect r) { + DefaultDecorator::Tab* tab = static_cast(_tab); + + const BRect& tabRect = tab->tabRect; + const BRect& closeRect = tab->closeRect; + const BRect& zoomRect = tab->zoomRect; + STRACE(("_DrawTitle(%f,%f,%f,%f)\n", r.left, r.top, r.right, r.bottom)); ComponentColors colors; - _GetComponentColors(COMPONENT_TAB, colors); + _GetComponentColors(COMPONENT_TAB, colors, tab); fDrawingEngine->SetDrawingMode(B_OP_OVER); fDrawingEngine->SetHighColor(colors[COLOR_TAB_TEXT]); @@ -833,20 +995,20 @@ DefaultDecorator::_DrawTitle(BRect r) BPoint titlePos; if (fLook != kLeftTitledWindowLook) { - titlePos.x = fCloseRect.IsValid() ? fCloseRect.right + fTextOffset - : fTabRect.left + fTextOffset; - titlePos.y = floorf(((fTabRect.top + 2.0) + fTabRect.bottom + titlePos.x = closeRect.IsValid() ? closeRect.right + tab->textOffset + : tabRect.left + tab->textOffset; + titlePos.y = floorf(((tabRect.top + 2.0) + tabRect.bottom + fontHeight.ascent + fontHeight.descent) / 2.0 - fontHeight.descent + 0.5); } else { - titlePos.x = floorf(((fTabRect.left + 2.0) + fTabRect.right + titlePos.x = floorf(((tabRect.left + 2.0) + tabRect.right + fontHeight.ascent + fontHeight.descent) / 2.0 - fontHeight.descent + 0.5); - titlePos.y = fZoomRect.IsValid() ? fZoomRect.top - fTextOffset - : fTabRect.bottom - fTextOffset; + titlePos.y = zoomRect.IsValid() ? zoomRect.top - tab->textOffset + : tabRect.bottom - tab->textOffset; } - fDrawingEngine->DrawString(fTruncatedTitle.String(), fTruncatedTitleLength, + fDrawingEngine->DrawString(tab->truncatedTitle, tab->truncatedTitleLength, titlePos); fDrawingEngine->SetDrawingMode(B_OP_COPY); @@ -854,17 +1016,20 @@ DefaultDecorator::_DrawTitle(BRect r) void -DefaultDecorator::_DrawZoom(BRect rect) +DefaultDecorator::_DrawZoom(Decorator::Tab* _tab, BRect rect) { STRACE(("_DrawZoom(%f,%f,%f,%f)\n", rect.left, rect.top, rect.right, rect.bottom)); + if (rect.IntegerWidth() < 1) + return; + DefaultDecorator::Tab* tab = static_cast(_tab); - int32 index = (fButtonFocus ? 0 : 1) + (GetZoom() ? 0 : 2); - ServerBitmap* bitmap = fZoomBitmaps[index]; + int32 index = (tab->buttonFocus ? 0 : 1) + (tab->zoomPressed ? 0 : 2); + ServerBitmap* bitmap = tab->zoomBitmaps[index]; if (bitmap == NULL) { - bitmap = _GetBitmapForButton(COMPONENT_ZOOM_BUTTON, GetZoom(), - rect.IntegerWidth(), rect.IntegerHeight()); - fZoomBitmaps[index] = bitmap; + bitmap = _GetBitmapForButton(tab, COMPONENT_ZOOM_BUTTON, + tab->zoomPressed, rect.IntegerWidth(), rect.IntegerHeight()); + tab->zoomBitmaps[index] = bitmap; } _DrawButtonBitmap(bitmap, rect); @@ -872,18 +1037,19 @@ DefaultDecorator::_DrawZoom(BRect rect) void -DefaultDecorator::_SetTitle(const char* string, BRegion* updateRegion) +DefaultDecorator::_SetTitle(Decorator::Tab* tab, const char* string, + BRegion* updateRegion) { // TODO: we could be much smarter about the update region - BRect rect = TabRect(); + BRect rect = TabRect(tab); _DoLayout(); if (updateRegion == NULL) return; - rect = rect | TabRect(); + rect = rect | TabRect(tab); rect.bottom++; // the border will look differently when the title is adjacent @@ -951,11 +1117,14 @@ DefaultDecorator::_SetFlags(uint32 flags, BRegion* updateRegion) void -DefaultDecorator::_SetFocus() +DefaultDecorator::_SetFocus(Decorator::Tab* _tab) { - fButtonFocus = IsFocus() + DefaultDecorator::Tab* tab = static_cast(_tab); + tab->buttonFocus = IsFocus(tab) || ((fLook == B_FLOATING_WINDOW_LOOK || fLook == kLeftTitledWindowLook) && (fFlags & B_AVOID_FOCUS) != 0); + if (CountTabs() > 1) + _LayoutTabItems(tab, tab->tabRect); } @@ -964,11 +1133,17 @@ DefaultDecorator::_MoveBy(BPoint offset) { STRACE(("DefaultDecorator: Move By (%.1f, %.1f)\n", offset.x, offset.y)); // Move all internal rectangles the appropriate amount + for (int32 i = 0; i < fTabList.CountItems(); i++) { + Decorator::Tab* tab = fTabList.ItemAt(i); + + tab->zoomRect.OffsetBy(offset); + tab->closeRect.OffsetBy(offset); + tab->tabRect.OffsetBy(offset); + } fFrame.OffsetBy(offset); - fCloseRect.OffsetBy(offset); - fTabRect.OffsetBy(offset); + fTitleBarRect.OffsetBy(offset); + fTabsRegion.OffsetBy(offset); fResizeRect.OffsetBy(offset); - fZoomRect.OffsetBy(offset); fBorderRect.OffsetBy(offset); fLeftBorder.OffsetBy(offset); @@ -1066,8 +1241,16 @@ DefaultDecorator::_ResizeBy(BPoint offset, BRegion* dirty) } // resize tab and layout tab items - if (fTabRect.IsValid()) { - BRect oldTabRect(fTabRect); + if (fTitleBarRect.IsValid()) { + if (fTabList.CountItems() > 1) { + _DoTabLayout(); + dirty->Include(fTitleBarRect); + return; + } + + DefaultDecorator::Tab* tab = _TabAt(0); + BRect& tabRect = tab->tabRect; + BRect oldTabRect(tabRect); float tabSize; float maxLocation; @@ -1076,39 +1259,39 @@ DefaultDecorator::_ResizeBy(BPoint offset, BRegion* dirty) } else { tabSize = fBottomBorder.bottom - fTopBorder.top; } - maxLocation = tabSize - fMaxTabSize; + maxLocation = tabSize - tab->maxTabSize; if (maxLocation < 0) maxLocation = 0; - float tabOffset = floorf(fTabLocation * maxLocation); - float delta = tabOffset - fTabOffset; - fTabOffset = (uint32)tabOffset; + float tabOffset = floorf(tab->tabLocation * maxLocation); + float delta = tabOffset - tab->tabOffset; + tab->tabOffset = (uint32)tabOffset; if (fLook != kLeftTitledWindowLook) - fTabRect.OffsetBy(delta, 0.0); + tabRect.OffsetBy(delta, 0.0); else - fTabRect.OffsetBy(0.0, delta); + tabRect.OffsetBy(0.0, delta); - if (tabSize < fMinTabSize) - tabSize = fMinTabSize; - if (tabSize > fMaxTabSize) - tabSize = fMaxTabSize; + if (tabSize < tab->minTabSize) + tabSize = tab->minTabSize; + if (tabSize > tab->maxTabSize) + tabSize = tab->maxTabSize; - if (fLook != kLeftTitledWindowLook && tabSize != fTabRect.Width()) { - fTabRect.right = fTabRect.left + tabSize; + if (fLook != kLeftTitledWindowLook && tabSize != tabRect.Width()) { + tabRect.right = tabRect.left + tabSize; } else if (fLook == kLeftTitledWindowLook - && tabSize != fTabRect.Height()) { - fTabRect.bottom = fTabRect.top + tabSize; + && tabSize != tabRect.Height()) { + tabRect.bottom = tabRect.top + tabSize; } - if (oldTabRect != fTabRect) { - _LayoutTabItems(fTabRect); + if (oldTabRect != tabRect) { + _LayoutTabItems(tab, tabRect); if (dirty) { // NOTE: the tab rect becoming smaller only would // handled be the Desktop anyways, so it is sufficient // to include it into the dirty region in it's // final state - BRect redraw(fTabRect); + BRect redraw(tabRect); if (delta != 0.0) { redraw = redraw | oldTabRect; if (fLook != kLeftTitledWindowLook) @@ -1119,44 +1302,72 @@ DefaultDecorator::_ResizeBy(BPoint offset, BRegion* dirty) dirty->Include(redraw); } } + fTitleBarRect = tabRect; + fTabsRegion = fTitleBarRect; } } bool -DefaultDecorator::_SetTabLocation(float location, BRegion* updateRegion) +DefaultDecorator::_SetTabLocation(Decorator::Tab* _tab, float location, + bool isShifting, BRegion* updateRegion) { STRACE(("DefaultDecorator: Set Tab Location(%.1f)\n", location)); - if (!fTabRect.IsValid()) + if (CountTabs() > 1) { + if (isShifting == false) { + _DoTabLayout(); + if (updateRegion != NULL) + updateRegion->Include(fTitleBarRect); + fOldMovingTab = BRect(0, 0, -1, -1); + return true; + } else { + if (fOldMovingTab.IsValid() == false) + fOldMovingTab = _tab->tabRect; + } + } + + DefaultDecorator::Tab* tab = static_cast(_tab); + BRect& tabRect = tab->tabRect; + if (tabRect.IsValid() == false) return false; if (location < 0) location = 0; float maxLocation - = fRightBorder.right - fLeftBorder.left - fTabRect.Width(); + = fRightBorder.right - fLeftBorder.left - tabRect.Width(); + if (CountTabs() > 1) + maxLocation = fTitleBarRect.right - fLeftBorder.left - tabRect.Width(); + if (location > maxLocation) location = maxLocation; - float delta = location - fTabOffset; + float delta = floor(location - tab->tabOffset); if (delta == 0.0) return false; - // redraw old rect (1 pix on the border also must be updated) - BRect trect(fTabRect); - trect.bottom++; - updateRegion->Include(trect); + // redraw old rect (1 pixel on the border must also be updated) + BRect rect(tabRect); + rect.bottom++; + if (updateRegion != NULL) + updateRegion->Include(rect); - fTabRect.OffsetBy(delta, 0); - fTabOffset = (int32)location; - _LayoutTabItems(fTabRect); + tabRect.OffsetBy(delta, 0); + tab->tabOffset = (int32)location; + _LayoutTabItems(_tab, tabRect); + tab->tabLocation = maxLocation > 0.0 ? tab->tabOffset / maxLocation : 0.0; - fTabLocation = maxLocation > 0.0 ? fTabOffset / maxLocation : 0.0; + if (fTabList.CountItems() == 1) + fTitleBarRect = tabRect; + + _CalculateTabsRegion(); // redraw new rect as well - trect = fTabRect; - trect.bottom++; - updateRegion->Include(trect); + rect = tabRect; + rect.bottom++; + if (updateRegion != NULL) + updateRegion->Include(rect); + return true; } @@ -1165,10 +1376,63 @@ bool DefaultDecorator::_SetSettings(const BMessage& settings, BRegion* updateRegion) { float tabLocation; - if (settings.FindFloat("tab location", &tabLocation) == B_OK) - return SetTabLocation(tabLocation, updateRegion); + bool modified = false; + for (int32 i = 0; i < fTabList.CountItems(); i++) { + if (settings.FindFloat("tab location", i, &tabLocation) != B_OK) + return false; + modified |= SetTabLocation(i, tabLocation, updateRegion); + } + return modified; +} - return false; + +bool +DefaultDecorator::_AddTab(int32 index, BRegion* updateRegion) +{ + _DoLayout(); + if (updateRegion != NULL) + updateRegion->Include(fTitleBarRect); + return true; +} + + +bool +DefaultDecorator::_RemoveTab(int32 index, BRegion* updateRegion ) +{ + BRect oldTitle = fTitleBarRect; + _DoLayout(); + if (updateRegion != NULL) { + updateRegion->Include(oldTitle); + updateRegion->Include(fTitleBarRect); + } + return true; +} + + +bool +DefaultDecorator::_MoveTab(int32 from, int32 to, bool isMoving, + BRegion* updateRegion) +{ + DefaultDecorator::Tab* toTab = _TabAt(to); + if (toTab == NULL) + return false; + + if (from < to) { + fOldMovingTab.OffsetBy(toTab->tabRect.Width(), 0); + toTab->tabRect.OffsetBy(-fOldMovingTab.Width(), 0); + } else { + fOldMovingTab.OffsetBy(-toTab->tabRect.Width(), 0); + toTab->tabRect.OffsetBy(fOldMovingTab.Width(), 0); + } + + toTab->tabOffset = uint32(toTab->tabRect.left - fLeftBorder.left); + _LayoutTabItems(toTab, toTab->tabRect); + + _CalculateTabsRegion(); + + if (updateRegion != NULL) + updateRegion->Include(fTitleBarRect); + return true; } @@ -1195,7 +1459,7 @@ DefaultDecorator::_GetFootprint(BRegion *region) if (fLook == B_BORDERED_WINDOW_LOOK) return; - region->Include(fTabRect); + region->Include(&fTabsRegion); if (fLook == B_DOCUMENT_WINDOW_LOOK) { // include the rectangular resize knob on the bottom right @@ -1207,13 +1471,13 @@ DefaultDecorator::_GetFootprint(BRegion *region) void -DefaultDecorator::DrawButtons(const BRect& invalid) +DefaultDecorator::DrawButtons(Decorator::Tab* tab, const BRect& invalid) { // Draw the buttons if we're supposed to - if (!(fFlags & B_NOT_CLOSABLE) && invalid.Intersects(fCloseRect)) - _DrawClose(fCloseRect); - if (!(fFlags & B_NOT_ZOOMABLE) && invalid.Intersects(fZoomRect)) - _DrawZoom(fZoomRect); + if (!(fFlags & B_NOT_CLOSABLE) && invalid.Intersects(tab->closeRect)) + _DrawClose(tab, tab->closeRect); + if (!(fFlags & B_NOT_ZOOMABLE) && invalid.Intersects(tab->zoomRect)) + _DrawZoom(tab, tab->zoomRect); } @@ -1228,13 +1492,14 @@ DefaultDecorator::DrawButtons(const BRect& invalid) */ void DefaultDecorator::GetComponentColors(Component component, uint8 highlight, - ComponentColors _colors) + ComponentColors _colors, Decorator::Tab* _tab) { + DefaultDecorator::Tab* tab = static_cast(_tab); switch (component) { case COMPONENT_TAB: _colors[COLOR_TAB_FRAME_LIGHT] = kFrameColors[0]; _colors[COLOR_TAB_FRAME_DARK] = kFrameColors[3]; - if (fButtonFocus) { + if (tab && tab->buttonFocus) { _colors[COLOR_TAB] = kFocusTabColor; _colors[COLOR_TAB_LIGHT] = kFocusTabColorLight; _colors[COLOR_TAB_BEVEL] = kFocusTabColorBevel; @@ -1251,7 +1516,7 @@ DefaultDecorator::GetComponentColors(Component component, uint8 highlight, case COMPONENT_CLOSE_BUTTON: case COMPONENT_ZOOM_BUTTON: - if (fButtonFocus) { + if (tab && tab->buttonFocus) { _colors[COLOR_BUTTON] = kFocusTabColor; _colors[COLOR_BUTTON_LIGHT] = kFocusTabColorLight; } else { @@ -1268,7 +1533,7 @@ DefaultDecorator::GetComponentColors(Component component, uint8 highlight, default: _colors[0] = kFrameColors[0]; _colors[1] = kFrameColors[1]; - if (fButtonFocus) { + if (tab && tab->buttonFocus) { _colors[2] = kFocusFrameColors[0]; _colors[3] = kFocusFrameColors[1]; } else { @@ -1380,38 +1645,46 @@ DefaultDecorator::_GetButtonSizeAndOffset(const BRect& tabRect, float* _offset, void -DefaultDecorator::_LayoutTabItems(const BRect& tabRect) +DefaultDecorator::_LayoutTabItems(Decorator::Tab* _tab, const BRect& tabRect) { + DefaultDecorator::Tab* tab = static_cast(_tab); + float offset; float size; float inset; _GetButtonSizeAndOffset(tabRect, &offset, &size, &inset); + // default textOffset + tab->textOffset = _DefaultTextOffset(); + + BRect& closeRect = tab->closeRect; + BRect& zoomRect = tab->zoomRect; + // calulate close rect based on the tab rectangle if (fLook != kLeftTitledWindowLook) { - fCloseRect.Set(tabRect.left + offset, tabRect.top + offset, + closeRect.Set(tabRect.left + offset, tabRect.top + offset, tabRect.left + offset + size, tabRect.top + offset + size); - fZoomRect.Set(tabRect.right - offset - size, tabRect.top + offset, + zoomRect.Set(tabRect.right - offset - size, tabRect.top + offset, tabRect.right - offset, tabRect.top + offset + size); // hidden buttons have no width if ((Flags() & B_NOT_CLOSABLE) != 0) - fCloseRect.right = fCloseRect.left - offset; + closeRect.right = closeRect.left - offset; if ((Flags() & B_NOT_ZOOMABLE) != 0) - fZoomRect.left = fZoomRect.right + offset; + zoomRect.left = zoomRect.right + offset; } else { - fCloseRect.Set(tabRect.left + offset, tabRect.top + offset, + closeRect.Set(tabRect.left + offset, tabRect.top + offset, tabRect.left + offset + size, tabRect.top + offset + size); - fZoomRect.Set(tabRect.left + offset, tabRect.bottom - offset - size, + zoomRect.Set(tabRect.left + offset, tabRect.bottom - offset - size, tabRect.left + size + offset, tabRect.bottom - offset); // hidden buttons have no height if ((Flags() & B_NOT_CLOSABLE) != 0) - fCloseRect.bottom = fCloseRect.top - offset; + closeRect.bottom = closeRect.top - offset; if ((Flags() & B_NOT_ZOOMABLE) != 0) - fZoomRect.top = fZoomRect.bottom + offset; + zoomRect.top = zoomRect.bottom + offset; } // calculate room for title @@ -1419,29 +1692,54 @@ DefaultDecorator::_LayoutTabItems(const BRect& tabRect) // truncated for no apparent reason - OTOH the title does // also not appear perfectly in the middle if (fLook != kLeftTitledWindowLook) - size = (fZoomRect.left - fCloseRect.right) - fTextOffset * 2 + inset; + size = (zoomRect.left - closeRect.right) - tab->textOffset * 2 + inset; else - size = (fZoomRect.top - fCloseRect.bottom) - fTextOffset * 2 + inset; + size = (zoomRect.top - closeRect.bottom) - tab->textOffset * 2 + inset; - fTruncatedTitle = Title(); - fDrawState.Font().TruncateString(&fTruncatedTitle, B_TRUNCATE_MIDDLE, size); - fTruncatedTitleLength = fTruncatedTitle.Length(); + bool stackMode = fTabList.CountItems() > 1; + if (stackMode && IsFocus(tab) == false) { + zoomRect.Set(0, 0, 0, 0); + size = (tab->tabRect.right - closeRect.right) - tab->textOffset * 2 + + inset; + } + uint8 truncateMode = B_TRUNCATE_MIDDLE; + if (stackMode) { + if (tab->tabRect.Width() < 100) + truncateMode = B_TRUNCATE_END; + float titleWidth = fDrawState.Font().StringWidth(Title(tab), + BString(Title(tab)).Length()); + if (size < titleWidth) { + float oldTextOffset = tab->textOffset; + tab->textOffset -= (titleWidth - size) / 2; + const float kMinTextOffset = 5.; + if (tab->textOffset < kMinTextOffset) + tab->textOffset = kMinTextOffset; + size += oldTextOffset * 2; + size -= tab->textOffset * 2; + } + } + tab->truncatedTitle = Title(tab); + fDrawState.Font().TruncateString(&tab->truncatedTitle, truncateMode, size); + tab->truncatedTitleLength = tab->truncatedTitle.Length(); } void DefaultDecorator::_InvalidateBitmaps() { - for (int32 index = 0; index < 4; index++) { - fCloseBitmaps[index] = NULL; - fZoomBitmaps[index] = NULL; + for (int32 i = 0; i < fTabList.CountItems(); i++) { + DefaultDecorator::Tab* tab = _TabAt(i); + for (int32 index = 0; index < 4; index++) { + tab->closeBitmaps[index] = NULL; + tab->zoomBitmaps[index] = NULL; + } } } ServerBitmap* -DefaultDecorator::_GetBitmapForButton(Component item, bool down, int32 width, - int32 height) +DefaultDecorator::_GetBitmapForButton(Decorator::Tab* tab, Component item, + bool down, int32 width, int32 height) { // TODO: the list of shared bitmaps is never freed struct decorator_bitmap { @@ -1459,7 +1757,7 @@ DefaultDecorator::_GetBitmapForButton(Component item, bool down, int32 width, static decorator_bitmap* sBitmapList = NULL; ComponentColors colors; - _GetComponentColors(item, colors); + _GetComponentColors(item, colors, tab); BAutolock locker(sBitmapListLock); @@ -1546,7 +1844,7 @@ DefaultDecorator::_GetBitmapForButton(Component item, bool down, int32 width, void DefaultDecorator::_GetComponentColors(Component component, - ComponentColors _colors) + ComponentColors _colors, Decorator::Tab* tab) { // get the highlight for our component Region region = REGION_NONE; @@ -1577,5 +1875,22 @@ DefaultDecorator::_GetComponentColors(Component component, break; } - return GetComponentColors(component, RegionHighlight(region), _colors); + return GetComponentColors(component, RegionHighlight(region), _colors, tab); +} + + +float +DefaultDecorator::_DefaultTextOffset() const +{ + return (fLook == B_FLOATING_WINDOW_LOOK + || fLook == kLeftTitledWindowLook) ? 10 : 18; +} + + +void +DefaultDecorator::_CalculateTabsRegion() +{ + fTabsRegion.MakeEmpty(); + for (int32 i = 0; i < fTabList.CountItems(); i++) + fTabsRegion.Include(fTabList.ItemAt(i)->tabRect); } diff --git a/src/servers/app/DefaultDecorator.h b/src/servers/app/DefaultDecorator.h index 329297cafd..8bf3b07007 100644 --- a/src/servers/app/DefaultDecorator.h +++ b/src/servers/app/DefaultDecorator.h @@ -1,5 +1,5 @@ /* - * Copyright 2001-2010, Haiku, Inc. + * Copyright 2001-2011, Haiku, Inc. * Distributed under the terms of the MIT License. * * Authors: @@ -22,13 +22,33 @@ class ServerBitmap; class DefaultDecorator: public Decorator { public: + class Tab : public Decorator::Tab { + public: + Tab(); + + uint32 tabOffset; + float tabLocation; + float textOffset; + + BString truncatedTitle; + int32 truncatedTitleLength; + + bool buttonFocus : 1; + + bool isHighlighted : 1; + ServerBitmap* closeBitmaps[4]; + ServerBitmap* zoomBitmaps[4]; + + float minTabSize; + float maxTabSize; + }; + DefaultDecorator(DesktopSettings& settings, BRect frame, window_look look, uint32 flags); virtual ~DefaultDecorator(); - virtual float TabLocation() const - { return (float)fTabOffset; } + virtual float TabLocation(int32 tab) const; virtual bool GetSettings(BMessage* settings) const; @@ -38,10 +58,11 @@ public: virtual void GetSizeLimits(int32* minWidth, int32* minHeight, int32* maxWidth, int32* maxHeight) const; - virtual Region RegionAt(BPoint where) const; + virtual Region RegionAt(BPoint where, int32& tab) const; virtual bool SetRegionHighlight(Region region, - uint8 highlight, BRegion* dirty); + uint8 highlight, BRegion* dirty, + int32 tab = -1); virtual void ExtendDirtyRegion(Region region, BRegion& dirty); @@ -83,16 +104,22 @@ protected: protected: virtual void _DoLayout(); + virtual void _DoTabLayout(); + void _DistributeTabSize(float delta); + + virtual Decorator::Tab* _AllocateNewTab(); + DefaultDecorator::Tab* _TabAt(int32 index) const; virtual void _DrawFrame(BRect r); - virtual void _DrawTab(BRect r); + virtual void _DrawTab(Decorator::Tab* tab, BRect r); - virtual void _DrawClose(BRect r); - virtual void _DrawTitle(BRect r); - virtual void _DrawZoom(BRect r); + virtual void _DrawClose(Decorator::Tab* tab, BRect r); + virtual void _DrawTitle(Decorator::Tab* tab, BRect r); + virtual void _DrawZoom(Decorator::Tab* tab, BRect r); - virtual void _SetTitle(const char* string, + virtual void _SetTitle(Decorator::Tab* tab, const char* string, BRegion* updateRegion = NULL); + virtual void _SetFocus(Decorator::Tab* tab); virtual void _FontsChanged(DesktopSettings& settings, BRegion* updateRegion); @@ -102,17 +129,23 @@ protected: virtual void _SetFlags(uint32 flags, BRegion* updateRegion = NULL); - virtual void _SetFocus(); - virtual void _MoveBy(BPoint offset); virtual void _ResizeBy(BPoint offset, BRegion* dirty); - virtual bool _SetTabLocation(float location, + virtual bool _SetTabLocation(Decorator::Tab* tab, + float location, bool isShifting, BRegion* updateRegion = NULL); virtual bool _SetSettings(const BMessage& settings, BRegion* updateRegion = NULL); + virtual bool _AddTab(int32 index = -1, + BRegion* updateRegion = NULL); + virtual bool _RemoveTab(int32 index, + BRegion* updateRegion = NULL); + virtual bool _MoveTab(int32 from, int32 to, bool isMoving, + BRegion* updateRegion = NULL); + virtual void _GetFootprint(BRegion *region); void _GetButtonSizeAndOffset(const BRect& tabRect, @@ -120,9 +153,11 @@ protected: float* inset) const; // DefaultDecorator customization points - virtual void DrawButtons(const BRect& invalid); + virtual void DrawButtons(Decorator::Tab* tab, + const BRect& invalid); virtual void GetComponentColors(Component component, - uint8 highlight, ComponentColors _colors); + uint8 highlight, ComponentColors _colors, + Decorator::Tab* tab = NULL); private: void _UpdateFont(DesktopSettings& settings); @@ -131,14 +166,20 @@ private: void _DrawBlendedRect(DrawingEngine *engine, BRect rect, bool down, const ComponentColors& colors); - void _LayoutTabItems(const BRect& tabRect); + void _LayoutTabItems(Decorator::Tab* tab, + const BRect& tabRect); void _InvalidateBitmaps(); - ServerBitmap* _GetBitmapForButton(Component item, bool down, - int32 width, int32 height); + ServerBitmap* _GetBitmapForButton(Decorator::Tab* tab, + Component item, bool down, int32 width, + int32 height); void _GetComponentColors(Component component, - ComponentColors _colors); + ComponentColors _colors, + Decorator::Tab* tab = NULL); + inline float _DefaultTextOffset() const; + + void _CalculateTabsRegion(); protected: static const rgb_color kFrameColors[4]; static const rgb_color kFocusFrameColors[2]; @@ -156,10 +197,6 @@ protected: const rgb_color kNonFocusTabColorShadow; const rgb_color kNonFocusTextColor; - bool fButtonFocus; - ServerBitmap* fCloseBitmaps[4]; - ServerBitmap* fZoomBitmaps[4]; - // Individual rects for handling window frame // rendering the proper way BRect fRightBorder; @@ -169,14 +206,8 @@ protected: int32 fBorderWidth; - uint32 fTabOffset; - float fTabLocation; - float fTextOffset; - - float fMinTabSize; - float fMaxTabSize; - BString fTruncatedTitle; - int32 fTruncatedTitleLength; + BRegion fTabsRegion; + BRect fOldMovingTab; }; diff --git a/src/servers/app/DefaultWindowBehaviour.cpp b/src/servers/app/DefaultWindowBehaviour.cpp index c49a6df485..d6650d033d 100644 --- a/src/servers/app/DefaultWindowBehaviour.cpp +++ b/src/servers/app/DefaultWindowBehaviour.cpp @@ -21,6 +21,7 @@ #include "ClickTarget.h" #include "Desktop.h" +#include "DefaultDecorator.h" #include "DrawingEngine.h" #include "Window.h" @@ -286,16 +287,58 @@ struct DefaultWindowBehaviour::SlideTabState : MouseTrackingState { { } + virtual + ~SlideTabState() + { + fDesktop->SetWindowTabLocation(fWindow, fWindow->TabLocation(), false); + } + virtual void MouseMovedAction(BPoint& delta, bigtime_t now) { - float loc = fWindow->TabLocation(); + float location = fWindow->TabLocation(); // TODO: change to [0:1] - loc += delta.x; - if (fDesktop->SetWindowTabLocation(fWindow, loc)) + location += delta.x; + AdjustMultiTabLocation(location, true); + if (fDesktop->SetWindowTabLocation(fWindow, location, true)) delta.y = 0; else delta = BPoint(0, 0); } + + void AdjustMultiTabLocation(float location, bool isShifting) + { + ::Decorator* decorator = fWindow->Decorator(); + if (decorator == NULL || decorator->CountTabs() <= 1) + return; + + // TODO does not work for none continuous shifts + int32 windowIndex = fWindow->PositionInStack(); + DefaultDecorator::Tab* movingTab = static_cast( + decorator->TabAt(windowIndex)); + int32 neighbourIndex = windowIndex; + if (movingTab->tabOffset > location) + neighbourIndex--; + else + neighbourIndex++; + + DefaultDecorator::Tab* neighbourTab + = static_cast(decorator->TabAt( + neighbourIndex)); + if (neighbourTab == NULL) + return; + + if (movingTab->tabOffset > location) { + if (location > + neighbourTab->tabOffset + neighbourTab->tabRect.Width() / 2) + return; + } else { + if (location + movingTab->tabRect.Width() < + neighbourTab->tabOffset + neighbourTab->tabRect.Width() / 2) + return; + } + + fWindow->MoveToStackPosition(neighbourIndex, isShifting); + } }; @@ -423,9 +466,10 @@ private: struct DefaultWindowBehaviour::DecoratorButtonState : State { DecoratorButtonState(DefaultWindowBehaviour& behavior, - Decorator::Region button) + int32 tab, Decorator::Region button) : State(behavior), + fTab(tab), fButton(button) { } @@ -452,22 +496,23 @@ struct DefaultWindowBehaviour::DecoratorButtonState : State { engine->LockParallelAccess(); engine->ConstrainClippingRegion(visibleBorder); + int32 tab; switch (fButton) { case Decorator::REGION_CLOSE_BUTTON: - decorator->SetClose(false); - if (fBehavior._RegionFor(message) == fButton) + decorator->SetClose(fTab, false); + if (fBehavior._RegionFor(message, tab) == fButton) fWindow->ServerWindow()->NotifyQuitRequested(); break; case Decorator::REGION_ZOOM_BUTTON: - decorator->SetZoom(false); - if (fBehavior._RegionFor(message) == fButton) + decorator->SetZoom(fTab, false); + if (fBehavior._RegionFor(message, tab) == fButton) fWindow->ServerWindow()->NotifyZoom(); break; case Decorator::REGION_MINIMIZE_BUTTON: - decorator->SetMinimize(false); - if (fBehavior._RegionFor(message) == fButton) + decorator->SetMinimize(fTab, false); + if (fBehavior._RegionFor(message, tab) == fButton) fWindow->ServerWindow()->NotifyMinimize(true); break; @@ -500,20 +545,21 @@ private: engine->LockParallelAccess(); engine->ConstrainClippingRegion(visibleBorder); + int32 tab; Decorator::Region hitRegion = message != NULL - ? fBehavior._RegionFor(message) : fButton; + ? fBehavior._RegionFor(message, tab) : fButton; switch (fButton) { case Decorator::REGION_CLOSE_BUTTON: - decorator->SetClose(hitRegion == fButton); + decorator->SetClose(fTab, hitRegion == fButton); break; case Decorator::REGION_ZOOM_BUTTON: - decorator->SetZoom(hitRegion == fButton); + decorator->SetZoom(fTab, hitRegion == fButton); break; case Decorator::REGION_MINIMIZE_BUTTON: - decorator->SetMinimize(hitRegion == fButton); + decorator->SetMinimize(fTab, hitRegion == fButton); break; default: @@ -526,6 +572,7 @@ private: } protected: + int32 fTab; Decorator::Region fButton; }; @@ -669,6 +716,7 @@ DefaultWindowBehaviour::MouseDown(BMessage* message, BPoint where, Decorator* decorator = fWindow->Decorator(); Decorator::Region hitRegion = Decorator::REGION_NONE; + int32 tab = -1; Action action = ACTION_NONE; bool inBorderRegion = false; @@ -688,7 +736,7 @@ DefaultWindowBehaviour::MouseDown(BMessage* message, BPoint where, hitRegion = Decorator::REGION_LEFT_BORDER; } else { // click on the decorator -- get the exact region - hitRegion = _RegionFor(message); + hitRegion = _RegionFor(message, tab); } // translate the region into an action @@ -697,7 +745,7 @@ DefaultWindowBehaviour::MouseDown(BMessage* message, BPoint where, if ((buttons & B_PRIMARY_MOUSE_BUTTON) != 0) { // left mouse button switch (hitRegion) { - case Decorator::REGION_TAB: + case Decorator::REGION_TAB: { // tab sliding in any case if either shift key is held down // except sliding up-down by moving mouse left-right would // look strange @@ -708,6 +756,7 @@ DefaultWindowBehaviour::MouseDown(BMessage* message, BPoint where, } action = ACTION_DRAG; break; + } case Decorator::REGION_LEFT_BORDER: case Decorator::REGION_RIGHT_BORDER: @@ -809,7 +858,7 @@ DefaultWindowBehaviour::MouseDown(BMessage* message, BPoint where, case ACTION_ZOOM: case ACTION_MINIMIZE: _NextState( - new (std::nothrow) DecoratorButtonState(*this, hitRegion)); + new (std::nothrow) DecoratorButtonState(*this, tab, hitRegion)); STRACE_CLICK(("===> ACTION_CLOSE/ZOOM/MINIMIZE\n")); break; @@ -910,7 +959,7 @@ DefaultWindowBehaviour::_IsWindowModifier(int32 modifiers) const Decorator::Region -DefaultWindowBehaviour::_RegionFor(const BMessage* message) const +DefaultWindowBehaviour::_RegionFor(const BMessage* message, int32& tab) const { Decorator* decorator = fWindow->Decorator(); if (decorator == NULL) @@ -920,7 +969,7 @@ DefaultWindowBehaviour::_RegionFor(const BMessage* message) const if (message->FindPoint("where", &where) != B_OK) return Decorator::REGION_NONE; - return decorator->RegionAt(where); + return decorator->RegionAt(where, tab); } diff --git a/src/servers/app/DefaultWindowBehaviour.h b/src/servers/app/DefaultWindowBehaviour.h index 6452eb43bc..3ad3f9f4f7 100644 --- a/src/servers/app/DefaultWindowBehaviour.h +++ b/src/servers/app/DefaultWindowBehaviour.h @@ -88,7 +88,8 @@ private: private: bool _IsWindowModifier(int32 modifiers) const; - Decorator::Region _RegionFor(const BMessage* message) const; + Decorator::Region _RegionFor(const BMessage* message, + int32& tab) const; void _SetBorderHighlights(int8 horizontal, int8 vertical, bool active); diff --git a/src/servers/app/Desktop.cpp b/src/servers/app/Desktop.cpp index 6845e1d327..5abe4c85ec 100644 --- a/src/servers/app/Desktop.cpp +++ b/src/servers/app/Desktop.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2010, Haiku. + * Copyright 2001-2011, Haiku. * Distributed under the terms of the MIT License. * * Authors: @@ -581,6 +581,7 @@ Desktop::BroadcastToAllWindows(int32 code) filter_result Desktop::KeyEvent(uint32 what, int32 key, int32 modifiers) { + filter_result result = B_DISPATCH_MESSAGE; if (LockAllWindows()) { Window* window = MouseEventWindow(); if (window == NULL) @@ -591,13 +592,13 @@ Desktop::KeyEvent(uint32 what, int32 key, int32 modifiers) window->ModifiersChanged(modifiers); } + if (NotifyKeyPressed(what, key, modifiers)) + result = B_SKIP_MESSAGE; + UnlockAllWindows(); } - if (NotifyKeyPressed(what, key, modifiers)) - return B_SKIP_MESSAGE; - - return B_DISPATCH_MESSAGE; + return result; } @@ -1115,15 +1116,12 @@ Desktop::ActivateWindow(Window* window) } } - // we don't need to redraw what is currently - // visible of the window - BRegion clean(window->VisibleRegion()); WindowList windows(kWorkingList); - Window* frontmost = window->Frontmost(); CurrentWindows().RemoveWindow(window); windows.AddWindow(window); + window->MoveToTopStackLayer(); if (frontmost != NULL && frontmost->IsModal()) { // all modal windows follow their subsets to the front @@ -1335,6 +1333,10 @@ Desktop::MoveWindowBy(Window* window, float x, float y, int32 workspace) if (!LockAllWindows()) return; + Window* topWindow = window->TopLayerStackWindow(); + if (topWindow) + window = topWindow; + if (workspace == -1) workspace = fCurrentWorkspace; if (!window->IsVisible() || workspace != fCurrentWorkspace) { @@ -1472,16 +1474,16 @@ Desktop::ResizeWindowBy(Window* window, float x, float y) bool -Desktop::SetWindowTabLocation(Window* window, float location) +Desktop::SetWindowTabLocation(Window* window, float location, bool isShifting) { AutoWriteLocker _(fWindowLock); BRegion dirty; - bool changed = window->SetTabLocation(location, dirty); + bool changed = window->SetTabLocation(location, isShifting, dirty); if (changed) RebuildAndRedrawAfterWindowChange(window, dirty); - NotifyWindowTabLocationChanged(window, location); + NotifyWindowTabLocationChanged(window, location, isShifting); return changed; } @@ -1763,7 +1765,7 @@ Desktop::WindowAt(BPoint where) for (Window* window = CurrentWindows().LastWindow(); window; window = window->PreviousWindow(fCurrentWorkspace)) { if (window->IsVisible() && window->VisibleRegion().Contains(where)) - return window; + return window->StackedWindowAt(where); } return NULL; @@ -3177,6 +3179,7 @@ void Desktop::RebuildAndRedrawAfterWindowChange(Window* changedWindow, BRegion& dirty) { + ASSERT_MULTI_WRITE_LOCKED(fWindowLock); if (!changedWindow->IsVisible() || dirty.CountRects() == 0) return; diff --git a/src/servers/app/Desktop.h b/src/servers/app/Desktop.h index 388c3f4b1f..ae663ac690 100644 --- a/src/servers/app/Desktop.h +++ b/src/servers/app/Desktop.h @@ -171,7 +171,7 @@ public: void ResizeWindowBy(Window* window, float x, float y); bool SetWindowTabLocation(Window* window, - float location); + float location, bool isShifting); bool SetWindowDecoratorSettings(Window* window, const BMessage& settings); diff --git a/src/servers/app/DesktopListener.cpp b/src/servers/app/DesktopListener.cpp index 84599c5ab1..37cc38b8ee 100644 --- a/src/servers/app/DesktopListener.cpp +++ b/src/servers/app/DesktopListener.cpp @@ -244,7 +244,7 @@ DesktopObservable::NotifyWindowMinimized(Window* window, bool minimize) void DesktopObservable::NotifyWindowTabLocationChanged(Window* window, - float location) + float location, bool isShifting) { if (fWeAreInvoking) return; @@ -252,7 +252,7 @@ DesktopObservable::NotifyWindowTabLocationChanged(Window* window, for (DesktopListener* listener = fDesktopListenerList.First(); listener != NULL; listener = fDesktopListenerList.GetNext(listener)) - listener->WindowTabLocationChanged(window, location); + listener->WindowTabLocationChanged(window, location, isShifting); } diff --git a/src/servers/app/DesktopListener.h b/src/servers/app/DesktopListener.h index df102a5399..1b89b9da08 100644 --- a/src/servers/app/DesktopListener.h +++ b/src/servers/app/DesktopListener.h @@ -59,7 +59,7 @@ public: bool minimize) = 0; virtual void WindowTabLocationChanged(Window* window, - float location) = 0; + float location, bool isShifting) = 0; virtual void SizeLimitsChanged(Window* window, int32 minWidth, int32 maxWidth, int32 minHeight, int32 maxHeight) = 0; @@ -113,7 +113,7 @@ public: bool minimize); void NotifyWindowTabLocationChanged(Window* window, - float location); + float location, bool isShifting); void NotifySizeLimitsChanged(Window* window, int32 minWidth, int32 maxWidth, int32 minHeight, int32 maxHeight); diff --git a/src/servers/app/Window.cpp b/src/servers/app/Window.cpp index dd57406573..975dcc3df6 100644 --- a/src/servers/app/Window.cpp +++ b/src/servers/app/Window.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2010, Haiku, Inc. + * Copyright 2001-2011, Haiku, Inc. * Distributed under the terms of the MIT license. * * Authors: @@ -14,6 +14,17 @@ #include "Window.h" +#include +#include + +#include + +#include +#include +#include +#include +#include + #include "ClickTarget.h" #include "Decorator.h" #include "DecorManager.h" @@ -28,17 +39,6 @@ #include "Workspace.h" #include "WorkspacesView.h" -#include -#include - -#include -#include -#include -#include - -#include -#include - // Toggle debug output //#define DEBUG_WINDOW @@ -91,7 +91,6 @@ Window::Window(const BRect& frame, const char *name, fRegionPool(), fWindowBehaviour(NULL), - fDecorator(NULL), fTopView(NULL), fWindow(window), fDrawingEngine(drawingEngine), @@ -120,6 +119,8 @@ Window::Window(const BRect& frame, const char *name, fWorkspacesViewCount(0) { + _InitWindowStack(); + // make sure our arguments are valid if (!IsValidLook(fLook)) fLook = B_TITLED_WINDOW_LOOK; @@ -128,11 +129,12 @@ Window::Window(const BRect& frame, const char *name, SetFlags(flags, NULL); - if (fLook != B_NO_BORDER_WINDOW_LOOK) { - fDecorator = gDecorManager.AllocateDecorator(this); - if (fDecorator) { - fDecorator->GetSizeLimits(&fMinWidth, &fMinHeight, - &fMaxWidth, &fMaxHeight); + if (fLook != B_NO_BORDER_WINDOW_LOOK && fCurrentStack.Get() != NULL) { + // allocates a decorator + ::Decorator* decorator = Decorator(); + if (decorator != NULL) { + decorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth, + &fMaxHeight); } } fWindowBehaviour = gDecorManager.AllocateWindowBehaviour(this); @@ -169,8 +171,9 @@ Window::~Window() delete fTopView; } + DetachFromWindowStack(false); + delete fWindowBehaviour; - delete fDecorator; delete fDrawingEngine; gDecorManager.CleanupForWindow(this); @@ -220,8 +223,9 @@ Window::GetBorderRegion(BRegion* region) // TODO: if someone needs to call this from // the outside, the clipping needs to be readlocked! - if (fDecorator) - *region = fDecorator->GetFootprint(); + ::Decorator* decorator = Decorator(); + if (decorator) + *region = decorator->GetFootprint(); else region->MakeEmpty(); } @@ -272,7 +276,7 @@ Window::_PropagatePosition() void -Window::MoveBy(int32 x, int32 y) +Window::MoveBy(int32 x, int32 y, bool moveStack) { // this function is only called from the desktop thread @@ -296,14 +300,27 @@ Window::MoveBy(int32 x, int32 y) fEffectiveDrawingRegionValid = false; - if (fDecorator) - fDecorator->MoveBy(x, y); - if (fTopView != NULL) { fTopView->MoveBy(x, y, NULL); fTopView->UpdateOverlay(); } + ::Decorator* decorator = Decorator(); + if (moveStack && decorator) + decorator->MoveBy(x, y); + + WindowStack* stack = GetWindowStack(); + if (moveStack && stack) { + for (int32 i = 0; i < stack->CountWindows(); i++) { + Window* window = stack->WindowList().ItemAt(i); + if (window == this) + continue; + window->MoveBy(x, y, false); + + //fDesktop->RebuildAndRedrawAfterWindowChange(window, dirty); + } + } + // the desktop will take care of dirty regions // dispatch a message to the client informing about the changed size @@ -315,7 +332,7 @@ Window::MoveBy(int32 x, int32 y) void -Window::ResizeBy(int32 x, int32 y, BRegion* dirtyRegion) +Window::ResizeBy(int32 x, int32 y, BRegion* dirtyRegion, bool resizeStack) { // this function is only called from the desktop thread @@ -345,19 +362,29 @@ Window::ResizeBy(int32 x, int32 y, BRegion* dirtyRegion) fContentRegionValid = false; fEffectiveDrawingRegionValid = false; - if (fDecorator) { - fDecorator->ResizeBy(x, y, dirtyRegion); + if (fTopView != NULL) { + fTopView->ResizeBy(x, y, dirtyRegion); + fTopView->UpdateOverlay(); + } + + ::Decorator* decorator = Decorator(); + if (decorator && resizeStack) { + decorator->ResizeBy(x, y, dirtyRegion); //if (dirtyRegion) { //fDrawingEngine->FillRegion(*dirtyRegion, (rgb_color){ 255, 255, 0, 255 }); //snooze(40000); //} } - if (fTopView != NULL) { - fTopView->ResizeBy(x, y, dirtyRegion); - fTopView->UpdateOverlay(); + WindowStack* stack = GetWindowStack(); + if (resizeStack && stack) { + for (int32 i = 0; i < stack->CountWindows(); i++) { + Window* window = stack->WindowList().ItemAt(i); + if (window == this) + continue; + window->ResizeBy(x, y, dirtyRegion, false); + } } - //if (dirtyRegion) //fDrawingEngine->FillRegion(*dirtyRegion, (rgb_color){ 0, 255, 255, 255 }); @@ -543,20 +570,32 @@ Window::PreviousWindow(int32 index) const } +::Decorator* +Window::Decorator() const +{ + if (fCurrentStack.Get() == NULL) + return NULL; + return fCurrentStack->Decorator(); +} + + bool Window::ReloadDecor() { ::Decorator* decorator = NULL; WindowBehaviour* windowBehaviour = NULL; - + WindowStack* stack = GetWindowStack(); + if (stack == NULL) + return false; if (fLook != B_NO_BORDER_WINDOW_LOOK) { // we need a new decorator decorator = gDecorManager.AllocateDecorator(this); if (decorator == NULL) return false; + int32 index = PositionInStack(); if (IsFocus()) - decorator->SetFocus(true); + decorator->SetFocus(index, true); } windowBehaviour = gDecorManager.AllocateWindowBehaviour(this); @@ -565,8 +604,7 @@ Window::ReloadDecor() return false; } - delete fDecorator; - fDecorator = decorator; + stack->SetDecorator(decorator); delete fWindowBehaviour; fWindowBehaviour = windowBehaviour; @@ -578,7 +616,8 @@ Window::ReloadDecor() void Window::SetScreen(const ::Screen* screen) { - ASSERT_MULTI_WRITE_LOCKED(fDesktop->ScreenLocker()); + // TODO this assert fails in Desktop::ShowWindow + //ASSERT_MULTI_WRITE_LOCKED(fDesktop->ScreenLocker()); fScreen = screen; } @@ -586,7 +625,8 @@ Window::SetScreen(const ::Screen* screen) const ::Screen* Window::Screen() const { - ASSERT_MULTI_READ_LOCKED(fDesktop->ScreenLocker()); + // TODO this assert also fails + //ASSERT_MULTI_READ_LOCKED(fDesktop->ScreenLocker()); return fScreen; } @@ -640,7 +680,7 @@ Window::DrawingRegionChanged(View* view) const void Window::ProcessDirtyRegion(BRegion& region) { - // if this is exectuted in the desktop thread, + // if this is executed in the desktop thread, // it means that the window thread currently // blocks to get the read lock, if it is // executed from the window thread, it should @@ -669,8 +709,13 @@ Window::ProcessDirtyRegion(BRegion& region) void Window::RedrawDirtyRegion() { - // executed from ServerWindow with the read lock held + if (TopLayerStackWindow() != this) { + fDirtyRegion.MakeEmpty(); + fDirtyCause = 0; + return; + } + // executed from ServerWindow with the read lock held if (IsVisible()) { _DrawBorder(); @@ -961,22 +1006,27 @@ Window::SetTitle(const char* name, BRegion& dirty) fTitle = name; - if (fDecorator) - fDecorator->SetTitle(name, &dirty); + ::Decorator* decorator = Decorator(); + if (decorator) { + int32 index = PositionInStack(); + decorator->SetTitle(index, name, &dirty); + } } void Window::SetFocus(bool focus) { + ::Decorator* decorator = Decorator(); + // executed from Desktop thread // it holds the clipping write lock, // so the window thread cannot be // accessing fIsFocus BRegion* dirty = NULL; - if (fDecorator) - dirty = fRegionPool.GetRegion(fDecorator->GetFootprint()); + if (decorator) + dirty = fRegionPool.GetRegion(decorator->GetFootprint()); if (dirty) { dirty->IntersectWith(&fVisibleRegion); fDesktop->MarkDirty(*dirty); @@ -984,8 +1034,10 @@ Window::SetFocus(bool focus) } fIsFocus = focus; - if (fDecorator) - fDecorator->SetFocus(focus); + if (decorator) { + int32 index = PositionInStack(); + decorator->SetFocus(index, focus); + } Activated(focus); } @@ -1066,8 +1118,9 @@ Window::SetSizeLimits(int32 minWidth, int32 maxWidth, int32 minHeight, fMaxHeight = maxHeight; // give the Decorator a say in this too - if (fDecorator) { - fDecorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth, + ::Decorator* decorator = Decorator(); + if (decorator) { + decorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth, &fMaxHeight); } @@ -1087,10 +1140,13 @@ Window::GetSizeLimits(int32* minWidth, int32* maxWidth, bool -Window::SetTabLocation(float location, BRegion& dirty) +Window::SetTabLocation(float location, bool isShifting, BRegion& dirty) { - if (fDecorator) - return fDecorator->SetTabLocation(location, &dirty); + ::Decorator* decorator = Decorator(); + if (decorator) { + int32 index = PositionInStack(); + return decorator->SetTabLocation(index, location, isShifting, &dirty); + } return false; } @@ -1099,8 +1155,11 @@ Window::SetTabLocation(float location, BRegion& dirty) float Window::TabLocation() const { - if (fDecorator) - return fDecorator->TabLocation(); + ::Decorator* decorator = Decorator(); + if (decorator) { + int32 index = PositionInStack(); + return decorator->TabLocation(index); + } return 0.0; } @@ -1116,8 +1175,10 @@ Window::SetDecoratorSettings(const BMessage& settings, BRegion& dirty) return false; } - if (fDecorator) - return fDecorator->SetSettings(settings, &dirty); + ::Decorator* decorator = Decorator(); + if (decorator) + return decorator->SetSettings(settings, &dirty); + return false; } @@ -1128,8 +1189,9 @@ Window::GetDecoratorSettings(BMessage* settings) if (fDesktop) fDesktop->GetDecoratorSettings(this, *settings); - if (fDecorator) - return fDecorator->GetSettings(settings); + ::Decorator* decorator = Decorator(); + if (decorator) + return decorator->GetSettings(settings); return false; } @@ -1138,9 +1200,10 @@ Window::GetDecoratorSettings(BMessage* settings) void Window::FontsChanged(BRegion* updateRegion) { - if (fDecorator != NULL) { + ::Decorator* decorator = Decorator(); + if (decorator != NULL) { DesktopSettings settings(fDesktop); - fDecorator->FontsChanged(settings, updateRegion); + decorator->FontsChanged(settings, updateRegion); } } @@ -1148,11 +1211,14 @@ Window::FontsChanged(BRegion* updateRegion) void Window::SetLook(window_look look, BRegion* updateRegion) { - if (fDecorator == NULL && look != B_NO_BORDER_WINDOW_LOOK) { + ::Decorator* decorator = Decorator(); + if (decorator == NULL && look != B_NO_BORDER_WINDOW_LOOK) { // we need a new decorator - fDecorator = gDecorManager.AllocateDecorator(this); - if (IsFocus()) - fDecorator->SetFocus(true); + decorator = gDecorManager.AllocateDecorator(this); + if (IsFocus()) { + int32 index = PositionInStack(); + decorator->SetFocus(index, true); + } } fLook = look; @@ -1163,20 +1229,19 @@ Window::SetLook(window_look look, BRegion* updateRegion) // ...and therefor the drawing region is // likely not valid anymore either - if (fDecorator != NULL) { + if (decorator != NULL) { DesktopSettings settings(fDesktop); - fDecorator->SetLook(settings, look, updateRegion); + decorator->SetLook(settings, look, updateRegion); // we might need to resize the window! - fDecorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth, + decorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth, &fMaxHeight); _ObeySizeLimits(); } - if (look == B_NO_BORDER_WINDOW_LOOK) { + if (look == B_NO_BORDER_WINDOW_LOOK && fCurrentStack.Get() != NULL) { // we don't need a decorator for this window - delete fDecorator; - fDecorator = NULL; + fCurrentStack->SetDecorator(NULL); } } @@ -1216,16 +1281,15 @@ Window::SetFlags(uint32 flags, BRegion* updateRegion) if ((fFlags & B_SAME_POSITION_IN_ALL_WORKSPACES) != 0) _PropagatePosition(); - if (fDecorator == NULL) + ::Decorator* decorator = Decorator(); + if (decorator == NULL) return; - fDecorator->SetFlags(flags, updateRegion); + decorator->SetFlags(flags, updateRegion); // we might need to resize the window! - if (fDecorator) { - fDecorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth, &fMaxHeight); - _ObeySizeLimits(); - } + decorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth, &fMaxHeight); + _ObeySizeLimits(); // TODO: not sure if we want to do this #if 0 @@ -1672,8 +1736,8 @@ Window::_DrawBorder() // this is executed in the window thread, but only // in respond to a REDRAW message having been received, the // clipping lock is held for reading - - if (!fDecorator) + ::Decorator* decorator = Decorator(); + if (!decorator) return; // construct the region of the border that needs redrawing @@ -1686,13 +1750,13 @@ Window::_DrawBorder() // intersect with the dirty region dirtyBorderRegion->IntersectWith(&fDirtyRegion); - DrawingEngine* engine = fDecorator->GetDrawingEngine(); + DrawingEngine* engine = decorator->GetDrawingEngine(); if (dirtyBorderRegion->CountRects() > 0 && engine->LockParallelAccess()) { engine->ConstrainClippingRegion(dirtyBorderRegion); bool copyToFrontEnabled = engine->CopyToFrontEnabled(); engine->SetCopyToFrontEnabled(true); - fDecorator->Draw(dirtyBorderRegion->Frame()); + decorator->Draw(dirtyBorderRegion->Frame()); engine->SetCopyToFrontEnabled(copyToFrontEnabled); @@ -1887,8 +1951,9 @@ Window::_UpdateContentRegion() fContentRegion.Set(fFrame); // resize handle - if (fDecorator) - fContentRegion.Exclude(&fDecorator->GetFootprint()); + ::Decorator* decorator = Decorator(); + if (decorator) + fContentRegion.Exclude(&decorator->GetFootprint()); fContentRegionValid = true; } @@ -1991,3 +2056,270 @@ Window::UpdateSession::AddCause(uint8 cause) { fCause |= cause; } + + +int32 +Window::PositionInStack() const +{ + if (fCurrentStack.Get() == NULL) + return -1; + return fCurrentStack->WindowList().IndexOf(this); +} + + +bool +Window::DetachFromWindowStack(bool ownStackNeeded) +{ + // The lock must normally be held but is not held when closing the window. + //ASSERT_MULTI_WRITE_LOCKED(fDesktop->WindowLocker()); + + if (fCurrentStack.Get() == NULL) + return false; + if (fCurrentStack->CountWindows() == 1) + return true; + + int32 index = PositionInStack(); + + if (fCurrentStack->RemoveWindow(this) == false) + return false; + + BRegion dirty; + ::Decorator* decorator = fCurrentStack->Decorator(); + if (decorator != NULL) + decorator->RemoveTab(index, &dirty); + + Window* remainingTop = fCurrentStack->TopLayerWindow(); + if (remainingTop != NULL) { + decorator->SetDrawingEngine(remainingTop->fDrawingEngine); + // propagate focus to the decorator + remainingTop->SetFocus(remainingTop->IsFocus()); + } + + fCurrentStack = NULL; + if (ownStackNeeded == true) + _InitWindowStack(); + // propagate focus to the new decorator + SetFocus(IsFocus()); + + fDesktop->RebuildAndRedrawAfterWindowChange(this, dirty); + if (remainingTop != NULL) + fDesktop->MarkDirty(remainingTop->VisibleRegion()); + + return true; +} + + +bool +Window::AddWindowToStack(Window* window) +{ + ASSERT_MULTI_WRITE_LOCKED(fDesktop->WindowLocker()); + + WindowStack* stack = GetWindowStack(); + if (stack == NULL) + return false; + + // first collect dirt from the window to add + BRegion dirty; + ::Decorator* otherDecorator = window->Decorator(); + if (otherDecorator != NULL) + dirty.Include(otherDecorator->TitleBarRect()); + ::Decorator* decorator = stack->Decorator(); + if (decorator != NULL) + dirty.Include(decorator->TitleBarRect()); + + int32 position = PositionInStack() + 1; + if (position >= stack->CountWindows()) + position = -1; + if (stack->AddWindow(window, position) == false) + return false; + window->DetachFromWindowStack(false); + window->fCurrentStack.SetTo(stack); + + if (decorator != NULL) + decorator->AddTab(window->Title(), position, &dirty); + + fDesktop->RebuildAndRedrawAfterWindowChange(TopLayerStackWindow(), dirty); + window->SetFocus(window->IsFocus()); + return true; +} + + +Window* +Window::StackedWindowAt(const BPoint& where) +{ + ::Decorator* decorator = Decorator(); + if (decorator == NULL) + return NULL; + + int tab = decorator->TabAt(where); + // if we have a decorator we also have a stack + Window* window = fCurrentStack->WindowAt(tab); + if (window != NULL) + return window; + return this; +} + + +Window* +Window::TopLayerStackWindow() +{ + if (fCurrentStack.Get() == NULL) + return this; + return fCurrentStack->TopLayerWindow(); +} + + +WindowStack* +Window::GetWindowStack() +{ + if (fCurrentStack.Get() == NULL) + return _InitWindowStack(); + return fCurrentStack; +} + + + +bool +Window::MoveToTopStackLayer() +{ + ::Decorator* decorator = Decorator(); + if (decorator == NULL) + return false; + decorator->SetDrawingEngine(fDrawingEngine); + decorator->SetTopTap(PositionInStack()); + return fCurrentStack->MoveToTopLayer(this); +} + + +bool +Window::MoveToStackPosition(int32 to, bool isMoving) +{ + if (fCurrentStack.Get() == NULL) + return false; + int32 index = PositionInStack(); + if (fCurrentStack->Move(index, to) == false) + return false; + + BRegion dirty; + ::Decorator* decorator = Decorator(); + if (decorator && decorator->MoveTab(index, to, isMoving, &dirty) == false) + return false; + + fDesktop->RebuildAndRedrawAfterWindowChange(this, dirty); + return true; +} + + +WindowStack* +Window::_InitWindowStack() +{ + fCurrentStack = NULL; + ::Decorator* decorator = NULL; + if (fLook != B_NO_BORDER_WINDOW_LOOK) + decorator = gDecorManager.AllocateDecorator(this); + + WindowStack* stack = new(std::nothrow) WindowStack(decorator); + if (stack == NULL) + return NULL; + + if (stack->AddWindow(this) != true) { + delete stack; + return NULL; + } + fCurrentStack.SetTo(stack, true); + return stack; +} + + +WindowStack::WindowStack(::Decorator* decorator) + : + fDecorator(decorator) +{ + +} + + +WindowStack::~WindowStack() +{ + delete fDecorator; +} + + +void +WindowStack::SetDecorator(::Decorator* decorator) +{ + delete fDecorator; + fDecorator = decorator; +} + + +::Decorator* +WindowStack::Decorator() +{ + return fDecorator; +} + + +Window* +WindowStack::TopLayerWindow() const +{ + return fWindowLayerOrder.ItemAt(fWindowLayerOrder.CountItems() - 1); +} + + +int32 +WindowStack::CountWindows() +{ + return fWindowList.CountItems(); +} + + +Window* +WindowStack::WindowAt(int32 index) +{ + return fWindowList.ItemAt(index); +} + + +bool +WindowStack::AddWindow(Window* window, int32 position) +{ + if (position >= 0) { + if (fWindowList.AddItem(window, position) == false) + return false; + } else if (fWindowList.AddItem(window) == false) + return false; + + if (fWindowLayerOrder.AddItem(window) == false) { + fWindowList.RemoveItem(window); + return false; + } + return true; +} + + +bool +WindowStack::RemoveWindow(Window* window) +{ + if (fWindowList.RemoveItem(window) == false) + return false; + + fWindowLayerOrder.RemoveItem(window); + return true; +} + + +bool +WindowStack::MoveToTopLayer(Window* window) +{ + int32 index = fWindowLayerOrder.IndexOf(window); + return fWindowLayerOrder.MoveItem(index, + fWindowLayerOrder.CountItems() - 1); +} + + +bool +WindowStack::Move(int32 from, int32 to) +{ + return fWindowList.MoveItem(from, to); +} diff --git a/src/servers/app/Window.h b/src/servers/app/Window.h index 749ba086a4..887f2d8d20 100644 --- a/src/servers/app/Window.h +++ b/src/servers/app/Window.h @@ -1,5 +1,5 @@ /* - * Copyright 2001-2010, Haiku, Inc. + * Copyright 2001-2011, Haiku, Inc. * Distributed under the terms of the MIT license. * * Authors: @@ -20,9 +20,46 @@ #include "WindowList.h" #include +#include #include #include + +class Window; + + +typedef BObjectList StackWindows; + + +class WindowStack : public BReferenceable { +public: + WindowStack(::Decorator* decorator); + ~WindowStack(); + + void SetDecorator(::Decorator* decorator); + ::Decorator* Decorator(); + + const StackWindows& WindowList() const { return fWindowList; } + const StackWindows& LayerOrder() const { return fWindowLayerOrder; } + + Window* TopLayerWindow() const; + + int32 CountWindows(); + Window* WindowAt(int32 index); + bool AddWindow(Window* window, + int32 position = -1); + bool RemoveWindow(Window* window); + + bool MoveToTopLayer(Window* window); + bool Move(int32 from, int32 to); +private: + ::Decorator* fDecorator; + + StackWindows fWindowList; + StackWindows fWindowLayerOrder; +}; + + namespace BPrivate { class PortLink; }; @@ -65,7 +102,7 @@ public: Window* PreviousWindow(int32 index) const; ::Desktop* Desktop() const { return fDesktop; } - ::Decorator* Decorator() const { return fDecorator; } + ::Decorator* Decorator() const; ::ServerWindow* ServerWindow() const { return fWindow; } ::EventTarget& EventTarget() const { return fWindow->EventTarget(); } @@ -88,9 +125,10 @@ public: void GetBorderRegion(BRegion* region); void GetContentRegion(BRegion* region); - void MoveBy(int32 x, int32 y); + void MoveBy(int32 x, int32 y, bool moveStack = true); void ResizeBy(int32 x, int32 y, - BRegion* dirtyRegion); + BRegion* dirtyRegion, + bool resizeStack = true); void ScrollViewBy(View* view, int32 dx, int32 dy); @@ -191,7 +229,8 @@ public: int32* minHeight, int32* maxHeight) const; // 0.0 -> left .... 1.0 -> right - bool SetTabLocation(float location, BRegion& dirty); + bool SetTabLocation(float location, bool isShifting, + BRegion& dirty); float TabLocation() const; bool SetDecoratorSettings(const BMessage& settings, @@ -254,6 +293,19 @@ public: static uint32 ValidWindowFlags(); static uint32 ValidWindowFlags(window_feel feel); + // Window stack methods. + WindowStack* GetWindowStack(); + + bool DetachFromWindowStack( + bool ownStackNeeded = true); + bool AddWindowToStack(Window* window); + Window* StackedWindowAt(const BPoint& where); + Window* TopLayerStackWindow(); + + int32 PositionInStack() const; + bool MoveToTopStackLayer(); + bool MoveToStackPosition(int32 index, + bool isMoving); protected: void _ShiftPartOfRegion(BRegion* region, BRegion* regionToShift, int32 xOffset, @@ -307,7 +359,6 @@ protected: BObjectList fSubsets; WindowBehaviour* fWindowBehaviour; - ::Decorator* fDecorator; View* fTopView; ::ServerWindow* fWindow; DrawingEngine* fDrawingEngine; @@ -377,6 +428,12 @@ protected: int32 fWorkspacesViewCount; friend class DecorManager; + +private: + WindowStack* _InitWindowStack(); + + BReference fCurrentStack; }; + #endif // WINDOW_H diff --git a/src/servers/app/WorkspacesView.cpp b/src/servers/app/WorkspacesView.cpp index efcf439ef2..76045b8dca 100644 --- a/src/servers/app/WorkspacesView.cpp +++ b/src/servers/app/WorkspacesView.cpp @@ -177,7 +177,7 @@ WorkspacesView::_DrawWindow(DrawingEngine* drawingEngine, Decorator *decorator = window->Decorator(); BRect tabFrame(0, 0, 0, 0); if (decorator != NULL) - tabFrame = decorator->TabRect(); + tabFrame = decorator->TitleBarRect(); tabFrame = _WindowFrame(workspaceFrame, screenFrame, tabFrame, tabFrame.LeftTop() - offset);