Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 : #include "DeckLayouter.hxx"
21 : #include <sfx2/sidebar/Theme.hxx>
22 : #include "Panel.hxx"
23 : #include "PanelTitleBar.hxx"
24 : #include "Deck.hxx"
25 :
26 : #include <vcl/window.hxx>
27 : #include <vcl/scrbar.hxx>
28 :
29 : using namespace css;
30 : using namespace css::uno;
31 :
32 : namespace sfx2 { namespace sidebar {
33 :
34 : namespace {
35 : static const sal_Int32 MinimalPanelHeight (25);
36 :
37 : enum LayoutMode
38 : {
39 : MinimumOrLarger,
40 : PreferredOrLarger,
41 : Preferred
42 : };
43 3991 : class LayoutItem
44 : {
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;
52 :
53 3991 : LayoutItem()
54 3991 : : mpPanel(),maLayoutSize(0,0,0),mnDistributedHeight(0),mnWeight(0),mnPanelIndex(0),mbShowTitleBar(true)
55 3991 : {}
56 : };
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);
94 : }
95 :
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)
102 :
103 6245 : 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)
112 : {
113 6245 : if (rContentArea.GetWidth()<=0 || rContentArea.GetHeight()<=0)
114 9717 : return;
115 2773 : Rectangle aBox (PlaceDeckTitle(rDeckTitleBar, rContentArea));
116 :
117 2773 : if ( ! rPanels.empty())
118 : {
119 : // Prepare the layout item container.
120 2703 : ::std::vector<LayoutItem> aLayoutItems;
121 2703 : aLayoutItems.resize(rPanels.size());
122 6694 : for (sal_Int32 nIndex(0),nCount(rPanels.size()); nIndex<nCount; ++nIndex)
123 : {
124 3991 : aLayoutItems[nIndex].mpPanel = rPanels[nIndex];
125 3991 : aLayoutItems[nIndex].mnPanelIndex = nIndex;
126 : }
127 : aBox = LayoutPanels(
128 : aBox,
129 : rMinimalWidth,
130 : aLayoutItems,
131 : rScrollClipWindow,
132 : rScrollContainer,
133 : rVerticalScrollBar,
134 2703 : false);
135 : }
136 2773 : UpdateFiller(rFiller, aBox);
137 : }
138 :
139 : namespace {
140 :
141 2966 : 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)
149 : {
150 2966 : Rectangle aBox (PlaceVerticalScrollBar(rVerticalScrollBar, rContentArea, bShowVerticalScrollBar));
151 :
152 2966 : const sal_Int32 nWidth (aBox.GetWidth());
153 :
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 2966 : sal_Int32 nAvailableHeight (aBox.GetHeight());
158 2966 : GetRequestedSizes(rLayoutItems, nAvailableHeight, rMinimalWidth, aBox);
159 2966 : const sal_Int32 nTotalDecorationHeight (aBox.GetHeight() - nAvailableHeight);
160 :
161 : // Analyze the requested heights.
162 : // Determine the height that is available for panel content
163 : // and count the different layouts.
164 2966 : sal_Int32 nTotalPreferredHeight (0);
165 2966 : sal_Int32 nTotalMinimumHeight (0);
166 7916 : IterateLayoutItems(iItem,rLayoutItems)
167 : {
168 4950 : nTotalMinimumHeight += iItem->maLayoutSize.Minimum;
169 4950 : nTotalPreferredHeight += iItem->maLayoutSize.Preferred;
170 : }
171 :
172 2966 : if (nTotalMinimumHeight > nAvailableHeight
173 526 : && ! bShowVerticalScrollBar)
174 : {
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 263 : true);
186 : }
187 :
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.
198 :
199 2703 : LayoutMode eMode (MinimumOrLarger);
200 2703 : if (bShowVerticalScrollBar)
201 263 : eMode = Preferred;
202 2440 : else if (nTotalPreferredHeight <= nAvailableHeight)
203 2440 : eMode = PreferredOrLarger;
204 : else
205 0 : eMode = MinimumOrLarger;
206 :
207 2703 : if (eMode != Preferred)
208 : {
209 2440 : const sal_Int32 nTotalHeight (eMode==MinimumOrLarger ? nTotalMinimumHeight : nTotalPreferredHeight);
210 :
211 : DistributeHeights(
212 : rLayoutItems,
213 : nAvailableHeight-nTotalHeight,
214 2440 : aBox.GetHeight(),
215 4880 : eMode==MinimumOrLarger);
216 : }
217 :
218 : // Set position and size of the mpScrollClipWindow to the available
219 : // size. Its child, the mpScrollContainer, may have a bigger
220 : // height.
221 2703 : rScrollClipWindow.setPosSizePixel(aBox.Left(), aBox.Top(), aBox.GetWidth(), aBox.GetHeight());
222 :
223 : const sal_Int32 nContentHeight (
224 : eMode==Preferred
225 : ? nTotalPreferredHeight + nTotalDecorationHeight
226 2703 : : aBox.GetHeight());
227 2703 : sal_Int32 nY = rVerticalScrollBar.GetThumbPos();
228 2703 : if (nContentHeight-nY < aBox.GetHeight())
229 0 : nY = nContentHeight-aBox.GetHeight();
230 2703 : if (nY < 0)
231 0 : nY = 0;
232 : rScrollContainer.setPosSizePixel(
233 : 0,
234 : -nY,
235 : nWidth,
236 2703 : nContentHeight);
237 :
238 2703 : if (bShowVerticalScrollBar)
239 263 : SetupVerticalScrollBar(rVerticalScrollBar, nContentHeight, aBox.GetHeight());
240 :
241 2703 : const sal_Int32 nUsedHeight (PlacePanels(rLayoutItems, nWidth, eMode, rScrollContainer));
242 2703 : aBox.Top() += nUsedHeight;
243 2703 : return aBox;
244 : }
245 :
246 2703 : sal_Int32 PlacePanels (
247 : ::std::vector<LayoutItem>& rLayoutItems,
248 : const sal_Int32 nWidth,
249 : const LayoutMode eMode,
250 : vcl::Window& rScrollContainer)
251 : {
252 2703 : ::std::vector<sal_Int32> aSeparators;
253 2703 : const sal_Int32 nDeckSeparatorHeight (Theme::GetInteger(Theme::Int_DeckSeparatorHeight));
254 2703 : sal_Int32 nY (0);
255 :
256 : // Assign heights and places.
257 6694 : IterateLayoutItems(iItem,rLayoutItems)
258 : {
259 3991 : if (!iItem->mpPanel)
260 0 : continue;
261 :
262 3991 : Panel& rPanel (*iItem->mpPanel);
263 :
264 : // Separator above the panel title bar.
265 3991 : aSeparators.push_back(nY);
266 3991 : nY += nDeckSeparatorHeight;
267 :
268 : // Place the title bar.
269 3991 : PanelTitleBar* pTitleBar = rPanel.GetTitleBar();
270 3991 : if (pTitleBar != NULL)
271 : {
272 3991 : const sal_Int32 nPanelTitleBarHeight (Theme::GetInteger(Theme::Int_PanelTitleBarHeight) * rPanel.GetDPIScaleFactor());
273 :
274 3991 : if (iItem->mbShowTitleBar)
275 : {
276 1847 : pTitleBar->setPosSizePixel(0, nY, nWidth, nPanelTitleBarHeight);
277 1847 : pTitleBar->Show();
278 1847 : nY += nPanelTitleBarHeight;
279 : }
280 : else
281 : {
282 2144 : pTitleBar->Hide();
283 : }
284 : }
285 :
286 3991 : if (rPanel.IsExpanded())
287 : {
288 3686 : rPanel.Show();
289 :
290 : // Determine the height of the panel depending on layout
291 : // mode and distributed heights.
292 3686 : sal_Int32 nPanelHeight (0);
293 3686 : switch(eMode)
294 : {
295 : case MinimumOrLarger:
296 0 : nPanelHeight = iItem->maLayoutSize.Minimum + iItem->mnDistributedHeight;
297 0 : break;
298 : case PreferredOrLarger:
299 2736 : nPanelHeight = iItem->maLayoutSize.Preferred + iItem->mnDistributedHeight;
300 2736 : break;
301 : case Preferred:
302 950 : nPanelHeight = iItem->maLayoutSize.Preferred;
303 950 : break;
304 : default:
305 : OSL_ASSERT(false);
306 0 : break;
307 : }
308 :
309 : // Place the panel.
310 3686 : rPanel.setPosSizePixel(0, nY, nWidth, nPanelHeight);
311 3686 : rPanel.Invalidate();
312 :
313 3686 : nY += nPanelHeight;
314 : }
315 : else
316 : {
317 305 : rPanel.Hide();
318 :
319 : // Add a separator below the collapsed panel, if it is the
320 : // last panel in the deck.
321 305 : if (iItem == rLayoutItems.end()-1)
322 : {
323 : // Separator below the panel title bar.
324 299 : aSeparators.push_back(nY);
325 299 : nY += nDeckSeparatorHeight;
326 : }
327 : }
328 : }
329 :
330 : Deck::ScrollContainerWindow* pScrollContainerWindow
331 2703 : = dynamic_cast<Deck::ScrollContainerWindow*>(&rScrollContainer);
332 2703 : if (pScrollContainerWindow != NULL)
333 2703 : pScrollContainerWindow->SetSeparators(aSeparators);
334 :
335 2703 : return nY;
336 : }
337 :
338 2966 : void GetRequestedSizes (
339 : ::std::vector<LayoutItem>& rLayoutItems,
340 : sal_Int32& rAvailableHeight,
341 : sal_Int32& rMinimalWidth,
342 : const Rectangle& rContentBox)
343 : {
344 2966 : rAvailableHeight = rContentBox.GetHeight();
345 :
346 2966 : const sal_Int32 nDeckSeparatorHeight (Theme::GetInteger(Theme::Int_DeckSeparatorHeight));
347 :
348 7916 : IterateLayoutItems(iItem,rLayoutItems)
349 : {
350 4950 : ui::LayoutSize aLayoutSize (ui::LayoutSize(0,0,0));
351 4950 : if (iItem->mpPanel != nullptr)
352 : {
353 9900 : if (rLayoutItems.size() == 1
354 4950 : && iItem->mpPanel->IsTitleBarOptional())
355 : {
356 : // There is only one panel and its title bar is
357 : // optional => hide it.
358 2144 : rAvailableHeight -= nDeckSeparatorHeight;
359 2144 : iItem->mbShowTitleBar = false;
360 : }
361 : else
362 : {
363 : // Show the title bar and a separator above and below
364 : // the title bar.
365 2806 : const sal_Int32 nPanelTitleBarHeight (Theme::GetInteger(Theme::Int_PanelTitleBarHeight) * iItem->mpPanel->GetDPIScaleFactor());
366 :
367 2806 : rAvailableHeight -= nPanelTitleBarHeight;
368 2806 : rAvailableHeight -= nDeckSeparatorHeight;
369 : }
370 :
371 4950 : if (iItem->mpPanel->IsExpanded())
372 : {
373 4636 : Reference<ui::XSidebarPanel> xPanel (iItem->mpPanel->GetPanelComponent());
374 4636 : if (xPanel.is())
375 : {
376 4636 : aLayoutSize = xPanel->getHeightForWidth(rContentBox.GetWidth());
377 :
378 4636 : sal_Int32 nWidth = xPanel->getMinimalWidth();
379 4636 : if (nWidth > rMinimalWidth)
380 1282 : rMinimalWidth = nWidth;
381 : }
382 : else
383 0 : aLayoutSize = ui::LayoutSize(MinimalPanelHeight, -1, 0);
384 : }
385 : }
386 4950 : iItem->maLayoutSize = aLayoutSize;
387 : }
388 2966 : }
389 :
390 2440 : void DistributeHeights (
391 : ::std::vector<LayoutItem>& rLayoutItems,
392 : const sal_Int32 nHeightToDistribute,
393 : const sal_Int32 nContainerHeight,
394 : const bool bMinimumHeightIsBase)
395 : {
396 2440 : if (nHeightToDistribute <= 0)
397 0 : return;
398 :
399 2440 : sal_Int32 nRemainingHeightToDistribute (nHeightToDistribute);
400 :
401 : // Compute the weights as difference between panel base height
402 : // (either its minimum or preferred height) and the container height.
403 2440 : sal_Int32 nTotalWeight (0);
404 2440 : sal_Int32 nNoMaximumCount (0);
405 5472 : IterateLayoutItems(iItem,rLayoutItems)
406 : {
407 3032 : if (iItem->maLayoutSize.Maximum == 0)
408 296 : continue;
409 2736 : if (iItem->maLayoutSize.Maximum < 0)
410 2144 : ++nNoMaximumCount;
411 :
412 : const sal_Int32 nBaseHeight (
413 : bMinimumHeightIsBase
414 0 : ? iItem->maLayoutSize.Minimum
415 2736 : : iItem->maLayoutSize.Preferred);
416 2736 : if (nBaseHeight < nContainerHeight)
417 : {
418 2736 : iItem->mnWeight = nContainerHeight - nBaseHeight;
419 2736 : nTotalWeight += iItem->mnWeight;
420 : }
421 : }
422 :
423 2440 : if (nTotalWeight == 0)
424 0 : return;
425 :
426 : // First pass of height distribution.
427 5472 : IterateLayoutItems(iItem,rLayoutItems)
428 : {
429 : const sal_Int32 nBaseHeight (
430 : bMinimumHeightIsBase
431 0 : ? iItem->maLayoutSize.Minimum
432 3032 : : iItem->maLayoutSize.Preferred);
433 3032 : sal_Int32 nDistributedHeight (iItem->mnWeight * nHeightToDistribute / nTotalWeight);
434 6064 : if (nBaseHeight+nDistributedHeight > iItem->maLayoutSize.Maximum
435 3032 : && iItem->maLayoutSize.Maximum >= 0)
436 : {
437 592 : nDistributedHeight = ::std::max<sal_Int32>(0,iItem->maLayoutSize.Maximum - nBaseHeight);
438 : }
439 3032 : iItem->mnDistributedHeight = nDistributedHeight;
440 3032 : nRemainingHeightToDistribute -= nDistributedHeight;
441 : }
442 :
443 2440 : if (nRemainingHeightToDistribute == 0)
444 2144 : return;
445 : OSL_ASSERT(nRemainingHeightToDistribute > 0);
446 :
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 296 : if (nNoMaximumCount == 0)
452 : {
453 : // There are no panels with unrestricted height.
454 296 : return;
455 : }
456 0 : const sal_Int32 nAdditionalHeightPerPanel (nRemainingHeightToDistribute / nNoMaximumCount);
457 : // Handle rounding error.
458 : sal_Int32 nAdditionalHeightForFirstPanel (nRemainingHeightToDistribute
459 0 : - nNoMaximumCount*nAdditionalHeightPerPanel);
460 0 : IterateLayoutItems(iItem,rLayoutItems)
461 : {
462 0 : if (iItem->maLayoutSize.Maximum < 0)
463 : {
464 0 : iItem->mnDistributedHeight += nAdditionalHeightPerPanel + nAdditionalHeightForFirstPanel;
465 0 : nRemainingHeightToDistribute -= nAdditionalHeightPerPanel + nAdditionalHeightForFirstPanel;
466 : }
467 : }
468 :
469 : OSL_ASSERT(nRemainingHeightToDistribute==0);
470 : }
471 :
472 2773 : Rectangle PlaceDeckTitle (
473 : vcl::Window& rDeckTitleBar,
474 : const Rectangle& rAvailableSpace)
475 : {
476 2773 : if (static_cast<DockingWindow*>(rDeckTitleBar.GetParent()->GetParent())->IsFloatingMode())
477 : {
478 : // When the side bar is undocked then the outer system window displays the deck title.
479 0 : rDeckTitleBar.Hide();
480 0 : return rAvailableSpace;
481 : }
482 : else
483 : {
484 2773 : const sal_Int32 nDeckTitleBarHeight (Theme::GetInteger(Theme::Int_DeckTitleBarHeight) * rDeckTitleBar.GetDPIScaleFactor());
485 : rDeckTitleBar.setPosSizePixel(
486 : rAvailableSpace.Left(),
487 : rAvailableSpace.Top(),
488 : rAvailableSpace.GetWidth(),
489 2773 : nDeckTitleBarHeight);
490 2773 : rDeckTitleBar.Show();
491 : return Rectangle(
492 : rAvailableSpace.Left(),
493 2773 : rAvailableSpace.Top() + nDeckTitleBarHeight,
494 : rAvailableSpace.Right(),
495 5546 : rAvailableSpace.Bottom());
496 : }
497 : }
498 :
499 2966 : Rectangle PlaceVerticalScrollBar (
500 : ScrollBar& rVerticalScrollBar,
501 : const Rectangle& rAvailableSpace,
502 : const bool bShowVerticalScrollBar)
503 : {
504 2966 : if (bShowVerticalScrollBar)
505 : {
506 263 : const sal_Int32 nScrollBarWidth (rVerticalScrollBar.GetSizePixel().Width());
507 : rVerticalScrollBar.setPosSizePixel(
508 263 : rAvailableSpace.Right() - nScrollBarWidth + 1,
509 : rAvailableSpace.Top(),
510 : nScrollBarWidth,
511 526 : rAvailableSpace.GetHeight());
512 263 : rVerticalScrollBar.Show();
513 : return Rectangle(
514 : rAvailableSpace.Left(),
515 : rAvailableSpace.Top(),
516 263 : rAvailableSpace.Right() - nScrollBarWidth,
517 526 : rAvailableSpace.Bottom());
518 : }
519 : else
520 : {
521 2703 : rVerticalScrollBar.Hide();
522 2703 : return rAvailableSpace;
523 : }
524 : }
525 :
526 263 : void SetupVerticalScrollBar(
527 : ScrollBar& rVerticalScrollBar,
528 : const sal_Int32 nContentHeight,
529 : const sal_Int32 nVisibleHeight)
530 : {
531 : OSL_ASSERT(nContentHeight > nVisibleHeight);
532 :
533 263 : rVerticalScrollBar.SetRangeMin(0);
534 263 : rVerticalScrollBar.SetRangeMax(nContentHeight-1);
535 263 : rVerticalScrollBar.SetVisibleSize(nVisibleHeight);
536 263 : }
537 :
538 2773 : void UpdateFiller (
539 : vcl::Window& rFiller,
540 : const Rectangle& rBox)
541 : {
542 2773 : if (rBox.GetHeight() > 0)
543 : {
544 : // Show the filler.
545 366 : rFiller.SetBackground(Theme::GetPaint(Theme::Paint_PanelBackground).GetWallpaper());
546 366 : rFiller.SetPosSizePixel(rBox.TopLeft(), rBox.GetSize());
547 366 : rFiller.Show();
548 : }
549 : else
550 : {
551 : // Hide the filler.
552 2407 : rFiller.Hide();
553 : }
554 2773 : }
555 :
556 : }
557 :
558 648 : } } // end of namespace sfx2::sidebar
559 :
560 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|