1*5900e8ecSAndrew Rist /************************************************************** 2cdf0e10cSrcweir * 3*5900e8ecSAndrew Rist * Licensed to the Apache Software Foundation (ASF) under one 4*5900e8ecSAndrew Rist * or more contributor license agreements. See the NOTICE file 5*5900e8ecSAndrew Rist * distributed with this work for additional information 6*5900e8ecSAndrew Rist * regarding copyright ownership. The ASF licenses this file 7*5900e8ecSAndrew Rist * to you under the Apache License, Version 2.0 (the 8*5900e8ecSAndrew Rist * "License"); you may not use this file except in compliance 9*5900e8ecSAndrew Rist * with the License. You may obtain a copy of the License at 10*5900e8ecSAndrew Rist * 11*5900e8ecSAndrew Rist * http://www.apache.org/licenses/LICENSE-2.0 12*5900e8ecSAndrew Rist * 13*5900e8ecSAndrew Rist * Unless required by applicable law or agreed to in writing, 14*5900e8ecSAndrew Rist * software distributed under the License is distributed on an 15*5900e8ecSAndrew Rist * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16*5900e8ecSAndrew Rist * KIND, either express or implied. See the License for the 17*5900e8ecSAndrew Rist * specific language governing permissions and limitations 18*5900e8ecSAndrew Rist * under the License. 19*5900e8ecSAndrew Rist * 20*5900e8ecSAndrew Rist *************************************************************/ 21*5900e8ecSAndrew Rist 22*5900e8ecSAndrew Rist 23cdf0e10cSrcweir 24cdf0e10cSrcweir #include "precompiled_svtools.hxx" 25cdf0e10cSrcweir 26cdf0e10cSrcweir #include "tabbargeometry.hxx" 27cdf0e10cSrcweir 28cdf0e10cSrcweir #include <basegfx/range/b2drange.hxx> 29cdf0e10cSrcweir #include <basegfx/matrix/b2dhommatrix.hxx> 30cdf0e10cSrcweir #include <basegfx/numeric/ftools.hxx> 31cdf0e10cSrcweir 32cdf0e10cSrcweir #include <vcl/window.hxx> 33cdf0e10cSrcweir 34cdf0e10cSrcweir #include <algorithm> 35cdf0e10cSrcweir 36cdf0e10cSrcweir // the width (or height, depending on alignment) of the scroll buttons 37cdf0e10cSrcweir #define BUTTON_FLOW_WIDTH 20 38cdf0e10cSrcweir // the space between the scroll buttons and the items 39cdf0e10cSrcweir #define BUTTON_FLOW_SPACE 2 40cdf0e10cSrcweir // outer space to apply between the tab bar borders and any content. Note that those refer to a "normalized" geometry, 41cdf0e10cSrcweir // i.e. if the tab bar were aligned at the top 42cdf0e10cSrcweir #define OUTER_SPACE_LEFT 2 43cdf0e10cSrcweir #define OUTER_SPACE_TOP 4 44cdf0e10cSrcweir #define OUTER_SPACE_RIGHT 4 45cdf0e10cSrcweir #define OUTER_SPACE_BOTTOM 2 46cdf0e10cSrcweir 47cdf0e10cSrcweir // outer space to apply between the area for the items, and the actual items. They refer to a normalized geometry. 48cdf0e10cSrcweir #define ITEMS_INSET_LEFT 4 49cdf0e10cSrcweir #define ITEMS_INSET_TOP 3 50cdf0e10cSrcweir #define ITEMS_INSET_RIGHT 4 51cdf0e10cSrcweir #define ITEMS_INSET_BOTTOM 0 52cdf0e10cSrcweir 53cdf0e10cSrcweir //...................................................................................................................... 54cdf0e10cSrcweir namespace svt 55cdf0e10cSrcweir { 56cdf0e10cSrcweir //...................................................................................................................... 57cdf0e10cSrcweir 58cdf0e10cSrcweir //================================================================================================================== 59cdf0e10cSrcweir //= helper 60cdf0e10cSrcweir //================================================================================================================== 61cdf0e10cSrcweir namespace 62cdf0e10cSrcweir { 63cdf0e10cSrcweir //-------------------------------------------------------------------------------------------------------------- lcl_transform(Rectangle & io_rRect,const::basegfx::B2DHomMatrix & i_rTransformation)64cdf0e10cSrcweir static void lcl_transform( Rectangle& io_rRect, const ::basegfx::B2DHomMatrix& i_rTransformation ) 65cdf0e10cSrcweir { 66cdf0e10cSrcweir ::basegfx::B2DRange aRect( io_rRect.Left(), io_rRect.Top(), io_rRect.Right(), io_rRect.Bottom() ); 67cdf0e10cSrcweir aRect.transform( i_rTransformation ); 68cdf0e10cSrcweir io_rRect.Left() = long( aRect.getMinX() ); 69cdf0e10cSrcweir io_rRect.Top() = long( aRect.getMinY() ); 70cdf0e10cSrcweir io_rRect.Right() = long( aRect.getMaxX() ); 71cdf0e10cSrcweir io_rRect.Bottom() = long( aRect.getMaxY() ); 72cdf0e10cSrcweir } 73cdf0e10cSrcweir 74cdf0e10cSrcweir //-------------------------------------------------------------------------------------------------------------- 75cdf0e10cSrcweir /** transforms the given, possible rotated playground, 76cdf0e10cSrcweir */ lcl_rotate(const Rectangle & i_rReference,Rectangle & io_rArea,const bool i_bRight)77cdf0e10cSrcweir void lcl_rotate( const Rectangle& i_rReference, Rectangle& io_rArea, const bool i_bRight ) 78cdf0e10cSrcweir { 79cdf0e10cSrcweir // step 1: move the to-be-upper-left corner (left/bottom) of the rectangle to (0,0) 80cdf0e10cSrcweir ::basegfx::B2DHomMatrix aTransformation; 81cdf0e10cSrcweir aTransformation.translate( 82cdf0e10cSrcweir i_bRight ? -i_rReference.Left() : -i_rReference.Right(), 83cdf0e10cSrcweir i_bRight ? -i_rReference.Bottom() : -i_rReference.Top() 84cdf0e10cSrcweir ); 85cdf0e10cSrcweir 86cdf0e10cSrcweir // step 2: rotate by -90 degrees 87cdf0e10cSrcweir aTransformation.rotate( i_bRight ? +F_PI2 : -F_PI2 ); 88cdf0e10cSrcweir // note: 89cdf0e10cSrcweir // on the screen, the ordinate goes top-down, while basegfx calculates in a system where the 90cdf0e10cSrcweir // ordinate goes bottom-up; thus the "wrong" sign before F_PI2 here 91cdf0e10cSrcweir 92cdf0e10cSrcweir // step 3: move back to original coordinates 93cdf0e10cSrcweir aTransformation.translate( i_rReference.Left(), i_rReference.Top() ); 94cdf0e10cSrcweir 95cdf0e10cSrcweir // apply transformation 96cdf0e10cSrcweir lcl_transform( io_rArea, aTransformation ); 97cdf0e10cSrcweir } 98cdf0e10cSrcweir } 99cdf0e10cSrcweir 100cdf0e10cSrcweir //------------------------------------------------------------------------------------------------------------------ lcl_mirrorHorizontally(const Rectangle & i_rReferenceArea,Rectangle & io_rArea)101cdf0e10cSrcweir void lcl_mirrorHorizontally( const Rectangle& i_rReferenceArea, Rectangle& io_rArea ) 102cdf0e10cSrcweir { 103cdf0e10cSrcweir io_rArea.Left() = i_rReferenceArea.Left() + i_rReferenceArea.Right() - io_rArea.Left(); 104cdf0e10cSrcweir io_rArea.Right() = i_rReferenceArea.Left() + i_rReferenceArea.Right() - io_rArea.Right(); 105cdf0e10cSrcweir ::std::swap( io_rArea.Left(), io_rArea.Right() ); 106cdf0e10cSrcweir } 107cdf0e10cSrcweir 108cdf0e10cSrcweir //------------------------------------------------------------------------------------------------------------------ lcl_mirrorVertically(const Rectangle & i_rReferenceArea,Rectangle & io_rArea)109cdf0e10cSrcweir void lcl_mirrorVertically( const Rectangle& i_rReferenceArea, Rectangle& io_rArea ) 110cdf0e10cSrcweir { 111cdf0e10cSrcweir io_rArea.Top() = i_rReferenceArea.Top() + i_rReferenceArea.Bottom() - io_rArea.Top(); 112cdf0e10cSrcweir io_rArea.Bottom() = i_rReferenceArea.Top() + i_rReferenceArea.Bottom() - io_rArea.Bottom(); 113cdf0e10cSrcweir ::std::swap( io_rArea.Top(), io_rArea.Bottom() ); 114cdf0e10cSrcweir } 115cdf0e10cSrcweir 116cdf0e10cSrcweir //================================================================================================================== 117cdf0e10cSrcweir //= NormalizedArea 118cdf0e10cSrcweir //================================================================================================================== 119cdf0e10cSrcweir //------------------------------------------------------------------------------------------------------------------ NormalizedArea()120cdf0e10cSrcweir NormalizedArea::NormalizedArea() 121cdf0e10cSrcweir :m_aReference() 122cdf0e10cSrcweir { 123cdf0e10cSrcweir } 124cdf0e10cSrcweir 125cdf0e10cSrcweir //------------------------------------------------------------------------------------------------------------------ NormalizedArea(const Rectangle & i_rReference,const bool i_bIsVertical)126cdf0e10cSrcweir NormalizedArea::NormalizedArea( const Rectangle& i_rReference, const bool i_bIsVertical ) 127cdf0e10cSrcweir :m_aReference( i_bIsVertical ? Rectangle( i_rReference.TopLeft(), Size( i_rReference.GetHeight(), i_rReference.GetWidth() ) ) : i_rReference ) 128cdf0e10cSrcweir { 129cdf0e10cSrcweir } 130cdf0e10cSrcweir 131cdf0e10cSrcweir //------------------------------------------------------------------------------------------------------------------ getTransformed(const Rectangle & i_rArea,const TabAlignment i_eTargetAlignment) const132cdf0e10cSrcweir Rectangle NormalizedArea::getTransformed( const Rectangle& i_rArea, const TabAlignment i_eTargetAlignment ) const 133cdf0e10cSrcweir { 134cdf0e10cSrcweir Rectangle aResult( i_rArea ); 135cdf0e10cSrcweir 136cdf0e10cSrcweir if ( ( i_eTargetAlignment == TABS_RIGHT ) 137cdf0e10cSrcweir || ( i_eTargetAlignment == TABS_LEFT ) 138cdf0e10cSrcweir ) 139cdf0e10cSrcweir { 140cdf0e10cSrcweir lcl_rotate( m_aReference, aResult, true ); 141cdf0e10cSrcweir 142cdf0e10cSrcweir if ( i_eTargetAlignment == TABS_LEFT ) 143cdf0e10cSrcweir { 144cdf0e10cSrcweir Rectangle aReference( m_aReference ); 145cdf0e10cSrcweir aReference.Transpose(); 146cdf0e10cSrcweir lcl_mirrorHorizontally( aReference, aResult ); 147cdf0e10cSrcweir } 148cdf0e10cSrcweir } 149cdf0e10cSrcweir else 150cdf0e10cSrcweir if ( i_eTargetAlignment == TABS_BOTTOM ) 151cdf0e10cSrcweir { 152cdf0e10cSrcweir lcl_mirrorVertically( m_aReference, aResult ); 153cdf0e10cSrcweir } 154cdf0e10cSrcweir 155cdf0e10cSrcweir return aResult; 156cdf0e10cSrcweir } 157cdf0e10cSrcweir 158cdf0e10cSrcweir //------------------------------------------------------------------------------------------------------------------ getNormalized(const Rectangle & i_rArea,const TabAlignment i_eTargetAlignment) const159cdf0e10cSrcweir Rectangle NormalizedArea::getNormalized( const Rectangle& i_rArea, const TabAlignment i_eTargetAlignment ) const 160cdf0e10cSrcweir { 161cdf0e10cSrcweir Rectangle aResult( i_rArea ); 162cdf0e10cSrcweir 163cdf0e10cSrcweir if ( ( i_eTargetAlignment == TABS_RIGHT ) 164cdf0e10cSrcweir || ( i_eTargetAlignment == TABS_LEFT ) 165cdf0e10cSrcweir ) 166cdf0e10cSrcweir { 167cdf0e10cSrcweir Rectangle aReference( m_aReference ); 168cdf0e10cSrcweir lcl_rotate( m_aReference, aReference, true ); 169cdf0e10cSrcweir 170cdf0e10cSrcweir if ( i_eTargetAlignment == TABS_LEFT ) 171cdf0e10cSrcweir { 172cdf0e10cSrcweir lcl_mirrorHorizontally( aReference, aResult ); 173cdf0e10cSrcweir } 174cdf0e10cSrcweir 175cdf0e10cSrcweir lcl_rotate( aReference, aResult, false ); 176cdf0e10cSrcweir } 177cdf0e10cSrcweir else 178cdf0e10cSrcweir if ( i_eTargetAlignment == TABS_BOTTOM ) 179cdf0e10cSrcweir { 180cdf0e10cSrcweir lcl_mirrorVertically( m_aReference, aResult ); 181cdf0e10cSrcweir } 182cdf0e10cSrcweir return aResult; 183cdf0e10cSrcweir } 184cdf0e10cSrcweir 185cdf0e10cSrcweir //================================================================================================================== 186cdf0e10cSrcweir //= TabBarGeometry 187cdf0e10cSrcweir //================================================================================================================== 188cdf0e10cSrcweir //------------------------------------------------------------------------------------------------------------------ TabBarGeometry(const TabItemContent i_eItemContent)189cdf0e10cSrcweir TabBarGeometry::TabBarGeometry( const TabItemContent i_eItemContent ) 190cdf0e10cSrcweir :m_eTabItemContent( i_eItemContent ) 191cdf0e10cSrcweir ,m_aItemsInset() 192cdf0e10cSrcweir ,m_aButtonBackRect() 193cdf0e10cSrcweir ,m_aItemsRect() 194cdf0e10cSrcweir ,m_aButtonForwardRect() 195cdf0e10cSrcweir { 196cdf0e10cSrcweir m_aItemsInset.Left() = ITEMS_INSET_LEFT; 197cdf0e10cSrcweir m_aItemsInset.Top() = ITEMS_INSET_TOP; 198cdf0e10cSrcweir m_aItemsInset.Right() = ITEMS_INSET_RIGHT; 199cdf0e10cSrcweir m_aItemsInset.Bottom() = ITEMS_INSET_BOTTOM; 200cdf0e10cSrcweir } 201cdf0e10cSrcweir 202cdf0e10cSrcweir //------------------------------------------------------------------------------------------------------------------ ~TabBarGeometry()203cdf0e10cSrcweir TabBarGeometry::~TabBarGeometry() 204cdf0e10cSrcweir { 205cdf0e10cSrcweir } 206cdf0e10cSrcweir 207cdf0e10cSrcweir //------------------------------------------------------------------------------------------------------------------ impl_fitItems(ItemDescriptors & io_rItems) const208cdf0e10cSrcweir bool TabBarGeometry::impl_fitItems( ItemDescriptors& io_rItems ) const 209cdf0e10cSrcweir { 210cdf0e10cSrcweir if ( io_rItems.empty() ) 211cdf0e10cSrcweir // nothing to do, "no items" perfectly fit into any space we have ... 212cdf0e10cSrcweir return true; 213cdf0e10cSrcweir 214cdf0e10cSrcweir // the available size 215cdf0e10cSrcweir Size aOutputSize( getItemsRect().GetSize() ); 216cdf0e10cSrcweir // shrunk by the outer space 217cdf0e10cSrcweir aOutputSize.Width() -= m_aItemsInset.Right(); 218cdf0e10cSrcweir aOutputSize.Height() -= m_aItemsInset.Bottom(); 219cdf0e10cSrcweir const Rectangle aFitInto( Point( 0, 0 ), aOutputSize ); 220cdf0e10cSrcweir 221cdf0e10cSrcweir TabItemContent eItemContent( getItemContent() ); 222cdf0e10cSrcweir if ( eItemContent == TABITEM_AUTO ) 223cdf0e10cSrcweir { 224cdf0e10cSrcweir // the "content modes" to try 225cdf0e10cSrcweir TabItemContent eTryThis[] = 226cdf0e10cSrcweir { 227cdf0e10cSrcweir TABITEM_IMAGE_ONLY, // assumed to have the smallest rects 228cdf0e10cSrcweir TABITEM_TEXT_ONLY, 229cdf0e10cSrcweir TABITEM_IMAGE_AND_TEXT // assumed to have the largest rects 230cdf0e10cSrcweir }; 231cdf0e10cSrcweir 232cdf0e10cSrcweir 233cdf0e10cSrcweir // determine which of the different version fits 234cdf0e10cSrcweir eItemContent = eTryThis[0]; 235cdf0e10cSrcweir size_t nTryIndex = 2; 236cdf0e10cSrcweir while ( nTryIndex > 0 ) 237cdf0e10cSrcweir { 238cdf0e10cSrcweir const Point aBottomRight( io_rItems.rbegin()->GetRect( eTryThis[ nTryIndex ] ).BottomRight() ); 239cdf0e10cSrcweir if ( aFitInto.IsInside( aBottomRight ) ) 240cdf0e10cSrcweir { 241cdf0e10cSrcweir eItemContent = eTryThis[ nTryIndex ]; 242cdf0e10cSrcweir break; 243cdf0e10cSrcweir } 244cdf0e10cSrcweir --nTryIndex; 245cdf0e10cSrcweir } 246cdf0e10cSrcweir } 247cdf0e10cSrcweir 248cdf0e10cSrcweir // propagate to the items 249cdf0e10cSrcweir for ( ItemDescriptors::iterator item = io_rItems.begin(); 250cdf0e10cSrcweir item != io_rItems.end(); 251cdf0e10cSrcweir ++item 252cdf0e10cSrcweir ) 253cdf0e10cSrcweir { 254cdf0e10cSrcweir item->eContent = eItemContent; 255cdf0e10cSrcweir } 256cdf0e10cSrcweir 257cdf0e10cSrcweir const ItemDescriptor& rLastItem( *io_rItems.rbegin() ); 258cdf0e10cSrcweir const Point aLastItemBottomRight( rLastItem.GetCurrentRect().BottomRight() ); 259cdf0e10cSrcweir return aFitInto.Left() <= aLastItemBottomRight.X() 260cdf0e10cSrcweir && aFitInto.Right() >= aLastItemBottomRight.X(); 261cdf0e10cSrcweir } 262cdf0e10cSrcweir 263cdf0e10cSrcweir //------------------------------------------------------------------------------------------------------------------ getOptimalSize(ItemDescriptors & io_rItems,const bool i_bMinimalSize) const264cdf0e10cSrcweir Size TabBarGeometry::getOptimalSize( ItemDescriptors& io_rItems, const bool i_bMinimalSize ) const 265cdf0e10cSrcweir { 266cdf0e10cSrcweir if ( io_rItems.empty() ) 267cdf0e10cSrcweir return Size( 268cdf0e10cSrcweir m_aItemsInset.Left() + m_aItemsInset.Right(), 269cdf0e10cSrcweir m_aItemsInset.Top() + m_aItemsInset.Bottom() 270cdf0e10cSrcweir ); 271cdf0e10cSrcweir 272cdf0e10cSrcweir // the rect of the last item 273cdf0e10cSrcweir const Rectangle& rLastItemRect( i_bMinimalSize ? io_rItems.rbegin()->aIconOnlyArea : io_rItems.rbegin()->aCompleteArea ); 274cdf0e10cSrcweir return Size( 275cdf0e10cSrcweir rLastItemRect.Left() + 1 + m_aItemsInset.Right(), 276cdf0e10cSrcweir rLastItemRect.Top() + 1 + rLastItemRect.Bottom() + m_aItemsInset.Bottom() 277cdf0e10cSrcweir ); 278cdf0e10cSrcweir } 279cdf0e10cSrcweir 280cdf0e10cSrcweir //------------------------------------------------------------------------------------------------------------------ relayout(const Size & i_rActualOutputSize,ItemDescriptors & io_rItems)281cdf0e10cSrcweir void TabBarGeometry::relayout( const Size& i_rActualOutputSize, ItemDescriptors& io_rItems ) 282cdf0e10cSrcweir { 283cdf0e10cSrcweir // assume all items fit 284cdf0e10cSrcweir Point aButtonBackPos( OUTER_SPACE_LEFT, OUTER_SPACE_TOP ); 285cdf0e10cSrcweir m_aButtonBackRect = Rectangle( aButtonBackPos, Size( 1, 1 ) ); 286cdf0e10cSrcweir m_aButtonBackRect.SetEmpty(); 287cdf0e10cSrcweir 288cdf0e10cSrcweir Point aButtonForwardPos( i_rActualOutputSize.Width(), OUTER_SPACE_TOP ); 289cdf0e10cSrcweir m_aButtonForwardRect = Rectangle( aButtonForwardPos, Size( 1, 1 ) ); 290cdf0e10cSrcweir m_aButtonForwardRect.SetEmpty(); 291cdf0e10cSrcweir 292cdf0e10cSrcweir Point aItemsPos( OUTER_SPACE_LEFT, 0 ); 293cdf0e10cSrcweir Size aItemsSize( i_rActualOutputSize.Width() - OUTER_SPACE_LEFT - OUTER_SPACE_RIGHT, i_rActualOutputSize.Height() ); 294cdf0e10cSrcweir m_aItemsRect = Rectangle( aItemsPos, aItemsSize ); 295cdf0e10cSrcweir 296cdf0e10cSrcweir if ( !impl_fitItems( io_rItems ) ) 297cdf0e10cSrcweir { 298cdf0e10cSrcweir // assumption was wrong, the items do not fit => calculate rects for the scroll buttons 299cdf0e10cSrcweir const Size aButtonSize( BUTTON_FLOW_WIDTH, i_rActualOutputSize.Height() - OUTER_SPACE_TOP - OUTER_SPACE_BOTTOM ); 300cdf0e10cSrcweir 301cdf0e10cSrcweir aButtonBackPos = Point( OUTER_SPACE_LEFT, OUTER_SPACE_TOP ); 302cdf0e10cSrcweir m_aButtonBackRect = Rectangle( aButtonBackPos, aButtonSize ); 303cdf0e10cSrcweir 304cdf0e10cSrcweir aButtonForwardPos = Point( i_rActualOutputSize.Width() - BUTTON_FLOW_WIDTH - OUTER_SPACE_RIGHT, OUTER_SPACE_TOP ); 305cdf0e10cSrcweir m_aButtonForwardRect = Rectangle( aButtonForwardPos, aButtonSize ); 306cdf0e10cSrcweir 307cdf0e10cSrcweir aItemsPos.X() = aButtonBackPos.X() + aButtonSize.Width() + BUTTON_FLOW_SPACE; 308cdf0e10cSrcweir aItemsSize.Width() = aButtonForwardPos.X() - BUTTON_FLOW_SPACE - aItemsPos.X(); 309cdf0e10cSrcweir m_aItemsRect = Rectangle( aItemsPos, aItemsSize ); 310cdf0e10cSrcweir 311cdf0e10cSrcweir // fit items, again. In the TABITEM_AUTO case, the smaller playground for the items might lead to another 312cdf0e10cSrcweir // item content. 313cdf0e10cSrcweir impl_fitItems( io_rItems ); 314cdf0e10cSrcweir } 315cdf0e10cSrcweir } 316cdf0e10cSrcweir 317cdf0e10cSrcweir //------------------------------------------------------------------------------------------------------------------ getFirstItemPosition() const318cdf0e10cSrcweir Point TabBarGeometry::getFirstItemPosition() const 319cdf0e10cSrcweir { 320cdf0e10cSrcweir return Point( m_aItemsInset.Left(), m_aItemsInset.Top() ); 321cdf0e10cSrcweir } 322cdf0e10cSrcweir 323cdf0e10cSrcweir //...................................................................................................................... 324cdf0e10cSrcweir } // namespace svt 325cdf0e10cSrcweir //...................................................................................................................... 326