1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
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>
19 #pragma clang diagnostic push
20 #pragma clang diagnostic ignored "-Wunused-macros"
22 // [-loplugin:reservedid]:
23 #define _LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION
25 #pragma clang diagnostic pop
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>
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>
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);
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();
101 if (eHalign
== VclAlign::Fill
&& eValign
== VclAlign::Fill
)
103 setLayoutPosSize(rChild
, rAllocPos
, rChildAlloc
);
107 Point
aChildPos(rAllocPos
);
108 Size
aChildSize(rChildAlloc
);
109 Size
aChildPreferredSize(getLayoutRequisition(rChild
));
115 case VclAlign::Start
:
116 if (aChildPreferredSize
.Width() < rChildAlloc
.Width())
117 aChildSize
.setWidth( aChildPreferredSize
.Width() );
120 if (aChildPreferredSize
.Width() < rChildAlloc
.Width())
121 aChildSize
.setWidth( aChildPreferredSize
.Width() );
122 aChildPos
.AdjustX(rChildAlloc
.Width() );
123 aChildPos
.AdjustX( -(aChildSize
.Width()) );
125 case VclAlign::Center
:
126 if (aChildPreferredSize
.Width() < aChildSize
.Width())
127 aChildSize
.setWidth( aChildPreferredSize
.Width() );
128 aChildPos
.AdjustX((rChildAlloc
.Width() - aChildSize
.Width()) / 2 );
136 case VclAlign::Start
:
137 if (aChildPreferredSize
.Height() < rChildAlloc
.Height())
138 aChildSize
.setHeight( aChildPreferredSize
.Height() );
141 if (aChildPreferredSize
.Height() < rChildAlloc
.Height())
142 aChildSize
.setHeight( aChildPreferredSize
.Height() );
143 aChildPos
.AdjustY(rChildAlloc
.Height() );
144 aChildPos
.AdjustY( -(aChildSize
.Height()) );
146 case VclAlign::Center
:
147 if (aChildPreferredSize
.Height() < aChildSize
.Height())
148 aChildSize
.setHeight( aChildPreferredSize
.Height() );
149 aChildPos
.AdjustY((rChildAlloc
.Height() - aChildSize
.Height()) / 2 );
153 setLayoutPosSize(rChild
, aChildPos
, aChildSize
);
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
;
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();
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();
227 CommandEvent
aCEvt(rCEvt
.GetMousePosPixel() + GetPosPixel(), rCEvt
.GetCommand(), rCEvt
.IsMouseEvent(), rCEvt
.GetEventData());
228 pParent
->Command(aCEvt
);
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
);
246 setPrimaryDimension(rSize
, std::max(nPrimaryBoxDimension
, nPrimaryChildDimension
));
248 setPrimaryDimension(rSize
, nPrimaryBoxDimension
+ nPrimaryChildDimension
);
251 Size
VclBox::calculateRequisition() const
253 sal_uInt16 nVisibleChildren
= 0;
256 for (vcl::Window
*pChild
= GetWindow(GetWindowType::FirstChild
); pChild
; pChild
= pChild
->GetWindow(GetWindowType::Next
))
258 if (!pChild
->IsVisible())
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())
281 bool bExpand
= getPrimaryDimensionChildExpand(*pChild
);
286 if (!nVisibleChildren
)
289 tools::Long nAllocPrimaryDimension
= getPrimaryDimension(rAllocation
);
291 tools::Long nHomogeneousDimension
= 0, nExtraSpace
= 0;
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())
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
>())
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();
337 setPrimaryDimension(aBoxSize
, nHomogeneousDimension
);
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();
356 setPrimaryDimension(aChildSize
, std::max(static_cast<tools::Long
>(1),
357 std::min(getPrimaryDimension(rAllocation
), getPrimaryDimension(aBoxSize
) - nPadding
* 2)));
359 setPrimaryCoordinate(aChildPos
, nPrimaryCoordinate
+ nPadding
);
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
);
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
));
392 return VclContainer::set_property(rKey
, rValue
);
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
409 return css::accessibility::AccessibleRole::PANEL
;
411 static sal_uInt16 eRole
= Application::GetToolkitName() == "gtk4" ?
412 css::accessibility::AccessibleRole::PANEL
:
413 css::accessibility::AccessibleRole::FILLER
;
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
425 if (nVisibleChildren
)
427 tools::Long nPrimaryDimension
= getPrimaryDimension(rSize
);
429 nPrimaryDimension
*= nVisibleChildren
;
430 setPrimaryDimension(aRet
, nPrimaryDimension
+ m_nSpacing
* (nVisibleChildren
-1));
431 setSecondaryDimension(aRet
, getSecondaryDimension(rSize
));
437 Size
VclButtonBox::addReqGroups(const VclButtonBox::Requisition
&rReq
) const
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
)));
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
);
485 aVec
.push_back(std::max(nPrimaryChildDimension
, nMinWidth
));
492 VclButtonBox::Requisition
VclButtonBox::calculatePrimarySecondaryRequisitions() const
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())
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());
526 nSubGroupSecondary
= std::max(nSubGroupSecondary
, getSecondaryDimension(aChildSize
));
527 aSubGroupSizes
.push_back(getPrimaryDimension(aChildSize
));
528 aSubGroupNonHomogeneous
.push_back(pChild
->get_non_homogeneous());
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
);
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
,
561 tools::Long nMaxSubNonOutlier
= getMaxNonOutlier(aSubGroupSizes
,
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));
591 Size
VclButtonBox::addSpacing(const Size
&rSize
, sal_uInt16 nVisibleChildren
) const
595 if (nVisibleChildren
)
597 tools::Long nPrimaryDimension
= getPrimaryDimension(rSize
);
598 setPrimaryDimension(aRet
,
599 nPrimaryDimension
+ m_nSpacing
* (nVisibleChildren
-1));
600 setSecondaryDimension(aRet
, getSecondaryDimension(rSize
));
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
;
631 SAL_WARN("vcl.layout", "unknown layout style " << rValue
);
633 m_eLayoutStyle
= eStyle
;
636 return VclBox::set_property(rKey
, rValue
);
640 void VclButtonBox::setAllocation(const Size
&rAllocation
)
642 Requisition
aReq(calculatePrimarySecondaryRequisitions());
644 if (aReq
.m_aMainGroupDimensions
.empty() && aReq
.m_aSubGroupDimensions
.empty())
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
);
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
);
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);
685 SAL_WARN("vcl.layout", "todo unimplemented layout style");
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
);
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())
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
);
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");
740 case VclButtonBoxStyle::Spread
:
741 rJsonWriter
.put("layoutstyle", "spread");
744 case VclButtonBoxStyle::Edge
:
745 rJsonWriter
.put("layoutstyle", "edge");
748 case VclButtonBoxStyle::Center
:
749 rJsonWriter
.put("layoutstyle", "center");
752 case VclButtonBoxStyle::Start
:
753 rJsonWriter
.put("layoutstyle", "start");
756 case VclButtonBoxStyle::End
:
757 rJsonWriter
.put("layoutstyle", "end");
766 std::u16string_view m_aType
;
772 static int getButtonPriority(std::u16string_view rType
)
774 static const size_t N_TYPES
= 6;
775 static const ButtonOrder aDiscardCancelSave
[N_TYPES
] =
785 static const ButtonOrder aSaveDiscardCancel
[N_TYPES
] =
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
;
819 bool m_bVerticalContainer
;
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();
839 bool bPackA
= pA
->get_secondary();
840 bool bPackB
= pB
->get_secondary();
841 if (!m_bVerticalContainer
)
843 //for horizontal boxes group secondaries before primaries
851 //for vertical boxes group secondaries after primaries
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
873 std::stable_sort(aChilds
.begin(), aChilds
.end(), sortButtons(rContainer
.get_orientation()));
874 BuilderUtils::reorderWithinParent(aChilds
, true);
881 VclPtr
<vcl::Window
> pChild
;
882 sal_Int32 nSpanWidth
;
883 sal_Int32 nSpanHeight
;
898 typedef boost::multi_array
<GridEntry
, 2> array_type
;
901 #pragma warning(push)
902 #pragma warning(disable : 4459)
904 static array_type
assembleGrid(const VclGrid
&rGrid
)
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]);
932 #pragma warning(push)
933 #pragma warning(disable : 4459)
935 GridEntry
&rEntry
= A
[nLeftAttach
][nTopAttach
];
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"
971 const GridEntry
&rEntry
= A
[x
][y
];
972 #if defined __GNUC__ && !defined __clang__ && __GNUC__ >= 13 && __GNUC__ <= 15
973 #pragma GCC diagnostic pop
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
])
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
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
])
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
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)
1062 array_type
B(boost::extents
[nNonEmptyCols
][nNonEmptyRows
]);
1063 #if defined _MSC_VER
1064 #pragma warning(pop)
1066 for (sal_Int32 x
= 0, x2
= 0; x
< nMaxX
; ++x
)
1068 if (!aNonEmptyCols
[x
])
1070 for (sal_Int32 y
= 0, y2
= 0; y
< nMaxY
; ++y
)
1072 if (!aNonEmptyRows
[y
])
1074 GridEntry
&rEntry
= A
[x
][y
];
1075 B
[x2
][y2
++] = rEntry
;
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)
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)
1117 const vcl::Window
*pChild
= rEntry
.pChild
;
1118 if (!pChild
|| !pChild
->IsVisible())
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
);
1134 rWidths
[x
].m_nValue
= std::max(rWidths
[x
].m_nValue
, aChildSize
.Width());
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
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"
1151 const GridEntry
&rEntry
= A
[x
][y
];
1152 #if defined __GNUC__ && !defined __clang__ && __GNUC__ >= 13 && __GNUC__ <= 15
1153 #pragma GCC diagnostic pop
1155 const vcl::Window
*pChild
= rEntry
.pChild
;
1156 if (!pChild
|| !pChild
->IsVisible())
1159 sal_Int32 nWidth
= rEntry
.nSpanWidth
;
1160 sal_Int32 nHeight
= rEntry
.nSpanHeight
;
1162 if (nWidth
== 1 && nHeight
== 1)
1165 Size aChildSize
= VclContainer::getLayoutRequisition(*pChild
);
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
)
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
;
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
)
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
;
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);
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();
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();
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);
1293 sal_Int32 nMaxX
= A
.shape()[0];
1294 sal_Int32 nMaxY
= A
.shape()[1];
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();
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
)
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
1331 aRequisition
= calculateRequisitionForSpacings(nRowSpacing
, nColSpacing
);
1332 if (aRequisition
.Width() <= rAllocation
.Width())
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();
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
)
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
1374 aRequisition
= calculateRequisitionForSpacings(nRowSpacing
, nColSpacing
);
1375 if (aRequisition
.Height() <= rAllocation
.Height())
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"
1403 GridEntry
&rEntry
= A
[x
][y
];
1404 #if defined __GNUC__ && !defined __clang__ && __GNUC__ == 13
1405 #pragma GCC diagnostic pop
1407 vcl::Window
*pChild
= rEntry
.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")
1450 return VclContainer::set_property(rKey
, rValue
);
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
);
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()
1486 void VclFrame::dispose()
1492 //To-Do, hook a DecorationView into VclFrame ?
1494 Size
VclFrame::calculateRequisition() const
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()) );
1514 void VclFrame::setAllocation(const Size
&rAllocation
)
1516 //SetBackground( Color(0xFF, 0x00, 0xFF) );
1518 Size
aAllocation(rAllocation
);
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);
1548 m_pLabel
->RemoveEventListener(LINK(this, VclFrame
, WindowEventListener
));
1551 m_pLabel
->AddEventListener(LINK(this, VclFrame
, WindowEventListener
));
1554 const vcl::Window
*VclFrame::get_label_widget() const
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
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
);
1577 return pWindowImpl
->mpLastChild
;
1578 if (pWindowImpl
->mpFirstChild
== pWindowImpl
->mpLastChild
) //only label exists
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();
1592 pLabel
->SetText(rLabel
);
1595 OUString
VclFrame::get_label() const
1597 const vcl::Window
*pLabel
= get_label_widget();
1599 return pLabel
->GetText();
1602 OUString
VclFrame::getDefaultAccessibleName() const
1604 const vcl::Window
*pLabel
= get_label_widget();
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
;
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()))
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
);
1653 = IsChecked() ? &*rCtrlData
.moDisclosureMinus
: &*rCtrlData
.moDisclosurePlus
;
1655 DrawImageFlags nStyle
= DrawImageFlags::NONE
;
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
);
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
);
1685 CheckBox::KeyInput( rKEvt
);
1689 VclExpander::VclExpander(vcl::Window
*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()
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();
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
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()) );
1770 void VclExpander::setAllocation(const Size
&rAllocation
)
1772 Size
aAllocation(rAllocation
);
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
);
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
);
1830 return VclBin::set_property(rKey
, rValue
);
1834 void VclExpander::StateChanged(StateChangedType nType
)
1836 VclBin::StateChanged( nType
);
1838 if (nType
== StateChangedType::InitShow
)
1840 vcl::Window
*pChild
= get_child();
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();
1872 pChild
->Show(rBtn
.IsChecked());
1874 Dialog
* pResizeDialog
= m_bResizeTopLevel
? GetParentDialog() : nullptr;
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
)
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();
1925 IMPL_LINK_NOARG(VclScrolledWindow
, ScrollBarHdl
, ScrollBar
*, void)
1927 vcl::Window
*pChild
= get_child();
1931 assert(dynamic_cast<VclViewport
*>(pChild
) && "scrolledwindow child should be a Viewport");
1933 pChild
= pChild
->GetWindow(GetWindowType::FirstChild
);
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
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
);
1974 void VclScrolledWindow::InitScrollBars(const Size
&rRequest
)
1976 const vcl::Window
*pChild
= get_child();
1977 if (!pChild
|| !pChild
->IsVisible())
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
)
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
;
2007 if (GetStyle() & WB_AUTOVSCROLL
)
2008 bShowVScroll
= nAvailHeight
< aChildReq
.Height();
2010 bShowVScroll
= (GetStyle() & WB_VSCROLL
) != 0;
2013 nAvailWidth
-= getLayoutRequisition(*m_pVScroll
).Width();
2017 if (GetStyle() & WB_AUTOHSCROLL
)
2019 bShowHScroll
= nAvailWidth
< aChildReq
.Width();
2022 nAvailHeight
-= getLayoutRequisition(*m_pHScroll
).Height();
2024 if (GetStyle() & WB_AUTOVSCROLL
)
2025 bShowVScroll
= nAvailHeight
< aChildReq
.Height();
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
);
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
);
2058 aScrollSize
.AdjustWidth(-nScrollBarWidth
);
2059 setLayoutAllocation(*m_pHScroll
, aScrollPos
, aScrollSize
);
2060 aInnerSize
.AdjustHeight( -nScrollBarHeight
);
2065 Point
aBoxPos(aInnerSize
.Width() + m_nBorderWidth
, aInnerSize
.Height() + m_nBorderWidth
);
2066 m_aScrollBarBox
->SetPosSizePixel(aBoxPos
, Size(nScrollBarWidth
, nScrollBarHeight
));
2067 m_aScrollBarBox
->Show();
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);
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
);
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
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
;
2151 bool bRet
= VclBin::set_property(rKey
, rValue
);
2152 m_pVScroll
->Show((GetStyle() & WB_VSCROLL
) != 0);
2153 m_pHScroll
->Show((GetStyle() & WB_HSCROLL
) != 0);
2157 bool VclScrolledWindow::EventNotify(NotifyEvent
& rNEvt
)
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
)
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
);
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");
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");
2242 rJsonWriter
.put("policy", "never");
2246 void VclViewport::setAllocation(const Size
&rAllocation
)
2248 vcl::Window
*pChild
= get_child();
2249 if (!(pChild
&& pChild
->IsVisible()))
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())
2285 setLayoutAllocation(*pChild
, aChildPos
, rAllocation
);
2289 Size
VclEventBox::calculateRequisition() const
2293 for (const vcl::Window
* pChild
= get_child(); pChild
;
2294 pChild
= pChild
->GetWindow(GetWindowType::Next
))
2296 if (!pChild
->IsVisible())
2298 Size aChildSize
= getLayoutRequisition(*pChild
);
2299 aRet
.setWidth( std::max(aRet
.Width(), aChildSize
.Width()) );
2300 aRet
.setHeight( std::max(aRet
.Height(), aChildSize
.Height()) );
2306 void VclEventBox::Command(const CommandEvent
&)
2308 //discard events by default to block them reaching children
2311 VclEventBox::~VclEventBox()
2316 void VclEventBox::dispose()
2318 m_aEventBoxHelper
.disposeAndClear();
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
)
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
;
2367 SAL_WARN("vcl.layout", "unknown size group mode" << rValue
);
2373 SAL_INFO("vcl.layout", "unhandled property: " << rKey
);
2377 void MessageDialog::create_message_area()
2379 setDeferredProperties();
2384 VclContainer
*pContainer
= get_content_area();
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());
2401 case VclMessageType::Warning
:
2402 m_pImage
->SetImage(GetStandardWarningBoxImage());
2404 case VclMessageType::Question
:
2405 m_pImage
->SetImage(GetStandardQueryBoxImage());
2407 case VclMessageType::Error
:
2408 m_pImage
->SetImage(GetStandardErrorBoxImage());
2410 case VclMessageType::Other
:
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();
2442 VclPtr
<PushButton
> pBtn
;
2443 short nDefaultResponse
= get_default_response();
2444 switch (m_eButtonsType
)
2446 case VclButtonsType::NONE
:
2448 case VclButtonsType::Ok
:
2449 pBtn
.set( VclPtr
<OKButton
>::Create(pButtonBox
) );
2450 pBtn
->SetStyle(pBtn
->GetStyle() & WB_DEFBUTTON
);
2452 pBtn
->set_id(u
"ok"_ustr
);
2453 add_button(pBtn
, RET_OK
, true);
2454 nDefaultResponse
= RET_OK
;
2456 case VclButtonsType::Close
:
2457 pBtn
.set( VclPtr
<CloseButton
>::Create(pButtonBox
) );
2458 pBtn
->SetStyle(pBtn
->GetStyle() & WB_DEFBUTTON
);
2460 pBtn
->set_id(u
"close"_ustr
);
2461 add_button(pBtn
, RET_CLOSE
, true);
2462 nDefaultResponse
= RET_CLOSE
;
2464 case VclButtonsType::Cancel
:
2465 pBtn
.set( VclPtr
<CancelButton
>::Create(pButtonBox
) );
2466 pBtn
->SetStyle(pBtn
->GetStyle() & WB_DEFBUTTON
);
2468 pBtn
->set_id(u
"cancel"_ustr
);
2469 add_button(pBtn
, RET_CANCEL
, true);
2470 nDefaultResponse
= RET_CANCEL
;
2472 case VclButtonsType::YesNo
:
2473 pBtn
= VclPtr
<PushButton
>::Create(pButtonBox
);
2474 pBtn
->SetText(GetStandardText(StandardButtonType::Yes
));
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
));
2482 pBtn
->set_id(u
"no"_ustr
);
2483 add_button(pBtn
, RET_NO
, true);
2484 nDefaultResponse
= RET_NO
;
2486 case VclButtonsType::OkCancel
:
2487 pBtn
.set( VclPtr
<OKButton
>::Create(pButtonBox
) );
2489 pBtn
->set_id(u
"ok"_ustr
);
2490 add_button(pBtn
, RET_OK
, true);
2492 pBtn
.set( VclPtr
<CancelButton
>::Create(pButtonBox
) );
2494 pBtn
->set_id(u
"cancel"_ustr
);
2495 add_button(pBtn
, RET_CANCEL
, true);
2496 nDefaultResponse
= RET_CANCEL
;
2499 set_default_response(nDefaultResponse
);
2500 sort_native_button_order(*pButtonBox
);
2501 m_pMessageBox
->Show();
2505 void MessageDialog::create_owned_areas()
2508 set_border_width(3);
2510 set_border_width(12);
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)
2527 , m_pMessageBox(nullptr)
2529 , m_pPrimaryMessage(nullptr)
2530 , m_pSecondaryMessage(nullptr)
2532 SetType(WindowType::MESSBOX
);
2535 MessageDialog::MessageDialog(vcl::Window
* pParent
,
2537 VclMessageType eMessageType
,
2538 VclButtonsType eButtonsType
)
2539 : Dialog(pParent
, WB_MOVEABLE
| WB_3DLOOK
| WB_CLOSEABLE
)
2540 , m_eButtonsType(eButtonsType
)
2541 , m_eMessageType(eMessageType
)
2543 , m_pMessageBox(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());
2558 case VclMessageType::Warning
:
2559 SetText(GetStandardWarningBoxText());
2561 case VclMessageType::Question
:
2562 SetText(GetStandardQueryBoxText());
2564 case VclMessageType::Error
:
2565 SetText(GetStandardErrorBoxText());
2566 SetTaskBarState(VclTaskBarStates::Error
);
2568 case VclMessageType::Other
:
2569 SetText(Application::GetDisplayName());
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();
2589 MessageDialog::~MessageDialog()
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);
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
)
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
;
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
;
2667 SAL_WARN("vcl.layout", "unknown buttons type mode" << rValue
);
2669 m_eButtonsType
= eMode
;
2672 return Dialog::set_property(rKey
, rValue
);
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
);
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
))
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
);
2750 for (vcl::Window
* pChild
= GetWindow(GetWindowType::FirstChild
); pChild
;
2751 pChild
= pChild
->GetWindow(GetWindowType::Next
))
2753 if (!pChild
->IsVisible())
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
);
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;
2800 for (const vcl::Window
* pChild
= GetWindow(GetWindowType::FirstChild
); pChild
;
2801 pChild
= pChild
->GetWindow(GetWindowType::Next
))
2803 if (!pChild
->IsVisible())
2807 if (bInitialAllocation
)
2808 nFirstHeight
= getLayoutRequisition(*pChild
).Height();
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();
2818 nSecondHeight
= pChild
->GetSizePixel().Height() + pChild
->get_margin_top() + pChild
->get_margin_bottom();
2819 bSecondCanResize
= pChild
->get_expand();
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
2836 for (const vcl::Window
* pChild
= GetWindow(GetWindowType::FirstChild
); pChild
;
2837 pChild
= pChild
->GetWindow(GetWindowType::Next
))
2839 if (!pChild
->IsVisible())
2841 Size aChildSize
= getLayoutRequisition(*pChild
);
2842 aRet
.setWidth( std::max(aRet
.Width(), aChildSize
.Width()) );
2843 aRet
.AdjustHeight(aChildSize
.Height() );
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());
2869 for (vcl::Window
* pChild
= GetWindow(GetWindowType::FirstChild
); pChild
;
2870 pChild
= pChild
->GetWindow(GetWindowType::Next
))
2872 if (!pChild
->IsVisible())
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
);
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;
2919 for (const vcl::Window
* pChild
= GetWindow(GetWindowType::FirstChild
); pChild
;
2920 pChild
= pChild
->GetWindow(GetWindowType::Next
))
2922 if (!pChild
->IsVisible())
2926 if (bInitialAllocation
)
2927 nFirstWidth
= getLayoutRequisition(*pChild
).Width();
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();
2937 nSecondWidth
= pChild
->GetSizePixel().Width() + pChild
->get_margin_start() + pChild
->get_margin_end();
2938 bSecondCanResize
= pChild
->get_expand();
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
2955 for (const vcl::Window
* pChild
= GetWindow(GetWindowType::FirstChild
); pChild
;
2956 pChild
= pChild
->GetWindow(GetWindowType::Next
))
2958 if (!pChild
->IsVisible())
2960 Size aChildSize
= getLayoutRequisition(*pChild
);
2961 aRet
.setHeight( std::max(aRet
.Height(), aChildSize
.Height()) );
2962 aRet
.AdjustWidth(aChildSize
.Width() );
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())
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 );
2993 vcl::Window
* getNonLayoutParent(vcl::Window
*pWindow
)
2997 pWindow
= pWindow
->GetParent();
2998 if (!pWindow
|| !isContainerWindow(*pWindow
))
3004 bool isVisibleInLayout(const vcl::Window
*pWindow
)
3006 bool bVisible
= true;
3009 bVisible
= pWindow
->IsVisible();
3010 pWindow
= pWindow
->GetParent();
3011 if (!pWindow
|| !isContainerWindow(*pWindow
))
3017 bool isEnabledInLayout(const vcl::Window
*pWindow
)
3019 bool bEnabled
= true;
3022 bEnabled
= pWindow
->IsEnabled();
3023 pWindow
= pWindow
->GetParent();
3024 if (!pWindow
|| !isContainerWindow(*pWindow
))
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
)))
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())
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))
3063 rtl::Reference
<TransferDataContainer
> xContainer
= m_xTransferHelper
;
3064 if (!m_xTransferHelper
.is())
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()
3108 VclScrolledWindow::~VclScrolledWindow()
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: */