1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include "tabbargeometry.hxx"
23 #include <basegfx/range/b2drange.hxx>
24 #include <basegfx/matrix/b2dhommatrix.hxx>
25 #include <basegfx/numeric/ftools.hxx>
27 #include <vcl/window.hxx>
31 // the width (or height, depending on alignment) of the scroll buttons
32 #define BUTTON_FLOW_WIDTH 20
33 // the space between the scroll buttons and the items
34 #define BUTTON_FLOW_SPACE 2
35 // outer space to apply between the tab bar borders and any content. Note that those refer to a "normalized" geometry,
36 // i.e. if the tab bar were aligned at the top
37 #define OUTER_SPACE_LEFT 2
38 #define OUTER_SPACE_TOP 4
39 #define OUTER_SPACE_RIGHT 4
40 #define OUTER_SPACE_BOTTOM 2
42 // outer space to apply between the area for the items, and the actual items. They refer to a normalized geometry.
43 #define ITEMS_INSET_LEFT 4
44 #define ITEMS_INSET_TOP 3
45 #define ITEMS_INSET_RIGHT 4
46 #define ITEMS_INSET_BOTTOM 0
48 //......................................................................................................................
51 //......................................................................................................................
53 //==================================================================================================================
55 //==================================================================================================================
58 //--------------------------------------------------------------------------------------------------------------
59 static void lcl_transform( Rectangle
& io_rRect
, const ::basegfx::B2DHomMatrix
& i_rTransformation
)
61 ::basegfx::B2DRange
aRect( io_rRect
.Left(), io_rRect
.Top(), io_rRect
.Right(), io_rRect
.Bottom() );
62 aRect
.transform( i_rTransformation
);
63 io_rRect
.Left() = long( aRect
.getMinX() );
64 io_rRect
.Top() = long( aRect
.getMinY() );
65 io_rRect
.Right() = long( aRect
.getMaxX() );
66 io_rRect
.Bottom() = long( aRect
.getMaxY() );
69 //--------------------------------------------------------------------------------------------------------------
70 /** transforms the given, possible rotated playground,
72 void lcl_rotate( const Rectangle
& i_rReference
, Rectangle
& io_rArea
, const bool i_bRight
)
74 // step 1: move the to-be-upper-left corner (left/bottom) of the rectangle to (0,0)
75 ::basegfx::B2DHomMatrix aTransformation
;
76 aTransformation
.translate(
77 i_bRight
? -i_rReference
.Left() : -i_rReference
.Right(),
78 i_bRight
? -i_rReference
.Bottom() : -i_rReference
.Top()
81 // step 2: rotate by -90 degrees
82 aTransformation
.rotate( i_bRight
? +F_PI2
: -F_PI2
);
84 // on the screen, the ordinate goes top-down, while basegfx calculates in a system where the
85 // ordinate goes bottom-up; thus the "wrong" sign before F_PI2 here
87 // step 3: move back to original coordinates
88 aTransformation
.translate( i_rReference
.Left(), i_rReference
.Top() );
90 // apply transformation
91 lcl_transform( io_rArea
, aTransformation
);
95 //------------------------------------------------------------------------------------------------------------------
96 void lcl_mirrorHorizontally( const Rectangle
& i_rReferenceArea
, Rectangle
& io_rArea
)
98 io_rArea
.Left() = i_rReferenceArea
.Left() + i_rReferenceArea
.Right() - io_rArea
.Left();
99 io_rArea
.Right() = i_rReferenceArea
.Left() + i_rReferenceArea
.Right() - io_rArea
.Right();
100 ::std::swap( io_rArea
.Left(), io_rArea
.Right() );
103 //------------------------------------------------------------------------------------------------------------------
104 void lcl_mirrorVertically( const Rectangle
& i_rReferenceArea
, Rectangle
& io_rArea
)
106 io_rArea
.Top() = i_rReferenceArea
.Top() + i_rReferenceArea
.Bottom() - io_rArea
.Top();
107 io_rArea
.Bottom() = i_rReferenceArea
.Top() + i_rReferenceArea
.Bottom() - io_rArea
.Bottom();
108 ::std::swap( io_rArea
.Top(), io_rArea
.Bottom() );
111 //==================================================================================================================
113 //==================================================================================================================
114 //------------------------------------------------------------------------------------------------------------------
115 NormalizedArea::NormalizedArea()
120 //------------------------------------------------------------------------------------------------------------------
121 NormalizedArea::NormalizedArea( const Rectangle
& i_rReference
, const bool i_bIsVertical
)
122 :m_aReference( i_bIsVertical
? Rectangle( i_rReference
.TopLeft(), Size( i_rReference
.GetHeight(), i_rReference
.GetWidth() ) ) : i_rReference
)
126 //------------------------------------------------------------------------------------------------------------------
127 Rectangle
NormalizedArea::getTransformed( const Rectangle
& i_rArea
, const TabAlignment i_eTargetAlignment
) const
129 Rectangle
aResult( i_rArea
);
131 if ( ( i_eTargetAlignment
== TABS_RIGHT
)
132 || ( i_eTargetAlignment
== TABS_LEFT
)
135 lcl_rotate( m_aReference
, aResult
, true );
137 if ( i_eTargetAlignment
== TABS_LEFT
)
139 Rectangle
aReference( m_aReference
);
140 aReference
.Transpose();
141 lcl_mirrorHorizontally( aReference
, aResult
);
144 else if ( i_eTargetAlignment
== TABS_BOTTOM
)
146 lcl_mirrorVertically( m_aReference
, aResult
);
152 //------------------------------------------------------------------------------------------------------------------
153 Rectangle
NormalizedArea::getNormalized( const Rectangle
& i_rArea
, const TabAlignment i_eTargetAlignment
) const
155 Rectangle
aResult( i_rArea
);
157 if ( ( i_eTargetAlignment
== TABS_RIGHT
)
158 || ( i_eTargetAlignment
== TABS_LEFT
)
161 Rectangle
aReference( m_aReference
);
162 lcl_rotate( m_aReference
, aReference
, true );
164 if ( i_eTargetAlignment
== TABS_LEFT
)
166 lcl_mirrorHorizontally( aReference
, aResult
);
169 lcl_rotate( aReference
, aResult
, false );
171 else if ( i_eTargetAlignment
== TABS_BOTTOM
)
173 lcl_mirrorVertically( m_aReference
, aResult
);
178 //==================================================================================================================
180 //==================================================================================================================
181 //------------------------------------------------------------------------------------------------------------------
182 TabBarGeometry::TabBarGeometry( const TabItemContent i_eItemContent
)
183 :m_eTabItemContent( i_eItemContent
)
187 ,m_aButtonForwardRect()
189 m_aItemsInset
.Left() = ITEMS_INSET_LEFT
;
190 m_aItemsInset
.Top() = ITEMS_INSET_TOP
;
191 m_aItemsInset
.Right() = ITEMS_INSET_RIGHT
;
192 m_aItemsInset
.Bottom() = ITEMS_INSET_BOTTOM
;
195 //------------------------------------------------------------------------------------------------------------------
196 TabBarGeometry::~TabBarGeometry()
200 //------------------------------------------------------------------------------------------------------------------
201 bool TabBarGeometry::impl_fitItems( ItemDescriptors
& io_rItems
) const
203 if ( io_rItems
.empty() )
204 // nothing to do, "no items" perfectly fit into any space we have ...
207 // the available size
208 Size
aOutputSize( getItemsRect().GetSize() );
209 // shrunk by the outer space
210 aOutputSize
.Width() -= m_aItemsInset
.Right();
211 aOutputSize
.Height() -= m_aItemsInset
.Bottom();
212 const Rectangle
aFitInto( Point( 0, 0 ), aOutputSize
);
214 TabItemContent
eItemContent( getItemContent() );
215 if ( eItemContent
== TABITEM_AUTO
)
217 // the "content modes" to try
218 TabItemContent eTryThis
[] =
220 TABITEM_IMAGE_ONLY
, // assumed to have the smallest rects
222 TABITEM_IMAGE_AND_TEXT
// assumed to have the largest rects
226 // determine which of the different version fits
227 eItemContent
= eTryThis
[0];
228 size_t nTryIndex
= 2;
229 while ( nTryIndex
> 0 )
231 const Point
aBottomRight( io_rItems
.rbegin()->GetRect( eTryThis
[ nTryIndex
] ).BottomRight() );
232 if ( aFitInto
.IsInside( aBottomRight
) )
234 eItemContent
= eTryThis
[ nTryIndex
];
241 // propagate to the items
242 for ( ItemDescriptors::iterator item
= io_rItems
.begin();
243 item
!= io_rItems
.end();
247 item
->eContent
= eItemContent
;
250 const ItemDescriptor
& rLastItem( *io_rItems
.rbegin() );
251 const Point
aLastItemBottomRight( rLastItem
.GetCurrentRect().BottomRight() );
252 return aFitInto
.Left() <= aLastItemBottomRight
.X()
253 && aFitInto
.Right() >= aLastItemBottomRight
.X();
256 //------------------------------------------------------------------------------------------------------------------
257 Size
TabBarGeometry::getOptimalSize(ItemDescriptors
& io_rItems
) const
259 if ( io_rItems
.empty() )
261 m_aItemsInset
.Left() + m_aItemsInset
.Right(),
262 m_aItemsInset
.Top() + m_aItemsInset
.Bottom()
265 // the rect of the last item
266 const Rectangle
& rLastItemRect(io_rItems
.rbegin()->aCompleteArea
);
268 rLastItemRect
.Left() + 1 + m_aItemsInset
.Right(),
269 rLastItemRect
.Top() + 1 + rLastItemRect
.Bottom() + m_aItemsInset
.Bottom()
273 //------------------------------------------------------------------------------------------------------------------
274 void TabBarGeometry::relayout( const Size
& i_rActualOutputSize
, ItemDescriptors
& io_rItems
)
276 // assume all items fit
277 Point
aButtonBackPos( OUTER_SPACE_LEFT
, OUTER_SPACE_TOP
);
278 m_aButtonBackRect
= Rectangle( aButtonBackPos
, Size( 1, 1 ) );
279 m_aButtonBackRect
.SetEmpty();
281 Point
aButtonForwardPos( i_rActualOutputSize
.Width(), OUTER_SPACE_TOP
);
282 m_aButtonForwardRect
= Rectangle( aButtonForwardPos
, Size( 1, 1 ) );
283 m_aButtonForwardRect
.SetEmpty();
285 Point
aItemsPos( OUTER_SPACE_LEFT
, 0 );
286 Size
aItemsSize( i_rActualOutputSize
.Width() - OUTER_SPACE_LEFT
- OUTER_SPACE_RIGHT
, i_rActualOutputSize
.Height() );
287 m_aItemsRect
= Rectangle( aItemsPos
, aItemsSize
);
289 if ( !impl_fitItems( io_rItems
) )
291 // assumption was wrong, the items do not fit => calculate rects for the scroll buttons
292 const Size
aButtonSize( BUTTON_FLOW_WIDTH
, i_rActualOutputSize
.Height() - OUTER_SPACE_TOP
- OUTER_SPACE_BOTTOM
);
294 aButtonBackPos
= Point( OUTER_SPACE_LEFT
, OUTER_SPACE_TOP
);
295 m_aButtonBackRect
= Rectangle( aButtonBackPos
, aButtonSize
);
297 aButtonForwardPos
= Point( i_rActualOutputSize
.Width() - BUTTON_FLOW_WIDTH
- OUTER_SPACE_RIGHT
, OUTER_SPACE_TOP
);
298 m_aButtonForwardRect
= Rectangle( aButtonForwardPos
, aButtonSize
);
300 aItemsPos
.X() = aButtonBackPos
.X() + aButtonSize
.Width() + BUTTON_FLOW_SPACE
;
301 aItemsSize
.Width() = aButtonForwardPos
.X() - BUTTON_FLOW_SPACE
- aItemsPos
.X();
302 m_aItemsRect
= Rectangle( aItemsPos
, aItemsSize
);
304 // fit items, again. In the TABITEM_AUTO case, the smaller playground for the items might lead to another
306 impl_fitItems( io_rItems
);
310 //------------------------------------------------------------------------------------------------------------------
311 Point
TabBarGeometry::getFirstItemPosition() const
313 return Point( m_aItemsInset
.Left(), m_aItemsInset
.Top() );
316 //......................................................................................................................
318 //......................................................................................................................
320 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */