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 <sidebar/DeckTitleBar.hxx>
22 #include <sidebar/PanelTitleBar.hxx>
23 #include <sfx2/sidebar/Panel.hxx>
24 #include <sfx2/sidebar/Theme.hxx>
25 #include <sfx2/sidebar/SidebarDockingWindow.hxx>
26 #include <sfx2/sidebar/SidebarController.hxx>
27 #include <sfx2/viewsh.hxx>
28 #include <comphelper/lok.hxx>
29 #include <osl/diagnose.h>
31 #include <comphelper/processfactory.hxx>
33 #include <com/sun/star/uno/Reference.hxx>
34 #include <com/sun/star/frame/Desktop.hpp>
35 #include <com/sun/star/frame/XDesktop2.hpp>
36 #include <com/sun/star/frame/XFrame.hpp>
37 #include <com/sun/star/ui/XSidebarPanel.hpp>
40 #include <vcl/jsdialog/executor.hxx>
43 using namespace css::uno
;
45 namespace sfx2::sidebar
{
48 const sal_Int32
MinimalPanelHeight (25);
59 std::shared_ptr
<Panel
> mpPanel
;
60 css::ui::LayoutSize maLayoutSize
;
61 sal_Int32 mnDistributedHeight
;
65 LayoutItem(std::shared_ptr
<Panel
> pPanel
)
66 : mpPanel(std::move(pPanel
))
67 , maLayoutSize(0, 0, 0)
68 , mnDistributedHeight(0)
70 , mbShowTitleBar(true)
75 const tools::Rectangle
& rContentArea
,
76 sal_Int32
& rMinimalWidth
,
77 sal_Int32
& rMinimalHeight
,
78 ::std::vector
<LayoutItem
>& rLayoutItems
,
79 weld::ScrolledWindow
& pVerticalScrollBar
,
80 const bool bShowVerticalScrollBar
);
81 void GetRequestedSizes (
82 ::std::vector
<LayoutItem
>& rLayoutItem
,
83 sal_Int32
& rAvailableHeight
,
84 sal_Int32
& rMinimalWidth
,
85 const tools::Rectangle
& rContentBox
);
86 void DistributeHeights (
87 ::std::vector
<LayoutItem
>& rLayoutItems
,
88 const sal_Int32 nHeightToDistribute
,
89 const sal_Int32 nContainerHeight
,
90 const bool bMinimumHeightIsBase
);
91 sal_Int32
PlacePanels (
92 ::std::vector
<LayoutItem
>& rLayoutItems
,
93 const LayoutMode eMode_
);
94 tools::Rectangle
PlaceDeckTitle (
95 const SidebarDockingWindow
* pDockingWindow
,
96 DeckTitleBar
& rTitleBar
,
97 const tools::Rectangle
& rAvailableSpace
);
98 tools::Rectangle
PlaceVerticalScrollBar (
99 weld::ScrolledWindow
& rVerticalScrollBar
,
100 const tools::Rectangle
& rAvailableSpace
,
101 const bool bShowVerticalScrollBar
);
102 void SetupVerticalScrollBar(
103 weld::ScrolledWindow
& rVerticalScrollBar
,
104 const sal_Int32 nContentHeight
,
105 const sal_Int32 nVisibleHeight
);
108 void DeckLayouter::LayoutDeck (
109 const SidebarDockingWindow
* pDockingWindow
,
110 const tools::Rectangle
& rContentArea
,
111 sal_Int32
& rMinimalWidth
,
112 sal_Int32
& rMinimalHeight
,
113 SharedPanelContainer
& rPanels
,
114 DeckTitleBar
& rDeckTitleBar
,
115 weld::ScrolledWindow
& rVerticalScrollBar
)
117 if (rContentArea
.GetWidth()<=0 || rContentArea
.GetHeight()<=0)
119 tools::Rectangle
aBox(PlaceDeckTitle(pDockingWindow
, rDeckTitleBar
, rContentArea
));
121 if ( rPanels
.empty())
124 // Prepare the layout item container.
125 ::std::vector
<LayoutItem
> aLayoutItems
;
126 aLayoutItems
.reserve(rPanels
.size());
127 for (auto& rPanel
: rPanels
)
128 aLayoutItems
.emplace_back(rPanel
);
142 const tools::Rectangle
& rContentArea
,
143 sal_Int32
& rMinimalWidth
,
144 sal_Int32
& rMinimalHeight
,
145 ::std::vector
<LayoutItem
>& rLayoutItems
,
146 weld::ScrolledWindow
& rVerticalScrollBar
,
147 const bool bShowVerticalScrollBar
)
149 tools::Rectangle
aBox (PlaceVerticalScrollBar(rVerticalScrollBar
, rContentArea
, bShowVerticalScrollBar
));
151 // Get the requested heights of the panels and the available
152 // height that is left when all panel titles and separators are
153 // taken into account.
154 sal_Int32
nAvailableHeight (aBox
.GetHeight());
155 GetRequestedSizes(rLayoutItems
, nAvailableHeight
, rMinimalWidth
, aBox
);
156 const sal_Int32
nTotalDecorationHeight (aBox
.GetHeight() - nAvailableHeight
);
158 // Analyze the requested heights.
159 // Determine the height that is available for panel content
160 // and count the different layouts.
161 sal_Int32
nTotalPreferredHeight (0);
162 sal_Int32
nTotalMinimumHeight (0);
164 for (const auto& rItem
: rLayoutItems
)
166 nTotalMinimumHeight
+= rItem
.maLayoutSize
.Minimum
;
167 nTotalPreferredHeight
+= rItem
.maLayoutSize
.Preferred
;
170 if (nTotalMinimumHeight
> nAvailableHeight
&& !bShowVerticalScrollBar
171 && !comphelper::LibreOfficeKit::isActive())
173 // Not enough space, even when all panels are shrunk to their
175 // Show a vertical scrollbar.
186 // We are now in one of three modes.
187 // - The preferred height fits into the available size:
188 // Use the preferred size, distribute the remaining height by
190 // - The total minimum height fits into the available size:
191 // Use the minimum size, distribute the remaining height by
193 // - The total minimum height does not fit into the available
195 // Use the unmodified preferred height for all panels.
197 LayoutMode
eMode(MinimumOrLarger
);
198 if (bShowVerticalScrollBar
)
202 const sal_Int32
nContentHeight(nTotalPreferredHeight
+ nTotalDecorationHeight
);
203 SetupVerticalScrollBar(rVerticalScrollBar
, nContentHeight
, aBox
.GetHeight());
207 if (nTotalPreferredHeight
<= nAvailableHeight
)
208 eMode
= PreferredOrLarger
;
210 eMode
= MinimumOrLarger
;
212 const sal_Int32
nTotalHeight (eMode
==MinimumOrLarger
? nTotalMinimumHeight
: nTotalPreferredHeight
);
216 nAvailableHeight
-nTotalHeight
,
218 eMode
==MinimumOrLarger
);
221 const sal_Int32
nUsedHeight(PlacePanels(rLayoutItems
, eMode
));
222 rMinimalHeight
= nUsedHeight
;
225 sal_Int32
PlacePanels (
226 ::std::vector
<LayoutItem
>& rLayoutItems
,
227 const LayoutMode eMode
)
229 const sal_Int32
nDeckSeparatorHeight (Theme::GetInteger(Theme::Int_DeckSeparatorHeight
));
232 // Assign heights and places.
233 for(::std::vector
<LayoutItem
>::const_iterator
iItem(rLayoutItems
.begin()),
234 iEnd(rLayoutItems
.end());
241 Panel
& rPanel (*iItem
->mpPanel
);
243 rPanel
.set_margin_top(nDeckSeparatorHeight
);
244 rPanel
.set_margin_bottom(0);
246 // Separator above the panel title bar.
247 if (!rPanel
.IsLurking())
249 nY
+= nDeckSeparatorHeight
;
252 bool bShowTitlebar
= iItem
->mbShowTitleBar
;
253 PanelTitleBar
* pTitleBar
= rPanel
.GetTitleBar();
254 pTitleBar
->Show(bShowTitlebar
);
255 rPanel
.set_vexpand(!bShowTitlebar
);
256 weld::Container
* pContents
= rPanel
.GetContents();
257 pContents
->set_vexpand(true);
259 bool bExpanded
= rPanel
.IsExpanded() && !rPanel
.IsLurking();
260 if (bShowTitlebar
|| bExpanded
)
264 sal_Int32
nPanelHeight(0);
267 // Determine the height of the panel depending on layout
268 // mode and distributed heights.
271 case MinimumOrLarger
:
272 nPanelHeight
= iItem
->maLayoutSize
.Minimum
+ iItem
->mnDistributedHeight
;
274 case PreferredOrLarger
:
275 nPanelHeight
= iItem
->maLayoutSize
.Preferred
+ iItem
->mnDistributedHeight
;
278 nPanelHeight
= iItem
->maLayoutSize
.Preferred
;
286 nPanelHeight
+= pTitleBar
->get_preferred_size().Height();
288 rPanel
.SetHeightPixel(nPanelHeight
);
299 // Add a separator below the collapsed panel, if it is the
300 // last panel in the deck.
301 if (iItem
== rLayoutItems
.end()-1)
303 // Separator below the panel title bar.
304 rPanel
.set_margin_bottom(nDeckSeparatorHeight
);
305 nY
+= nDeckSeparatorHeight
;
310 if (comphelper::LibreOfficeKit::isActive())
312 sal_uInt64 nShellId
= reinterpret_cast<sal_uInt64
>(SfxViewShell::Current());
313 jsdialog::SendFullUpdate(OUString::number(nShellId
) + "sidebar", "Panel");
319 void GetRequestedSizes (
320 ::std::vector
<LayoutItem
>& rLayoutItems
,
321 sal_Int32
& rAvailableHeight
,
322 sal_Int32
& rMinimalWidth
,
323 const tools::Rectangle
& rContentBox
)
325 rAvailableHeight
= rContentBox
.GetHeight();
327 const sal_Int32
nDeckSeparatorHeight (Theme::GetInteger(Theme::Int_DeckSeparatorHeight
));
329 for (auto& rItem
: rLayoutItems
)
331 rItem
.maLayoutSize
= ui::LayoutSize(0,0,0);
333 if (rItem
.mpPanel
== nullptr)
336 if (rItem
.mpPanel
->IsLurking())
338 rItem
.mbShowTitleBar
= false;
342 if (rLayoutItems
.size() == 1
343 && rItem
.mpPanel
->IsTitleBarOptional())
345 // There is only one panel and its title bar is
346 // optional => hide it.
347 rAvailableHeight
-= nDeckSeparatorHeight
;
348 rItem
.mbShowTitleBar
= false;
352 // Show the title bar and a separator above and below
354 PanelTitleBar
* pTitleBar
= rItem
.mpPanel
->GetTitleBar();
355 const sal_Int32 nPanelTitleBarHeight
= pTitleBar
->get_preferred_size().Height();
357 rAvailableHeight
-= nPanelTitleBarHeight
;
358 rAvailableHeight
-= nDeckSeparatorHeight
;
361 if (rItem
.mpPanel
->IsExpanded() && rItem
.mpPanel
->GetPanelComponent().is())
363 Reference
<ui::XSidebarPanel
> xPanel (rItem
.mpPanel
->GetPanelComponent());
365 rItem
.maLayoutSize
= xPanel
->getHeightForWidth(rContentBox
.GetWidth());
366 if (!(0 <= rItem
.maLayoutSize
.Minimum
&& rItem
.maLayoutSize
.Minimum
<= rItem
.maLayoutSize
.Preferred
367 && rItem
.maLayoutSize
.Preferred
<= rItem
.maLayoutSize
.Maximum
))
369 SAL_INFO("sfx.sidebar", "Please follow LayoutSize constraints: 0 ≤ "
370 "Minimum ≤ Preferred ≤ Maximum."
371 " Currently: Minimum: "
372 << rItem
.maLayoutSize
.Minimum
373 << " Preferred: " << rItem
.maLayoutSize
.Preferred
374 << " Maximum: " << rItem
.maLayoutSize
.Maximum
);
377 sal_Int32 nWidth
= rMinimalWidth
;
380 // The demo sidebar extension "Analog Clock" fails with
381 // java.lang.AbstractMethodError here
382 nWidth
= xPanel
->getMinimalWidth();
388 uno::Reference
<frame::XDesktop2
> xDesktop
389 = frame::Desktop::create(comphelper::getProcessComponentContext());
390 uno::Reference
<frame::XFrame
> xFrame
= xDesktop
->getActiveFrame();
393 SidebarController
* pController
394 = SidebarController::GetSidebarControllerForFrame(xFrame
);
395 if (pController
&& pController
->getMaximumWidth() < nWidth
)
397 // Add 100 extra pixels to still have the sidebar resizable
398 // (See also documentation of XSidebarPanel::getMinimalWidth)
399 pController
->setMaximumWidth(nWidth
+ 100);
403 if (nWidth
> rMinimalWidth
)
404 rMinimalWidth
= nWidth
;
407 rItem
.maLayoutSize
= ui::LayoutSize(MinimalPanelHeight
, -1, 0);
411 void DistributeHeights (
412 ::std::vector
<LayoutItem
>& rLayoutItems
,
413 const sal_Int32 nHeightToDistribute
,
414 const sal_Int32 nContainerHeight
,
415 const bool bMinimumHeightIsBase
)
417 if (nHeightToDistribute
<= 0)
420 sal_Int32
nRemainingHeightToDistribute (nHeightToDistribute
);
422 // Compute the weights as difference between panel base height
423 // (either its minimum or preferred height) and the container height.
424 sal_Int32
nTotalWeight (0);
425 sal_Int32
nNoMaximumCount (0);
427 for (auto& rItem
: rLayoutItems
)
429 if (rItem
.maLayoutSize
.Maximum
== 0)
431 if (rItem
.maLayoutSize
.Maximum
< 0)
434 const sal_Int32
nBaseHeight (
436 ? rItem
.maLayoutSize
.Minimum
437 : rItem
.maLayoutSize
.Preferred
);
438 if (nBaseHeight
< nContainerHeight
)
440 rItem
.mnWeight
= nContainerHeight
- nBaseHeight
;
441 nTotalWeight
+= rItem
.mnWeight
;
445 if (nTotalWeight
== 0)
448 // First pass of height distribution.
449 for (auto& rItem
: rLayoutItems
)
451 const sal_Int32
nBaseHeight (
453 ? rItem
.maLayoutSize
.Minimum
454 : rItem
.maLayoutSize
.Preferred
);
455 sal_Int32
nDistributedHeight (rItem
.mnWeight
* nHeightToDistribute
/ nTotalWeight
);
456 if (nBaseHeight
+nDistributedHeight
> rItem
.maLayoutSize
.Maximum
457 && rItem
.maLayoutSize
.Maximum
>= 0)
459 nDistributedHeight
= ::std::max
<sal_Int32
>(0, rItem
.maLayoutSize
.Maximum
- nBaseHeight
);
461 rItem
.mnDistributedHeight
= nDistributedHeight
;
462 nRemainingHeightToDistribute
-= nDistributedHeight
;
465 if (nRemainingHeightToDistribute
== 0)
467 OSL_ASSERT(nRemainingHeightToDistribute
> 0);
469 // It is possible that not all of the height could be distributed
470 // because of Maximum heights being smaller than expected.
471 // Distribute the remaining height between the panels that have no
472 // Maximum (ie Maximum==-1).
473 if (nNoMaximumCount
== 0)
475 // There are no panels with unrestricted height.
479 const sal_Int32
nAdditionalHeightPerPanel(nRemainingHeightToDistribute
/ nNoMaximumCount
);
480 // Handle rounding error.
481 sal_Int32
nAdditionalHeightForFirstPanel (nRemainingHeightToDistribute
482 - nNoMaximumCount
*nAdditionalHeightPerPanel
);
484 for (auto& rItem
: rLayoutItems
)
486 if (rItem
.maLayoutSize
.Maximum
< 0)
488 rItem
.mnDistributedHeight
+= nAdditionalHeightPerPanel
+ nAdditionalHeightForFirstPanel
;
489 nRemainingHeightToDistribute
-= nAdditionalHeightPerPanel
+ nAdditionalHeightForFirstPanel
;
493 OSL_ASSERT(nRemainingHeightToDistribute
==0);
496 tools::Rectangle
PlaceDeckTitle(
497 const SidebarDockingWindow
* pDockingWindow
,
498 DeckTitleBar
& rDeckTitleBar
,
499 const tools::Rectangle
& rAvailableSpace
)
501 if (pDockingWindow
->IsFloatingMode())
503 // When the side bar is undocked then the outer system window displays the deck title.
504 rDeckTitleBar
.Show(false);
505 return rAvailableSpace
;
509 rDeckTitleBar
.Show(true);
510 const sal_Int32
nDeckTitleBarHeight(rDeckTitleBar
.get_preferred_size().Height());
511 return tools::Rectangle(
512 rAvailableSpace
.Left(),
513 rAvailableSpace
.Top() + nDeckTitleBarHeight
,
514 rAvailableSpace
.Right(),
515 rAvailableSpace
.Bottom());
519 tools::Rectangle
PlaceVerticalScrollBar (
520 weld::ScrolledWindow
& rVerticalScrollBar
,
521 const tools::Rectangle
& rAvailableSpace
,
522 const bool bShowVerticalScrollBar
)
524 if (bShowVerticalScrollBar
)
526 const sal_Int32
nScrollBarWidth(rVerticalScrollBar
.get_scroll_thickness());
527 rVerticalScrollBar
.set_vpolicy(VclPolicyType::ALWAYS
);
528 return tools::Rectangle(
529 rAvailableSpace
.Left(),
530 rAvailableSpace
.Top(),
531 rAvailableSpace
.Right() - nScrollBarWidth
,
532 rAvailableSpace
.Bottom());
536 rVerticalScrollBar
.set_vpolicy(VclPolicyType::NEVER
);
537 return rAvailableSpace
;
541 void SetupVerticalScrollBar(
542 weld::ScrolledWindow
& rVerticalScrollBar
,
543 const sal_Int32 nContentHeight
,
544 const sal_Int32 nVisibleHeight
)
546 OSL_ASSERT(nContentHeight
> nVisibleHeight
);
548 rVerticalScrollBar
.vadjustment_set_upper(nContentHeight
-1);
549 rVerticalScrollBar
.vadjustment_set_page_size(nVisibleHeight
);
554 } // end of namespace sfx2::sidebar
556 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */