Update git submodules
[LibreOffice.git] / vcl / source / window / layout.cxx
blob9d0dc28b4f919e0b3a2421a1d5ec885833305d6a
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/.
8 */
10 #include <sal/config.h>
12 // Needed since LLVM 15 libc++ (hence the ignored -Wunused-macros for older libc++) when
13 // #include <boost/multi_array.hpp> below includes Boost 1.79.0
14 // workdir/UnpackedTarball/boost/boost/functional.hpp using std::unary_function, but must
15 // come very early here in case <functional> is already (indirectly) included earlier:
16 #include <config_libcxx.h>
17 #if HAVE_LIBCPP
18 #if defined __clang__
19 #pragma clang diagnostic push
20 #pragma clang diagnostic ignored "-Wunused-macros"
21 #endif
22 // [-loplugin:reservedid]:
23 #define _LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION
24 #if defined __clang__
25 #pragma clang diagnostic pop
26 #endif
27 #endif
29 #include <string_view>
31 #include <config_features.h>
32 #include <com/sun/star/accessibility/AccessibleRole.hpp>
33 #include <comphelper/base64.hxx>
34 #include <comphelper/lok.hxx>
35 #include <o3tl/enumarray.hxx>
36 #include <o3tl/enumrange.hxx>
37 #include <o3tl/string_view.hxx>
38 #include <tools/stream.hxx>
39 #include <utility>
40 #include <vcl/builder.hxx>
41 #include <vcl/toolkit/button.hxx>
42 #include <vcl/cvtgrf.hxx>
43 #include <vcl/decoview.hxx>
44 #include <vcl/help.hxx>
45 #include <vcl/toolkit/dialog.hxx>
46 #include <vcl/layout.hxx>
47 #include <vcl/toolkit/scrbar.hxx>
48 #include <vcl/stdtext.hxx>
49 #include <vcl/split.hxx>
50 #include <vcl/svapp.hxx>
51 #include <vcl/settings.hxx>
52 #include <vcl/virdev.hxx>
53 #include <bitmaps.hlst>
54 #include <messagedialog.hxx>
55 #include <svdata.hxx>
56 #include <window.h>
57 #include <boost/multi_array.hpp>
58 #include <vcl/toolkit/vclmedit.hxx>
59 #include <vcl/uitest/uiobject.hxx>
60 #include <sal/log.hxx>
61 #include <tools/json_writer.hxx>
63 VclContainer::VclContainer(vcl::Window *pParent, WinBits nStyle)
64 : Window(WindowType::CONTAINER)
65 , m_bLayoutDirty(true)
67 ImplInit(pParent, nStyle, nullptr);
68 EnableChildTransparentMode();
69 SetPaintTransparent(true);
70 SetBackground();
73 sal_uInt16 VclContainer::getDefaultAccessibleRole() const
75 return css::accessibility::AccessibleRole::PANEL;
78 Size VclContainer::GetOptimalSize() const
80 return calculateRequisition();
83 void VclContainer::setLayoutPosSize(vcl::Window &rWindow, const Point &rPos, const Size &rSize)
85 sal_Int32 nBorderWidth = rWindow.get_border_width();
86 sal_Int32 nLeft = rWindow.get_margin_start() + nBorderWidth;
87 sal_Int32 nTop = rWindow.get_margin_top() + nBorderWidth;
88 sal_Int32 nRight = rWindow.get_margin_end() + nBorderWidth;
89 sal_Int32 nBottom = rWindow.get_margin_bottom() + nBorderWidth;
90 Point aPos(rPos.X() + nLeft, rPos.Y() + nTop);
91 Size aSize(rSize.Width() - nLeft - nRight, rSize.Height() - nTop - nBottom);
92 rWindow.SetPosSizePixel(aPos, aSize);
95 void VclContainer::setLayoutAllocation(vcl::Window &rChild, const Point &rAllocPos, const Size &rChildAlloc)
97 VclAlign eHalign = rChild.get_halign();
98 VclAlign eValign = rChild.get_valign();
100 //typical case
101 if (eHalign == VclAlign::Fill && eValign == VclAlign::Fill)
103 setLayoutPosSize(rChild, rAllocPos, rChildAlloc);
104 return;
107 Point aChildPos(rAllocPos);
108 Size aChildSize(rChildAlloc);
109 Size aChildPreferredSize(getLayoutRequisition(rChild));
111 switch (eHalign)
113 case VclAlign::Fill:
114 break;
115 case VclAlign::Start:
116 if (aChildPreferredSize.Width() < rChildAlloc.Width())
117 aChildSize.setWidth( aChildPreferredSize.Width() );
118 break;
119 case VclAlign::End:
120 if (aChildPreferredSize.Width() < rChildAlloc.Width())
121 aChildSize.setWidth( aChildPreferredSize.Width() );
122 aChildPos.AdjustX(rChildAlloc.Width() );
123 aChildPos.AdjustX( -(aChildSize.Width()) );
124 break;
125 case VclAlign::Center:
126 if (aChildPreferredSize.Width() < aChildSize.Width())
127 aChildSize.setWidth( aChildPreferredSize.Width() );
128 aChildPos.AdjustX((rChildAlloc.Width() - aChildSize.Width()) / 2 );
129 break;
132 switch (eValign)
134 case VclAlign::Fill:
135 break;
136 case VclAlign::Start:
137 if (aChildPreferredSize.Height() < rChildAlloc.Height())
138 aChildSize.setHeight( aChildPreferredSize.Height() );
139 break;
140 case VclAlign::End:
141 if (aChildPreferredSize.Height() < rChildAlloc.Height())
142 aChildSize.setHeight( aChildPreferredSize.Height() );
143 aChildPos.AdjustY(rChildAlloc.Height() );
144 aChildPos.AdjustY( -(aChildSize.Height()) );
145 break;
146 case VclAlign::Center:
147 if (aChildPreferredSize.Height() < aChildSize.Height())
148 aChildSize.setHeight( aChildPreferredSize.Height() );
149 aChildPos.AdjustY((rChildAlloc.Height() - aChildSize.Height()) / 2 );
150 break;
153 setLayoutPosSize(rChild, aChildPos, aChildSize);
156 namespace
158 Size subtractBorder(const vcl::Window &rWindow, const Size& rSize)
160 sal_Int32 nBorderWidth = rWindow.get_border_width();
161 sal_Int32 nLeft = rWindow.get_margin_start() + nBorderWidth;
162 sal_Int32 nTop = rWindow.get_margin_top() + nBorderWidth;
163 sal_Int32 nRight = rWindow.get_margin_end() + nBorderWidth;
164 sal_Int32 nBottom = rWindow.get_margin_bottom() + nBorderWidth;
165 Size aSize(rSize);
166 return Size(aSize.Width() + nLeft + nRight, aSize.Height() + nTop + nBottom);
170 Size VclContainer::getLayoutRequisition(const vcl::Window &rWindow)
172 return subtractBorder(rWindow, rWindow.get_preferred_size());
175 void VclContainer::SetPosSizePixel(const Point& rAllocPos, const Size& rAllocation)
177 bool bSizeChanged = rAllocation != GetOutputSizePixel();
178 Window::SetPosSizePixel(rAllocPos, rAllocation);
179 if (m_bLayoutDirty || bSizeChanged)
181 m_bLayoutDirty = false;
182 setAllocation(rAllocation);
186 void VclContainer::SetPosPixel(const Point& rAllocPos)
188 Point aAllocPos = rAllocPos;
189 sal_Int32 nBorderWidth = get_border_width();
190 aAllocPos.AdjustX(nBorderWidth + get_margin_start() );
191 aAllocPos.AdjustY(nBorderWidth + get_margin_top() );
193 if (aAllocPos != GetPosPixel())
194 Window::SetPosPixel(aAllocPos);
197 void VclContainer::SetSizePixel(const Size& rAllocation)
199 Size aAllocation = rAllocation;
200 sal_Int32 nBorderWidth = get_border_width();
201 aAllocation.AdjustWidth( -(nBorderWidth*2 + get_margin_start() + get_margin_end()) );
202 aAllocation.AdjustHeight( -(nBorderWidth*2 + get_margin_top() + get_margin_bottom()) );
203 bool bSizeChanged = aAllocation != GetSizePixel();
204 if (bSizeChanged)
205 Window::SetSizePixel(aAllocation);
206 if (m_bLayoutDirty || bSizeChanged)
208 m_bLayoutDirty = false;
209 setAllocation(aAllocation);
213 void VclContainer::queue_resize(StateChangedType eReason)
215 m_bLayoutDirty = true;
216 Window::queue_resize(eReason);
219 // support for screenshot context menu
220 void VclContainer::Command(const CommandEvent& rCEvt)
222 if (CommandEventId::ContextMenu == rCEvt.GetCommand())
224 auto pParent = GetParent();
225 if (pParent)
227 CommandEvent aCEvt(rCEvt.GetMousePosPixel() + GetPosPixel(), rCEvt.GetCommand(), rCEvt.IsMouseEvent(), rCEvt.GetEventData());
228 pParent->Command(aCEvt);
229 return;
233 // call parent (do not consume)
234 Window::Command(rCEvt);
237 void VclBox::accumulateMaxes(const Size &rChildSize, Size &rSize) const
239 tools::Long nSecondaryChildDimension = getSecondaryDimension(rChildSize);
240 tools::Long nSecondaryBoxDimension = getSecondaryDimension(rSize);
241 setSecondaryDimension(rSize, std::max(nSecondaryChildDimension, nSecondaryBoxDimension));
243 tools::Long nPrimaryChildDimension = getPrimaryDimension(rChildSize);
244 tools::Long nPrimaryBoxDimension = getPrimaryDimension(rSize);
245 if (m_bHomogeneous)
246 setPrimaryDimension(rSize, std::max(nPrimaryBoxDimension, nPrimaryChildDimension));
247 else
248 setPrimaryDimension(rSize, nPrimaryBoxDimension + nPrimaryChildDimension);
251 Size VclBox::calculateRequisition() const
253 sal_uInt16 nVisibleChildren = 0;
255 Size aSize;
256 for (vcl::Window *pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next))
258 if (!pChild->IsVisible())
259 continue;
260 ++nVisibleChildren;
261 Size aChildSize = getLayoutRequisition(*pChild);
263 tools::Long nPrimaryDimension = getPrimaryDimension(aChildSize);
264 nPrimaryDimension += pChild->get_padding() * 2;
265 setPrimaryDimension(aChildSize, nPrimaryDimension);
267 accumulateMaxes(aChildSize, aSize);
270 return finalizeMaxes(aSize, nVisibleChildren);
273 void VclBox::setAllocation(const Size &rAllocation)
275 sal_uInt16 nVisibleChildren = 0, nExpandChildren = 0;
276 for (vcl::Window *pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next))
278 if (!pChild->IsVisible())
279 continue;
280 ++nVisibleChildren;
281 bool bExpand = getPrimaryDimensionChildExpand(*pChild);
282 if (bExpand)
283 ++nExpandChildren;
286 if (!nVisibleChildren)
287 return;
289 tools::Long nAllocPrimaryDimension = getPrimaryDimension(rAllocation);
291 tools::Long nHomogeneousDimension = 0, nExtraSpace = 0;
292 if (m_bHomogeneous)
294 nHomogeneousDimension = (nAllocPrimaryDimension -
295 (nVisibleChildren - 1) * m_nSpacing) / nVisibleChildren;
297 else if (nExpandChildren)
299 Size aRequisition = calculateRequisition();
300 tools::Long nPrimaryDimension = getPrimaryDimension(rAllocation);
301 nExtraSpace = (nPrimaryDimension - getPrimaryDimension(aRequisition)) / nExpandChildren;
304 //Split into those we pack from the start onwards, and those we pack from the end backwards
305 o3tl::enumarray<VclPackType,std::vector<vcl::Window*>> aWindows;
306 for (vcl::Window *pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next))
308 if (!pChild->IsVisible())
309 continue;
311 VclPackType ePacking = pChild->get_pack_type();
312 aWindows[ePacking].push_back(pChild);
315 //See VclBuilder::sortIntoBestTabTraversalOrder for why they are in visual
316 //order under the parent which requires us to reverse them here to
317 //pack from the end back
318 std::reverse(aWindows[VclPackType::End].begin(),aWindows[VclPackType::End].end());
320 for (VclPackType ePackType : o3tl::enumrange<VclPackType>())
322 Point aPos(0, 0);
323 if (ePackType == VclPackType::End)
325 tools::Long nPrimaryCoordinate = getPrimaryCoordinate(aPos);
326 setPrimaryCoordinate(aPos, nPrimaryCoordinate + nAllocPrimaryDimension);
329 for (auto const& window : aWindows[ePackType])
331 vcl::Window *pChild = window;
333 tools::Long nPadding = pChild->get_padding();
335 Size aBoxSize;
336 if (m_bHomogeneous)
337 setPrimaryDimension(aBoxSize, nHomogeneousDimension);
338 else
340 aBoxSize = getLayoutRequisition(*pChild);
341 tools::Long nPrimaryDimension = getPrimaryDimension(aBoxSize);
342 nPrimaryDimension += nPadding * 2;
343 if (getPrimaryDimensionChildExpand(*pChild))
344 nPrimaryDimension += nExtraSpace;
345 setPrimaryDimension(aBoxSize, nPrimaryDimension);
347 setSecondaryDimension(aBoxSize, getSecondaryDimension(rAllocation));
349 Point aChildPos(aPos);
350 Size aChildSize(aBoxSize);
351 tools::Long nPrimaryCoordinate = getPrimaryCoordinate(aPos);
353 bool bFill = pChild->get_fill();
354 if (bFill)
356 setPrimaryDimension(aChildSize, std::max(static_cast<tools::Long>(1),
357 std::min(getPrimaryDimension(rAllocation), getPrimaryDimension(aBoxSize) - nPadding * 2)));
359 setPrimaryCoordinate(aChildPos, nPrimaryCoordinate + nPadding);
361 else
363 setPrimaryDimension(aChildSize,
364 getPrimaryDimension(getLayoutRequisition(*pChild)));
366 setPrimaryCoordinate(aChildPos, nPrimaryCoordinate +
367 (getPrimaryDimension(aBoxSize) - getPrimaryDimension(aChildSize)) / 2);
370 tools::Long nDiff = getPrimaryDimension(aBoxSize) + m_nSpacing;
371 if (ePackType == VclPackType::Start)
372 setPrimaryCoordinate(aPos, nPrimaryCoordinate + nDiff);
373 else
375 setPrimaryCoordinate(aPos, nPrimaryCoordinate - nDiff);
376 setPrimaryCoordinate(aChildPos, getPrimaryCoordinate(aChildPos) -
377 getPrimaryDimension(aBoxSize));
380 setLayoutAllocation(*pChild, aChildPos, aChildSize);
385 bool VclBox::set_property(const OUString &rKey, const OUString &rValue)
387 if (rKey == "spacing")
388 set_spacing(rValue.toInt32());
389 else if (rKey == "homogeneous")
390 set_homogeneous(toBool(rValue));
391 else
392 return VclContainer::set_property(rKey, rValue);
393 return true;
396 void VclBox::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
398 VclContainer::DumpAsPropertyTree(rJsonWriter);
399 rJsonWriter.put("vertical", m_bVerticalContainer);
402 sal_uInt16 VclBox::getDefaultAccessibleRole() const
404 // fdo#74284 call Boxes Panels, keep them as "Filler" under
405 // at least Linux seeing as that's what Gtk3 did for GtkBoxes.
406 // Though now with Gtk4 that uses GTK_ACCESSIBLE_ROLE_GROUP
407 // which maps to ATSPI_ROLE_PANEL
408 #if defined(_WIN32)
409 return css::accessibility::AccessibleRole::PANEL;
410 #else
411 static sal_uInt16 eRole = Application::GetToolkitName() == "gtk4" ?
412 css::accessibility::AccessibleRole::PANEL :
413 css::accessibility::AccessibleRole::FILLER;
414 return eRole;
415 #endif
418 #define DEFAULT_CHILD_MIN_WIDTH 85
419 #define DEFAULT_CHILD_MIN_HEIGHT 27
421 Size VclBox::finalizeMaxes(const Size &rSize, sal_uInt16 nVisibleChildren) const
423 Size aRet;
425 if (nVisibleChildren)
427 tools::Long nPrimaryDimension = getPrimaryDimension(rSize);
428 if (m_bHomogeneous)
429 nPrimaryDimension *= nVisibleChildren;
430 setPrimaryDimension(aRet, nPrimaryDimension + m_nSpacing * (nVisibleChildren-1));
431 setSecondaryDimension(aRet, getSecondaryDimension(rSize));
434 return aRet;
437 Size VclButtonBox::addReqGroups(const VclButtonBox::Requisition &rReq) const
439 Size aRet;
441 tools::Long nMainGroupDimension = getPrimaryDimension(rReq.m_aMainGroupSize);
442 tools::Long nSubGroupDimension = getPrimaryDimension(rReq.m_aSubGroupSize);
444 setPrimaryDimension(aRet, nMainGroupDimension + nSubGroupDimension);
446 setSecondaryDimension(aRet,
447 std::max(getSecondaryDimension(rReq.m_aMainGroupSize),
448 getSecondaryDimension(rReq.m_aSubGroupSize)));
450 return aRet;
453 static tools::Long getMaxNonOutlier(const std::vector<tools::Long> &rG, tools::Long nAvgDimension)
455 tools::Long nMaxDimensionNonOutlier = 0;
456 for (auto const& nPrimaryChildDimension : rG)
458 if (nPrimaryChildDimension < nAvgDimension * 1.5)
460 nMaxDimensionNonOutlier = std::max(nPrimaryChildDimension,
461 nMaxDimensionNonOutlier);
464 return nMaxDimensionNonOutlier;
467 static std::vector<tools::Long> setButtonSizes(const std::vector<tools::Long> &rG,
468 const std::vector<bool> &rNonHomogeneous,
469 tools::Long nAvgDimension, tools::Long nMaxNonOutlier, tools::Long nMinWidth)
471 std::vector<tools::Long> aVec;
472 //set everything < 1.5 times the average to the same width, leave the
473 //outliers un-touched
474 std::vector<bool>::const_iterator aJ = rNonHomogeneous.begin();
475 auto nNonOutlierWidth = std::max(nMaxNonOutlier, nMinWidth);
476 for (auto const& nPrimaryChildDimension : rG)
478 bool bNonHomogeneous = *aJ;
479 if (!bNonHomogeneous && nPrimaryChildDimension < nAvgDimension * 1.5)
481 aVec.push_back(nNonOutlierWidth);
483 else
485 aVec.push_back(std::max(nPrimaryChildDimension, nMinWidth));
487 ++aJ;
489 return aVec;
492 VclButtonBox::Requisition VclButtonBox::calculatePrimarySecondaryRequisitions() const
494 Requisition aReq;
496 Size aMainGroupSize(DEFAULT_CHILD_MIN_WIDTH, DEFAULT_CHILD_MIN_HEIGHT); //to-do, pull from theme
497 Size aSubGroupSize(DEFAULT_CHILD_MIN_WIDTH, DEFAULT_CHILD_MIN_HEIGHT); //to-do, pull from theme
499 tools::Long nMinMainGroupPrimary = getPrimaryDimension(aMainGroupSize);
500 tools::Long nMinSubGroupPrimary = getPrimaryDimension(aSubGroupSize);
501 tools::Long nMainGroupSecondary = getSecondaryDimension(aMainGroupSize);
502 tools::Long nSubGroupSecondary = getSecondaryDimension(aSubGroupSize);
504 bool bIgnoreSecondaryPacking = (m_eLayoutStyle == VclButtonBoxStyle::Spread || m_eLayoutStyle == VclButtonBoxStyle::Center);
506 std::vector<tools::Long> aMainGroupSizes;
507 std::vector<bool> aMainGroupNonHomogeneous;
508 std::vector<tools::Long> aSubGroupSizes;
509 std::vector<bool> aSubGroupNonHomogeneous;
511 for (const vcl::Window *pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next))
513 if (!pChild->IsVisible())
514 continue;
515 Size aChildSize = getLayoutRequisition(*pChild);
516 if (bIgnoreSecondaryPacking || !pChild->get_secondary())
518 //set the max secondary dimension
519 nMainGroupSecondary = std::max(nMainGroupSecondary, getSecondaryDimension(aChildSize));
520 //collect the primary dimensions
521 aMainGroupSizes.push_back(getPrimaryDimension(aChildSize));
522 aMainGroupNonHomogeneous.push_back(pChild->get_non_homogeneous());
524 else
526 nSubGroupSecondary = std::max(nSubGroupSecondary, getSecondaryDimension(aChildSize));
527 aSubGroupSizes.push_back(getPrimaryDimension(aChildSize));
528 aSubGroupNonHomogeneous.push_back(pChild->get_non_homogeneous());
532 if (m_bHomogeneous)
534 tools::Long nMaxMainDimension = aMainGroupSizes.empty() ? 0 :
535 *std::max_element(aMainGroupSizes.begin(), aMainGroupSizes.end());
536 nMaxMainDimension = std::max(nMaxMainDimension, nMinMainGroupPrimary);
537 tools::Long nMaxSubDimension = aSubGroupSizes.empty() ? 0 :
538 *std::max_element(aSubGroupSizes.begin(), aSubGroupSizes.end());
539 nMaxSubDimension = std::max(nMaxSubDimension, nMinSubGroupPrimary);
540 tools::Long nMaxDimension = std::max(nMaxMainDimension, nMaxSubDimension);
541 aReq.m_aMainGroupDimensions.resize(aMainGroupSizes.size(), nMaxDimension);
542 aReq.m_aSubGroupDimensions.resize(aSubGroupSizes.size(), nMaxDimension);
544 else
546 //Ideally set everything to the same size, but find outlier widgets
547 //that are way wider than the average and leave them
548 //at their natural size and set the remainder to share the
549 //max size of the remaining members of the buttonbox
550 tools::Long nAccDimension = std::accumulate(aMainGroupSizes.begin(),
551 aMainGroupSizes.end(), 0);
552 nAccDimension = std::accumulate(aSubGroupSizes.begin(),
553 aSubGroupSizes.end(), nAccDimension);
555 size_t nTotalSize = aMainGroupSizes.size() + aSubGroupSizes.size();
557 tools::Long nAvgDimension = nTotalSize ? nAccDimension / nTotalSize : 0;
559 tools::Long nMaxMainNonOutlier = getMaxNonOutlier(aMainGroupSizes,
560 nAvgDimension);
561 tools::Long nMaxSubNonOutlier = getMaxNonOutlier(aSubGroupSizes,
562 nAvgDimension);
563 tools::Long nMaxNonOutlier = std::max(nMaxMainNonOutlier, nMaxSubNonOutlier);
565 aReq.m_aMainGroupDimensions = setButtonSizes(aMainGroupSizes,
566 aMainGroupNonHomogeneous,
567 nAvgDimension, nMaxNonOutlier, nMinMainGroupPrimary);
568 aReq.m_aSubGroupDimensions = setButtonSizes(aSubGroupSizes,
569 aSubGroupNonHomogeneous,
570 nAvgDimension, nMaxNonOutlier, nMinSubGroupPrimary);
573 if (!aReq.m_aMainGroupDimensions.empty())
575 setSecondaryDimension(aReq.m_aMainGroupSize, nMainGroupSecondary);
576 setPrimaryDimension(aReq.m_aMainGroupSize,
577 std::accumulate(aReq.m_aMainGroupDimensions.begin(),
578 aReq.m_aMainGroupDimensions.end(), 0));
580 if (!aReq.m_aSubGroupDimensions.empty())
582 setSecondaryDimension(aReq.m_aSubGroupSize, nSubGroupSecondary);
583 setPrimaryDimension(aReq.m_aSubGroupSize,
584 std::accumulate(aReq.m_aSubGroupDimensions.begin(),
585 aReq.m_aSubGroupDimensions.end(), 0));
588 return aReq;
591 Size VclButtonBox::addSpacing(const Size &rSize, sal_uInt16 nVisibleChildren) const
593 Size aRet;
595 if (nVisibleChildren)
597 tools::Long nPrimaryDimension = getPrimaryDimension(rSize);
598 setPrimaryDimension(aRet,
599 nPrimaryDimension + m_nSpacing * (nVisibleChildren-1));
600 setSecondaryDimension(aRet, getSecondaryDimension(rSize));
603 return aRet;
606 Size VclButtonBox::calculateRequisition() const
608 Requisition aReq(calculatePrimarySecondaryRequisitions());
609 sal_uInt16 nVisibleChildren = aReq.m_aMainGroupDimensions.size() +
610 aReq.m_aSubGroupDimensions.size();
611 return addSpacing(addReqGroups(aReq), nVisibleChildren);
614 bool VclButtonBox::set_property(const OUString &rKey, const OUString &rValue)
616 if (rKey == "layout-style")
618 VclButtonBoxStyle eStyle = VclButtonBoxStyle::Default;
619 if (rValue == "spread")
620 eStyle = VclButtonBoxStyle::Spread;
621 else if (rValue == "edge")
622 eStyle = VclButtonBoxStyle::Edge;
623 else if (rValue == "start")
624 eStyle = VclButtonBoxStyle::Start;
625 else if (rValue == "end")
626 eStyle = VclButtonBoxStyle::End;
627 else if (rValue == "center")
628 eStyle = VclButtonBoxStyle::Center;
629 else
631 SAL_WARN("vcl.layout", "unknown layout style " << rValue);
633 m_eLayoutStyle = eStyle;
635 else
636 return VclBox::set_property(rKey, rValue);
637 return true;
640 void VclButtonBox::setAllocation(const Size &rAllocation)
642 Requisition aReq(calculatePrimarySecondaryRequisitions());
644 if (aReq.m_aMainGroupDimensions.empty() && aReq.m_aSubGroupDimensions.empty())
645 return;
647 tools::Long nAllocPrimaryDimension = getPrimaryDimension(rAllocation);
649 Point aMainGroupPos, aOtherGroupPos;
650 int nSpacing = m_nSpacing;
652 //To-Do, other layout styles
653 switch (m_eLayoutStyle)
655 case VclButtonBoxStyle::Start:
656 if (!aReq.m_aSubGroupDimensions.empty())
658 tools::Long nOtherPrimaryDimension = getPrimaryDimension(
659 addSpacing(aReq.m_aSubGroupSize, aReq.m_aSubGroupDimensions.size()));
660 setPrimaryCoordinate(aOtherGroupPos,
661 nAllocPrimaryDimension - nOtherPrimaryDimension);
663 break;
664 case VclButtonBoxStyle::Spread:
665 if (!aReq.m_aMainGroupDimensions.empty())
667 tools::Long nMainPrimaryDimension = getPrimaryDimension(
668 addSpacing(aReq.m_aMainGroupSize, aReq.m_aMainGroupDimensions.size()));
669 tools::Long nExtraSpace = nAllocPrimaryDimension - nMainPrimaryDimension;
670 nExtraSpace += (aReq.m_aMainGroupDimensions.size()-1) * nSpacing;
671 nSpacing = nExtraSpace/(aReq.m_aMainGroupDimensions.size()+1);
672 setPrimaryCoordinate(aMainGroupPos, nSpacing);
674 break;
675 case VclButtonBoxStyle::Center:
676 if (!aReq.m_aMainGroupDimensions.empty())
678 tools::Long nMainPrimaryDimension = getPrimaryDimension(
679 addSpacing(aReq.m_aMainGroupSize, aReq.m_aMainGroupDimensions.size()));
680 tools::Long nExtraSpace = nAllocPrimaryDimension - nMainPrimaryDimension;
681 setPrimaryCoordinate(aMainGroupPos, nExtraSpace/2);
683 break;
684 default:
685 SAL_WARN("vcl.layout", "todo unimplemented layout style");
686 [[fallthrough]];
687 case VclButtonBoxStyle::Default:
688 case VclButtonBoxStyle::End:
689 if (!aReq.m_aMainGroupDimensions.empty())
691 tools::Long nMainPrimaryDimension = getPrimaryDimension(
692 addSpacing(aReq.m_aMainGroupSize, aReq.m_aMainGroupDimensions.size()));
693 setPrimaryCoordinate(aMainGroupPos,
694 nAllocPrimaryDimension - nMainPrimaryDimension);
696 break;
699 Size aChildSize;
700 setSecondaryDimension(aChildSize, getSecondaryDimension(rAllocation));
702 std::vector<tools::Long>::const_iterator aPrimaryI = aReq.m_aMainGroupDimensions.begin();
703 std::vector<tools::Long>::const_iterator aSecondaryI = aReq.m_aSubGroupDimensions.begin();
704 bool bIgnoreSecondaryPacking = (m_eLayoutStyle == VclButtonBoxStyle::Spread || m_eLayoutStyle == VclButtonBoxStyle::Center);
705 for (vcl::Window *pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next))
707 if (!pChild->IsVisible())
708 continue;
710 if (bIgnoreSecondaryPacking || !pChild->get_secondary())
712 tools::Long nMainGroupPrimaryDimension = *aPrimaryI++;
713 setPrimaryDimension(aChildSize, nMainGroupPrimaryDimension);
714 setLayoutAllocation(*pChild, aMainGroupPos, aChildSize);
715 tools::Long nPrimaryCoordinate = getPrimaryCoordinate(aMainGroupPos);
716 setPrimaryCoordinate(aMainGroupPos, nPrimaryCoordinate + nMainGroupPrimaryDimension + nSpacing);
718 else
720 tools::Long nSubGroupPrimaryDimension = *aSecondaryI++;
721 setPrimaryDimension(aChildSize, nSubGroupPrimaryDimension);
722 setLayoutAllocation(*pChild, aOtherGroupPos, aChildSize);
723 tools::Long nPrimaryCoordinate = getPrimaryCoordinate(aOtherGroupPos);
724 setPrimaryCoordinate(aOtherGroupPos, nPrimaryCoordinate + nSubGroupPrimaryDimension + nSpacing);
729 void VclButtonBox::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
731 VclBox::DumpAsPropertyTree(rJsonWriter);
732 rJsonWriter.put("type", "buttonbox");
734 switch(m_eLayoutStyle)
736 case VclButtonBoxStyle::Default:
737 rJsonWriter.put("layoutstyle", "default");
738 break;
740 case VclButtonBoxStyle::Spread:
741 rJsonWriter.put("layoutstyle", "spread");
742 break;
744 case VclButtonBoxStyle::Edge:
745 rJsonWriter.put("layoutstyle", "edge");
746 break;
748 case VclButtonBoxStyle::Center:
749 rJsonWriter.put("layoutstyle", "center");
750 break;
752 case VclButtonBoxStyle::Start:
753 rJsonWriter.put("layoutstyle", "start");
754 break;
756 case VclButtonBoxStyle::End:
757 rJsonWriter.put("layoutstyle", "end");
758 break;
762 namespace {
764 struct ButtonOrder
766 std::u16string_view m_aType;
767 int m_nPriority;
772 static int getButtonPriority(std::u16string_view rType)
774 static const size_t N_TYPES = 6;
775 static const ButtonOrder aDiscardCancelSave[N_TYPES] =
777 { u"discard", 0 },
778 { u"cancel", 1 },
779 { u"no", 2 },
780 { u"save", 3 },
781 { u"yes", 3 },
782 { u"ok", 3 }
785 static const ButtonOrder aSaveDiscardCancel[N_TYPES] =
787 { u"save", 0 },
788 { u"yes", 0 },
789 { u"ok", 0 },
790 { u"discard", 1 },
791 { u"no", 1 },
792 { u"cancel", 2 }
795 const ButtonOrder* pOrder = &aDiscardCancelSave[0];
797 const OUString &rEnv = Application::GetDesktopEnvironment();
799 if (rEnv.equalsIgnoreAsciiCase("windows") ||
800 rEnv.equalsIgnoreAsciiCase("lxqt") ||
801 rEnv.startsWithIgnoreAsciiCase("plasma"))
803 pOrder = &aSaveDiscardCancel[0];
806 for (size_t i = 0; i < N_TYPES; ++i, ++pOrder)
808 if (rType == pOrder->m_aType)
809 return pOrder->m_nPriority;
812 return -1;
815 namespace {
817 class sortButtons
819 bool m_bVerticalContainer;
820 public:
821 explicit sortButtons(bool bVerticalContainer)
822 : m_bVerticalContainer(bVerticalContainer)
825 bool operator()(const vcl::Window *pA, const vcl::Window *pB) const;
830 bool sortButtons::operator()(const vcl::Window *pA, const vcl::Window *pB) const
832 //sort into two groups of pack start and pack end
833 VclPackType ePackA = pA->get_pack_type();
834 VclPackType ePackB = pB->get_pack_type();
835 if (ePackA < ePackB)
836 return true;
837 if (ePackA > ePackB)
838 return false;
839 bool bPackA = pA->get_secondary();
840 bool bPackB = pB->get_secondary();
841 if (!m_bVerticalContainer)
843 //for horizontal boxes group secondaries before primaries
844 if (bPackA > bPackB)
845 return true;
846 if (bPackA < bPackB)
847 return false;
849 else
851 //for vertical boxes group secondaries after primaries
852 if (bPackA < bPackB)
853 return true;
854 if (bPackA > bPackB)
855 return false;
858 //now order within groups according to platform rules
859 return getButtonPriority(pA->get_id()) < getButtonPriority(pB->get_id());
862 void sort_native_button_order(const VclBox& rContainer)
864 std::vector<vcl::Window*> aChilds;
865 for (vcl::Window* pChild = rContainer.GetWindow(GetWindowType::FirstChild); pChild;
866 pChild = pChild->GetWindow(GetWindowType::Next))
868 aChilds.push_back(pChild);
871 //sort child order within parent so that we match the platform
872 //button order
873 std::stable_sort(aChilds.begin(), aChilds.end(), sortButtons(rContainer.get_orientation()));
874 BuilderUtils::reorderWithinParent(aChilds, true);
877 namespace {
879 struct GridEntry
881 VclPtr<vcl::Window> pChild;
882 sal_Int32 nSpanWidth;
883 sal_Int32 nSpanHeight;
884 int x;
885 int y;
886 GridEntry()
887 : pChild(nullptr)
888 , nSpanWidth(0)
889 , nSpanHeight(0)
890 , x(-1)
891 , y(-1)
898 typedef boost::multi_array<GridEntry, 2> array_type;
900 #if defined _MSC_VER
901 #pragma warning(push)
902 #pragma warning(disable : 4459)
903 #endif
904 static array_type assembleGrid(const VclGrid &rGrid)
906 #if defined _MSC_VER
907 #pragma warning(pop)
908 #endif
909 array_type A;
911 for (vcl::Window* pChild = rGrid.GetWindow(GetWindowType::FirstChild); pChild;
912 pChild = pChild->GetWindow(GetWindowType::Next))
914 sal_Int32 nLeftAttach = std::max<sal_Int32>(pChild->get_grid_left_attach(), 0);
915 sal_Int32 nWidth = pChild->get_grid_width();
916 sal_Int32 nMaxXPos = nLeftAttach+nWidth-1;
918 sal_Int32 nTopAttach = std::max<sal_Int32>(pChild->get_grid_top_attach(), 0);
919 sal_Int32 nHeight = pChild->get_grid_height();
920 sal_Int32 nMaxYPos = nTopAttach+nHeight-1;
922 sal_Int32 nCurrentMaxXPos = A.shape()[0]-1;
923 sal_Int32 nCurrentMaxYPos = A.shape()[1]-1;
924 if (nMaxXPos > nCurrentMaxXPos || nMaxYPos > nCurrentMaxYPos)
926 nCurrentMaxXPos = std::max(nMaxXPos, nCurrentMaxXPos);
927 nCurrentMaxYPos = std::max(nMaxYPos, nCurrentMaxYPos);
928 A.resize(boost::extents[nCurrentMaxXPos+1][nCurrentMaxYPos+1]);
931 #if defined _MSC_VER
932 #pragma warning(push)
933 #pragma warning(disable : 4459)
934 #endif
935 GridEntry &rEntry = A[nLeftAttach][nTopAttach];
936 #if defined _MSC_VER
937 #pragma warning(pop)
938 #endif
939 rEntry.pChild = pChild;
940 rEntry.nSpanWidth = nWidth;
941 rEntry.nSpanHeight = nHeight;
942 rEntry.x = nLeftAttach;
943 rEntry.y = nTopAttach;
945 for (sal_Int32 nSpanX = 0; nSpanX < nWidth; ++nSpanX)
947 for (sal_Int32 nSpanY = 0; nSpanY < nHeight; ++nSpanY)
949 GridEntry &rSpan = A[nLeftAttach+nSpanX][nTopAttach+nSpanY];
950 rSpan.x = nLeftAttach;
951 rSpan.y = nTopAttach;
956 //see if we have any empty rows/cols
957 sal_Int32 nMaxX = A.shape()[0];
958 sal_Int32 nMaxY = A.shape()[1];
960 std::vector<bool> aNonEmptyCols(nMaxX);
961 std::vector<bool> aNonEmptyRows(nMaxY);
963 for (sal_Int32 x = 0; x < nMaxX; ++x)
965 for (sal_Int32 y = 0; y < nMaxY; ++y)
967 #if defined __GNUC__ && !defined __clang__ && __GNUC__ >= 13 && __GNUC__ <= 15
968 #pragma GCC diagnostic push
969 #pragma GCC diagnostic ignored "-Wdangling-reference"
970 #endif
971 const GridEntry &rEntry = A[x][y];
972 #if defined __GNUC__ && !defined __clang__ && __GNUC__ >= 13 && __GNUC__ <= 15
973 #pragma GCC diagnostic pop
974 #endif
975 const vcl::Window *pChild = rEntry.pChild;
976 if (pChild && pChild->IsVisible())
978 aNonEmptyCols[x] = true;
979 if (rGrid.get_column_homogeneous())
981 for (sal_Int32 nSpanX = 1; nSpanX < rEntry.nSpanWidth; ++nSpanX)
982 aNonEmptyCols[x+nSpanX] = true;
984 aNonEmptyRows[y] = true;
985 if (rGrid.get_row_homogeneous())
987 for (sal_Int32 nSpanY = 1; nSpanY < rEntry.nSpanHeight; ++nSpanY)
988 aNonEmptyRows[y+nSpanY] = true;
994 if (!rGrid.get_column_homogeneous())
996 //reduce the spans of elements that span empty columns
997 for (sal_Int32 x = 0; x < nMaxX; ++x)
999 std::set<GridEntry*> candidates;
1000 for (sal_Int32 y = 0; y < nMaxY; ++y)
1002 if (aNonEmptyCols[x])
1003 continue;
1004 GridEntry &rSpan = A[x][y];
1005 //cell x/y is spanned by the widget at cell rSpan.x/rSpan.y,
1006 //just points back to itself if there's no cell spanning
1007 if ((rSpan.x == -1) || (rSpan.y == -1))
1009 //there is no entry for this cell, i.e. this is a cell
1010 //with no widget in it, or spanned by any other widget
1011 continue;
1013 GridEntry &rEntry = A[rSpan.x][rSpan.y];
1014 candidates.insert(&rEntry);
1016 for (auto const& candidate : candidates)
1018 GridEntry *pEntry = candidate;
1019 --pEntry->nSpanWidth;
1024 if (!rGrid.get_row_homogeneous())
1026 //reduce the spans of elements that span empty rows
1027 for (sal_Int32 y = 0; y < nMaxY; ++y)
1029 std::set<GridEntry*> candidates;
1030 for (sal_Int32 x = 0; x < nMaxX; ++x)
1032 if (aNonEmptyRows[y])
1033 continue;
1034 GridEntry &rSpan = A[x][y];
1035 //cell x/y is spanned by the widget at cell rSpan.x/rSpan.y,
1036 //just points back to itself if there's no cell spanning
1037 if ((rSpan.x == -1) || (rSpan.y == -1))
1039 //there is no entry for this cell, i.e. this is a cell
1040 //with no widget in it, or spanned by any other widget
1041 continue;
1043 GridEntry &rEntry = A[rSpan.x][rSpan.y];
1044 candidates.insert(&rEntry);
1046 for (auto const& candidate : candidates)
1048 GridEntry *pEntry = candidate;
1049 --pEntry->nSpanHeight;
1054 sal_Int32 nNonEmptyCols = std::count(aNonEmptyCols.begin(), aNonEmptyCols.end(), true);
1055 sal_Int32 nNonEmptyRows = std::count(aNonEmptyRows.begin(), aNonEmptyRows.end(), true);
1057 //make new grid without empty rows and columns
1058 #if defined _MSC_VER
1059 #pragma warning(push)
1060 #pragma warning(disable : 4459)
1061 #endif
1062 array_type B(boost::extents[nNonEmptyCols][nNonEmptyRows]);
1063 #if defined _MSC_VER
1064 #pragma warning(pop)
1065 #endif
1066 for (sal_Int32 x = 0, x2 = 0; x < nMaxX; ++x)
1068 if (!aNonEmptyCols[x])
1069 continue;
1070 for (sal_Int32 y = 0, y2 = 0; y < nMaxY; ++y)
1072 if (!aNonEmptyRows[y])
1073 continue;
1074 GridEntry &rEntry = A[x][y];
1075 B[x2][y2++] = rEntry;
1077 ++x2;
1080 return B;
1083 static bool isNullGrid(const array_type &A)
1085 sal_Int32 nMaxX = A.shape()[0];
1086 sal_Int32 nMaxY = A.shape()[1];
1088 return !nMaxX || !nMaxY;
1091 static void calcMaxs(const array_type &A, std::vector<VclGrid::Value> &rWidths, std::vector<VclGrid::Value> &rHeights)
1093 sal_Int32 nMaxX = A.shape()[0];
1094 sal_Int32 nMaxY = A.shape()[1];
1096 rWidths.resize(nMaxX);
1097 rHeights.resize(nMaxY);
1099 //first use the non spanning entries to set default width/heights
1100 for (sal_Int32 x = 0; x < nMaxX; ++x)
1102 for (sal_Int32 y = 0; y < nMaxY; ++y)
1104 #if defined __GNUC__ && !defined __clang__ && __GNUC__ >= 13 && __GNUC__ <= 15
1105 #pragma GCC diagnostic push
1106 #pragma GCC diagnostic ignored "-Wdangling-reference"
1107 #elif defined _MSC_VER
1108 #pragma warning(push)
1109 #pragma warning(disable : 4459)
1110 #endif
1111 const GridEntry &rEntry = A[x][y];
1112 #if defined __GNUC__ && !defined __clang__ && __GNUC__ >= 13 && __GNUC__ <= 15
1113 #pragma GCC diagnostic pop
1114 #elif defined _MSC_VER
1115 #pragma warning(pop)
1116 #endif
1117 const vcl::Window *pChild = rEntry.pChild;
1118 if (!pChild || !pChild->IsVisible())
1119 continue;
1121 sal_Int32 nWidth = rEntry.nSpanWidth;
1122 sal_Int32 nHeight = rEntry.nSpanHeight;
1124 for (sal_Int32 nSpanX = 0; nSpanX < nWidth; ++nSpanX)
1125 rWidths[x+nSpanX].m_bExpand |= pChild->get_hexpand();
1127 for (sal_Int32 nSpanY = 0; nSpanY < nHeight; ++nSpanY)
1128 rHeights[y+nSpanY].m_bExpand |= pChild->get_vexpand();
1130 if (nWidth == 1 || nHeight == 1)
1132 Size aChildSize = VclContainer::getLayoutRequisition(*pChild);
1133 if (nWidth == 1)
1134 rWidths[x].m_nValue = std::max(rWidths[x].m_nValue, aChildSize.Width());
1135 if (nHeight == 1)
1136 rHeights[y].m_nValue = std::max(rHeights[y].m_nValue, aChildSize.Height());
1141 //now use the spanning entries and split any extra sizes across expanding rows/cols
1142 //where possible
1143 for (sal_Int32 x = 0; x < nMaxX; ++x)
1145 for (sal_Int32 y = 0; y < nMaxY; ++y)
1147 #if defined __GNUC__ && !defined __clang__ && __GNUC__ >= 13 && __GNUC__ <= 15
1148 #pragma GCC diagnostic push
1149 #pragma GCC diagnostic ignored "-Wdangling-reference"
1150 #endif
1151 const GridEntry &rEntry = A[x][y];
1152 #if defined __GNUC__ && !defined __clang__ && __GNUC__ >= 13 && __GNUC__ <= 15
1153 #pragma GCC diagnostic pop
1154 #endif
1155 const vcl::Window *pChild = rEntry.pChild;
1156 if (!pChild || !pChild->IsVisible())
1157 continue;
1159 sal_Int32 nWidth = rEntry.nSpanWidth;
1160 sal_Int32 nHeight = rEntry.nSpanHeight;
1162 if (nWidth == 1 && nHeight == 1)
1163 continue;
1165 Size aChildSize = VclContainer::getLayoutRequisition(*pChild);
1167 if (nWidth > 1)
1169 sal_Int32 nExistingWidth = 0;
1170 for (sal_Int32 nSpanX = 0; nSpanX < nWidth; ++nSpanX)
1171 nExistingWidth += rWidths[x+nSpanX].m_nValue;
1173 sal_Int32 nExtraWidth = aChildSize.Width() - nExistingWidth;
1175 if (nExtraWidth > 0)
1177 bool bForceExpandAll = false;
1178 sal_Int32 nExpandables = 0;
1179 for (sal_Int32 nSpanX = 0; nSpanX < nWidth; ++nSpanX)
1180 if (rWidths[x+nSpanX].m_bExpand)
1181 ++nExpandables;
1182 if (nExpandables == 0)
1184 nExpandables = nWidth;
1185 bForceExpandAll = true;
1188 for (sal_Int32 nSpanX = 0; nSpanX < nWidth; ++nSpanX)
1190 if (rWidths[x+nSpanX].m_bExpand || bForceExpandAll)
1191 rWidths[x+nSpanX].m_nValue += nExtraWidth/nExpandables;
1196 if (nHeight > 1)
1198 sal_Int32 nExistingHeight = 0;
1199 for (sal_Int32 nSpanY = 0; nSpanY < nHeight; ++nSpanY)
1200 nExistingHeight += rHeights[y+nSpanY].m_nValue;
1202 sal_Int32 nExtraHeight = aChildSize.Height() - nExistingHeight;
1204 if (nExtraHeight > 0)
1206 bool bForceExpandAll = false;
1207 sal_Int32 nExpandables = 0;
1208 for (sal_Int32 nSpanY = 0; nSpanY < nHeight; ++nSpanY)
1209 if (rHeights[y+nSpanY].m_bExpand)
1210 ++nExpandables;
1211 if (nExpandables == 0)
1213 nExpandables = nHeight;
1214 bForceExpandAll = true;
1217 for (sal_Int32 nSpanY = 0; nSpanY < nHeight; ++nSpanY)
1219 if (rHeights[y+nSpanY].m_bExpand || bForceExpandAll)
1220 rHeights[y+nSpanY].m_nValue += nExtraHeight/nExpandables;
1228 static bool compareValues(const VclGrid::Value &i, const VclGrid::Value &j)
1230 return i.m_nValue < j.m_nValue;
1233 static VclGrid::Value accumulateValues(const VclGrid::Value &i, const VclGrid::Value &j)
1235 VclGrid::Value aRet;
1236 aRet.m_nValue = i.m_nValue + j.m_nValue;
1237 aRet.m_bExpand = i.m_bExpand || j.m_bExpand;
1238 return aRet;
1241 Size VclGrid::calculateRequisition() const
1243 return calculateRequisitionForSpacings(get_row_spacing(), get_column_spacing());
1246 Size VclGrid::calculateRequisitionForSpacings(sal_Int32 nRowSpacing, sal_Int32 nColSpacing) const
1248 array_type A = assembleGrid(*this);
1250 if (isNullGrid(A))
1251 return Size();
1253 std::vector<Value> aWidths;
1254 std::vector<Value> aHeights;
1255 calcMaxs(A, aWidths, aHeights);
1257 tools::Long nTotalWidth = 0;
1258 if (get_column_homogeneous())
1260 nTotalWidth = std::max_element(aWidths.begin(), aWidths.end(), compareValues)->m_nValue;
1261 nTotalWidth *= aWidths.size();
1263 else
1265 nTotalWidth = std::accumulate(aWidths.begin(), aWidths.end(), Value(), accumulateValues).m_nValue;
1268 nTotalWidth += nColSpacing * (aWidths.size()-1);
1270 tools::Long nTotalHeight = 0;
1271 if (get_row_homogeneous())
1273 nTotalHeight = std::max_element(aHeights.begin(), aHeights.end(), compareValues)->m_nValue;
1274 nTotalHeight *= aHeights.size();
1276 else
1278 nTotalHeight = std::accumulate(aHeights.begin(), aHeights.end(), Value(), accumulateValues).m_nValue;
1281 nTotalHeight += nRowSpacing * (aHeights.size()-1);
1283 return Size(nTotalWidth, nTotalHeight);
1286 void VclGrid::setAllocation(const Size& rAllocation)
1288 array_type A = assembleGrid(*this);
1290 if (isNullGrid(A))
1291 return;
1293 sal_Int32 nMaxX = A.shape()[0];
1294 sal_Int32 nMaxY = A.shape()[1];
1296 Size aRequisition;
1297 std::vector<Value> aWidths(nMaxX);
1298 std::vector<Value> aHeights(nMaxY);
1299 if (!get_column_homogeneous() || !get_row_homogeneous())
1301 aRequisition = calculateRequisition();
1302 calcMaxs(A, aWidths, aHeights);
1305 sal_Int32 nColSpacing(get_column_spacing());
1306 sal_Int32 nRowSpacing(get_row_spacing());
1308 tools::Long nAvailableWidth = rAllocation.Width();
1309 if (nMaxX)
1310 nAvailableWidth -= nColSpacing * (nMaxX - 1);
1311 if (get_column_homogeneous())
1313 for (sal_Int32 x = 0; x < nMaxX; ++x)
1314 aWidths[x].m_nValue = nAvailableWidth/nMaxX;
1316 else if (rAllocation.Width() != aRequisition.Width())
1318 sal_Int32 nExpandables = 0;
1319 for (sal_Int32 x = 0; x < nMaxX; ++x)
1320 if (aWidths[x].m_bExpand)
1321 ++nExpandables;
1322 tools::Long nExtraWidthForExpanders = nExpandables ? (rAllocation.Width() - aRequisition.Width()) / nExpandables : 0;
1324 //We don't fit and there is no volunteer to be shrunk
1325 if (!nExpandables && rAllocation.Width() < aRequisition.Width())
1327 //first reduce spacing
1328 while (nColSpacing)
1330 nColSpacing /= 2;
1331 aRequisition = calculateRequisitionForSpacings(nRowSpacing, nColSpacing);
1332 if (aRequisition.Width() <= rAllocation.Width())
1333 break;
1336 //share out the remaining pain to everyone
1337 tools::Long nExtraWidth = (rAllocation.Width() - aRequisition.Width()) / nMaxX;
1339 for (sal_Int32 x = 0; x < nMaxX; ++x)
1340 aWidths[x].m_nValue += nExtraWidth;
1343 if (nExtraWidthForExpanders)
1345 for (sal_Int32 x = 0; x < nMaxX; ++x)
1346 if (aWidths[x].m_bExpand)
1347 aWidths[x].m_nValue += nExtraWidthForExpanders;
1351 tools::Long nAvailableHeight = rAllocation.Height();
1352 if (nMaxY)
1353 nAvailableHeight -= nRowSpacing * (nMaxY - 1);
1354 if (get_row_homogeneous())
1356 for (sal_Int32 y = 0; y < nMaxY; ++y)
1357 aHeights[y].m_nValue = nAvailableHeight/nMaxY;
1359 else if (rAllocation.Height() != aRequisition.Height())
1361 sal_Int32 nExpandables = 0;
1362 for (sal_Int32 y = 0; y < nMaxY; ++y)
1363 if (aHeights[y].m_bExpand)
1364 ++nExpandables;
1365 tools::Long nExtraHeightForExpanders = nExpandables ? (rAllocation.Height() - aRequisition.Height()) / nExpandables : 0;
1367 //We don't fit and there is no volunteer to be shrunk
1368 if (!nExpandables && rAllocation.Height() < aRequisition.Height())
1370 //first reduce spacing
1371 while (nRowSpacing)
1373 nRowSpacing /= 2;
1374 aRequisition = calculateRequisitionForSpacings(nRowSpacing, nColSpacing);
1375 if (aRequisition.Height() <= rAllocation.Height())
1376 break;
1379 //share out the remaining pain to everyone
1380 tools::Long nExtraHeight = (rAllocation.Height() - aRequisition.Height()) / nMaxY;
1382 for (sal_Int32 y = 0; y < nMaxY; ++y)
1383 aHeights[y].m_nValue += nExtraHeight;
1386 if (nExtraHeightForExpanders)
1388 for (sal_Int32 y = 0; y < nMaxY; ++y)
1389 if (aHeights[y].m_bExpand)
1390 aHeights[y].m_nValue += nExtraHeightForExpanders;
1394 Point aAllocPos(0, 0);
1395 for (sal_Int32 x = 0; x < nMaxX; ++x)
1397 for (sal_Int32 y = 0; y < nMaxY; ++y)
1399 #if defined __GNUC__ && !defined __clang__ && __GNUC__ == 13
1400 #pragma GCC diagnostic push
1401 #pragma GCC diagnostic ignored "-Wdangling-reference"
1402 #endif
1403 GridEntry &rEntry = A[x][y];
1404 #if defined __GNUC__ && !defined __clang__ && __GNUC__ == 13
1405 #pragma GCC diagnostic pop
1406 #endif
1407 vcl::Window *pChild = rEntry.pChild;
1408 if (pChild)
1410 Size aChildAlloc(0, 0);
1412 sal_Int32 nWidth = rEntry.nSpanWidth;
1413 for (sal_Int32 nSpanX = 0; nSpanX < nWidth; ++nSpanX)
1414 aChildAlloc.AdjustWidth(aWidths[x+nSpanX].m_nValue );
1415 aChildAlloc.AdjustWidth(nColSpacing*(nWidth-1) );
1417 sal_Int32 nHeight = rEntry.nSpanHeight;
1418 for (sal_Int32 nSpanY = 0; nSpanY < nHeight; ++nSpanY)
1419 aChildAlloc.AdjustHeight(aHeights[y+nSpanY].m_nValue );
1420 aChildAlloc.AdjustHeight(nRowSpacing*(nHeight-1) );
1422 setLayoutAllocation(*pChild, aAllocPos, aChildAlloc);
1424 aAllocPos.AdjustY(aHeights[y].m_nValue + nRowSpacing );
1426 aAllocPos.AdjustX(aWidths[x].m_nValue + nColSpacing );
1427 aAllocPos.setY( 0 );
1431 void VclGrid::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
1433 VclContainer::DumpAsPropertyTree(rJsonWriter);
1434 rJsonWriter.put("type", "grid");
1437 bool VclGrid::set_property(const OUString &rKey, const OUString &rValue)
1439 if (rKey == "row-spacing")
1440 set_row_spacing(rValue.toInt32());
1441 else if (rKey == "column-spacing")
1442 set_column_spacing(rValue.toInt32());
1443 else if (rKey == "row-homogeneous")
1444 m_bRowHomogeneous = toBool(rValue);
1445 else if (rKey == "column-homogeneous")
1446 m_bColumnHomogeneous = toBool(rValue);
1447 else if (rKey == "n-rows")
1448 /*nothing to do*/;
1449 else
1450 return VclContainer::set_property(rKey, rValue);
1451 return true;
1454 const vcl::Window *VclBin::get_child() const
1456 const WindowImpl* pWindowImpl = ImplGetWindowImpl();
1458 return pWindowImpl->mpFirstChild;
1461 vcl::Window *VclBin::get_child()
1463 return const_cast<vcl::Window*>(const_cast<const VclBin*>(this)->get_child());
1466 Size VclBin::calculateRequisition() const
1468 const vcl::Window *pChild = get_child();
1469 if (pChild && pChild->IsVisible())
1470 return getLayoutRequisition(*pChild);
1471 return Size(0, 0);
1474 void VclBin::setAllocation(const Size &rAllocation)
1476 vcl::Window *pChild = get_child();
1477 if (pChild && pChild->IsVisible())
1478 setLayoutAllocation(*pChild, Point(0, 0), rAllocation);
1481 VclFrame::~VclFrame()
1483 disposeOnce();
1486 void VclFrame::dispose()
1488 m_pLabel.clear();
1489 VclBin::dispose();
1492 //To-Do, hook a DecorationView into VclFrame ?
1494 Size VclFrame::calculateRequisition() const
1496 Size aRet(0, 0);
1498 const vcl::Window *pChild = get_child();
1499 const vcl::Window *pLabel = get_label_widget();
1501 if (pChild && pChild->IsVisible())
1502 aRet = getLayoutRequisition(*pChild);
1504 if (pLabel && pLabel->IsVisible())
1506 Size aLabelSize = getLayoutRequisition(*pLabel);
1507 aRet.AdjustHeight(aLabelSize.Height() );
1508 aRet.setWidth( std::max(aLabelSize.Width(), aRet.Width()) );
1511 return aRet;
1514 void VclFrame::setAllocation(const Size &rAllocation)
1516 //SetBackground( Color(0xFF, 0x00, 0xFF) );
1518 Size aAllocation(rAllocation);
1519 Point aChildPos;
1521 vcl::Window *pChild = get_child();
1522 vcl::Window *pLabel = get_label_widget();
1524 if (pLabel && pLabel->IsVisible())
1526 Size aLabelSize = getLayoutRequisition(*pLabel);
1527 aLabelSize.setHeight( std::min(aLabelSize.Height(), aAllocation.Height()) );
1528 aLabelSize.setWidth( std::min(aLabelSize.Width(), aAllocation.Width()) );
1529 setLayoutAllocation(*pLabel, aChildPos, aLabelSize);
1530 aAllocation.AdjustHeight( -(aLabelSize.Height()) );
1531 aChildPos.AdjustY(aLabelSize.Height() );
1534 if (pChild && pChild->IsVisible())
1535 setLayoutAllocation(*pChild, aChildPos, aAllocation);
1538 IMPL_LINK(VclFrame, WindowEventListener, VclWindowEvent&, rEvent, void)
1540 if (rEvent.GetId() == VclEventId::ObjectDying)
1541 designate_label(nullptr);
1544 void VclFrame::designate_label(vcl::Window *pWindow)
1546 assert(!pWindow || pWindow->GetParent() == this);
1547 if (m_pLabel)
1548 m_pLabel->RemoveEventListener(LINK(this, VclFrame, WindowEventListener));
1549 m_pLabel = pWindow;
1550 if (m_pLabel)
1551 m_pLabel->AddEventListener(LINK(this, VclFrame, WindowEventListener));
1554 const vcl::Window *VclFrame::get_label_widget() const
1556 if (m_pLabel)
1557 return m_pLabel;
1558 assert(GetChildCount() <= 2);
1559 //The label widget is normally the first (of two) children
1560 const WindowImpl* pWindowImpl = ImplGetWindowImpl();
1561 if (pWindowImpl->mpFirstChild == pWindowImpl->mpLastChild) //no label exists
1562 return nullptr;
1563 return pWindowImpl->mpFirstChild;
1566 vcl::Window *VclFrame::get_label_widget()
1568 return const_cast<vcl::Window*>(const_cast<const VclFrame*>(this)->get_label_widget());
1571 const vcl::Window *VclFrame::get_child() const
1573 //The child widget is the normally the last (of two) children
1574 const WindowImpl* pWindowImpl = ImplGetWindowImpl();
1575 assert(GetChildCount() == 2 || pWindowImpl->mbInDispose);
1576 if (!m_pLabel)
1577 return pWindowImpl->mpLastChild;
1578 if (pWindowImpl->mpFirstChild == pWindowImpl->mpLastChild) //only label exists
1579 return nullptr;
1580 return pWindowImpl->mpLastChild;
1583 vcl::Window *VclFrame::get_child()
1585 return const_cast<vcl::Window*>(const_cast<const VclFrame*>(this)->get_child());
1588 void VclFrame::set_label(const OUString &rLabel)
1590 vcl::Window *pLabel = get_label_widget();
1591 assert(pLabel);
1592 pLabel->SetText(rLabel);
1595 OUString VclFrame::get_label() const
1597 const vcl::Window *pLabel = get_label_widget();
1598 assert(pLabel);
1599 return pLabel->GetText();
1602 OUString VclFrame::getDefaultAccessibleName() const
1604 const vcl::Window *pLabel = get_label_widget();
1605 if (pLabel)
1606 return pLabel->GetAccessibleName();
1607 return VclBin::getDefaultAccessibleName();
1610 void VclFrame::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
1612 VclBin::DumpAsPropertyTree(rJsonWriter);
1613 rJsonWriter.put("type", "frame");
1616 class DisclosureButton final : public CheckBox
1618 virtual void ImplDrawCheckBoxState(vcl::RenderContext& rRenderContext) override
1620 /* HACK: DisclosureButton is currently assuming, that the disclosure sign
1621 will fit into the rectangle occupied by a normal checkbox on all themes.
1622 If this does not hold true for some theme, ImplGetCheckImageSize
1623 would have to be overridden for DisclosureButton; also GetNativeControlRegion
1624 for ControlType::ListNode would have to be implemented and taken into account
1627 tools::Rectangle aStateRect(GetStateRect());
1629 ImplControlValue aControlValue(GetState() == TRISTATE_TRUE ? ButtonValue::On : ButtonValue::Off);
1630 tools::Rectangle aCtrlRegion(aStateRect);
1631 ControlState nState = ControlState::NONE;
1633 if (HasFocus())
1634 nState |= ControlState::FOCUSED;
1635 if (GetButtonState() & DrawButtonFlags::Default)
1636 nState |= ControlState::DEFAULT;
1637 if (Window::IsEnabled())
1638 nState |= ControlState::ENABLED;
1639 if (IsMouseOver() && GetMouseRect().Contains(GetPointerPosPixel()))
1640 nState |= ControlState::ROLLOVER;
1642 if (rRenderContext.DrawNativeControl(ControlType::ListNode, ControlPart::Entire, aCtrlRegion,
1643 nState, aControlValue, OUString()))
1644 return;
1646 ImplSVCtrlData& rCtrlData(ImplGetSVData()->maCtrlData);
1647 if (!rCtrlData.moDisclosurePlus)
1648 rCtrlData.moDisclosurePlus.emplace(StockImage::Yes, SV_DISCLOSURE_PLUS);
1649 if (!rCtrlData.moDisclosureMinus)
1650 rCtrlData.moDisclosureMinus.emplace(StockImage::Yes, SV_DISCLOSURE_MINUS);
1652 Image* pImg
1653 = IsChecked() ? &*rCtrlData.moDisclosureMinus : &*rCtrlData.moDisclosurePlus;
1655 DrawImageFlags nStyle = DrawImageFlags::NONE;
1656 if (!IsEnabled())
1657 nStyle |= DrawImageFlags::Disable;
1659 Size aSize(aStateRect.GetSize());
1660 Size aImgSize(pImg->GetSizePixel());
1661 Point aOff((aSize.Width() - aImgSize.Width()) / 2,
1662 (aSize.Height() - aImgSize.Height()) / 2);
1663 aOff += aStateRect.TopLeft();
1664 rRenderContext.DrawImage(aOff, *pImg, nStyle);
1667 public:
1668 explicit DisclosureButton(vcl::Window* pParent)
1669 : CheckBox(pParent, 0)
1673 virtual void KeyInput( const KeyEvent& rKEvt ) override
1675 vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
1677 if( !aKeyCode.GetModifier() &&
1678 ( ( aKeyCode.GetCode() == KEY_ADD ) ||
1679 ( aKeyCode.GetCode() == KEY_SUBTRACT ) )
1682 Check( aKeyCode.GetCode() == KEY_ADD );
1684 else
1685 CheckBox::KeyInput( rKEvt );
1689 VclExpander::VclExpander(vcl::Window *pParent)
1690 : VclBin(pParent)
1691 , m_bResizeTopLevel(false)
1692 , m_pDisclosureButton(VclPtr<DisclosureButton>::Create(this))
1694 m_pDisclosureButton->SetToggleHdl(LINK(this, VclExpander, ClickHdl));
1695 m_pDisclosureButton->Show();
1698 VclExpander::~VclExpander()
1700 disposeOnce();
1703 bool VclExpander::get_expanded() const
1705 return m_pDisclosureButton->IsChecked();
1708 void VclExpander::set_expanded(bool bExpanded)
1710 m_pDisclosureButton->Check(bExpanded);
1713 void VclExpander::set_label(const OUString& rLabel)
1715 m_pDisclosureButton->SetText(rLabel);
1718 OUString VclExpander::get_label() const
1720 return m_pDisclosureButton->GetText();
1723 void VclExpander::dispose()
1725 m_pDisclosureButton.disposeAndClear();
1726 VclBin::dispose();
1729 const vcl::Window *VclExpander::get_child() const
1731 const WindowImpl* pWindowImpl = ImplGetWindowImpl();
1733 assert(pWindowImpl->mpFirstChild == m_pDisclosureButton);
1735 return pWindowImpl->mpFirstChild->GetWindow(GetWindowType::Next);
1738 vcl::Window *VclExpander::get_child()
1740 return const_cast<vcl::Window*>(const_cast<const VclExpander*>(this)->get_child());
1743 Size VclExpander::calculateRequisition() const
1745 Size aRet(0, 0);
1747 WindowImpl* pWindowImpl = ImplGetWindowImpl();
1749 const vcl::Window *pChild = get_child();
1750 const vcl::Window *pLabel = pChild != pWindowImpl->mpLastChild ? pWindowImpl->mpLastChild.get() : nullptr;
1752 if (pChild && pChild->IsVisible() && m_pDisclosureButton->IsChecked())
1753 aRet = getLayoutRequisition(*pChild);
1755 Size aExpanderSize = getLayoutRequisition(*m_pDisclosureButton);
1757 if (pLabel && pLabel->IsVisible())
1759 Size aLabelSize = getLayoutRequisition(*pLabel);
1760 aExpanderSize.setHeight( std::max(aExpanderSize.Height(), aLabelSize.Height()) );
1761 aExpanderSize.AdjustWidth(aLabelSize.Width() );
1764 aRet.AdjustHeight(aExpanderSize.Height() );
1765 aRet.setWidth( std::max(aExpanderSize.Width(), aRet.Width()) );
1767 return aRet;
1770 void VclExpander::setAllocation(const Size &rAllocation)
1772 Size aAllocation(rAllocation);
1773 Point aChildPos;
1775 WindowImpl* pWindowImpl = ImplGetWindowImpl();
1777 //The label widget is the last (of two) children
1778 vcl::Window *pChild = get_child();
1779 vcl::Window *pLabel = pChild != pWindowImpl->mpLastChild.get() ? pWindowImpl->mpLastChild.get() : nullptr;
1781 Size aButtonSize = getLayoutRequisition(*m_pDisclosureButton);
1782 Size aLabelSize;
1783 Size aExpanderSize = aButtonSize;
1784 if (pLabel && pLabel->IsVisible())
1786 aLabelSize = getLayoutRequisition(*pLabel);
1787 aExpanderSize.setHeight( std::max(aExpanderSize.Height(), aLabelSize.Height()) );
1788 aExpanderSize.AdjustWidth(aLabelSize.Width() );
1791 aExpanderSize.setHeight( std::min(aExpanderSize.Height(), aAllocation.Height()) );
1792 aExpanderSize.setWidth( std::min(aExpanderSize.Width(), aAllocation.Width()) );
1794 aButtonSize.setHeight( std::min(aButtonSize.Height(), aExpanderSize.Height()) );
1795 aButtonSize.setWidth( std::min(aButtonSize.Width(), aExpanderSize.Width()) );
1797 tools::Long nExtraExpanderHeight = aExpanderSize.Height() - aButtonSize.Height();
1798 Point aButtonPos(aChildPos.X(), aChildPos.Y() + nExtraExpanderHeight/2);
1799 setLayoutAllocation(*m_pDisclosureButton, aButtonPos, aButtonSize);
1801 if (pLabel && pLabel->IsVisible())
1803 aLabelSize.setHeight( std::min(aLabelSize.Height(), aExpanderSize.Height()) );
1804 aLabelSize.setWidth( std::min(aLabelSize.Width(),
1805 aExpanderSize.Width() - aButtonSize.Width()) );
1807 tools::Long nExtraLabelHeight = aExpanderSize.Height() - aLabelSize.Height();
1808 Point aLabelPos(aChildPos.X() + aButtonSize.Width(), aChildPos.Y() + nExtraLabelHeight/2);
1809 setLayoutAllocation(*pLabel, aLabelPos, aLabelSize);
1812 aAllocation.AdjustHeight( -(aExpanderSize.Height()) );
1813 aChildPos.AdjustY(aExpanderSize.Height() );
1815 if (pChild && pChild->IsVisible())
1817 if (!m_pDisclosureButton->IsChecked())
1818 aAllocation = Size();
1819 setLayoutAllocation(*pChild, aChildPos, aAllocation);
1823 bool VclExpander::set_property(const OUString &rKey, const OUString &rValue)
1825 if (rKey == "expanded")
1826 set_expanded(toBool(rValue));
1827 else if (rKey == "resize-toplevel")
1828 m_bResizeTopLevel = toBool(rValue);
1829 else
1830 return VclBin::set_property(rKey, rValue);
1831 return true;
1834 void VclExpander::StateChanged(StateChangedType nType)
1836 VclBin::StateChanged( nType );
1838 if (nType == StateChangedType::InitShow)
1840 vcl::Window *pChild = get_child();
1841 if (pChild)
1842 pChild->Show(m_pDisclosureButton->IsChecked());
1846 const vcl::Window *VclExpander::get_label_widget() const
1848 return m_pDisclosureButton;
1851 vcl::Window *VclExpander::get_label_widget()
1853 return const_cast<vcl::Window*>(const_cast<const VclExpander*>(this)->get_label_widget());
1856 void VclExpander::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
1858 VclContainer::DumpAsPropertyTree(rJsonWriter);
1859 rJsonWriter.put("type", "expander");
1862 FactoryFunction VclExpander::GetUITestFactory() const
1864 return ExpanderUIObject::create;
1867 IMPL_LINK( VclExpander, ClickHdl, CheckBox&, rBtn, void )
1869 vcl::Window *pChild = get_child();
1870 if (pChild)
1872 pChild->Show(rBtn.IsChecked());
1873 queue_resize();
1874 Dialog* pResizeDialog = m_bResizeTopLevel ? GetParentDialog() : nullptr;
1875 if (pResizeDialog)
1876 pResizeDialog->setOptimalLayoutSize(true);
1878 maExpandedHdl.Call(*this);
1881 VclScrolledWindow::VclScrolledWindow(vcl::Window *pParent)
1882 : VclBin(pParent, WB_HIDE | WB_CLIPCHILDREN | WB_AUTOHSCROLL | WB_AUTOVSCROLL | WB_TABSTOP)
1883 , m_bUserManagedScrolling(false)
1884 , m_eDrawFrameStyle(DrawFrameStyle::NONE)
1885 , m_eDrawFrameFlags(DrawFrameFlags::WindowBorder)
1886 , m_pVScroll(VclPtr<ScrollBar>::Create(this, WB_HIDE | WB_VERT))
1887 , m_pHScroll(VclPtr<ScrollBar>::Create(this, WB_HIDE | WB_HORZ))
1888 , m_aScrollBarBox(VclPtr<ScrollBarBox>::Create(this, WB_HIDE))
1890 SetType(WindowType::SCROLLWINDOW);
1892 AllSettings aAllSettings = GetSettings();
1893 StyleSettings aStyle = aAllSettings.GetStyleSettings();
1894 aStyle.SetMonoColor(aStyle.GetShadowColor());
1895 aAllSettings.SetStyleSettings(aStyle);
1896 GetOutDev()->SetSettings(aAllSettings);
1898 Link<ScrollBar*,void> aLink( LINK( this, VclScrolledWindow, ScrollBarHdl ) );
1899 m_pVScroll->SetScrollHdl(aLink);
1900 m_pHScroll->SetScrollHdl(aLink);
1902 m_nBorderWidth = CalcBorderWidth();
1905 int VclScrolledWindow::CalcBorderWidth() const
1907 if (m_eDrawFrameStyle == DrawFrameStyle::NONE)
1908 return 0;
1909 const tools::Rectangle aRect(tools::Rectangle(Point(0, 0), Size(100, 100)));
1910 DecorationView aDecoView(const_cast<OutputDevice*>(GetOutDev()));
1911 // don't actually draw anything, just measure what size it would be and the diff is the desired border size to reserve
1912 const tools::Rectangle aContentRect = aDecoView.DrawFrame(aRect, m_eDrawFrameStyle, m_eDrawFrameFlags | DrawFrameFlags::NoDraw);
1913 const auto nBorderWidth = (aRect.GetWidth() - aContentRect.GetWidth()) / 2;
1914 return std::max<int>(nBorderWidth, 1);
1917 void VclScrolledWindow::dispose()
1919 m_pVScroll.disposeAndClear();
1920 m_pHScroll.disposeAndClear();
1921 m_aScrollBarBox.disposeAndClear();
1922 VclBin::dispose();
1925 IMPL_LINK_NOARG(VclScrolledWindow, ScrollBarHdl, ScrollBar*, void)
1927 vcl::Window *pChild = get_child();
1928 if (!pChild)
1929 return;
1931 assert(dynamic_cast<VclViewport*>(pChild) && "scrolledwindow child should be a Viewport");
1933 pChild = pChild->GetWindow(GetWindowType::FirstChild);
1935 if (!pChild)
1936 return;
1938 Point aWinPos(-m_pHScroll->GetThumbPos(), -m_pVScroll->GetThumbPos());
1939 pChild->SetPosPixel(aWinPos);
1942 const vcl::Window *VclScrolledWindow::get_child() const
1944 const WindowImpl* pWindowImpl = ImplGetWindowImpl();
1945 assert(GetChildCount() == 4 || pWindowImpl->mbInDispose);
1946 return pWindowImpl->mpLastChild;
1949 vcl::Window *VclScrolledWindow::get_child()
1951 return const_cast<vcl::Window*>(const_cast<const VclScrolledWindow*>(this)->get_child());
1954 Size VclScrolledWindow::calculateRequisition() const
1956 Size aRet(0, 0);
1958 const vcl::Window *pChild = get_child();
1959 if (pChild && pChild->IsVisible())
1960 aRet = getLayoutRequisition(*pChild);
1962 if (GetStyle() & WB_VSCROLL)
1963 aRet.AdjustWidth(getLayoutRequisition(*m_pVScroll).Width() );
1965 if (GetStyle() & WB_HSCROLL)
1966 aRet.AdjustHeight(getLayoutRequisition(*m_pHScroll).Height() );
1968 aRet.AdjustHeight(2 * m_nBorderWidth);
1969 aRet.AdjustWidth(2 * m_nBorderWidth);
1971 return aRet;
1974 void VclScrolledWindow::InitScrollBars(const Size &rRequest)
1976 const vcl::Window *pChild = get_child();
1977 if (!pChild || !pChild->IsVisible())
1978 return;
1980 Size aOutSize(getVisibleChildSize());
1982 m_pVScroll->SetRangeMax(rRequest.Height());
1983 m_pVScroll->SetVisibleSize(aOutSize.Height());
1984 m_pVScroll->SetPageSize(16);
1986 m_pHScroll->SetRangeMax(rRequest.Width());
1987 m_pHScroll->SetVisibleSize(aOutSize.Width());
1988 m_pHScroll->SetPageSize(16);
1990 m_pVScroll->Scroll();
1991 m_pHScroll->Scroll();
1994 void VclScrolledWindow::doSetAllocation(const Size &rAllocation, bool bRetryOnFailure)
1996 Size aChildReq;
1998 vcl::Window *pChild = get_child();
1999 if (pChild && pChild->IsVisible())
2000 aChildReq = getLayoutRequisition(*pChild);
2002 tools::Long nAvailHeight = rAllocation.Height() - 2 * m_nBorderWidth;
2003 tools::Long nAvailWidth = rAllocation.Width() - 2 * m_nBorderWidth;
2005 // vert. ScrollBar
2006 bool bShowVScroll;
2007 if (GetStyle() & WB_AUTOVSCROLL)
2008 bShowVScroll = nAvailHeight < aChildReq.Height();
2009 else
2010 bShowVScroll = (GetStyle() & WB_VSCROLL) != 0;
2012 if (bShowVScroll)
2013 nAvailWidth -= getLayoutRequisition(*m_pVScroll).Width();
2015 // horz. ScrollBar
2016 bool bShowHScroll;
2017 if (GetStyle() & WB_AUTOHSCROLL)
2019 bShowHScroll = nAvailWidth < aChildReq.Width();
2021 if (bShowHScroll)
2022 nAvailHeight -= getLayoutRequisition(*m_pHScroll).Height();
2024 if (GetStyle() & WB_AUTOVSCROLL)
2025 bShowVScroll = nAvailHeight < aChildReq.Height();
2027 else
2028 bShowHScroll = (GetStyle() & WB_HSCROLL) != 0;
2030 if (m_pHScroll->IsVisible() != bShowHScroll)
2031 m_pHScroll->Show(bShowHScroll);
2032 if (m_pVScroll->IsVisible() != bShowVScroll)
2033 m_pVScroll->Show(bShowVScroll);
2035 Size aInnerSize(rAllocation);
2036 aInnerSize.AdjustWidth(-2 * m_nBorderWidth);
2037 aInnerSize.AdjustHeight(-2 * m_nBorderWidth);
2039 bool bBothVisible = m_pVScroll->IsVisible() && m_pHScroll->IsVisible();
2040 auto nScrollBarWidth = getLayoutRequisition(*m_pVScroll).Width();
2041 auto nScrollBarHeight = getLayoutRequisition(*m_pHScroll).Height();
2043 if (m_pVScroll->IsVisible())
2045 Point aScrollPos(rAllocation.Width() - nScrollBarWidth - m_nBorderWidth, m_nBorderWidth);
2046 Size aScrollSize(nScrollBarWidth, rAllocation.Height() - 2 * m_nBorderWidth);
2047 if (bBothVisible)
2048 aScrollSize.AdjustHeight(-nScrollBarHeight);
2049 setLayoutAllocation(*m_pVScroll, aScrollPos, aScrollSize);
2050 aInnerSize.AdjustWidth( -nScrollBarWidth );
2053 if (m_pHScroll->IsVisible())
2055 Point aScrollPos(m_nBorderWidth, rAllocation.Height() - nScrollBarHeight);
2056 Size aScrollSize(rAllocation.Width() - 2 * m_nBorderWidth, nScrollBarHeight);
2057 if (bBothVisible)
2058 aScrollSize.AdjustWidth(-nScrollBarWidth);
2059 setLayoutAllocation(*m_pHScroll, aScrollPos, aScrollSize);
2060 aInnerSize.AdjustHeight( -nScrollBarHeight );
2063 if (bBothVisible)
2065 Point aBoxPos(aInnerSize.Width() + m_nBorderWidth, aInnerSize.Height() + m_nBorderWidth);
2066 m_aScrollBarBox->SetPosSizePixel(aBoxPos, Size(nScrollBarWidth, nScrollBarHeight));
2067 m_aScrollBarBox->Show();
2069 else
2071 m_aScrollBarBox->Hide();
2074 if (pChild && pChild->IsVisible())
2076 assert(dynamic_cast<VclViewport*>(pChild) && "scrolledwindow child should be a Viewport");
2078 WinBits nOldBits = (GetStyle() & (WB_AUTOVSCROLL | WB_VSCROLL | WB_AUTOHSCROLL | WB_HSCROLL));
2080 setLayoutAllocation(*pChild, Point(m_nBorderWidth, m_nBorderWidth), aInnerSize);
2082 // tdf#128758 if the layout allocation triggered some callback that
2083 // immediately invalidates the layout by adding scrollbars then
2084 // normally this would simply retrigger layout and another toplevel
2085 // attempt is made later. But the initial layout attempt blocks
2086 // relayouts, so just make another single effort here.
2087 WinBits nNewBits = (GetStyle() & (WB_AUTOVSCROLL | WB_VSCROLL | WB_AUTOHSCROLL | WB_HSCROLL));
2088 if (nOldBits != nNewBits && bRetryOnFailure)
2090 doSetAllocation(rAllocation, false);
2091 return;
2095 if (!m_bUserManagedScrolling)
2096 InitScrollBars(aChildReq);
2099 void VclScrolledWindow::setAllocation(const Size &rAllocation)
2101 doSetAllocation(rAllocation, true);
2104 Size VclScrolledWindow::getVisibleChildSize() const
2106 Size aRet(GetSizePixel());
2107 if (m_pVScroll->IsVisible())
2108 aRet.AdjustWidth( -(m_pVScroll->GetSizePixel().Width()) );
2109 if (m_pHScroll->IsVisible())
2110 aRet.AdjustHeight( -(m_pHScroll->GetSizePixel().Height()) );
2111 aRet.AdjustHeight(-2 * m_nBorderWidth);
2112 aRet.AdjustWidth(-2 * m_nBorderWidth);
2113 return aRet;
2116 bool VclScrolledWindow::set_property(const OUString &rKey, const OUString &rValue)
2118 if (rKey == "shadow-type" || rKey == "name")
2120 if (rKey == "shadow-type")
2122 // despite the style names, this looks like the best mapping
2123 if (rValue == "in")
2124 m_eDrawFrameStyle = DrawFrameStyle::Out;
2125 else if (rValue == "out")
2126 m_eDrawFrameStyle = DrawFrameStyle::In;
2127 else if (rValue == "etched-in")
2128 m_eDrawFrameStyle = DrawFrameStyle::DoubleOut;
2129 else if (rValue == "etched-out")
2130 m_eDrawFrameStyle = DrawFrameStyle::DoubleIn;
2131 else if (rValue == "none")
2132 m_eDrawFrameStyle = DrawFrameStyle::NONE;
2134 else if (rKey == "name")
2136 m_eDrawFrameFlags = DrawFrameFlags::WindowBorder;
2137 if (rValue == "monoborder")
2138 m_eDrawFrameFlags |= DrawFrameFlags::Mono;
2141 auto nBorderWidth = CalcBorderWidth();
2142 if (m_nBorderWidth != nBorderWidth)
2144 m_nBorderWidth = nBorderWidth;
2145 queue_resize();
2148 return true;
2151 bool bRet = VclBin::set_property(rKey, rValue);
2152 m_pVScroll->Show((GetStyle() & WB_VSCROLL) != 0);
2153 m_pHScroll->Show((GetStyle() & WB_HSCROLL) != 0);
2154 return bRet;
2157 bool VclScrolledWindow::EventNotify(NotifyEvent& rNEvt)
2159 bool bDone = false;
2160 if ( rNEvt.GetType() == NotifyEventType::COMMAND )
2162 const CommandEvent& rCEvt = *rNEvt.GetCommandEvent();
2163 if ( rCEvt.GetCommand() == CommandEventId::Wheel )
2165 const CommandWheelData* pData = rCEvt.GetWheelData();
2166 if( !pData->GetModifier() && ( pData->GetMode() == CommandWheelMode::SCROLL ) )
2168 // tdf#140537 only handle scroll commands in the valid shown scrollbars
2169 bDone = HandleScrollCommand(rCEvt,
2170 m_pHScroll->IsVisible() ? m_pHScroll : nullptr,
2171 m_pVScroll->IsVisible() ? m_pVScroll : nullptr);
2174 else if (rCEvt.GetCommand() == CommandEventId::GesturePan)
2176 bDone = HandleScrollCommand(rCEvt, m_pHScroll->IsVisible() ? m_pHScroll : nullptr,
2177 m_pVScroll->IsVisible() ? m_pVScroll : nullptr);
2181 return bDone || VclBin::EventNotify( rNEvt );
2184 void VclScrolledWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
2186 VclBin::Paint(rRenderContext, rRect);
2187 if (m_eDrawFrameStyle == DrawFrameStyle::NONE)
2188 return;
2189 const tools::Rectangle aRect(tools::Rectangle(Point(0,0), GetSizePixel()));
2190 DecorationView aDecoView(&rRenderContext);
2191 const tools::Rectangle aContentRect = aDecoView.DrawFrame(aRect, m_eDrawFrameStyle, m_eDrawFrameFlags);
2192 const auto nBorderWidth = (aRect.GetWidth() - aContentRect.GetWidth()) / 2;
2193 SAL_WARN_IF(nBorderWidth > m_nBorderWidth, "vcl.layout", "desired border at paint " <<
2194 nBorderWidth << " is larger than expected " << m_nBorderWidth);
2197 namespace {
2198 void lcl_dumpScrollbar(::tools::JsonWriter& rJsonWriter, ScrollBar& rScrollBar)
2200 rJsonWriter.put("lower", rScrollBar.GetRangeMin());
2201 rJsonWriter.put("upper", rScrollBar.GetRangeMax());
2202 rJsonWriter.put("step_increment", rScrollBar.GetLineSize());
2203 rJsonWriter.put("page_increment", rScrollBar.GetPageSize());
2204 rJsonWriter.put("value", rScrollBar.GetThumbPos());
2205 rJsonWriter.put("page_size", rScrollBar.GetVisibleSize());
2209 void VclScrolledWindow::DumpAsPropertyTree(::tools::JsonWriter& rJsonWriter)
2211 VclBin::DumpAsPropertyTree(rJsonWriter);
2213 rJsonWriter.put("user_managed_scrolling", m_bUserManagedScrolling);
2216 auto aVertical = rJsonWriter.startNode("vertical");
2218 ScrollBar& rScrollBar = getVertScrollBar();
2219 lcl_dumpScrollbar(rJsonWriter, rScrollBar);
2221 WinBits nWinBits = GetStyle();
2222 if (nWinBits & WB_VSCROLL)
2223 rJsonWriter.put("policy", "always");
2224 else if (nWinBits & WB_AUTOVSCROLL)
2225 rJsonWriter.put("policy", "auto");
2226 else
2227 rJsonWriter.put("policy", "never");
2231 auto aHorizontal = rJsonWriter.startNode("horizontal");
2233 ScrollBar& rScrollBar = getHorzScrollBar();
2234 lcl_dumpScrollbar(rJsonWriter, rScrollBar);
2236 WinBits nWinBits = GetStyle();
2237 if (nWinBits & WB_HSCROLL)
2238 rJsonWriter.put("policy", "always");
2239 else if (nWinBits & WB_AUTOHSCROLL)
2240 rJsonWriter.put("policy", "auto");
2241 else
2242 rJsonWriter.put("policy", "never");
2246 void VclViewport::setAllocation(const Size &rAllocation)
2248 vcl::Window *pChild = get_child();
2249 if (!(pChild && pChild->IsVisible()))
2250 return;
2252 Size aReq(getLayoutRequisition(*pChild));
2253 aReq.setWidth( std::max(aReq.Width(), rAllocation.Width()) );
2254 aReq.setHeight( std::max(aReq.Height(), rAllocation.Height()) );
2255 Point aKeepPos(pChild->GetPosPixel());
2256 if (m_bInitialAllocation)
2258 aKeepPos = Point(0, 0);
2259 m_bInitialAllocation = false;
2261 setLayoutAllocation(*pChild, aKeepPos, aReq);
2264 const vcl::Window *VclEventBox::get_child() const
2266 const WindowImpl* pWindowImpl = ImplGetWindowImpl();
2268 assert(pWindowImpl->mpFirstChild.get() == m_aEventBoxHelper.get());
2270 return pWindowImpl->mpFirstChild->GetWindow(GetWindowType::Next);
2273 vcl::Window *VclEventBox::get_child()
2275 return const_cast<vcl::Window*>(const_cast<const VclEventBox*>(this)->get_child());
2278 void VclEventBox::setAllocation(const Size& rAllocation)
2280 Point aChildPos(0, 0);
2281 for (vcl::Window *pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next))
2283 if (!pChild->IsVisible())
2284 continue;
2285 setLayoutAllocation(*pChild, aChildPos, rAllocation);
2289 Size VclEventBox::calculateRequisition() const
2291 Size aRet(0, 0);
2293 for (const vcl::Window* pChild = get_child(); pChild;
2294 pChild = pChild->GetWindow(GetWindowType::Next))
2296 if (!pChild->IsVisible())
2297 continue;
2298 Size aChildSize = getLayoutRequisition(*pChild);
2299 aRet.setWidth( std::max(aRet.Width(), aChildSize.Width()) );
2300 aRet.setHeight( std::max(aRet.Height(), aChildSize.Height()) );
2303 return aRet;
2306 void VclEventBox::Command(const CommandEvent&)
2308 //discard events by default to block them reaching children
2311 VclEventBox::~VclEventBox()
2313 disposeOnce();
2316 void VclEventBox::dispose()
2318 m_aEventBoxHelper.disposeAndClear();
2319 VclBin::dispose();
2322 void VclSizeGroup::trigger_queue_resize()
2324 //sufficient to trigger one widget to trigger all of them
2325 if (!m_aWindows.empty())
2327 (*m_aWindows.begin())->queue_resize();
2331 void VclSizeGroup::set_ignore_hidden(bool bIgnoreHidden)
2333 if (bIgnoreHidden != m_bIgnoreHidden)
2335 m_bIgnoreHidden = bIgnoreHidden;
2336 trigger_queue_resize();
2340 void VclSizeGroup::set_mode(VclSizeGroupMode eMode)
2342 if (eMode != m_eMode)
2344 m_eMode = eMode;
2345 trigger_queue_resize();
2350 void VclSizeGroup::set_property(const OUString &rKey, const OUString &rValue)
2352 if (rKey == "ignore-hidden")
2353 set_ignore_hidden(toBool(rValue));
2354 else if (rKey == "mode")
2356 VclSizeGroupMode eMode = VclSizeGroupMode::Horizontal;
2357 if (rValue == "none")
2358 eMode = VclSizeGroupMode::NONE;
2359 else if (rValue == "horizontal")
2360 eMode = VclSizeGroupMode::Horizontal;
2361 else if (rValue == "vertical")
2362 eMode = VclSizeGroupMode::Vertical;
2363 else if (rValue == "both")
2364 eMode = VclSizeGroupMode::Both;
2365 else
2367 SAL_WARN("vcl.layout", "unknown size group mode" << rValue);
2369 set_mode(eMode);
2371 else
2373 SAL_INFO("vcl.layout", "unhandled property: " << rKey);
2377 void MessageDialog::create_message_area()
2379 setDeferredProperties();
2381 if (m_pGrid)
2382 return;
2384 VclContainer *pContainer = get_content_area();
2385 assert(pContainer);
2387 m_pGrid.set( VclPtr<VclGrid>::Create(pContainer) );
2388 m_pGrid->reorderWithinParent(0);
2389 m_pGrid->set_column_spacing(12);
2390 m_pMessageBox.set(VclPtr<VclVBox>::Create(m_pGrid));
2391 m_pMessageBox->set_grid_left_attach(1);
2392 m_pMessageBox->set_grid_top_attach(0);
2393 m_pMessageBox->set_spacing(GetTextHeight());
2395 m_pImage = VclPtr<FixedImage>::Create(m_pGrid, WB_CENTER | WB_VCENTER | WB_3DLOOK);
2396 switch (m_eMessageType)
2398 case VclMessageType::Info:
2399 m_pImage->SetImage(GetStandardInfoBoxImage());
2400 break;
2401 case VclMessageType::Warning:
2402 m_pImage->SetImage(GetStandardWarningBoxImage());
2403 break;
2404 case VclMessageType::Question:
2405 m_pImage->SetImage(GetStandardQueryBoxImage());
2406 break;
2407 case VclMessageType::Error:
2408 m_pImage->SetImage(GetStandardErrorBoxImage());
2409 break;
2410 case VclMessageType::Other:
2411 break;
2413 m_pImage->set_grid_left_attach(0);
2414 m_pImage->set_grid_top_attach(0);
2415 m_pImage->set_valign(VclAlign::Start);
2416 m_pImage->Show(m_eMessageType != VclMessageType::Other);
2418 WinBits nWinStyle = WB_CLIPCHILDREN | WB_LEFT | WB_VCENTER | WB_NOLABEL | WB_NOTABSTOP;
2420 bool bHasSecondaryText = !m_sSecondaryString.isEmpty();
2422 m_pPrimaryMessage = VclPtr<VclMultiLineEdit>::Create(m_pMessageBox, nWinStyle);
2423 m_pPrimaryMessage->SetPaintTransparent(true);
2424 m_pPrimaryMessage->EnableCursor(false);
2426 m_pPrimaryMessage->set_hexpand(true);
2427 m_pPrimaryMessage->SetText(m_sPrimaryString);
2428 m_pPrimaryMessage->Show(!m_sPrimaryString.isEmpty());
2430 m_pSecondaryMessage = VclPtr<VclMultiLineEdit>::Create(m_pMessageBox, nWinStyle);
2431 m_pSecondaryMessage->SetPaintTransparent(true);
2432 m_pSecondaryMessage->EnableCursor(false);
2433 m_pSecondaryMessage->set_hexpand(true);
2434 m_pSecondaryMessage->SetText(m_sSecondaryString);
2435 m_pSecondaryMessage->Show(bHasSecondaryText);
2437 MessageDialog::SetMessagesWidths(this, m_pPrimaryMessage, bHasSecondaryText ? m_pSecondaryMessage.get() : nullptr);
2439 VclButtonBox *pButtonBox = get_action_area();
2440 assert(pButtonBox);
2442 VclPtr<PushButton> pBtn;
2443 short nDefaultResponse = get_default_response();
2444 switch (m_eButtonsType)
2446 case VclButtonsType::NONE:
2447 break;
2448 case VclButtonsType::Ok:
2449 pBtn.set( VclPtr<OKButton>::Create(pButtonBox) );
2450 pBtn->SetStyle(pBtn->GetStyle() & WB_DEFBUTTON);
2451 pBtn->Show();
2452 pBtn->set_id(u"ok"_ustr);
2453 add_button(pBtn, RET_OK, true);
2454 nDefaultResponse = RET_OK;
2455 break;
2456 case VclButtonsType::Close:
2457 pBtn.set( VclPtr<CloseButton>::Create(pButtonBox) );
2458 pBtn->SetStyle(pBtn->GetStyle() & WB_DEFBUTTON);
2459 pBtn->Show();
2460 pBtn->set_id(u"close"_ustr);
2461 add_button(pBtn, RET_CLOSE, true);
2462 nDefaultResponse = RET_CLOSE;
2463 break;
2464 case VclButtonsType::Cancel:
2465 pBtn.set( VclPtr<CancelButton>::Create(pButtonBox) );
2466 pBtn->SetStyle(pBtn->GetStyle() & WB_DEFBUTTON);
2467 pBtn->Show();
2468 pBtn->set_id(u"cancel"_ustr);
2469 add_button(pBtn, RET_CANCEL, true);
2470 nDefaultResponse = RET_CANCEL;
2471 break;
2472 case VclButtonsType::YesNo:
2473 pBtn = VclPtr<PushButton>::Create(pButtonBox);
2474 pBtn->SetText(GetStandardText(StandardButtonType::Yes));
2475 pBtn->Show();
2476 pBtn->set_id(u"yes"_ustr);
2477 add_button(pBtn, RET_YES, true);
2479 pBtn.set( VclPtr<PushButton>::Create(pButtonBox) );
2480 pBtn->SetText(GetStandardText(StandardButtonType::No));
2481 pBtn->Show();
2482 pBtn->set_id(u"no"_ustr);
2483 add_button(pBtn, RET_NO, true);
2484 nDefaultResponse = RET_NO;
2485 break;
2486 case VclButtonsType::OkCancel:
2487 pBtn.set( VclPtr<OKButton>::Create(pButtonBox) );
2488 pBtn->Show();
2489 pBtn->set_id(u"ok"_ustr);
2490 add_button(pBtn, RET_OK, true);
2492 pBtn.set( VclPtr<CancelButton>::Create(pButtonBox) );
2493 pBtn->Show();
2494 pBtn->set_id(u"cancel"_ustr);
2495 add_button(pBtn, RET_CANCEL, true);
2496 nDefaultResponse = RET_CANCEL;
2497 break;
2499 set_default_response(nDefaultResponse);
2500 sort_native_button_order(*pButtonBox);
2501 m_pMessageBox->Show();
2502 m_pGrid->Show();
2505 void MessageDialog::create_owned_areas()
2507 #if defined _WIN32
2508 set_border_width(3);
2509 #else
2510 set_border_width(12);
2511 #endif
2512 m_pOwnedContentArea.set(VclPtr<VclVBox>::Create(this, false, 24));
2513 set_content_area(m_pOwnedContentArea);
2514 m_pOwnedContentArea->Show();
2515 m_pOwnedActionArea.set( VclPtr<VclHButtonBox>::Create(m_pOwnedContentArea) );
2516 set_action_area(m_pOwnedActionArea);
2517 m_pOwnedActionArea->Show();
2520 MessageDialog::MessageDialog(vcl::Window* pParent, WinBits nStyle)
2521 : Dialog(pParent, nStyle)
2522 , m_eButtonsType(VclButtonsType::NONE)
2523 , m_eMessageType(VclMessageType::Info)
2524 , m_pOwnedContentArea(nullptr)
2525 , m_pOwnedActionArea(nullptr)
2526 , m_pGrid(nullptr)
2527 , m_pMessageBox(nullptr)
2528 , m_pImage(nullptr)
2529 , m_pPrimaryMessage(nullptr)
2530 , m_pSecondaryMessage(nullptr)
2532 SetType(WindowType::MESSBOX);
2535 MessageDialog::MessageDialog(vcl::Window* pParent,
2536 OUString aMessage,
2537 VclMessageType eMessageType,
2538 VclButtonsType eButtonsType)
2539 : Dialog(pParent, WB_MOVEABLE | WB_3DLOOK | WB_CLOSEABLE)
2540 , m_eButtonsType(eButtonsType)
2541 , m_eMessageType(eMessageType)
2542 , m_pGrid(nullptr)
2543 , m_pMessageBox(nullptr)
2544 , m_pImage(nullptr)
2545 , m_pPrimaryMessage(nullptr)
2546 , m_pSecondaryMessage(nullptr)
2547 , m_sPrimaryString(std::move(aMessage))
2549 SetType(WindowType::MESSBOX);
2550 create_owned_areas();
2551 create_message_area();
2553 switch (m_eMessageType)
2555 case VclMessageType::Info:
2556 SetText(GetStandardInfoBoxText());
2557 break;
2558 case VclMessageType::Warning:
2559 SetText(GetStandardWarningBoxText());
2560 break;
2561 case VclMessageType::Question:
2562 SetText(GetStandardQueryBoxText());
2563 break;
2564 case VclMessageType::Error:
2565 SetText(GetStandardErrorBoxText());
2566 SetTaskBarState(VclTaskBarStates::Error);
2567 break;
2568 case VclMessageType::Other:
2569 SetText(Application::GetDisplayName());
2570 break;
2574 void MessageDialog::dispose()
2576 SetTaskBarState(VclTaskBarStates::Normal);
2578 disposeOwnedButtons();
2579 m_pPrimaryMessage.disposeAndClear();
2580 m_pSecondaryMessage.disposeAndClear();
2581 m_pImage.disposeAndClear();
2582 m_pMessageBox.disposeAndClear();
2583 m_pGrid.disposeAndClear();
2584 m_pOwnedActionArea.disposeAndClear();
2585 m_pOwnedContentArea.disposeAndClear();
2586 Dialog::dispose();
2589 MessageDialog::~MessageDialog()
2591 disposeOnce();
2594 void MessageDialog::SetMessagesWidths(vcl::Window const *pParent,
2595 VclMultiLineEdit *pPrimaryMessage, VclMultiLineEdit *pSecondaryMessage)
2597 if (pSecondaryMessage)
2599 assert(pPrimaryMessage);
2600 vcl::Font aFont = pParent->GetSettings().GetStyleSettings().GetLabelFont();
2601 aFont.SetFontSize(Size(0, aFont.GetFontSize().Height() * 1.2));
2602 aFont.SetWeight(WEIGHT_BOLD);
2603 pPrimaryMessage->SetControlFont(aFont);
2604 pPrimaryMessage->SetMaxTextWidth(pPrimaryMessage->approximate_char_width() * 44);
2605 pSecondaryMessage->SetMaxTextWidth(pSecondaryMessage->approximate_char_width() * 60);
2607 else
2608 pPrimaryMessage->SetMaxTextWidth(pPrimaryMessage->approximate_char_width() * 60);
2611 OUString const & MessageDialog::get_primary_text() const
2613 const_cast<MessageDialog*>(this)->setDeferredProperties();
2615 return m_sPrimaryString;
2618 OUString const & MessageDialog::get_secondary_text() const
2620 const_cast<MessageDialog*>(this)->setDeferredProperties();
2622 return m_sSecondaryString;
2625 bool MessageDialog::set_property(const OUString &rKey, const OUString &rValue)
2627 if (rKey == "text")
2628 set_primary_text(rValue);
2629 else if (rKey == "secondary-text")
2630 set_secondary_text(rValue);
2631 else if (rKey == "message-type")
2633 VclMessageType eMode = VclMessageType::Info;
2634 if (rValue == "info")
2635 eMode = VclMessageType::Info;
2636 else if (rValue == "warning")
2637 eMode = VclMessageType::Warning;
2638 else if (rValue == "question")
2639 eMode = VclMessageType::Question;
2640 else if (rValue == "error")
2641 eMode = VclMessageType::Error;
2642 else if (rValue == "other")
2643 eMode = VclMessageType::Other;
2644 else
2646 SAL_WARN("vcl.layout", "unknown message type mode" << rValue);
2648 m_eMessageType = eMode;
2650 else if (rKey == "buttons")
2652 VclButtonsType eMode = VclButtonsType::NONE;
2653 if (rValue == "none")
2654 eMode = VclButtonsType::NONE;
2655 else if (rValue == "ok")
2656 eMode = VclButtonsType::Ok;
2657 else if (rValue == "cancel")
2658 eMode = VclButtonsType::Cancel;
2659 else if (rValue == "close")
2660 eMode = VclButtonsType::Close;
2661 else if (rValue == "yes-no")
2662 eMode = VclButtonsType::YesNo;
2663 else if (rValue == "ok-cancel")
2664 eMode = VclButtonsType::OkCancel;
2665 else
2667 SAL_WARN("vcl.layout", "unknown buttons type mode" << rValue);
2669 m_eButtonsType = eMode;
2671 else
2672 return Dialog::set_property(rKey, rValue);
2673 return true;
2676 void MessageDialog::set_primary_text(const OUString &rPrimaryString)
2678 m_sPrimaryString = rPrimaryString;
2679 if (m_pPrimaryMessage)
2681 m_pPrimaryMessage->SetText(m_sPrimaryString);
2682 m_pPrimaryMessage->Show(!m_sPrimaryString.isEmpty());
2683 MessageDialog::SetMessagesWidths(this, m_pPrimaryMessage, !m_sSecondaryString.isEmpty() ? m_pSecondaryMessage.get() : nullptr);
2687 void MessageDialog::set_secondary_text(const OUString &rSecondaryString)
2689 m_sSecondaryString = rSecondaryString;
2690 if (m_pSecondaryMessage)
2692 m_pSecondaryMessage->SetText("\n" + m_sSecondaryString);
2693 m_pSecondaryMessage->Show(!m_sSecondaryString.isEmpty());
2694 MessageDialog::SetMessagesWidths(this, m_pPrimaryMessage, !m_sSecondaryString.isEmpty() ? m_pSecondaryMessage.get() : nullptr);
2698 void MessageDialog::StateChanged(StateChangedType nType)
2700 Dialog::StateChanged(nType);
2701 if (nType == StateChangedType::InitShow)
2703 // MessageBox should be at least as wide as to see the title
2704 auto nTitleWidth = CalcTitleWidth();
2705 // Extra-Width for Close button
2706 nTitleWidth += mpWindowImpl->mnTopBorder;
2707 if (get_preferred_size().Width() < nTitleWidth)
2709 set_width_request(nTitleWidth);
2710 DoInitialLayout();
2715 VclPaned::VclPaned(vcl::Window *pParent, bool bVertical)
2716 : VclContainer(pParent, WB_HIDE | WB_CLIPCHILDREN)
2717 , m_pSplitter(VclPtr<Splitter>::Create(this, bVertical ? WB_VSCROLL : WB_HSCROLL))
2718 , m_nPosition(-1)
2720 m_pSplitter->SetBackground(Wallpaper(Application::GetSettings().GetStyleSettings().GetFaceColor()));
2721 m_pSplitter->Show();
2724 void VclPaned::dispose()
2726 m_pSplitter.disposeAndClear();
2727 VclContainer::dispose();
2730 VclVPaned::VclVPaned(vcl::Window *pParent)
2731 : VclPaned(pParent, true)
2733 m_pSplitter->SetSplitHdl(LINK(this, VclVPaned, SplitHdl));
2736 IMPL_LINK(VclVPaned, SplitHdl, Splitter*, pSplitter, void)
2738 tools::Long nSize = pSplitter->GetSplitPosPixel();
2739 Size aSplitterSize(m_pSplitter->GetSizePixel());
2740 Size aAllocation(GetSizePixel());
2741 arrange(aAllocation, nSize, aAllocation.Height() - nSize - aSplitterSize.Height());
2744 void VclVPaned::arrange(const Size& rAllocation, tools::Long nFirstHeight, tools::Long nSecondHeight)
2746 Size aSplitterSize(rAllocation.Width(), getLayoutRequisition(*m_pSplitter).Height());
2747 Size aFirstChildSize(rAllocation.Width(), nFirstHeight);
2748 Size aSecondChildSize(rAllocation.Width(), nSecondHeight);
2749 int nElement = 0;
2750 for (vcl::Window* pChild = GetWindow(GetWindowType::FirstChild); pChild;
2751 pChild = pChild->GetWindow(GetWindowType::Next))
2753 if (!pChild->IsVisible())
2754 continue;
2755 if (nElement == 0)
2757 Point aSplitterPos(0, aFirstChildSize.Height());
2758 setLayoutAllocation(*m_pSplitter, aSplitterPos, aSplitterSize);
2759 m_nPosition = aSplitterPos.Y() + aSplitterSize.Height() / 2;
2761 else if (nElement == 1)
2763 Point aChildPos(0, 0);
2764 setLayoutAllocation(*pChild, aChildPos, aFirstChildSize);
2766 else if (nElement == 2)
2768 Point aChildPos(0, aFirstChildSize.Height() + aSplitterSize.Height());
2769 setLayoutAllocation(*pChild, aChildPos, aSecondChildSize);
2771 ++nElement;
2775 void VclVPaned::set_position(tools::Long nPosition)
2777 VclPaned::set_position(nPosition);
2779 Size aAllocation(GetSizePixel());
2780 Size aSplitterSize(m_pSplitter->GetSizePixel());
2782 nPosition -= aSplitterSize.Height() / 2;
2784 arrange(aAllocation, nPosition, aAllocation.Height() - nPosition - aSplitterSize.Height());
2787 void VclVPaned::setAllocation(const Size& rAllocation)
2789 //supporting "shrink" could be done by adjusting the allowed drag rectangle
2790 m_pSplitter->SetDragRectPixel(tools::Rectangle(Point(0, 0), rAllocation));
2791 Size aSplitterSize(rAllocation.Width(), getLayoutRequisition(*m_pSplitter).Height());
2792 const tools::Long nHeight = rAllocation.Height() - aSplitterSize.Height();
2794 tools::Long nFirstHeight = 0;
2795 tools::Long nSecondHeight = 0;
2796 bool bFirstCanResize = true;
2797 bool bSecondCanResize = true;
2798 const bool bInitialAllocation = get_position() < 0;
2799 int nElement = 0;
2800 for (const vcl::Window* pChild = GetWindow(GetWindowType::FirstChild); pChild;
2801 pChild = pChild->GetWindow(GetWindowType::Next))
2803 if (!pChild->IsVisible())
2804 continue;
2805 if (nElement == 1)
2807 if (bInitialAllocation)
2808 nFirstHeight = getLayoutRequisition(*pChild).Height();
2809 else
2810 nFirstHeight = pChild->GetSizePixel().Height() + pChild->get_margin_top() + pChild->get_margin_bottom();
2811 bFirstCanResize = pChild->get_expand();
2813 else if (nElement == 2)
2815 if (bInitialAllocation)
2816 nSecondHeight = getLayoutRequisition(*pChild).Height();
2817 else
2818 nSecondHeight = pChild->GetSizePixel().Height() + pChild->get_margin_top() + pChild->get_margin_bottom();
2819 bSecondCanResize = pChild->get_expand();
2821 ++nElement;
2823 tools::Long nHeightRequest = nFirstHeight + nSecondHeight;
2824 tools::Long nHeightDiff = nHeight - nHeightRequest;
2825 if (bFirstCanResize == bSecondCanResize)
2826 nFirstHeight += nHeightDiff/2;
2827 else if (bFirstCanResize)
2828 nFirstHeight += nHeightDiff;
2829 arrange(rAllocation, nFirstHeight, rAllocation.Height() - nFirstHeight - aSplitterSize.Height());
2832 Size VclVPaned::calculateRequisition() const
2834 Size aRet(0, 0);
2836 for (const vcl::Window* pChild = GetWindow(GetWindowType::FirstChild); pChild;
2837 pChild = pChild->GetWindow(GetWindowType::Next))
2839 if (!pChild->IsVisible())
2840 continue;
2841 Size aChildSize = getLayoutRequisition(*pChild);
2842 aRet.setWidth( std::max(aRet.Width(), aChildSize.Width()) );
2843 aRet.AdjustHeight(aChildSize.Height() );
2846 return aRet;
2849 VclHPaned::VclHPaned(vcl::Window *pParent)
2850 : VclPaned(pParent, false)
2852 m_pSplitter->SetSplitHdl(LINK(this, VclHPaned, SplitHdl));
2855 IMPL_LINK(VclHPaned, SplitHdl, Splitter*, pSplitter, void)
2857 tools::Long nSize = pSplitter->GetSplitPosPixel();
2858 Size aSplitterSize(m_pSplitter->GetSizePixel());
2859 Size aAllocation(GetSizePixel());
2860 arrange(aAllocation, nSize, aAllocation.Width() - nSize - aSplitterSize.Width());
2863 void VclHPaned::arrange(const Size& rAllocation, tools::Long nFirstWidth, tools::Long nSecondWidth)
2865 Size aSplitterSize(getLayoutRequisition(*m_pSplitter).Width(), rAllocation.Height());
2866 Size aFirstChildSize(nFirstWidth, rAllocation.Height());
2867 Size aSecondChildSize(nSecondWidth, rAllocation.Height());
2868 int nElement = 0;
2869 for (vcl::Window* pChild = GetWindow(GetWindowType::FirstChild); pChild;
2870 pChild = pChild->GetWindow(GetWindowType::Next))
2872 if (!pChild->IsVisible())
2873 continue;
2874 if (nElement == 0)
2876 Point aSplitterPos(aFirstChildSize.Width(), 0);
2877 setLayoutAllocation(*m_pSplitter, aSplitterPos, aSplitterSize);
2878 m_nPosition = aSplitterPos.X() + aSplitterSize.Width() / 2;
2880 else if (nElement == 1)
2882 Point aChildPos(0, 0);
2883 setLayoutAllocation(*pChild, aChildPos, aFirstChildSize);
2885 else if (nElement == 2)
2887 Point aChildPos(aFirstChildSize.Width() + aSplitterSize.Width(), 0);
2888 setLayoutAllocation(*pChild, aChildPos, aSecondChildSize);
2890 ++nElement;
2894 void VclHPaned::set_position(tools::Long nPosition)
2896 VclPaned::set_position(nPosition);
2898 Size aAllocation(GetSizePixel());
2899 Size aSplitterSize(m_pSplitter->GetSizePixel());
2901 nPosition -= aSplitterSize.Width() / 2;
2903 arrange(aAllocation, nPosition, aAllocation.Width() - nPosition - aSplitterSize.Width());
2906 void VclHPaned::setAllocation(const Size& rAllocation)
2908 //supporting "shrink" could be done by adjusting the allowed drag rectangle
2909 m_pSplitter->SetDragRectPixel(tools::Rectangle(Point(0, 0), rAllocation));
2910 Size aSplitterSize(getLayoutRequisition(*m_pSplitter).Width(), rAllocation.Height());
2911 const tools::Long nWidth = rAllocation.Width() - aSplitterSize.Width();
2913 tools::Long nFirstWidth = 0;
2914 tools::Long nSecondWidth = 0;
2915 bool bFirstCanResize = true;
2916 bool bSecondCanResize = true;
2917 const bool bInitialAllocation = get_position() < 0;
2918 int nElement = 0;
2919 for (const vcl::Window* pChild = GetWindow(GetWindowType::FirstChild); pChild;
2920 pChild = pChild->GetWindow(GetWindowType::Next))
2922 if (!pChild->IsVisible())
2923 continue;
2924 if (nElement == 1)
2926 if (bInitialAllocation)
2927 nFirstWidth = getLayoutRequisition(*pChild).Width();
2928 else
2929 nFirstWidth = pChild->GetSizePixel().Width() + pChild->get_margin_start() + pChild->get_margin_end();
2930 bFirstCanResize = pChild->get_expand();
2932 else if (nElement == 2)
2934 if (bInitialAllocation)
2935 nSecondWidth = getLayoutRequisition(*pChild).Width();
2936 else
2937 nSecondWidth = pChild->GetSizePixel().Width() + pChild->get_margin_start() + pChild->get_margin_end();
2938 bSecondCanResize = pChild->get_expand();
2940 ++nElement;
2942 tools::Long nWidthRequest = nFirstWidth + nSecondWidth;
2943 tools::Long nWidthDiff = nWidth - nWidthRequest;
2944 if (bFirstCanResize == bSecondCanResize)
2945 nFirstWidth += nWidthDiff/2;
2946 else if (bFirstCanResize)
2947 nFirstWidth += nWidthDiff;
2948 arrange(rAllocation, nFirstWidth, rAllocation.Width() - nFirstWidth - aSplitterSize.Width());
2951 Size VclHPaned::calculateRequisition() const
2953 Size aRet(0, 0);
2955 for (const vcl::Window* pChild = GetWindow(GetWindowType::FirstChild); pChild;
2956 pChild = pChild->GetWindow(GetWindowType::Next))
2958 if (!pChild->IsVisible())
2959 continue;
2960 Size aChildSize = getLayoutRequisition(*pChild);
2961 aRet.setHeight( std::max(aRet.Height(), aChildSize.Height()) );
2962 aRet.AdjustWidth(aChildSize.Width() );
2965 return aRet;
2968 Size getLegacyBestSizeForChildren(const vcl::Window &rWindow)
2970 tools::Rectangle aBounds;
2972 for (const vcl::Window* pChild = rWindow.GetWindow(GetWindowType::FirstChild); pChild;
2973 pChild = pChild->GetWindow(GetWindowType::Next))
2975 if (!pChild->IsVisible())
2976 continue;
2978 tools::Rectangle aChildBounds(pChild->GetPosPixel(), pChild->GetSizePixel());
2979 aBounds.Union(aChildBounds);
2982 if (aBounds.IsEmpty())
2983 return rWindow.GetSizePixel();
2985 Size aRet(aBounds.GetSize());
2986 Point aTopLeft(aBounds.TopLeft());
2987 aRet.AdjustWidth(aTopLeft.X()*2 );
2988 aRet.AdjustHeight(aTopLeft.Y()*2 );
2990 return aRet;
2993 vcl::Window* getNonLayoutParent(vcl::Window *pWindow)
2995 while (pWindow)
2997 pWindow = pWindow->GetParent();
2998 if (!pWindow || !isContainerWindow(*pWindow))
2999 break;
3001 return pWindow;
3004 bool isVisibleInLayout(const vcl::Window *pWindow)
3006 bool bVisible = true;
3007 while (bVisible)
3009 bVisible = pWindow->IsVisible();
3010 pWindow = pWindow->GetParent();
3011 if (!pWindow || !isContainerWindow(*pWindow))
3012 break;
3014 return bVisible;
3017 bool isEnabledInLayout(const vcl::Window *pWindow)
3019 bool bEnabled = true;
3020 while (bEnabled)
3022 bEnabled = pWindow->IsEnabled();
3023 pWindow = pWindow->GetParent();
3024 if (!pWindow || !isContainerWindow(*pWindow))
3025 break;
3027 return bEnabled;
3030 bool isLayoutEnabled(const vcl::Window *pWindow)
3032 //Child is a container => we're layout enabled
3033 const vcl::Window *pChild = pWindow ? pWindow->GetWindow(GetWindowType::FirstChild) : nullptr;
3034 return pChild && isContainerWindow(*pChild) && !pChild->GetWindow(GetWindowType::Next);
3037 void VclDrawingArea::RequestHelp(const HelpEvent& rHelpEvent)
3039 if (!(rHelpEvent.GetMode() & (HelpEventMode::QUICK | HelpEventMode::BALLOON)))
3040 return;
3042 Point aPos(ScreenToOutputPixel(rHelpEvent.GetMousePosPixel()));
3043 tools::Rectangle aHelpArea(aPos.X(), aPos.Y());
3044 OUString sHelpTip = m_aQueryTooltipHdl.Call(aHelpArea);
3045 if (sHelpTip.isEmpty())
3046 return;
3047 Point aPt = OutputToScreenPixel(aHelpArea.TopLeft());
3048 aHelpArea.SetLeft(aPt.X());
3049 aHelpArea.SetTop(aPt.Y());
3050 aPt = OutputToScreenPixel(aHelpArea.BottomRight());
3051 aHelpArea.SetRight(aPt.X());
3052 aHelpArea.SetBottom(aPt.Y());
3053 // tdf#125369 recover newline support of tdf#101779
3054 QuickHelpFlags eHelpWinStyle = sHelpTip.indexOf('\n') != -1 ? QuickHelpFlags::TipStyleBalloon : QuickHelpFlags::NONE;
3055 Help::ShowQuickHelp(this, aHelpArea, sHelpTip, eHelpWinStyle);
3058 void VclDrawingArea::StartDrag(sal_Int8, const Point&)
3060 if (m_aStartDragHdl.Call(this))
3061 return;
3063 rtl::Reference<TransferDataContainer> xContainer = m_xTransferHelper;
3064 if (!m_xTransferHelper.is())
3065 return;
3067 xContainer->StartDrag(this, m_nDragAction);
3070 OUString VclDrawingArea::GetSurroundingText() const
3072 if (!m_aGetSurroundingHdl.IsSet())
3073 return Control::GetSurroundingText();
3074 OUString sSurroundingText;
3075 m_aGetSurroundingHdl.Call(sSurroundingText);
3076 return sSurroundingText;
3079 Selection VclDrawingArea::GetSurroundingTextSelection() const
3081 if (!m_aGetSurroundingHdl.IsSet())
3082 return Control::GetSurroundingTextSelection();
3083 OUString sSurroundingText;
3084 int nCursor = m_aGetSurroundingHdl.Call(sSurroundingText);
3085 return Selection(nCursor, nCursor);
3088 bool VclDrawingArea::DeleteSurroundingText(const Selection& rSelection)
3090 if (!m_aDeleteSurroundingHdl.IsSet())
3091 return Control::DeleteSurroundingText(rSelection);
3092 return m_aDeleteSurroundingHdl.Call(rSelection);
3095 VclHPaned::~VclHPaned()
3099 VclVPaned::~VclVPaned()
3103 VclPaned::~VclPaned()
3105 disposeOnce();
3108 VclScrolledWindow::~VclScrolledWindow()
3110 disposeOnce();
3113 void VclDrawingArea::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
3115 Control::DumpAsPropertyTree(rJsonWriter);
3116 rJsonWriter.put("type", "drawingarea");
3118 ScopedVclPtrInstance<VirtualDevice> pDevice;
3119 OutputDevice* pRefDevice = GetOutDev();
3120 Size aRenderSize(pRefDevice->PixelToLogic(GetOutputSizePixel()));
3121 Size aOutputSize = GetSizePixel();
3122 pDevice->SetOutputSize(aRenderSize);
3123 tools::Rectangle aRect(Point(0,0), aRenderSize);
3125 // Dark mode support
3126 pDevice->DrawWallpaper(aRect, pRefDevice->GetBackground());
3128 Paint(*pDevice, aRect);
3130 BitmapEx aImage = pDevice->GetBitmapEx(Point(0,0), aRenderSize);
3131 aImage.Scale(aOutputSize);
3132 rJsonWriter.put("imagewidth", aRenderSize.Width());
3133 rJsonWriter.put("imageheight", aRenderSize.Height());
3135 SvMemoryStream aOStm(65535, 65535);
3136 if(GraphicConverter::Export(aOStm, aImage, ConvertDataFormat::PNG) == ERRCODE_NONE)
3138 css::uno::Sequence<sal_Int8> aSeq( static_cast<sal_Int8 const *>(aOStm.GetData()), aOStm.Tell());
3139 OStringBuffer aBuffer("data:image/png;base64,");
3140 ::comphelper::Base64::encode(aBuffer, aSeq);
3141 rJsonWriter.put("image", aBuffer);
3143 rJsonWriter.put("text", GetQuickHelpText());
3146 FactoryFunction VclDrawingArea::GetUITestFactory() const
3148 if (m_pFactoryFunction)
3149 return m_pFactoryFunction;
3150 return DrawingAreaUIObject::create;
3153 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */