Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sfx2 / source / sidebar / DeckLayouter.cxx
blob05a82dd2003f7d21d905aed09d662761d12eaa38
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 <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>
39 #include <utility>
40 #include <vcl/jsdialog/executor.hxx>
42 using namespace css;
43 using namespace css::uno;
45 namespace sfx2::sidebar {
47 namespace {
48 const sal_Int32 MinimalPanelHeight (25);
50 enum LayoutMode
52 MinimumOrLarger,
53 PreferredOrLarger,
54 Preferred
56 class LayoutItem
58 public:
59 std::shared_ptr<Panel> mpPanel;
60 css::ui::LayoutSize maLayoutSize;
61 sal_Int32 mnDistributedHeight;
62 sal_Int32 mnWeight;
63 bool mbShowTitleBar;
65 LayoutItem(std::shared_ptr<Panel> pPanel)
66 : mpPanel(std::move(pPanel))
67 , maLayoutSize(0, 0, 0)
68 , mnDistributedHeight(0)
69 , mnWeight(0)
70 , mbShowTitleBar(true)
74 void LayoutPanels (
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)
118 return;
119 tools::Rectangle aBox(PlaceDeckTitle(pDockingWindow, rDeckTitleBar, rContentArea));
121 if ( rPanels.empty())
122 return;
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);
130 LayoutPanels(
131 aBox,
132 rMinimalWidth,
133 rMinimalHeight,
134 aLayoutItems,
135 rVerticalScrollBar,
136 false);
139 namespace {
141 void LayoutPanels (
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
174 // minimum height.
175 // Show a vertical scrollbar.
176 LayoutPanels(
177 rContentArea,
178 rMinimalWidth,
179 rMinimalHeight,
180 rLayoutItems,
181 rVerticalScrollBar,
182 true);
183 return;
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
189 // enlarging panels.
190 // - The total minimum height fits into the available size:
191 // Use the minimum size, distribute the remaining height by
192 // enlarging panels.
193 // - The total minimum height does not fit into the available
194 // size:
195 // Use the unmodified preferred height for all panels.
197 LayoutMode eMode(MinimumOrLarger);
198 if (bShowVerticalScrollBar)
200 eMode = Preferred;
202 const sal_Int32 nContentHeight(nTotalPreferredHeight + nTotalDecorationHeight);
203 SetupVerticalScrollBar(rVerticalScrollBar, nContentHeight, aBox.GetHeight());
205 else
207 if (nTotalPreferredHeight <= nAvailableHeight)
208 eMode = PreferredOrLarger;
209 else
210 eMode = MinimumOrLarger;
212 const sal_Int32 nTotalHeight (eMode==MinimumOrLarger ? nTotalMinimumHeight : nTotalPreferredHeight);
214 DistributeHeights(
215 rLayoutItems,
216 nAvailableHeight-nTotalHeight,
217 aBox.GetHeight(),
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));
230 sal_Int32 nY (0);
232 // Assign heights and places.
233 for(::std::vector<LayoutItem>::const_iterator iItem(rLayoutItems.begin()),
234 iEnd(rLayoutItems.end());
235 iItem!=iEnd;
236 ++iItem)
238 if (!iItem->mpPanel)
239 continue;
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)
262 rPanel.Show(true);
264 sal_Int32 nPanelHeight(0);
265 if (bExpanded)
267 // Determine the height of the panel depending on layout
268 // mode and distributed heights.
269 switch(eMode)
271 case MinimumOrLarger:
272 nPanelHeight = iItem->maLayoutSize.Minimum + iItem->mnDistributedHeight;
273 break;
274 case PreferredOrLarger:
275 nPanelHeight = iItem->maLayoutSize.Preferred + iItem->mnDistributedHeight;
276 break;
277 case Preferred:
278 nPanelHeight = iItem->maLayoutSize.Preferred;
279 break;
280 default:
281 OSL_ASSERT(false);
282 break;
285 if (bShowTitlebar)
286 nPanelHeight += pTitleBar->get_preferred_size().Height();
288 rPanel.SetHeightPixel(nPanelHeight);
290 nY += nPanelHeight;
292 else
294 rPanel.Show(false);
297 if (!bExpanded)
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");
316 return nY;
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)
334 continue;
336 if (rItem.mpPanel->IsLurking())
338 rItem.mbShowTitleBar = false;
339 continue;
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;
350 else
352 // Show the title bar and a separator above and below
353 // the title bar.
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();
384 catch (...)
388 uno::Reference<frame::XDesktop2> xDesktop
389 = frame::Desktop::create(comphelper::getProcessComponentContext());
390 uno::Reference<frame::XFrame> xFrame = xDesktop->getActiveFrame();
391 if (xFrame.is())
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;
406 else
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)
418 return;
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)
430 continue;
431 if (rItem.maLayoutSize.Maximum < 0)
432 ++nNoMaximumCount;
434 const sal_Int32 nBaseHeight (
435 bMinimumHeightIsBase
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)
446 return;
448 // First pass of height distribution.
449 for (auto& rItem : rLayoutItems)
451 const sal_Int32 nBaseHeight (
452 bMinimumHeightIsBase
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)
466 return;
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.
476 return;
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;
507 else
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());
534 else
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: */