Branch libreoffice-5-0-4
[LibreOffice.git] / sfx2 / source / sidebar / DeckLayouter.cxx
blob3a2edfc2aef53241a3171336d8a1d6207ef3423f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 .
20 #include "DeckLayouter.hxx"
21 #include <sfx2/sidebar/Theme.hxx>
22 #include "Panel.hxx"
23 #include "PanelTitleBar.hxx"
24 #include "Deck.hxx"
26 #include <vcl/window.hxx>
27 #include <vcl/scrbar.hxx>
29 using namespace css;
30 using namespace css::uno;
32 namespace sfx2 { namespace sidebar {
34 namespace {
35 static const sal_Int32 MinimalPanelHeight (25);
37 enum LayoutMode
39 MinimumOrLarger,
40 PreferredOrLarger,
41 Preferred
43 class LayoutItem
45 public:
46 VclPtr<Panel> mpPanel;
47 css::ui::LayoutSize maLayoutSize;
48 sal_Int32 mnDistributedHeight;
49 sal_Int32 mnWeight;
50 sal_Int32 mnPanelIndex;
51 bool mbShowTitleBar;
53 LayoutItem()
54 : mpPanel(),maLayoutSize(0,0,0),mnDistributedHeight(0),mnWeight(0),mnPanelIndex(0),mbShowTitleBar(true)
57 Rectangle LayoutPanels (
58 const Rectangle& rContentArea,
59 sal_Int32& rMinimalWidth,
60 ::std::vector<LayoutItem>& rLayoutItems,
61 vcl::Window& rScrollClipWindow,
62 vcl::Window& rScrollContainer,
63 ScrollBar& pVerticalScrollBar,
64 const bool bShowVerticalScrollBar);
65 void GetRequestedSizes (
66 ::std::vector<LayoutItem>& rLayoutItem,
67 sal_Int32& rAvailableHeight,
68 sal_Int32& rMinimalWidth,
69 const Rectangle& rContentBox);
70 void DistributeHeights (
71 ::std::vector<LayoutItem>& rLayoutItems,
72 const sal_Int32 nHeightToDistribute,
73 const sal_Int32 nContainerHeight,
74 const bool bMinimumHeightIsBase);
75 sal_Int32 PlacePanels (
76 ::std::vector<LayoutItem>& rLayoutItems,
77 const sal_Int32 nWidth,
78 const LayoutMode eMode,
79 vcl::Window& rScrollContainer);
80 Rectangle PlaceDeckTitle (
81 vcl::Window& rTittleBar,
82 const Rectangle& rAvailableSpace);
83 Rectangle PlaceVerticalScrollBar (
84 ScrollBar& rVerticalScrollBar,
85 const Rectangle& rAvailableSpace,
86 const bool bShowVerticalScrollBar);
87 void SetupVerticalScrollBar(
88 ScrollBar& rVerticalScrollBar,
89 const sal_Int32 nContentHeight,
90 const sal_Int32 nVisibleHeight);
91 void UpdateFiller (
92 vcl::Window& rFiller,
93 const Rectangle& rBox);
96 #define IterateLayoutItems(iterator_name,container) \
97 for(::std::vector<LayoutItem>::iterator \
98 iterator_name(container.begin()), \
99 iEnd(container.end()); \
100 iterator_name!=iEnd; \
101 ++iterator_name)
103 void DeckLayouter::LayoutDeck (
104 const Rectangle& rContentArea,
105 sal_Int32& rMinimalWidth,
106 SharedPanelContainer& rPanels,
107 vcl::Window& rDeckTitleBar,
108 vcl::Window& rScrollClipWindow,
109 vcl::Window& rScrollContainer,
110 vcl::Window& rFiller,
111 ScrollBar& rVerticalScrollBar)
113 if (rContentArea.GetWidth()<=0 || rContentArea.GetHeight()<=0)
114 return;
115 Rectangle aBox (PlaceDeckTitle(rDeckTitleBar, rContentArea));
117 if ( ! rPanels.empty())
119 // Prepare the layout item container.
120 ::std::vector<LayoutItem> aLayoutItems;
121 aLayoutItems.resize(rPanels.size());
122 for (sal_Int32 nIndex(0),nCount(rPanels.size()); nIndex<nCount; ++nIndex)
124 aLayoutItems[nIndex].mpPanel = rPanels[nIndex];
125 aLayoutItems[nIndex].mnPanelIndex = nIndex;
127 aBox = LayoutPanels(
128 aBox,
129 rMinimalWidth,
130 aLayoutItems,
131 rScrollClipWindow,
132 rScrollContainer,
133 rVerticalScrollBar,
134 false);
136 UpdateFiller(rFiller, aBox);
139 namespace {
141 Rectangle LayoutPanels (
142 const Rectangle& rContentArea,
143 sal_Int32& rMinimalWidth,
144 ::std::vector<LayoutItem>& rLayoutItems,
145 vcl::Window& rScrollClipWindow,
146 vcl::Window& rScrollContainer,
147 ScrollBar& rVerticalScrollBar,
148 const bool bShowVerticalScrollBar)
150 Rectangle aBox (PlaceVerticalScrollBar(rVerticalScrollBar, rContentArea, bShowVerticalScrollBar));
152 const sal_Int32 nWidth (aBox.GetWidth());
154 // Get the requested heights of the panels and the available
155 // height that is left when all panel titles and separators are
156 // taken into account.
157 sal_Int32 nAvailableHeight (aBox.GetHeight());
158 GetRequestedSizes(rLayoutItems, nAvailableHeight, rMinimalWidth, aBox);
159 const sal_Int32 nTotalDecorationHeight (aBox.GetHeight() - nAvailableHeight);
161 // Analyze the requested heights.
162 // Determine the height that is available for panel content
163 // and count the different layouts.
164 sal_Int32 nTotalPreferredHeight (0);
165 sal_Int32 nTotalMinimumHeight (0);
166 IterateLayoutItems(iItem,rLayoutItems)
168 nTotalMinimumHeight += iItem->maLayoutSize.Minimum;
169 nTotalPreferredHeight += iItem->maLayoutSize.Preferred;
172 if (nTotalMinimumHeight > nAvailableHeight
173 && ! bShowVerticalScrollBar)
175 // Not enough space, even when all panels are shrunk to their
176 // minimum height.
177 // Show a vertical scrollbar.
178 return LayoutPanels(
179 rContentArea,
180 rMinimalWidth,
181 rLayoutItems,
182 rScrollClipWindow,
183 rScrollContainer,
184 rVerticalScrollBar,
185 true);
188 // We are now in one of three modes.
189 // - The preferred height fits into the available size:
190 // Use the preferred size, distribute the remaining height bei
191 // enlarging panels.
192 // - The total minimum height fits into the available size:
193 // Use the minimum size, distribute the remaining height bei
194 // enlarging panels.
195 // - The total minimum height does not fit into the available
196 // size:
197 // Use the unmodified preferred height for all panels.
199 LayoutMode eMode (MinimumOrLarger);
200 if (bShowVerticalScrollBar)
201 eMode = Preferred;
202 else if (nTotalPreferredHeight <= nAvailableHeight)
203 eMode = PreferredOrLarger;
204 else
205 eMode = MinimumOrLarger;
207 if (eMode != Preferred)
209 const sal_Int32 nTotalHeight (eMode==MinimumOrLarger ? nTotalMinimumHeight : nTotalPreferredHeight);
211 DistributeHeights(
212 rLayoutItems,
213 nAvailableHeight-nTotalHeight,
214 aBox.GetHeight(),
215 eMode==MinimumOrLarger);
218 // Set position and size of the mpScrollClipWindow to the available
219 // size. Its child, the mpScrollContainer, may have a bigger
220 // height.
221 rScrollClipWindow.setPosSizePixel(aBox.Left(), aBox.Top(), aBox.GetWidth(), aBox.GetHeight());
223 const sal_Int32 nContentHeight (
224 eMode==Preferred
225 ? nTotalPreferredHeight + nTotalDecorationHeight
226 : aBox.GetHeight());
227 sal_Int32 nY = rVerticalScrollBar.GetThumbPos();
228 if (nContentHeight-nY < aBox.GetHeight())
229 nY = nContentHeight-aBox.GetHeight();
230 if (nY < 0)
231 nY = 0;
232 rScrollContainer.setPosSizePixel(
234 -nY,
235 nWidth,
236 nContentHeight);
238 if (bShowVerticalScrollBar)
239 SetupVerticalScrollBar(rVerticalScrollBar, nContentHeight, aBox.GetHeight());
241 const sal_Int32 nUsedHeight (PlacePanels(rLayoutItems, nWidth, eMode, rScrollContainer));
242 aBox.Top() += nUsedHeight;
243 return aBox;
246 sal_Int32 PlacePanels (
247 ::std::vector<LayoutItem>& rLayoutItems,
248 const sal_Int32 nWidth,
249 const LayoutMode eMode,
250 vcl::Window& rScrollContainer)
252 ::std::vector<sal_Int32> aSeparators;
253 const sal_Int32 nDeckSeparatorHeight (Theme::GetInteger(Theme::Int_DeckSeparatorHeight));
254 sal_Int32 nY (0);
256 // Assign heights and places.
257 IterateLayoutItems(iItem,rLayoutItems)
259 if (!iItem->mpPanel)
260 continue;
262 Panel& rPanel (*iItem->mpPanel);
264 // Separator above the panel title bar.
265 aSeparators.push_back(nY);
266 nY += nDeckSeparatorHeight;
268 // Place the title bar.
269 PanelTitleBar* pTitleBar = rPanel.GetTitleBar();
270 if (pTitleBar != NULL)
272 const sal_Int32 nPanelTitleBarHeight (Theme::GetInteger(Theme::Int_PanelTitleBarHeight) * rPanel.GetDPIScaleFactor());
274 if (iItem->mbShowTitleBar)
276 pTitleBar->setPosSizePixel(0, nY, nWidth, nPanelTitleBarHeight);
277 pTitleBar->Show();
278 nY += nPanelTitleBarHeight;
280 else
282 pTitleBar->Hide();
286 if (rPanel.IsExpanded())
288 rPanel.Show();
290 // Determine the height of the panel depending on layout
291 // mode and distributed heights.
292 sal_Int32 nPanelHeight (0);
293 switch(eMode)
295 case MinimumOrLarger:
296 nPanelHeight = iItem->maLayoutSize.Minimum + iItem->mnDistributedHeight;
297 break;
298 case PreferredOrLarger:
299 nPanelHeight = iItem->maLayoutSize.Preferred + iItem->mnDistributedHeight;
300 break;
301 case Preferred:
302 nPanelHeight = iItem->maLayoutSize.Preferred;
303 break;
304 default:
305 OSL_ASSERT(false);
306 break;
309 // Place the panel.
310 rPanel.setPosSizePixel(0, nY, nWidth, nPanelHeight);
311 rPanel.Invalidate();
313 nY += nPanelHeight;
315 else
317 rPanel.Hide();
319 // Add a separator below the collapsed panel, if it is the
320 // last panel in the deck.
321 if (iItem == rLayoutItems.end()-1)
323 // Separator below the panel title bar.
324 aSeparators.push_back(nY);
325 nY += nDeckSeparatorHeight;
330 Deck::ScrollContainerWindow* pScrollContainerWindow
331 = dynamic_cast<Deck::ScrollContainerWindow*>(&rScrollContainer);
332 if (pScrollContainerWindow != NULL)
333 pScrollContainerWindow->SetSeparators(aSeparators);
335 return nY;
338 void GetRequestedSizes (
339 ::std::vector<LayoutItem>& rLayoutItems,
340 sal_Int32& rAvailableHeight,
341 sal_Int32& rMinimalWidth,
342 const Rectangle& rContentBox)
344 rAvailableHeight = rContentBox.GetHeight();
346 const sal_Int32 nDeckSeparatorHeight (Theme::GetInteger(Theme::Int_DeckSeparatorHeight));
348 IterateLayoutItems(iItem,rLayoutItems)
350 ui::LayoutSize aLayoutSize (ui::LayoutSize(0,0,0));
351 if (iItem->mpPanel != nullptr)
353 if (rLayoutItems.size() == 1
354 && iItem->mpPanel->IsTitleBarOptional())
356 // There is only one panel and its title bar is
357 // optional => hide it.
358 rAvailableHeight -= nDeckSeparatorHeight;
359 iItem->mbShowTitleBar = false;
361 else
363 // Show the title bar and a separator above and below
364 // the title bar.
365 const sal_Int32 nPanelTitleBarHeight (Theme::GetInteger(Theme::Int_PanelTitleBarHeight) * iItem->mpPanel->GetDPIScaleFactor());
367 rAvailableHeight -= nPanelTitleBarHeight;
368 rAvailableHeight -= nDeckSeparatorHeight;
371 if (iItem->mpPanel->IsExpanded())
373 Reference<ui::XSidebarPanel> xPanel (iItem->mpPanel->GetPanelComponent());
374 if (xPanel.is())
376 aLayoutSize = xPanel->getHeightForWidth(rContentBox.GetWidth());
378 sal_Int32 nWidth = xPanel->getMinimalWidth();
379 if (nWidth > rMinimalWidth)
380 rMinimalWidth = nWidth;
382 else
383 aLayoutSize = ui::LayoutSize(MinimalPanelHeight, -1, 0);
386 iItem->maLayoutSize = aLayoutSize;
390 void DistributeHeights (
391 ::std::vector<LayoutItem>& rLayoutItems,
392 const sal_Int32 nHeightToDistribute,
393 const sal_Int32 nContainerHeight,
394 const bool bMinimumHeightIsBase)
396 if (nHeightToDistribute <= 0)
397 return;
399 sal_Int32 nRemainingHeightToDistribute (nHeightToDistribute);
401 // Compute the weights as difference between panel base height
402 // (either its minimum or preferred height) and the container height.
403 sal_Int32 nTotalWeight (0);
404 sal_Int32 nNoMaximumCount (0);
405 IterateLayoutItems(iItem,rLayoutItems)
407 if (iItem->maLayoutSize.Maximum == 0)
408 continue;
409 if (iItem->maLayoutSize.Maximum < 0)
410 ++nNoMaximumCount;
412 const sal_Int32 nBaseHeight (
413 bMinimumHeightIsBase
414 ? iItem->maLayoutSize.Minimum
415 : iItem->maLayoutSize.Preferred);
416 if (nBaseHeight < nContainerHeight)
418 iItem->mnWeight = nContainerHeight - nBaseHeight;
419 nTotalWeight += iItem->mnWeight;
423 if (nTotalWeight == 0)
424 return;
426 // First pass of height distribution.
427 IterateLayoutItems(iItem,rLayoutItems)
429 const sal_Int32 nBaseHeight (
430 bMinimumHeightIsBase
431 ? iItem->maLayoutSize.Minimum
432 : iItem->maLayoutSize.Preferred);
433 sal_Int32 nDistributedHeight (iItem->mnWeight * nHeightToDistribute / nTotalWeight);
434 if (nBaseHeight+nDistributedHeight > iItem->maLayoutSize.Maximum
435 && iItem->maLayoutSize.Maximum >= 0)
437 nDistributedHeight = ::std::max<sal_Int32>(0,iItem->maLayoutSize.Maximum - nBaseHeight);
439 iItem->mnDistributedHeight = nDistributedHeight;
440 nRemainingHeightToDistribute -= nDistributedHeight;
443 if (nRemainingHeightToDistribute == 0)
444 return;
445 OSL_ASSERT(nRemainingHeightToDistribute > 0);
447 // It is possible that not all of the height could be distributed
448 // because of Maximum heights being smaller than expected.
449 // Distribute the remaining height between the panels that have no
450 // Maximum (ie Maximum==-1).
451 if (nNoMaximumCount == 0)
453 // There are no panels with unrestricted height.
454 return;
456 const sal_Int32 nAdditionalHeightPerPanel (nRemainingHeightToDistribute / nNoMaximumCount);
457 // Handle rounding error.
458 sal_Int32 nAdditionalHeightForFirstPanel (nRemainingHeightToDistribute
459 - nNoMaximumCount*nAdditionalHeightPerPanel);
460 IterateLayoutItems(iItem,rLayoutItems)
462 if (iItem->maLayoutSize.Maximum < 0)
464 iItem->mnDistributedHeight += nAdditionalHeightPerPanel + nAdditionalHeightForFirstPanel;
465 nRemainingHeightToDistribute -= nAdditionalHeightPerPanel + nAdditionalHeightForFirstPanel;
469 OSL_ASSERT(nRemainingHeightToDistribute==0);
472 Rectangle PlaceDeckTitle (
473 vcl::Window& rDeckTitleBar,
474 const Rectangle& rAvailableSpace)
476 if (static_cast<DockingWindow*>(rDeckTitleBar.GetParent()->GetParent())->IsFloatingMode())
478 // When the side bar is undocked then the outer system window displays the deck title.
479 rDeckTitleBar.Hide();
480 return rAvailableSpace;
482 else
484 const sal_Int32 nDeckTitleBarHeight (Theme::GetInteger(Theme::Int_DeckTitleBarHeight) * rDeckTitleBar.GetDPIScaleFactor());
485 rDeckTitleBar.setPosSizePixel(
486 rAvailableSpace.Left(),
487 rAvailableSpace.Top(),
488 rAvailableSpace.GetWidth(),
489 nDeckTitleBarHeight);
490 rDeckTitleBar.Show();
491 return Rectangle(
492 rAvailableSpace.Left(),
493 rAvailableSpace.Top() + nDeckTitleBarHeight,
494 rAvailableSpace.Right(),
495 rAvailableSpace.Bottom());
499 Rectangle PlaceVerticalScrollBar (
500 ScrollBar& rVerticalScrollBar,
501 const Rectangle& rAvailableSpace,
502 const bool bShowVerticalScrollBar)
504 if (bShowVerticalScrollBar)
506 const sal_Int32 nScrollBarWidth (rVerticalScrollBar.GetSizePixel().Width());
507 rVerticalScrollBar.setPosSizePixel(
508 rAvailableSpace.Right() - nScrollBarWidth + 1,
509 rAvailableSpace.Top(),
510 nScrollBarWidth,
511 rAvailableSpace.GetHeight());
512 rVerticalScrollBar.Show();
513 return Rectangle(
514 rAvailableSpace.Left(),
515 rAvailableSpace.Top(),
516 rAvailableSpace.Right() - nScrollBarWidth,
517 rAvailableSpace.Bottom());
519 else
521 rVerticalScrollBar.Hide();
522 return rAvailableSpace;
526 void SetupVerticalScrollBar(
527 ScrollBar& rVerticalScrollBar,
528 const sal_Int32 nContentHeight,
529 const sal_Int32 nVisibleHeight)
531 OSL_ASSERT(nContentHeight > nVisibleHeight);
533 rVerticalScrollBar.SetRangeMin(0);
534 rVerticalScrollBar.SetRangeMax(nContentHeight-1);
535 rVerticalScrollBar.SetVisibleSize(nVisibleHeight);
538 void UpdateFiller (
539 vcl::Window& rFiller,
540 const Rectangle& rBox)
542 if (rBox.GetHeight() > 0)
544 // Show the filler.
545 rFiller.SetBackground(Theme::GetPaint(Theme::Paint_PanelBackground).GetWallpaper());
546 rFiller.SetPosSizePixel(rBox.TopLeft(), rBox.GetSize());
547 rFiller.Show();
549 else
551 // Hide the filler.
552 rFiller.Hide();
558 } } // end of namespace sfx2::sidebar
560 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */