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 .
20 #include <sidebar/DeckLayouter.hxx>
21 #include <sfx2/sidebar/Theme.hxx>
22 #include <sfx2/sidebar/Panel.hxx>
23 #include <sidebar/PanelTitleBar.hxx>
24 #include <sfx2/sidebar/Deck.hxx>
25 #include <sfx2/sidebar/SidebarController.hxx>
26 #include <comphelper/lok.hxx>
28 #include <comphelper/processfactory.hxx>
29 #include <vcl/window.hxx>
30 #include <vcl/scrbar.hxx>
32 #include <com/sun/star/uno/Reference.hxx>
33 #include <com/sun/star/frame/Desktop.hpp>
34 #include <com/sun/star/frame/XDesktop2.hpp>
35 #include <com/sun/star/frame/XFrame.hpp>
36 #include <com/sun/star/ui/XSidebarPanel.hpp>
39 using namespace css::uno
;
41 namespace sfx2::sidebar
{
44 const sal_Int32
MinimalPanelHeight (25);
55 VclPtr
<Panel
> mpPanel
;
56 css::ui::LayoutSize maLayoutSize
;
57 sal_Int32 mnDistributedHeight
;
61 LayoutItem(const VclPtr
<Panel
>& rPanel
)
63 , maLayoutSize(0, 0, 0)
64 , mnDistributedHeight(0)
66 , mbShowTitleBar(true)
70 tools::Rectangle
LayoutPanels (
71 const tools::Rectangle
& rContentArea
,
72 sal_Int32
& rMinimalWidth
,
73 sal_Int32
& rMinimalHeight
,
74 ::std::vector
<LayoutItem
>& rLayoutItems
,
75 vcl::Window
& rScrollClipWindow
,
76 vcl::Window
& rScrollContainer
,
77 ScrollBar
& pVerticalScrollBar
,
78 const bool bShowVerticalScrollBar
);
79 void GetRequestedSizes (
80 ::std::vector
<LayoutItem
>& rLayoutItem
,
81 sal_Int32
& rAvailableHeight
,
82 sal_Int32
& rMinimalWidth
,
83 const tools::Rectangle
& rContentBox
);
84 void DistributeHeights (
85 ::std::vector
<LayoutItem
>& rLayoutItems
,
86 const sal_Int32 nHeightToDistribute
,
87 const sal_Int32 nContainerHeight
,
88 const bool bMinimumHeightIsBase
);
89 bool MoveResizePixel(const VclPtr
<vcl::Window
> &pWindow
,
90 const Point
&rNewPos
, const Size
&rNewSize
);
91 sal_Int32
PlacePanels (
92 ::std::vector
<LayoutItem
>& rLayoutItems
,
93 const sal_Int32 nWidth
,
94 const LayoutMode eMode
,
95 vcl::Window
& rScrollContainer
);
96 tools::Rectangle
PlaceDeckTitle (
97 vcl::Window
& rTitleBar
,
98 const tools::Rectangle
& rAvailableSpace
);
99 tools::Rectangle
PlaceVerticalScrollBar (
100 ScrollBar
& rVerticalScrollBar
,
101 const tools::Rectangle
& rAvailableSpace
,
102 const bool bShowVerticalScrollBar
);
103 void SetupVerticalScrollBar(
104 ScrollBar
& rVerticalScrollBar
,
105 const sal_Int32 nContentHeight
,
106 const sal_Int32 nVisibleHeight
);
108 vcl::Window
& rFiller
,
109 const tools::Rectangle
& rBox
);
112 void DeckLayouter::LayoutDeck (
113 const tools::Rectangle
& rContentArea
,
114 sal_Int32
& rMinimalWidth
,
115 sal_Int32
& rMinimalHeight
,
116 SharedPanelContainer
& rPanels
,
117 vcl::Window
& rDeckTitleBar
,
118 vcl::Window
& rScrollClipWindow
,
119 vcl::Window
& rScrollContainer
,
120 vcl::Window
& rFiller
,
121 ScrollBar
& rVerticalScrollBar
)
123 if (rContentArea
.GetWidth()<=0 || rContentArea
.GetHeight()<=0)
125 tools::Rectangle
aBox (PlaceDeckTitle(rDeckTitleBar
, rContentArea
));
127 if ( ! rPanels
.empty())
129 // Prepare the layout item container.
130 ::std::vector
<LayoutItem
> aLayoutItems
;
131 aLayoutItems
.reserve(rPanels
.size());
132 for (const auto& rPanel
: rPanels
)
133 aLayoutItems
.emplace_back(rPanel
);
145 UpdateFiller(rFiller
, aBox
);
150 tools::Rectangle
LayoutPanels (
151 const tools::Rectangle
& rContentArea
,
152 sal_Int32
& rMinimalWidth
,
153 sal_Int32
& rMinimalHeight
,
154 ::std::vector
<LayoutItem
>& rLayoutItems
,
155 vcl::Window
& rScrollClipWindow
,
156 vcl::Window
& rScrollContainer
,
157 ScrollBar
& rVerticalScrollBar
,
158 const bool bShowVerticalScrollBar
)
160 tools::Rectangle
aBox (PlaceVerticalScrollBar(rVerticalScrollBar
, rContentArea
, bShowVerticalScrollBar
));
162 const sal_Int32
nWidth (aBox
.GetWidth());
164 // Get the requested heights of the panels and the available
165 // height that is left when all panel titles and separators are
166 // taken into account.
167 sal_Int32
nAvailableHeight (aBox
.GetHeight());
168 GetRequestedSizes(rLayoutItems
, nAvailableHeight
, rMinimalWidth
, aBox
);
169 const sal_Int32
nTotalDecorationHeight (aBox
.GetHeight() - nAvailableHeight
);
171 // Analyze the requested heights.
172 // Determine the height that is available for panel content
173 // and count the different layouts.
174 sal_Int32
nTotalPreferredHeight (0);
175 sal_Int32
nTotalMinimumHeight (0);
177 for (const auto& rItem
: rLayoutItems
)
179 nTotalMinimumHeight
+= rItem
.maLayoutSize
.Minimum
;
180 nTotalPreferredHeight
+= rItem
.maLayoutSize
.Preferred
;
183 if (nTotalMinimumHeight
> nAvailableHeight
&& !bShowVerticalScrollBar
184 && !comphelper::LibreOfficeKit::isActive())
186 // Not enough space, even when all panels are shrunk to their
188 // Show a vertical scrollbar.
200 // We are now in one of three modes.
201 // - The preferred height fits into the available size:
202 // Use the preferred size, distribute the remaining height by
204 // - The total minimum height fits into the available size:
205 // Use the minimum size, distribute the remaining height by
207 // - The total minimum height does not fit into the available
209 // Use the unmodified preferred height for all panels.
211 LayoutMode
eMode (MinimumOrLarger
);
212 if (bShowVerticalScrollBar
)
214 else if (nTotalPreferredHeight
<= nAvailableHeight
)
215 eMode
= PreferredOrLarger
;
217 eMode
= MinimumOrLarger
;
219 if (eMode
!= Preferred
)
221 const sal_Int32
nTotalHeight (eMode
==MinimumOrLarger
? nTotalMinimumHeight
: nTotalPreferredHeight
);
225 nAvailableHeight
-nTotalHeight
,
227 eMode
==MinimumOrLarger
);
230 // Set position and size of the mpScrollClipWindow to the available
231 // size. Its child, the mpScrollContainer, may have a bigger
233 rScrollClipWindow
.setPosSizePixel(aBox
.Left(), aBox
.Top(), aBox
.GetWidth(), aBox
.GetHeight());
235 const sal_Int32
nContentHeight (
237 ? nTotalPreferredHeight
+ nTotalDecorationHeight
239 sal_Int32 nY
= rVerticalScrollBar
.GetThumbPos();
240 if (nContentHeight
-nY
< aBox
.GetHeight())
241 nY
= nContentHeight
-aBox
.GetHeight();
244 rScrollContainer
.setPosSizePixel(
250 if (bShowVerticalScrollBar
)
251 SetupVerticalScrollBar(rVerticalScrollBar
, nContentHeight
, aBox
.GetHeight());
253 const sal_Int32
nUsedHeight (PlacePanels(rLayoutItems
, nWidth
, eMode
, rScrollContainer
));
254 aBox
.AdjustTop(nUsedHeight
);
255 rMinimalHeight
= nUsedHeight
;
259 bool MoveResizePixel(const VclPtr
<vcl::Window
> &pWindow
,
260 const Point
&rNewPos
, const Size
&rNewSize
)
262 Point aCurPos
= pWindow
->GetPosPixel();
263 Size aCurSize
= pWindow
->GetSizePixel();
264 if (rNewPos
== aCurPos
&& aCurSize
== rNewSize
)
266 pWindow
->setPosSizePixel(rNewPos
.X(), rNewPos
.Y(), rNewSize
.Width(), rNewSize
.Height());
270 sal_Int32
PlacePanels (
271 ::std::vector
<LayoutItem
>& rLayoutItems
,
272 const sal_Int32 nWidth
,
273 const LayoutMode eMode
,
274 vcl::Window
& rScrollContainer
)
276 ::std::vector
<sal_Int32
> aSeparators
;
277 const sal_Int32
nDeckSeparatorHeight (Theme::GetInteger(Theme::Int_DeckSeparatorHeight
));
280 vcl::Region aInvalidRegions
;
282 // Assign heights and places.
283 for(::std::vector
<LayoutItem
>::const_iterator
iItem(rLayoutItems
.begin()),
284 iEnd(rLayoutItems
.end());
291 Panel
& rPanel (*iItem
->mpPanel
);
293 // Separator above the panel title bar.
294 if (!rPanel
.IsLurking())
296 aSeparators
.push_back(nY
);
297 nY
+= nDeckSeparatorHeight
;
300 // Place the title bar.
301 VclPtr
<PanelTitleBar
> pTitleBar
= rPanel
.GetTitleBar();
304 const sal_Int32
nPanelTitleBarHeight (Theme::GetInteger(Theme::Int_PanelTitleBarHeight
) * rPanel
.GetDPIScaleFactor());
306 if (iItem
->mbShowTitleBar
)
308 pTitleBar
->setPosSizePixel(0, nY
, nWidth
, nPanelTitleBarHeight
);
310 nY
+= nPanelTitleBarHeight
;
318 if (rPanel
.IsExpanded() && !rPanel
.IsLurking())
322 // Determine the height of the panel depending on layout
323 // mode and distributed heights.
324 sal_Int32
nPanelHeight (0);
327 case MinimumOrLarger
:
328 nPanelHeight
= iItem
->maLayoutSize
.Minimum
+ iItem
->mnDistributedHeight
;
330 case PreferredOrLarger
:
331 nPanelHeight
= iItem
->maLayoutSize
.Preferred
+ iItem
->mnDistributedHeight
;
334 nPanelHeight
= iItem
->maLayoutSize
.Preferred
;
342 Point
aNewPos(0, nY
);
343 Size
aNewSize(nWidth
, nPanelHeight
);
345 // Only invalidate if we moved
346 if (MoveResizePixel(&rPanel
, aNewPos
, aNewSize
))
348 tools::Rectangle
aRect(aNewPos
, aNewSize
);
349 aInvalidRegions
.Union(rPanel
.PixelToLogic(aRect
));
358 // Add a separator below the collapsed panel, if it is the
359 // last panel in the deck.
360 if (iItem
== rLayoutItems
.end()-1)
362 // Separator below the panel title bar.
363 aSeparators
.push_back(nY
);
364 nY
+= nDeckSeparatorHeight
;
369 Deck::ScrollContainerWindow
* pScrollContainerWindow
370 = dynamic_cast<Deck::ScrollContainerWindow
*>(&rScrollContainer
);
371 if (pScrollContainerWindow
!= nullptr)
372 pScrollContainerWindow
->SetSeparators(aSeparators
);
374 rScrollContainer
.Invalidate(aInvalidRegions
);
379 void GetRequestedSizes (
380 ::std::vector
<LayoutItem
>& rLayoutItems
,
381 sal_Int32
& rAvailableHeight
,
382 sal_Int32
& rMinimalWidth
,
383 const tools::Rectangle
& rContentBox
)
385 rAvailableHeight
= rContentBox
.GetHeight();
387 const sal_Int32
nDeckSeparatorHeight (Theme::GetInteger(Theme::Int_DeckSeparatorHeight
));
389 for (auto& rItem
: rLayoutItems
)
391 rItem
.maLayoutSize
= ui::LayoutSize(0,0,0);
393 if (rItem
.mpPanel
== nullptr)
396 if (rItem
.mpPanel
->IsLurking())
398 rItem
.mbShowTitleBar
= false;
402 if (rLayoutItems
.size() == 1
403 && rItem
.mpPanel
->IsTitleBarOptional())
405 // There is only one panel and its title bar is
406 // optional => hide it.
407 rAvailableHeight
-= nDeckSeparatorHeight
;
408 rItem
.mbShowTitleBar
= false;
412 // Show the title bar and a separator above and below
414 const sal_Int32
nPanelTitleBarHeight (Theme::GetInteger(Theme::Int_PanelTitleBarHeight
) * rItem
.mpPanel
->GetDPIScaleFactor());
416 rAvailableHeight
-= nPanelTitleBarHeight
;
417 rAvailableHeight
-= nDeckSeparatorHeight
;
420 if (rItem
.mpPanel
->IsExpanded() && rItem
.mpPanel
->GetPanelComponent().is())
422 Reference
<ui::XSidebarPanel
> xPanel (rItem
.mpPanel
->GetPanelComponent());
424 rItem
.maLayoutSize
= xPanel
->getHeightForWidth(rContentBox
.GetWidth());
425 if (!(0 <= rItem
.maLayoutSize
.Minimum
&& rItem
.maLayoutSize
.Minimum
<= rItem
.maLayoutSize
.Preferred
426 && rItem
.maLayoutSize
.Preferred
<= rItem
.maLayoutSize
.Maximum
))
428 SAL_INFO("sfx.sidebar", "Please follow LayoutSize constraints: 0 ≤ "
429 "Minimum ≤ Preferred ≤ Maximum."
430 " Currently: Minimum: "
431 << rItem
.maLayoutSize
.Minimum
432 << " Preferred: " << rItem
.maLayoutSize
.Preferred
433 << " Maximum: " << rItem
.maLayoutSize
.Maximum
);
436 sal_Int32 nWidth
= rMinimalWidth
;
439 // The demo sidebar extension "Analog Clock" fails with
440 // java.lang.AbstractMethodError here
441 nWidth
= xPanel
->getMinimalWidth();
447 uno::Reference
<frame::XDesktop2
> xDesktop
448 = frame::Desktop::create(comphelper::getProcessComponentContext());
449 uno::Reference
<frame::XFrame
> xFrame
= xDesktop
->getActiveFrame();
452 SidebarController
* pController
453 = SidebarController::GetSidebarControllerForFrame(xFrame
);
454 if (pController
&& pController
->getMaximumWidth() < nWidth
)
456 // Add 100 extra pixels to still have the sidebar resizable
457 // (See also documentation of XSidebarPanel::getMinimalWidth)
458 pController
->setMaximumWidth(nWidth
+ 100);
462 if (nWidth
> rMinimalWidth
)
463 rMinimalWidth
= nWidth
;
466 rItem
.maLayoutSize
= ui::LayoutSize(MinimalPanelHeight
, -1, 0);
470 void DistributeHeights (
471 ::std::vector
<LayoutItem
>& rLayoutItems
,
472 const sal_Int32 nHeightToDistribute
,
473 const sal_Int32 nContainerHeight
,
474 const bool bMinimumHeightIsBase
)
476 if (nHeightToDistribute
<= 0)
479 sal_Int32
nRemainingHeightToDistribute (nHeightToDistribute
);
481 // Compute the weights as difference between panel base height
482 // (either its minimum or preferred height) and the container height.
483 sal_Int32
nTotalWeight (0);
484 sal_Int32
nNoMaximumCount (0);
486 for (auto& rItem
: rLayoutItems
)
488 if (rItem
.maLayoutSize
.Maximum
== 0)
490 if (rItem
.maLayoutSize
.Maximum
< 0)
493 const sal_Int32
nBaseHeight (
495 ? rItem
.maLayoutSize
.Minimum
496 : rItem
.maLayoutSize
.Preferred
);
497 if (nBaseHeight
< nContainerHeight
)
499 rItem
.mnWeight
= nContainerHeight
- nBaseHeight
;
500 nTotalWeight
+= rItem
.mnWeight
;
504 if (nTotalWeight
== 0)
507 // First pass of height distribution.
508 for (auto& rItem
: rLayoutItems
)
510 const sal_Int32
nBaseHeight (
512 ? rItem
.maLayoutSize
.Minimum
513 : rItem
.maLayoutSize
.Preferred
);
514 sal_Int32
nDistributedHeight (rItem
.mnWeight
* nHeightToDistribute
/ nTotalWeight
);
515 if (nBaseHeight
+nDistributedHeight
> rItem
.maLayoutSize
.Maximum
516 && rItem
.maLayoutSize
.Maximum
>= 0)
518 nDistributedHeight
= ::std::max
<sal_Int32
>(0, rItem
.maLayoutSize
.Maximum
- nBaseHeight
);
520 rItem
.mnDistributedHeight
= nDistributedHeight
;
521 nRemainingHeightToDistribute
-= nDistributedHeight
;
524 if (nRemainingHeightToDistribute
== 0)
526 OSL_ASSERT(nRemainingHeightToDistribute
> 0);
528 // It is possible that not all of the height could be distributed
529 // because of Maximum heights being smaller than expected.
530 // Distribute the remaining height between the panels that have no
531 // Maximum (ie Maximum==-1).
532 if (nNoMaximumCount
== 0)
534 // There are no panels with unrestricted height.
538 const sal_Int32
nAdditionalHeightPerPanel(nRemainingHeightToDistribute
/ nNoMaximumCount
);
539 // Handle rounding error.
540 sal_Int32
nAdditionalHeightForFirstPanel (nRemainingHeightToDistribute
541 - nNoMaximumCount
*nAdditionalHeightPerPanel
);
543 for (auto& rItem
: rLayoutItems
)
545 if (rItem
.maLayoutSize
.Maximum
< 0)
547 rItem
.mnDistributedHeight
+= nAdditionalHeightPerPanel
+ nAdditionalHeightForFirstPanel
;
548 nRemainingHeightToDistribute
-= nAdditionalHeightPerPanel
+ nAdditionalHeightForFirstPanel
;
552 OSL_ASSERT(nRemainingHeightToDistribute
==0);
555 tools::Rectangle
PlaceDeckTitle (
556 vcl::Window
& rDeckTitleBar
,
557 const tools::Rectangle
& rAvailableSpace
)
559 if (static_cast<DockingWindow
*>(rDeckTitleBar
.GetParent()->GetParent())->IsFloatingMode())
561 // When the side bar is undocked then the outer system window displays the deck title.
562 rDeckTitleBar
.Hide();
563 return rAvailableSpace
;
567 const sal_Int32
nDeckTitleBarHeight (Theme::GetInteger(Theme::Int_DeckTitleBarHeight
) * rDeckTitleBar
.GetDPIScaleFactor());
568 rDeckTitleBar
.setPosSizePixel(
569 rAvailableSpace
.Left(),
570 rAvailableSpace
.Top(),
571 rAvailableSpace
.GetWidth(),
572 nDeckTitleBarHeight
);
573 rDeckTitleBar
.Show();
574 return tools::Rectangle(
575 rAvailableSpace
.Left(),
576 rAvailableSpace
.Top() + nDeckTitleBarHeight
,
577 rAvailableSpace
.Right(),
578 rAvailableSpace
.Bottom());
582 tools::Rectangle
PlaceVerticalScrollBar (
583 ScrollBar
& rVerticalScrollBar
,
584 const tools::Rectangle
& rAvailableSpace
,
585 const bool bShowVerticalScrollBar
)
587 if (bShowVerticalScrollBar
)
589 const sal_Int32
nScrollBarWidth (rVerticalScrollBar
.GetSizePixel().Width());
590 rVerticalScrollBar
.setPosSizePixel(
591 rAvailableSpace
.Right() - nScrollBarWidth
+ 1,
592 rAvailableSpace
.Top(),
594 rAvailableSpace
.GetHeight());
595 rVerticalScrollBar
.Show();
596 return tools::Rectangle(
597 rAvailableSpace
.Left(),
598 rAvailableSpace
.Top(),
599 rAvailableSpace
.Right() - nScrollBarWidth
,
600 rAvailableSpace
.Bottom());
604 rVerticalScrollBar
.Hide();
605 return rAvailableSpace
;
609 void SetupVerticalScrollBar(
610 ScrollBar
& rVerticalScrollBar
,
611 const sal_Int32 nContentHeight
,
612 const sal_Int32 nVisibleHeight
)
614 OSL_ASSERT(nContentHeight
> nVisibleHeight
);
616 rVerticalScrollBar
.SetRangeMin(0);
617 rVerticalScrollBar
.SetRangeMax(nContentHeight
-1);
618 rVerticalScrollBar
.SetVisibleSize(nVisibleHeight
);
622 vcl::Window
& rFiller
,
623 const tools::Rectangle
& rBox
)
625 if (rBox
.GetHeight() > 0)
628 rFiller
.SetBackground(Theme::GetColor(Theme::Color_PanelBackground
));
629 rFiller
.SetPosSizePixel(rBox
.TopLeft(), rBox
.GetSize());
641 } // end of namespace sfx2::sidebar
643 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */