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
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() );
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
);
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() );
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() );
113 NormalizedArea::NormalizedArea()
118 NormalizedArea::NormalizedArea( const Rectangle
& i_rReference
, const bool i_bIsVertical
)
119 : m_aReference(i_rReference
)
123 const long nRotatedWidth
= i_rReference
.GetHeight();
124 const long nRotatedHeight
= i_rReference
.GetWidth();
125 m_aReference
= Rectangle(i_rReference
.TopLeft(), Size(nRotatedWidth
, nRotatedHeight
));
129 Rectangle
NormalizedArea::getTransformed( const Rectangle
& i_rArea
, const TabAlignment i_eTargetAlignment
) const
131 Rectangle
aResult( i_rArea
);
133 if ( ( i_eTargetAlignment
== TABS_RIGHT
)
134 || ( i_eTargetAlignment
== TABS_LEFT
)
137 lcl_rotate( m_aReference
, aResult
, true );
139 if ( i_eTargetAlignment
== TABS_LEFT
)
141 Rectangle
aReference( m_aReference
);
142 aReference
.Transpose();
143 lcl_mirrorHorizontally( aReference
, aResult
);
146 else if ( i_eTargetAlignment
== TABS_BOTTOM
)
148 lcl_mirrorVertically( m_aReference
, aResult
);
155 Rectangle
NormalizedArea::getNormalized( const Rectangle
& i_rArea
, const TabAlignment i_eTargetAlignment
) const
157 Rectangle
aResult( i_rArea
);
159 if ( ( i_eTargetAlignment
== TABS_RIGHT
)
160 || ( i_eTargetAlignment
== TABS_LEFT
)
163 Rectangle
aReference( m_aReference
);
164 lcl_rotate( m_aReference
, aReference
, true );
166 if ( i_eTargetAlignment
== TABS_LEFT
)
168 lcl_mirrorHorizontally( aReference
, aResult
);
171 lcl_rotate( aReference
, aResult
, false );
173 else if ( i_eTargetAlignment
== TABS_BOTTOM
)
175 lcl_mirrorVertically( m_aReference
, aResult
);
184 TabBarGeometry::TabBarGeometry( const TabItemContent i_eItemContent
)
185 :m_eTabItemContent( i_eItemContent
)
189 ,m_aButtonForwardRect()
191 m_aItemsInset
.Left() = ITEMS_INSET_LEFT
;
192 m_aItemsInset
.Top() = ITEMS_INSET_TOP
;
193 m_aItemsInset
.Right() = ITEMS_INSET_RIGHT
;
194 m_aItemsInset
.Bottom() = ITEMS_INSET_BOTTOM
;
198 TabBarGeometry::~TabBarGeometry()
203 bool TabBarGeometry::impl_fitItems( ItemDescriptors
& io_rItems
) const
205 if ( io_rItems
.empty() )
206 // nothing to do, "no items" perfectly fit into any space we have ...
209 // the available size
210 Size
aOutputSize( getItemsRect().GetSize() );
211 // shrunk by the outer space
212 aOutputSize
.Width() -= m_aItemsInset
.Right();
213 aOutputSize
.Height() -= m_aItemsInset
.Bottom();
214 const Rectangle
aFitInto( Point( 0, 0 ), aOutputSize
);
216 TabItemContent
eItemContent( getItemContent() );
217 if ( eItemContent
== TABITEM_AUTO
)
219 // the "content modes" to try
220 TabItemContent eTryThis
[] =
222 TABITEM_IMAGE_ONLY
, // assumed to have the smallest rects
224 TABITEM_IMAGE_AND_TEXT
// assumed to have the largest rects
228 // determine which of the different version fits
229 eItemContent
= eTryThis
[0];
230 size_t nTryIndex
= 2;
231 while ( nTryIndex
> 0 )
233 const Point
aBottomRight( io_rItems
.rbegin()->GetRect( eTryThis
[ nTryIndex
] ).BottomRight() );
234 if ( aFitInto
.IsInside( aBottomRight
) )
236 eItemContent
= eTryThis
[ nTryIndex
];
243 // propagate to the items
244 for ( ItemDescriptors::iterator item
= io_rItems
.begin();
245 item
!= io_rItems
.end();
249 item
->eContent
= eItemContent
;
252 const ItemDescriptor
& rLastItem( *io_rItems
.rbegin() );
253 const Point
aLastItemBottomRight( rLastItem
.GetCurrentRect().BottomRight() );
254 return aFitInto
.Left() <= aLastItemBottomRight
.X()
255 && aFitInto
.Right() >= aLastItemBottomRight
.X();
259 Size
TabBarGeometry::getOptimalSize(ItemDescriptors
& io_rItems
) const
261 if ( io_rItems
.empty() )
263 m_aItemsInset
.Left() + m_aItemsInset
.Right(),
264 m_aItemsInset
.Top() + m_aItemsInset
.Bottom()
267 // the rect of the last item
268 const Rectangle
& rLastItemRect(io_rItems
.rbegin()->aCompleteArea
);
270 rLastItemRect
.Left() + 1 + m_aItemsInset
.Right(),
271 rLastItemRect
.Top() + 1 + rLastItemRect
.Bottom() + m_aItemsInset
.Bottom()
276 void TabBarGeometry::relayout( const Size
& i_rActualOutputSize
, ItemDescriptors
& io_rItems
)
278 // assume all items fit
279 Point
aButtonBackPos( OUTER_SPACE_LEFT
, OUTER_SPACE_TOP
);
280 m_aButtonBackRect
= Rectangle( aButtonBackPos
, Size( 1, 1 ) );
281 m_aButtonBackRect
.SetEmpty();
283 Point
aButtonForwardPos( i_rActualOutputSize
.Width(), OUTER_SPACE_TOP
);
284 m_aButtonForwardRect
= Rectangle( aButtonForwardPos
, Size( 1, 1 ) );
285 m_aButtonForwardRect
.SetEmpty();
287 Point
aItemsPos( OUTER_SPACE_LEFT
, 0 );
288 Size
aItemsSize( i_rActualOutputSize
.Width() - OUTER_SPACE_LEFT
- OUTER_SPACE_RIGHT
, i_rActualOutputSize
.Height() );
289 m_aItemsRect
= Rectangle( aItemsPos
, aItemsSize
);
291 if ( !impl_fitItems( io_rItems
) )
293 // assumption was wrong, the items do not fit => calculate rects for the scroll buttons
294 const Size
aButtonSize( BUTTON_FLOW_WIDTH
, i_rActualOutputSize
.Height() - OUTER_SPACE_TOP
- OUTER_SPACE_BOTTOM
);
296 aButtonBackPos
= Point( OUTER_SPACE_LEFT
, OUTER_SPACE_TOP
);
297 m_aButtonBackRect
= Rectangle( aButtonBackPos
, aButtonSize
);
299 aButtonForwardPos
= Point( i_rActualOutputSize
.Width() - BUTTON_FLOW_WIDTH
- OUTER_SPACE_RIGHT
, OUTER_SPACE_TOP
);
300 m_aButtonForwardRect
= Rectangle( aButtonForwardPos
, aButtonSize
);
302 aItemsPos
.X() = aButtonBackPos
.X() + aButtonSize
.Width() + BUTTON_FLOW_SPACE
;
303 aItemsSize
.Width() = aButtonForwardPos
.X() - BUTTON_FLOW_SPACE
- aItemsPos
.X();
304 m_aItemsRect
= Rectangle( aItemsPos
, aItemsSize
);
306 // fit items, again. In the TABITEM_AUTO case, the smaller playground for the items might lead to another
308 impl_fitItems( io_rItems
);
313 Point
TabBarGeometry::getFirstItemPosition() const
315 return Point( m_aItemsInset
.Left(), m_aItemsInset
.Top() );
322 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */