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);