/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include // Needed since LLVM 15 libc++ (hence the ignored -Wunused-macros for older libc++) when // #include below includes Boost 1.79.0 // workdir/UnpackedTarball/boost/boost/functional.hpp using std::unary_function, but must // come very early here in case is already (indirectly) included earlier: #include #if HAVE_LIBCPP #if defined __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-macros" #endif // [-loplugin:reservedid]: #define _LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION #if defined __clang__ #pragma clang diagnostic pop #endif #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include VclContainer::VclContainer(vcl::Window *pParent, WinBits nStyle) : Window(WindowType::CONTAINER) , m_bLayoutDirty(true) { ImplInit(pParent, nStyle, nullptr); EnableChildTransparentMode(); SetPaintTransparent(true); SetBackground(); } sal_uInt16 VclContainer::getDefaultAccessibleRole() const { return css::accessibility::AccessibleRole::PANEL; } Size VclContainer::GetOptimalSize() const { return calculateRequisition(); } void VclContainer::setLayoutPosSize(vcl::Window &rWindow, const Point &rPos, const Size &rSize) { sal_Int32 nBorderWidth = rWindow.get_border_width(); sal_Int32 nLeft = rWindow.get_margin_start() + nBorderWidth; sal_Int32 nTop = rWindow.get_margin_top() + nBorderWidth; sal_Int32 nRight = rWindow.get_margin_end() + nBorderWidth; sal_Int32 nBottom = rWindow.get_margin_bottom() + nBorderWidth; Point aPos(rPos.X() + nLeft, rPos.Y() + nTop); Size aSize(rSize.Width() - nLeft - nRight, rSize.Height() - nTop - nBottom); rWindow.SetPosSizePixel(aPos, aSize); } void VclContainer::setLayoutAllocation(vcl::Window &rChild, const Point &rAllocPos, const Size &rChildAlloc) { VclAlign eHalign = rChild.get_halign(); VclAlign eValign = rChild.get_valign(); //typical case if (eHalign == VclAlign::Fill && eValign == VclAlign::Fill) { setLayoutPosSize(rChild, rAllocPos, rChildAlloc); return; } Point aChildPos(rAllocPos); Size aChildSize(rChildAlloc); Size aChildPreferredSize(getLayoutRequisition(rChild)); switch (eHalign) { case VclAlign::Fill: break; case VclAlign::Start: if (aChildPreferredSize.Width() < rChildAlloc.Width()) aChildSize.setWidth( aChildPreferredSize.Width() ); break; case VclAlign::End: if (aChildPreferredSize.Width() < rChildAlloc.Width()) aChildSize.setWidth( aChildPreferredSize.Width() ); aChildPos.AdjustX(rChildAlloc.Width() ); aChildPos.AdjustX( -(aChildSize.Width()) ); break; case VclAlign::Center: if (aChildPreferredSize.Width() < aChildSize.Width()) aChildSize.setWidth( aChildPreferredSize.Width() ); aChildPos.AdjustX((rChildAlloc.Width() - aChildSize.Width()) / 2 ); break; } switch (eValign) { case VclAlign::Fill: break; case VclAlign::Start: if (aChildPreferredSize.Height() < rChildAlloc.Height()) aChildSize.setHeight( aChildPreferredSize.Height() ); break; case VclAlign::End: if (aChildPreferredSize.Height() < rChildAlloc.Height()) aChildSize.setHeight( aChildPreferredSize.Height() ); aChildPos.AdjustY(rChildAlloc.Height() ); aChildPos.AdjustY( -(aChildSize.Height()) ); break; case VclAlign::Center: if (aChildPreferredSize.Height() < aChildSize.Height()) aChildSize.setHeight( aChildPreferredSize.Height() ); aChildPos.AdjustY((rChildAlloc.Height() - aChildSize.Height()) / 2 ); break; } setLayoutPosSize(rChild, aChildPos, aChildSize); } namespace { Size subtractBorder(const vcl::Window &rWindow, const Size& rSize) { sal_Int32 nBorderWidth = rWindow.get_border_width(); sal_Int32 nLeft = rWindow.get_margin_start() + nBorderWidth; sal_Int32 nTop = rWindow.get_margin_top() + nBorderWidth; sal_Int32 nRight = rWindow.get_margin_end() + nBorderWidth; sal_Int32 nBottom = rWindow.get_margin_bottom() + nBorderWidth; Size aSize(rSize); return Size(aSize.Width() + nLeft + nRight, aSize.Height() + nTop + nBottom); } } Size VclContainer::getLayoutRequisition(const vcl::Window &rWindow) { return subtractBorder(rWindow, rWindow.get_preferred_size()); } void VclContainer::SetPosSizePixel(const Point& rAllocPos, const Size& rAllocation) { bool bSizeChanged = rAllocation != GetOutputSizePixel(); Window::SetPosSizePixel(rAllocPos, rAllocation); if (m_bLayoutDirty || bSizeChanged) { m_bLayoutDirty = false; setAllocation(rAllocation); } } void VclContainer::SetPosPixel(const Point& rAllocPos) { Point aAllocPos = rAllocPos; sal_Int32 nBorderWidth = get_border_width(); aAllocPos.AdjustX(nBorderWidth + get_margin_start() ); aAllocPos.AdjustY(nBorderWidth + get_margin_top() ); if (aAllocPos != GetPosPixel()) Window::SetPosPixel(aAllocPos); } void VclContainer::SetSizePixel(const Size& rAllocation) { Size aAllocation = rAllocation; sal_Int32 nBorderWidth = get_border_width(); aAllocation.AdjustWidth( -(nBorderWidth*2 + get_margin_start() + get_margin_end()) ); aAllocation.AdjustHeight( -(nBorderWidth*2 + get_margin_top() + get_margin_bottom()) ); bool bSizeChanged = aAllocation != GetSizePixel(); if (bSizeChanged) Window::SetSizePixel(aAllocation); if (m_bLayoutDirty || bSizeChanged) { m_bLayoutDirty = false; setAllocation(aAllocation); } } void VclContainer::queue_resize(StateChangedType eReason) { m_bLayoutDirty = true; Window::queue_resize(eReason); } // support for screenshot context menu void VclContainer::Command(const CommandEvent& rCEvt) { if (CommandEventId::ContextMenu == rCEvt.GetCommand()) { auto pParent = GetParent(); if (pParent) { CommandEvent aCEvt(rCEvt.GetMousePosPixel() + GetPosPixel(), rCEvt.GetCommand(), rCEvt.IsMouseEvent(), rCEvt.GetEventData()); pParent->Command(aCEvt); return; } } // call parent (do not consume) Window::Command(rCEvt); } void VclBox::accumulateMaxes(const Size &rChildSize, Size &rSize) const { tools::Long nSecondaryChildDimension = getSecondaryDimension(rChildSize); tools::Long nSecondaryBoxDimension = getSecondaryDimension(rSize); setSecondaryDimension(rSize, std::max(nSecondaryChildDimension, nSecondaryBoxDimension)); tools::Long nPrimaryChildDimension = getPrimaryDimension(rChildSize); tools::Long nPrimaryBoxDimension = getPrimaryDimension(rSize); if (m_bHomogeneous) setPrimaryDimension(rSize, std::max(nPrimaryBoxDimension, nPrimaryChildDimension)); else setPrimaryDimension(rSize, nPrimaryBoxDimension + nPrimaryChildDimension); } Size VclBox::calculateRequisition() const { sal_uInt16 nVisibleChildren = 0; Size aSize; for (vcl::Window *pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next)) { if (!pChild->IsVisible()) continue; ++nVisibleChildren; Size aChildSize = getLayoutRequisition(*pChild); tools::Long nPrimaryDimension = getPrimaryDimension(aChildSize); nPrimaryDimension += pChild->get_padding() * 2; setPrimaryDimension(aChildSize, nPrimaryDimension); accumulateMaxes(aChildSize, aSize); } return finalizeMaxes(aSize, nVisibleChildren); } void VclBox::setAllocation(const Size &rAllocation) { sal_uInt16 nVisibleChildren = 0, nExpandChildren = 0; for (vcl::Window *pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next)) { if (!pChild->IsVisible()) continue; ++nVisibleChildren; bool bExpand = getPrimaryDimensionChildExpand(*pChild); if (bExpand) ++nExpandChildren; } if (!nVisibleChildren) return; tools::Long nAllocPrimaryDimension = getPrimaryDimension(rAllocation); tools::Long nHomogeneousDimension = 0, nExtraSpace = 0; if (m_bHomogeneous) { nHomogeneousDimension = (nAllocPrimaryDimension - (nVisibleChildren - 1) * m_nSpacing) / nVisibleChildren; } else if (nExpandChildren) { Size aRequisition = calculateRequisition(); tools::Long nPrimaryDimension = getPrimaryDimension(rAllocation); nExtraSpace = (nPrimaryDimension - getPrimaryDimension(aRequisition)) / nExpandChildren; } //Split into those we pack from the start onwards, and those we pack from the end backwards o3tl::enumarray> aWindows; for (vcl::Window *pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next)) { if (!pChild->IsVisible()) continue; VclPackType ePacking = pChild->get_pack_type(); aWindows[ePacking].push_back(pChild); } //See VclBuilder::sortIntoBestTabTraversalOrder for why they are in visual //order under the parent which requires us to reverse them here to //pack from the end back std::reverse(aWindows[VclPackType::End].begin(),aWindows[VclPackType::End].end()); for (VclPackType ePackType : o3tl::enumrange()) { Point aPos(0, 0); if (ePackType == VclPackType::End) { tools::Long nPrimaryCoordinate = getPrimaryCoordinate(aPos); setPrimaryCoordinate(aPos, nPrimaryCoordinate + nAllocPrimaryDimension); } for (auto const& window : aWindows[ePackType]) { vcl::Window *pChild = window; tools::Long nPadding = pChild->get_padding(); Size aBoxSize; if (m_bHomogeneous) setPrimaryDimension(aBoxSize, nHomogeneousDimension); else { aBoxSize = getLayoutRequisition(*pChild); tools::Long nPrimaryDimension = getPrimaryDimension(aBoxSize); nPrimaryDimension += nPadding * 2; if (getPrimaryDimensionChildExpand(*pChild)) nPrimaryDimension += nExtraSpace; setPrimaryDimension(aBoxSize, nPrimaryDimension); } setSecondaryDimension(aBoxSize, getSecondaryDimension(rAllocation)); Point aChildPos(aPos); Size aChildSize(aBoxSize); tools::Long nPrimaryCoordinate = getPrimaryCoordinate(aPos); bool bFill = pChild->get_fill(); if (bFill) { setPrimaryDimension(aChildSize, std::max(static_cast(1), std::min(getPrimaryDimension(rAllocation), getPrimaryDimension(aBoxSize) - nPadding * 2))); setPrimaryCoordinate(aChildPos, nPrimaryCoordinate + nPadding); } else { setPrimaryDimension(aChildSize, getPrimaryDimension(getLayoutRequisition(*pChild))); setPrimaryCoordinate(aChildPos, nPrimaryCoordinate + (getPrimaryDimension(aBoxSize) - getPrimaryDimension(aChildSize)) / 2); } tools::Long nDiff = getPrimaryDimension(aBoxSize) + m_nSpacing; if (ePackType == VclPackType::Start) setPrimaryCoordinate(aPos, nPrimaryCoordinate + nDiff); else { setPrimaryCoordinate(aPos, nPrimaryCoordinate - nDiff); setPrimaryCoordinate(aChildPos, getPrimaryCoordinate(aChildPos) - getPrimaryDimension(aBoxSize)); } setLayoutAllocation(*pChild, aChildPos, aChildSize); } } } bool VclBox::set_property(const OUString &rKey, const OUString &rValue) { if (rKey == "spacing") set_spacing(rValue.toInt32()); else if (rKey == "homogeneous") set_homogeneous(toBool(rValue)); else return VclContainer::set_property(rKey, rValue); return true; } void VclBox::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter) { VclContainer::DumpAsPropertyTree(rJsonWriter); rJsonWriter.put("vertical", m_bVerticalContainer); } sal_uInt16 VclBox::getDefaultAccessibleRole() const { // fdo#74284 call Boxes Panels, keep them as "Filler" under // at least Linux seeing as that's what Gtk3 did for GtkBoxes. // Though now with Gtk4 that uses GTK_ACCESSIBLE_ROLE_GROUP // which maps to ATSPI_ROLE_PANEL #if defined(_WIN32) return css::accessibility::AccessibleRole::PANEL; #else static sal_uInt16 eRole = Application::GetToolkitName() == "gtk4" ? css::accessibility::AccessibleRole::PANEL : css::accessibility::AccessibleRole::FILLER; return eRole; #endif } #define DEFAULT_CHILD_MIN_WIDTH 85 #define DEFAULT_CHILD_MIN_HEIGHT 27 Size VclBox::finalizeMaxes(const Size &rSize, sal_uInt16 nVisibleChildren) const { Size aRet; if (nVisibleChildren) { tools::Long nPrimaryDimension = getPrimaryDimension(rSize); if (m_bHomogeneous) nPrimaryDimension *= nVisibleChildren; setPrimaryDimension(aRet, nPrimaryDimension + m_nSpacing * (nVisibleChildren-1)); setSecondaryDimension(aRet, getSecondaryDimension(rSize)); } return aRet; } Size VclButtonBox::addReqGroups(const VclButtonBox::Requisition &rReq) const { Size aRet; tools::Long nMainGroupDimension = getPrimaryDimension(rReq.m_aMainGroupSize); tools::Long nSubGroupDimension = getPrimaryDimension(rReq.m_aSubGroupSize); setPrimaryDimension(aRet, nMainGroupDimension + nSubGroupDimension); setSecondaryDimension(aRet, std::max(getSecondaryDimension(rReq.m_aMainGroupSize), getSecondaryDimension(rReq.m_aSubGroupSize))); return aRet; } static tools::Long getMaxNonOutlier(const std::vector &rG, tools::Long nAvgDimension) { tools::Long nMaxDimensionNonOutlier = 0; for (auto const& nPrimaryChildDimension : rG) { if (nPrimaryChildDimension < nAvgDimension * 1.5) { nMaxDimensionNonOutlier = std::max(nPrimaryChildDimension, nMaxDimensionNonOutlier); } } return nMaxDimensionNonOutlier; } static std::vector setButtonSizes(const std::vector &rG, const std::vector &rNonHomogeneous, tools::Long nAvgDimension, tools::Long nMaxNonOutlier, tools::Long nMinWidth) { std::vector aVec; //set everything < 1.5 times the average to the same width, leave the //outliers un-touched std::vector::const_iterator aJ = rNonHomogeneous.begin(); auto nNonOutlierWidth = std::max(nMaxNonOutlier, nMinWidth); for (auto const& nPrimaryChildDimension : rG) { bool bNonHomogeneous = *aJ; if (!bNonHomogeneous && nPrimaryChildDimension < nAvgDimension * 1.5) { aVec.push_back(nNonOutlierWidth); } else { aVec.push_back(std::max(nPrimaryChildDimension, nMinWidth)); } ++aJ; } return aVec; } VclButtonBox::Requisition VclButtonBox::calculatePrimarySecondaryRequisitions() const { Requisition aReq; Size aMainGroupSize(DEFAULT_CHILD_MIN_WIDTH, DEFAULT_CHILD_MIN_HEIGHT); //to-do, pull from theme Size aSubGroupSize(DEFAULT_CHILD_MIN_WIDTH, DEFAULT_CHILD_MIN_HEIGHT); //to-do, pull from theme tools::Long nMinMainGroupPrimary = getPrimaryDimension(aMainGroupSize); tools::Long nMinSubGroupPrimary = getPrimaryDimension(aSubGroupSize); tools::Long nMainGroupSecondary = getSecondaryDimension(aMainGroupSize); tools::Long nSubGroupSecondary = getSecondaryDimension(aSubGroupSize); bool bIgnoreSecondaryPacking = (m_eLayoutStyle == VclButtonBoxStyle::Spread || m_eLayoutStyle == VclButtonBoxStyle::Center); std::vector aMainGroupSizes; std::vector aMainGroupNonHomogeneous; std::vector aSubGroupSizes; std::vector aSubGroupNonHomogeneous; for (const vcl::Window *pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next)) { if (!pChild->IsVisible()) continue; Size aChildSize = getLayoutRequisition(*pChild); if (bIgnoreSecondaryPacking || !pChild->get_secondary()) { //set the max secondary dimension nMainGroupSecondary = std::max(nMainGroupSecondary, getSecondaryDimension(aChildSize)); //collect the primary dimensions aMainGroupSizes.push_back(getPrimaryDimension(aChildSize)); aMainGroupNonHomogeneous.push_back(pChild->get_non_homogeneous()); } else { nSubGroupSecondary = std::max(nSubGroupSecondary, getSecondaryDimension(aChildSize)); aSubGroupSizes.push_back(getPrimaryDimension(aChildSize)); aSubGroupNonHomogeneous.push_back(pChild->get_non_homogeneous()); } } if (m_bHomogeneous) { tools::Long nMaxMainDimension = aMainGroupSizes.empty() ? 0 : *std::max_element(aMainGroupSizes.begin(), aMainGroupSizes.end()); nMaxMainDimension = std::max(nMaxMainDimension, nMinMainGroupPrimary); tools::Long nMaxSubDimension = aSubGroupSizes.empty() ? 0 : *std::max_element(aSubGroupSizes.begin(), aSubGroupSizes.end()); nMaxSubDimension = std::max(nMaxSubDimension, nMinSubGroupPrimary); tools::Long nMaxDimension = std::max(nMaxMainDimension, nMaxSubDimension); aReq.m_aMainGroupDimensions.resize(aMainGroupSizes.size(), nMaxDimension); aReq.m_aSubGroupDimensions.resize(aSubGroupSizes.size(), nMaxDimension); } else { //Ideally set everything to the same size, but find outlier widgets //that are way wider than the average and leave them //at their natural size and set the remainder to share the //max size of the remaining members of the buttonbox tools::Long nAccDimension = std::accumulate(aMainGroupSizes.begin(), aMainGroupSizes.end(), 0); nAccDimension = std::accumulate(aSubGroupSizes.begin(), aSubGroupSizes.end(), nAccDimension); size_t nTotalSize = aMainGroupSizes.size() + aSubGroupSizes.size(); tools::Long nAvgDimension = nTotalSize ? nAccDimension / nTotalSize : 0; tools::Long nMaxMainNonOutlier = getMaxNonOutlier(aMainGroupSizes, nAvgDimension); tools::Long nMaxSubNonOutlier = getMaxNonOutlier(aSubGroupSizes, nAvgDimension); tools::Long nMaxNonOutlier = std::max(nMaxMainNonOutlier, nMaxSubNonOutlier); aReq.m_aMainGroupDimensions = setButtonSizes(aMainGroupSizes, aMainGroupNonHomogeneous, nAvgDimension, nMaxNonOutlier, nMinMainGroupPrimary); aReq.m_aSubGroupDimensions = setButtonSizes(aSubGroupSizes, aSubGroupNonHomogeneous, nAvgDimension, nMaxNonOutlier, nMinSubGroupPrimary); } if (!aReq.m_aMainGroupDimensions.empty()) { setSecondaryDimension(aReq.m_aMainGroupSize, nMainGroupSecondary); setPrimaryDimension(aReq.m_aMainGroupSize, std::accumulate(aReq.m_aMainGroupDimensions.begin(), aReq.m_aMainGroupDimensions.end(), 0)); } if (!aReq.m_aSubGroupDimensions.empty()) { setSecondaryDimension(aReq.m_aSubGroupSize, nSubGroupSecondary); setPrimaryDimension(aReq.m_aSubGroupSize, std::accumulate(aReq.m_aSubGroupDimensions.begin(), aReq.m_aSubGroupDimensions.end(), 0)); } return aReq; } Size VclButtonBox::addSpacing(const Size &rSize, sal_uInt16 nVisibleChildren) const { Size aRet; if (nVisibleChildren) { tools::Long nPrimaryDimension = getPrimaryDimension(rSize); setPrimaryDimension(aRet, nPrimaryDimension + m_nSpacing * (nVisibleChildren-1)); setSecondaryDimension(aRet, getSecondaryDimension(rSize)); } return aRet; } Size VclButtonBox::calculateRequisition() const { Requisition aReq(calculatePrimarySecondaryRequisitions()); sal_uInt16 nVisibleChildren = aReq.m_aMainGroupDimensions.size() + aReq.m_aSubGroupDimensions.size(); return addSpacing(addReqGroups(aReq), nVisibleChildren); } bool VclButtonBox::set_property(const OUString &rKey, const OUString &rValue) { if (rKey == "layout-style") { VclButtonBoxStyle eStyle = VclButtonBoxStyle::Default; if (rValue == "spread") eStyle = VclButtonBoxStyle::Spread; else if (rValue == "edge") eStyle = VclButtonBoxStyle::Edge; else if (rValue == "start") eStyle = VclButtonBoxStyle::Start; else if (rValue == "end") eStyle = VclButtonBoxStyle::End; else if (rValue == "center") eStyle = VclButtonBoxStyle::Center; else { SAL_WARN("vcl.layout", "unknown layout style " << rValue); } m_eLayoutStyle = eStyle; } else return VclBox::set_property(rKey, rValue); return true; } void VclButtonBox::setAllocation(const Size &rAllocation) { Requisition aReq(calculatePrimarySecondaryRequisitions()); if (aReq.m_aMainGroupDimensions.empty() && aReq.m_aSubGroupDimensions.empty()) return; tools::Long nAllocPrimaryDimension = getPrimaryDimension(rAllocation); Point aMainGroupPos, aOtherGroupPos; int nSpacing = m_nSpacing; //To-Do, other layout styles switch (m_eLayoutStyle) { case VclButtonBoxStyle::Start: if (!aReq.m_aSubGroupDimensions.empty()) { tools::Long nOtherPrimaryDimension = getPrimaryDimension( addSpacing(aReq.m_aSubGroupSize, aReq.m_aSubGroupDimensions.size())); setPrimaryCoordinate(aOtherGroupPos, nAllocPrimaryDimension - nOtherPrimaryDimension); } break; case VclButtonBoxStyle::Spread: if (!aReq.m_aMainGroupDimensions.empty()) { tools::Long nMainPrimaryDimension = getPrimaryDimension( addSpacing(aReq.m_aMainGroupSize, aReq.m_aMainGroupDimensions.size())); tools::Long nExtraSpace = nAllocPrimaryDimension - nMainPrimaryDimension; nExtraSpace += (aReq.m_aMainGroupDimensions.size()-1) * nSpacing; nSpacing = nExtraSpace/(aReq.m_aMainGroupDimensions.size()+1); setPrimaryCoordinate(aMainGroupPos, nSpacing); } break; case VclButtonBoxStyle::Center: if (!aReq.m_aMainGroupDimensions.empty()) { tools::Long nMainPrimaryDimension = getPrimaryDimension( addSpacing(aReq.m_aMainGroupSize, aReq.m_aMainGroupDimensions.size())); tools::Long nExtraSpace = nAllocPrimaryDimension - nMainPrimaryDimension; setPrimaryCoordinate(aMainGroupPos, nExtraSpace/2); } break; default: SAL_WARN("vcl.layout", "todo unimplemented layout style"); [[fallthrough]]; case VclButtonBoxStyle::Default: case VclButtonBoxStyle::End: if (!aReq.m_aMainGroupDimensions.empty()) { tools::Long nMainPrimaryDimension = getPrimaryDimension( addSpacing(aReq.m_aMainGroupSize, aReq.m_aMainGroupDimensions.size())); setPrimaryCoordinate(aMainGroupPos, nAllocPrimaryDimension - nMainPrimaryDimension); } break; } Size aChildSize; setSecondaryDimension(aChildSize, getSecondaryDimension(rAllocation)); std::vector::const_iterator aPrimaryI = aReq.m_aMainGroupDimensions.begin(); std::vector::const_iterator aSecondaryI = aReq.m_aSubGroupDimensions.begin(); bool bIgnoreSecondaryPacking = (m_eLayoutStyle == VclButtonBoxStyle::Spread || m_eLayoutStyle == VclButtonBoxStyle::Center); for (vcl::Window *pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next)) { if (!pChild->IsVisible()) continue; if (bIgnoreSecondaryPacking || !pChild->get_secondary()) { tools::Long nMainGroupPrimaryDimension = *aPrimaryI++; setPrimaryDimension(aChildSize, nMainGroupPrimaryDimension); setLayoutAllocation(*pChild, aMainGroupPos, aChildSize); tools::Long nPrimaryCoordinate = getPrimaryCoordinate(aMainGroupPos); setPrimaryCoordinate(aMainGroupPos, nPrimaryCoordinate + nMainGroupPrimaryDimension + nSpacing); } else { tools::Long nSubGroupPrimaryDimension = *aSecondaryI++; setPrimaryDimension(aChildSize, nSubGroupPrimaryDimension); setLayoutAllocation(*pChild, aOtherGroupPos, aChildSize); tools::Long nPrimaryCoordinate = getPrimaryCoordinate(aOtherGroupPos); setPrimaryCoordinate(aOtherGroupPos, nPrimaryCoordinate + nSubGroupPrimaryDimension + nSpacing); } } } void VclButtonBox::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter) { VclBox::DumpAsPropertyTree(rJsonWriter); rJsonWriter.put("type", "buttonbox"); switch(m_eLayoutStyle) { case VclButtonBoxStyle::Default: rJsonWriter.put("layoutstyle", "default"); break; case VclButtonBoxStyle::Spread: rJsonWriter.put("layoutstyle", "spread"); break; case VclButtonBoxStyle::Edge: rJsonWriter.put("layoutstyle", "edge"); break; case VclButtonBoxStyle::Center: rJsonWriter.put("layoutstyle", "center"); break; case VclButtonBoxStyle::Start: rJsonWriter.put("layoutstyle", "start"); break; case VclButtonBoxStyle::End: rJsonWriter.put("layoutstyle", "end"); break; } } namespace { struct ButtonOrder { std::u16string_view m_aType; int m_nPriority; }; } static int getButtonPriority(std::u16string_view rType) { static const size_t N_TYPES = 6; static const ButtonOrder aDiscardCancelSave[N_TYPES] = { { u"discard", 0 }, { u"cancel", 1 }, { u"no", 2 }, { u"save", 3 }, { u"yes", 3 }, { u"ok", 3 } }; static const ButtonOrder aSaveDiscardCancel[N_TYPES] = { { u"save", 0 }, { u"yes", 0 }, { u"ok", 0 }, { u"discard", 1 }, { u"no", 1 }, { u"cancel", 2 } }; const ButtonOrder* pOrder = &aDiscardCancelSave[0]; const OUString &rEnv = Application::GetDesktopEnvironment(); if (rEnv.equalsIgnoreAsciiCase("windows") || rEnv.equalsIgnoreAsciiCase("lxqt") || rEnv.startsWithIgnoreAsciiCase("plasma")) { pOrder = &aSaveDiscardCancel[0]; } for (size_t i = 0; i < N_TYPES; ++i, ++pOrder) { if (rType == pOrder->m_aType) return pOrder->m_nPriority; } return -1; } namespace { class sortButtons { bool m_bVerticalContainer; public: explicit sortButtons(bool bVerticalContainer) : m_bVerticalContainer(bVerticalContainer) { } bool operator()(const vcl::Window *pA, const vcl::Window *pB) const; }; } bool sortButtons::operator()(const vcl::Window *pA, const vcl::Window *pB) const { //sort into two groups of pack start and pack end VclPackType ePackA = pA->get_pack_type(); VclPackType ePackB = pB->get_pack_type(); if (ePackA < ePackB) return true; if (ePackA > ePackB) return false; bool bPackA = pA->get_secondary(); bool bPackB = pB->get_secondary(); if (!m_bVerticalContainer) { //for horizontal boxes group secondaries before primaries if (bPackA > bPackB) return true; if (bPackA < bPackB) return false; } else { //for vertical boxes group secondaries after primaries if (bPackA < bPackB) return true; if (bPackA > bPackB) return false; } //now order within groups according to platform rules return getButtonPriority(pA->get_id()) < getButtonPriority(pB->get_id()); } void sort_native_button_order(const VclBox& rContainer) { std::vector aChilds; for (vcl::Window* pChild = rContainer.GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next)) { aChilds.push_back(pChild); } //sort child order within parent so that we match the platform //button order std::stable_sort(aChilds.begin(), aChilds.end(), sortButtons(rContainer.get_orientation())); BuilderUtils::reorderWithinParent(aChilds, true); } namespace { struct GridEntry { VclPtr pChild; sal_Int32 nSpanWidth; sal_Int32 nSpanHeight; int x; int y; GridEntry() : pChild(nullptr) , nSpanWidth(0) , nSpanHeight(0) , x(-1) , y(-1) { } }; } typedef boost::multi_array array_type; #if defined _MSC_VER #pragma warning(push) #pragma warning(disable : 4459) #endif static array_type assembleGrid(const VclGrid &rGrid) { #if defined _MSC_VER #pragma warning(pop) #endif array_type A; for (vcl::Window* pChild = rGrid.GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next)) { sal_Int32 nLeftAttach = std::max(pChild->get_grid_left_attach(), 0); sal_Int32 nWidth = pChild->get_grid_width(); sal_Int32 nMaxXPos = nLeftAttach+nWidth-1; sal_Int32 nTopAttach = std::max(pChild->get_grid_top_attach(), 0); sal_Int32 nHeight = pChild->get_grid_height(); sal_Int32 nMaxYPos = nTopAttach+nHeight-1; sal_Int32 nCurrentMaxXPos = A.shape()[0]-1; sal_Int32 nCurrentMaxYPos = A.shape()[1]-1; if (nMaxXPos > nCurrentMaxXPos || nMaxYPos > nCurrentMaxYPos) { nCurrentMaxXPos = std::max(nMaxXPos, nCurrentMaxXPos); nCurrentMaxYPos = std::max(nMaxYPos, nCurrentMaxYPos); A.resize(boost::extents[nCurrentMaxXPos+1][nCurrentMaxYPos+1]); } #if defined _MSC_VER #pragma warning(push) #pragma warning(disable : 4459) #endif GridEntry &rEntry = A[nLeftAttach][nTopAttach]; #if defined _MSC_VER #pragma warning(pop) #endif rEntry.pChild = pChild; rEntry.nSpanWidth = nWidth; rEntry.nSpanHeight = nHeight; rEntry.x = nLeftAttach; rEntry.y = nTopAttach; for (sal_Int32 nSpanX = 0; nSpanX < nWidth; ++nSpanX) { for (sal_Int32 nSpanY = 0; nSpanY < nHeight; ++nSpanY) { GridEntry &rSpan = A[nLeftAttach+nSpanX][nTopAttach+nSpanY]; rSpan.x = nLeftAttach; rSpan.y = nTopAttach; } } } //see if we have any empty rows/cols sal_Int32 nMaxX = A.shape()[0]; sal_Int32 nMaxY = A.shape()[1]; std::vector aNonEmptyCols(nMaxX); std::vector aNonEmptyRows(nMaxY); for (sal_Int32 x = 0; x < nMaxX; ++x) { for (sal_Int32 y = 0; y < nMaxY; ++y) { #if defined __GNUC__ && !defined __clang__ && __GNUC__ >= 13 && __GNUC__ <= 15 #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdangling-reference" #endif const GridEntry &rEntry = A[x][y]; #if defined __GNUC__ && !defined __clang__ && __GNUC__ >= 13 && __GNUC__ <= 15 #pragma GCC diagnostic pop #endif const vcl::Window *pChild = rEntry.pChild; if (pChild && pChild->IsVisible()) { aNonEmptyCols[x] = true; if (rGrid.get_column_homogeneous()) { for (sal_Int32 nSpanX = 1; nSpanX < rEntry.nSpanWidth; ++nSpanX) aNonEmptyCols[x+nSpanX] = true; } aNonEmptyRows[y] = true; if (rGrid.get_row_homogeneous()) { for (sal_Int32 nSpanY = 1; nSpanY < rEntry.nSpanHeight; ++nSpanY) aNonEmptyRows[y+nSpanY] = true; } } } } if (!rGrid.get_column_homogeneous()) { //reduce the spans of elements that span empty columns for (sal_Int32 x = 0; x < nMaxX; ++x) { std::set candidates; for (sal_Int32 y = 0; y < nMaxY; ++y) { if (aNonEmptyCols[x]) continue; GridEntry &rSpan = A[x][y]; //cell x/y is spanned by the widget at cell rSpan.x/rSpan.y, //just points back to itself if there's no cell spanning if ((rSpan.x == -1) || (rSpan.y == -1)) { //there is no entry for this cell, i.e. this is a cell //with no widget in it, or spanned by any other widget continue; } GridEntry &rEntry = A[rSpan.x][rSpan.y]; candidates.insert(&rEntry); } for (auto const& candidate : candidates) { GridEntry *pEntry = candidate; --pEntry->nSpanWidth; } } } if (!rGrid.get_row_homogeneous()) { //reduce the spans of elements that span empty rows for (sal_Int32 y = 0; y < nMaxY; ++y) { std::set candidates; for (sal_Int32 x = 0; x < nMaxX; ++x) { if (aNonEmptyRows[y]) continue; GridEntry &rSpan = A[x][y]; //cell x/y is spanned by the widget at cell rSpan.x/rSpan.y, //just points back to itself if there's no cell spanning if ((rSpan.x == -1) || (rSpan.y == -1)) { //there is no entry for this cell, i.e. this is a cell //with no widget in it, or spanned by any other widget continue; } GridEntry &rEntry = A[rSpan.x][rSpan.y]; candidates.insert(&rEntry); } for (auto const& candidate : candidates) { GridEntry *pEntry = candidate; --pEntry->nSpanHeight; } } } sal_Int32 nNonEmptyCols = std::count(aNonEmptyCols.begin(), aNonEmptyCols.end(), true); sal_Int32 nNonEmptyRows = std::count(aNonEmptyRows.begin(), aNonEmptyRows.end(), true); //make new grid without empty rows and columns #if defined _MSC_VER #pragma warning(push) #pragma warning(disable : 4459) #endif array_type B(boost::extents[nNonEmptyCols][nNonEmptyRows]); #if defined _MSC_VER #pragma warning(pop) #endif for (sal_Int32 x = 0, x2 = 0; x < nMaxX; ++x) { if (!aNonEmptyCols[x]) continue; for (sal_Int32 y = 0, y2 = 0; y < nMaxY; ++y) { if (!aNonEmptyRows[y]) continue; GridEntry &rEntry = A[x][y]; B[x2][y2++] = rEntry; } ++x2; } return B; } static bool isNullGrid(const array_type &A) { sal_Int32 nMaxX = A.shape()[0]; sal_Int32 nMaxY = A.shape()[1]; return !nMaxX || !nMaxY; } static void calcMaxs(const array_type &A, std::vector &rWidths, std::vector &rHeights) { sal_Int32 nMaxX = A.shape()[0]; sal_Int32 nMaxY = A.shape()[1]; rWidths.resize(nMaxX); rHeights.resize(nMaxY); //first use the non spanning entries to set default width/heights for (sal_Int32 x = 0; x < nMaxX; ++x) { for (sal_Int32 y = 0; y < nMaxY; ++y) { #if defined __GNUC__ && !defined __clang__ && __GNUC__ >= 13 && __GNUC__ <= 15 #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdangling-reference" #elif defined _MSC_VER #pragma warning(push) #pragma warning(disable : 4459) #endif const GridEntry &rEntry = A[x][y]; #if defined __GNUC__ && !defined __clang__ && __GNUC__ >= 13 && __GNUC__ <= 15 #pragma GCC diagnostic pop #elif defined _MSC_VER #pragma warning(pop) #endif const vcl::Window *pChild = rEntry.pChild; if (!pChild || !pChild->IsVisible()) continue; sal_Int32 nWidth = rEntry.nSpanWidth; sal_Int32 nHeight = rEntry.nSpanHeight; for (sal_Int32 nSpanX = 0; nSpanX < nWidth; ++nSpanX) rWidths[x+nSpanX].m_bExpand |= pChild->get_hexpand(); for (sal_Int32 nSpanY = 0; nSpanY < nHeight; ++nSpanY) rHeights[y+nSpanY].m_bExpand |= pChild->get_vexpand(); if (nWidth == 1 || nHeight == 1) { Size aChildSize = VclContainer::getLayoutRequisition(*pChild); if (nWidth == 1) rWidths[x].m_nValue = std::max(rWidths[x].m_nValue, aChildSize.Width()); if (nHeight == 1) rHeights[y].m_nValue = std::max(rHeights[y].m_nValue, aChildSize.Height()); } } } //now use the spanning entries and split any extra sizes across expanding rows/cols //where possible for (sal_Int32 x = 0; x < nMaxX; ++x) { for (sal_Int32 y = 0; y < nMaxY; ++y) { #if defined __GNUC__ && !defined __clang__ && __GNUC__ >= 13 && __GNUC__ <= 15 #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdangling-reference" #endif const GridEntry &rEntry = A[x][y]; #if defined __GNUC__ && !defined __clang__ && __GNUC__ >= 13 && __GNUC__ <= 15 #pragma GCC diagnostic pop #endif const vcl::Window *pChild = rEntry.pChild; if (!pChild || !pChild->IsVisible()) continue; sal_Int32 nWidth = rEntry.nSpanWidth; sal_Int32 nHeight = rEntry.nSpanHeight; if (nWidth == 1 && nHeight == 1) continue; Size aChildSize = VclContainer::getLayoutRequisition(*pChild); if (nWidth > 1) { sal_Int32 nExistingWidth = 0; for (sal_Int32 nSpanX = 0; nSpanX < nWidth; ++nSpanX) nExistingWidth += rWidths[x+nSpanX].m_nValue; sal_Int32 nExtraWidth = aChildSize.Width() - nExistingWidth; if (nExtraWidth > 0) { bool bForceExpandAll = false; sal_Int32 nExpandables = 0; for (sal_Int32 nSpanX = 0; nSpanX < nWidth; ++nSpanX) if (rWidths[x+nSpanX].m_bExpand) ++nExpandables; if (nExpandables == 0) { nExpandables = nWidth; bForceExpandAll = true; } for (sal_Int32 nSpanX = 0; nSpanX < nWidth; ++nSpanX) { if (rWidths[x+nSpanX].m_bExpand || bForceExpandAll) rWidths[x+nSpanX].m_nValue += nExtraWidth/nExpandables; } } } if (nHeight > 1) { sal_Int32 nExistingHeight = 0; for (sal_Int32 nSpanY = 0; nSpanY < nHeight; ++nSpanY) nExistingHeight += rHeights[y+nSpanY].m_nValue; sal_Int32 nExtraHeight = aChildSize.Height() - nExistingHeight; if (nExtraHeight > 0) { bool bForceExpandAll = false; sal_Int32 nExpandables = 0; for (sal_Int32 nSpanY = 0; nSpanY < nHeight; ++nSpanY) if (rHeights[y+nSpanY].m_bExpand) ++nExpandables; if (nExpandables == 0) { nExpandables = nHeight; bForceExpandAll = true; } for (sal_Int32 nSpanY = 0; nSpanY < nHeight; ++nSpanY) { if (rHeights[y+nSpanY].m_bExpand || bForceExpandAll) rHeights[y+nSpanY].m_nValue += nExtraHeight/nExpandables; } } } } } } static bool compareValues(const VclGrid::Value &i, const VclGrid::Value &j) { return i.m_nValue < j.m_nValue; } static VclGrid::Value accumulateValues(const VclGrid::Value &i, const VclGrid::Value &j) { VclGrid::Value aRet; aRet.m_nValue = i.m_nValue + j.m_nValue; aRet.m_bExpand = i.m_bExpand || j.m_bExpand; return aRet; } Size VclGrid::calculateRequisition() const { return calculateRequisitionForSpacings(get_row_spacing(), get_column_spacing()); } Size VclGrid::calculateRequisitionForSpacings(sal_Int32 nRowSpacing, sal_Int32 nColSpacing) const { array_type A = assembleGrid(*this); if (isNullGrid(A)) return Size(); std::vector aWidths; std::vector aHeights; calcMaxs(A, aWidths, aHeights); tools::Long nTotalWidth = 0; if (get_column_homogeneous()) { nTotalWidth = std::max_element(aWidths.begin(), aWidths.end(), compareValues)->m_nValue; nTotalWidth *= aWidths.size(); } else { nTotalWidth = std::accumulate(aWidths.begin(), aWidths.end(), Value(), accumulateValues).m_nValue; } nTotalWidth += nColSpacing * (aWidths.size()-1); tools::Long nTotalHeight = 0; if (get_row_homogeneous()) { nTotalHeight = std::max_element(aHeights.begin(), aHeights.end(), compareValues)->m_nValue; nTotalHeight *= aHeights.size(); } else { nTotalHeight = std::accumulate(aHeights.begin(), aHeights.end(), Value(), accumulateValues).m_nValue; } nTotalHeight += nRowSpacing * (aHeights.size()-1); return Size(nTotalWidth, nTotalHeight); } void VclGrid::setAllocation(const Size& rAllocation) { array_type A = assembleGrid(*this); if (isNullGrid(A)) return; sal_Int32 nMaxX = A.shape()[0]; sal_Int32 nMaxY = A.shape()[1]; Size aRequisition; std::vector aWidths(nMaxX); std::vector aHeights(nMaxY); if (!get_column_homogeneous() || !get_row_homogeneous()) { aRequisition = calculateRequisition(); calcMaxs(A, aWidths, aHeights); } sal_Int32 nColSpacing(get_column_spacing()); sal_Int32 nRowSpacing(get_row_spacing()); tools::Long nAvailableWidth = rAllocation.Width(); if (nMaxX) nAvailableWidth -= nColSpacing * (nMaxX - 1); if (get_column_homogeneous()) { for (sal_Int32 x = 0; x < nMaxX; ++x) aWidths[x].m_nValue = nAvailableWidth/nMaxX; } else if (rAllocation.Width() != aRequisition.Width()) { sal_Int32 nExpandables = 0; for (sal_Int32 x = 0; x < nMaxX; ++x) if (aWidths[x].m_bExpand) ++nExpandables; tools::Long nExtraWidthForExpanders = nExpandables ? (rAllocation.Width() - aRequisition.Width()) / nExpandables : 0; //We don't fit and there is no volunteer to be shrunk if (!nExpandables && rAllocation.Width() < aRequisition.Width()) { //first reduce spacing while (nColSpacing) { nColSpacing /= 2; aRequisition = calculateRequisitionForSpacings(nRowSpacing, nColSpacing); if (aRequisition.Width() <= rAllocation.Width()) break; } //share out the remaining pain to everyone tools::Long nExtraWidth = (rAllocation.Width() - aRequisition.Width()) / nMaxX; for (sal_Int32 x = 0; x < nMaxX; ++x) aWidths[x].m_nValue += nExtraWidth; } if (nExtraWidthForExpanders) { for (sal_Int32 x = 0; x < nMaxX; ++x) if (aWidths[x].m_bExpand) aWidths[x].m_nValue += nExtraWidthForExpanders; } } tools::Long nAvailableHeight = rAllocation.Height(); if (nMaxY) nAvailableHeight -= nRowSpacing * (nMaxY - 1); if (get_row_homogeneous()) { for (sal_Int32 y = 0; y < nMaxY; ++y) aHeights[y].m_nValue = nAvailableHeight/nMaxY; } else if (rAllocation.Height() != aRequisition.Height()) { sal_Int32 nExpandables = 0; for (sal_Int32 y = 0; y < nMaxY; ++y) if (aHeights[y].m_bExpand) ++nExpandables; tools::Long nExtraHeightForExpanders = nExpandables ? (rAllocation.Height() - aRequisition.Height()) / nExpandables : 0; //We don't fit and there is no volunteer to be shrunk if (!nExpandables && rAllocation.Height() < aRequisition.Height()) { //first reduce spacing while (nRowSpacing) { nRowSpacing /= 2; aRequisition = calculateRequisitionForSpacings(nRowSpacing, nColSpacing); if (aRequisition.Height() <= rAllocation.Height()) break; } //share out the remaining pain to everyone tools::Long nExtraHeight = (rAllocation.Height() - aRequisition.Height()) / nMaxY; for (sal_Int32 y = 0; y < nMaxY; ++y) aHeights[y].m_nValue += nExtraHeight; } if (nExtraHeightForExpanders) { for (sal_Int32 y = 0; y < nMaxY; ++y) if (aHeights[y].m_bExpand) aHeights[y].m_nValue += nExtraHeightForExpanders; } } Point aAllocPos(0, 0); for (sal_Int32 x = 0; x < nMaxX; ++x) { for (sal_Int32 y = 0; y < nMaxY; ++y) { #if defined __GNUC__ && !defined __clang__ && __GNUC__ == 13 #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdangling-reference" #endif GridEntry &rEntry = A[x][y]; #if defined __GNUC__ && !defined __clang__ && __GNUC__ == 13 #pragma GCC diagnostic pop #endif vcl::Window *pChild = rEntry.pChild; if (pChild) { Size aChildAlloc(0, 0); sal_Int32 nWidth = rEntry.nSpanWidth; for (sal_Int32 nSpanX = 0; nSpanX < nWidth; ++nSpanX) aChildAlloc.AdjustWidth(aWidths[x+nSpanX].m_nValue ); aChildAlloc.AdjustWidth(nColSpacing*(nWidth-1) ); sal_Int32 nHeight = rEntry.nSpanHeight; for (sal_Int32 nSpanY = 0; nSpanY < nHeight; ++nSpanY) aChildAlloc.AdjustHeight(aHeights[y+nSpanY].m_nValue ); aChildAlloc.AdjustHeight(nRowSpacing*(nHeight-1) ); setLayoutAllocation(*pChild, aAllocPos, aChildAlloc); } aAllocPos.AdjustY(aHeights[y].m_nValue + nRowSpacing ); } aAllocPos.AdjustX(aWidths[x].m_nValue + nColSpacing ); aAllocPos.setY( 0 ); } } void VclGrid::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter) { VclContainer::DumpAsPropertyTree(rJsonWriter); rJsonWriter.put("type", "grid"); } bool VclGrid::set_property(const OUString &rKey, const OUString &rValue) { if (rKey == "row-spacing") set_row_spacing(rValue.toInt32()); else if (rKey == "column-spacing") set_column_spacing(rValue.toInt32()); else if (rKey == "row-homogeneous") m_bRowHomogeneous = toBool(rValue); else if (rKey == "column-homogeneous") m_bColumnHomogeneous = toBool(rValue); else if (rKey == "n-rows") /*nothing to do*/; else return VclContainer::set_property(rKey, rValue); return true; } const vcl::Window *VclBin::get_child() const { const WindowImpl* pWindowImpl = ImplGetWindowImpl(); return pWindowImpl->mpFirstChild; } vcl::Window *VclBin::get_child() { return const_cast(const_cast(this)->get_child()); } Size VclBin::calculateRequisition() const { const vcl::Window *pChild = get_child(); if (pChild && pChild->IsVisible()) return getLayoutRequisition(*pChild); return Size(0, 0); } void VclBin::setAllocation(const Size &rAllocation) { vcl::Window *pChild = get_child(); if (pChild && pChild->IsVisible()) setLayoutAllocation(*pChild, Point(0, 0), rAllocation); } VclFrame::~VclFrame() { disposeOnce(); } void VclFrame::dispose() { m_pLabel.clear(); VclBin::dispose(); } //To-Do, hook a DecorationView into VclFrame ? Size VclFrame::calculateRequisition() const { Size aRet(0, 0); const vcl::Window *pChild = get_child(); const vcl::Window *pLabel = get_label_widget(); if (pChild && pChild->IsVisible()) aRet = getLayoutRequisition(*pChild); if (pLabel && pLabel->IsVisible()) { Size aLabelSize = getLayoutRequisition(*pLabel); aRet.AdjustHeight(aLabelSize.Height() ); aRet.setWidth( std::max(aLabelSize.Width(), aRet.Width()) ); } return aRet; } void VclFrame::setAllocation(const Size &rAllocation) { //SetBackground( Color(0xFF, 0x00, 0xFF) ); Size aAllocation(rAllocation); Point aChildPos; vcl::Window *pChild = get_child(); vcl::Window *pLabel = get_label_widget(); if (pLabel && pLabel->IsVisible()) { Size aLabelSize = getLayoutRequisition(*pLabel); aLabelSize.setHeight( std::min(aLabelSize.Height(), aAllocation.Height()) ); aLabelSize.setWidth( std::min(aLabelSize.Width(), aAllocation.Width()) ); setLayoutAllocation(*pLabel, aChildPos, aLabelSize); aAllocation.AdjustHeight( -(aLabelSize.Height()) ); aChildPos.AdjustY(aLabelSize.Height() ); } if (pChild && pChild->IsVisible()) setLayoutAllocation(*pChild, aChildPos, aAllocation); } IMPL_LINK(VclFrame, WindowEventListener, VclWindowEvent&, rEvent, void) { if (rEvent.GetId() == VclEventId::ObjectDying) designate_label(nullptr); } void VclFrame::designate_label(vcl::Window *pWindow) { assert(!pWindow || pWindow->GetParent() == this); if (m_pLabel) m_pLabel->RemoveEventListener(LINK(this, VclFrame, WindowEventListener)); m_pLabel = pWindow; if (m_pLabel) m_pLabel->AddEventListener(LINK(this, VclFrame, WindowEventListener)); } const vcl::Window *VclFrame::get_label_widget() const { if (m_pLabel) return m_pLabel; assert(GetChildCount() <= 2); //The label widget is normally the first (of two) children const WindowImpl* pWindowImpl = ImplGetWindowImpl(); if (pWindowImpl->mpFirstChild == pWindowImpl->mpLastChild) //no label exists return nullptr; return pWindowImpl->mpFirstChild; } vcl::Window *VclFrame::get_label_widget() { return const_cast(const_cast(this)->get_label_widget()); } const vcl::Window *VclFrame::get_child() const { //The child widget is the normally the last (of two) children const WindowImpl* pWindowImpl = ImplGetWindowImpl(); assert(GetChildCount() == 2 || pWindowImpl->mbInDispose); if (!m_pLabel) return pWindowImpl->mpLastChild; if (pWindowImpl->mpFirstChild == pWindowImpl->mpLastChild) //only label exists return nullptr; return pWindowImpl->mpLastChild; } vcl::Window *VclFrame::get_child() { return const_cast(const_cast(this)->get_child()); } void VclFrame::set_label(const OUString &rLabel) { vcl::Window *pLabel = get_label_widget(); assert(pLabel); pLabel->SetText(rLabel); } OUString VclFrame::get_label() const { const vcl::Window *pLabel = get_label_widget(); assert(pLabel); return pLabel->GetText(); } OUString VclFrame::getDefaultAccessibleName() const { const vcl::Window *pLabel = get_label_widget(); if (pLabel) return pLabel->GetAccessibleName(); return VclBin::getDefaultAccessibleName(); } void VclFrame::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter) { VclBin::DumpAsPropertyTree(rJsonWriter); rJsonWriter.put("type", "frame"); } class DisclosureButton final : public CheckBox { virtual void ImplDrawCheckBoxState(vcl::RenderContext& rRenderContext) override { /* HACK: DisclosureButton is currently assuming, that the disclosure sign will fit into the rectangle occupied by a normal checkbox on all themes. If this does not hold true for some theme, ImplGetCheckImageSize would have to be overridden for DisclosureButton; also GetNativeControlRegion for ControlType::ListNode would have to be implemented and taken into account */ tools::Rectangle aStateRect(GetStateRect()); ImplControlValue aControlValue(GetState() == TRISTATE_TRUE ? ButtonValue::On : ButtonValue::Off); tools::Rectangle aCtrlRegion(aStateRect); ControlState nState = ControlState::NONE; if (HasFocus()) nState |= ControlState::FOCUSED; if (GetButtonState() & DrawButtonFlags::Default) nState |= ControlState::DEFAULT; if (Window::IsEnabled()) nState |= ControlState::ENABLED; if (IsMouseOver() && GetMouseRect().Contains(GetPointerPosPixel())) nState |= ControlState::ROLLOVER; if (rRenderContext.DrawNativeControl(ControlType::ListNode, ControlPart::Entire, aCtrlRegion, nState, aControlValue, OUString())) return; ImplSVCtrlData& rCtrlData(ImplGetSVData()->maCtrlData); if (!rCtrlData.moDisclosurePlus) rCtrlData.moDisclosurePlus.emplace(StockImage::Yes, SV_DISCLOSURE_PLUS); if (!rCtrlData.moDisclosureMinus) rCtrlData.moDisclosureMinus.emplace(StockImage::Yes, SV_DISCLOSURE_MINUS); Image* pImg = IsChecked() ? &*rCtrlData.moDisclosureMinus : &*rCtrlData.moDisclosurePlus; DrawImageFlags nStyle = DrawImageFlags::NONE; if (!IsEnabled()) nStyle |= DrawImageFlags::Disable; Size aSize(aStateRect.GetSize()); Size aImgSize(pImg->GetSizePixel()); Point aOff((aSize.Width() - aImgSize.Width()) / 2, (aSize.Height() - aImgSize.Height()) / 2); aOff += aStateRect.TopLeft(); rRenderContext.DrawImage(aOff, *pImg, nStyle); } public: explicit DisclosureButton(vcl::Window* pParent) : CheckBox(pParent, 0) { } virtual void KeyInput( const KeyEvent& rKEvt ) override { vcl::KeyCode aKeyCode = rKEvt.GetKeyCode(); if( !aKeyCode.GetModifier() && ( ( aKeyCode.GetCode() == KEY_ADD ) || ( aKeyCode.GetCode() == KEY_SUBTRACT ) ) ) { Check( aKeyCode.GetCode() == KEY_ADD ); } else CheckBox::KeyInput( rKEvt ); } }; VclExpander::VclExpander(vcl::Window *pParent) : VclBin(pParent) , m_bResizeTopLevel(false) , m_pDisclosureButton(VclPtr::Create(this)) { m_pDisclosureButton->SetToggleHdl(LINK(this, VclExpander, ClickHdl)); m_pDisclosureButton->Show(); } VclExpander::~VclExpander() { disposeOnce(); } bool VclExpander::get_expanded() const { return m_pDisclosureButton->IsChecked(); } void VclExpander::set_expanded(bool bExpanded) { m_pDisclosureButton->Check(bExpanded); } void VclExpander::set_label(const OUString& rLabel) { m_pDisclosureButton->SetText(rLabel); } OUString VclExpander::get_label() const { return m_pDisclosureButton->GetText(); } void VclExpander::dispose() { m_pDisclosureButton.disposeAndClear(); VclBin::dispose(); } const vcl::Window *VclExpander::get_child() const { const WindowImpl* pWindowImpl = ImplGetWindowImpl(); assert(pWindowImpl->mpFirstChild == m_pDisclosureButton); return pWindowImpl->mpFirstChild->GetWindow(GetWindowType::Next); } vcl::Window *VclExpander::get_child() { return const_cast(const_cast(this)->get_child()); } Size VclExpander::calculateRequisition() const { Size aRet(0, 0); WindowImpl* pWindowImpl = ImplGetWindowImpl(); const vcl::Window *pChild = get_child(); const vcl::Window *pLabel = pChild != pWindowImpl->mpLastChild ? pWindowImpl->mpLastChild.get() : nullptr; if (pChild && pChild->IsVisible() && m_pDisclosureButton->IsChecked()) aRet = getLayoutRequisition(*pChild); Size aExpanderSize = getLayoutRequisition(*m_pDisclosureButton); if (pLabel && pLabel->IsVisible()) { Size aLabelSize = getLayoutRequisition(*pLabel); aExpanderSize.setHeight( std::max(aExpanderSize.Height(), aLabelSize.Height()) ); aExpanderSize.AdjustWidth(aLabelSize.Width() ); } aRet.AdjustHeight(aExpanderSize.Height() ); aRet.setWidth( std::max(aExpanderSize.Width(), aRet.Width()) ); return aRet; } void VclExpander::setAllocation(const Size &rAllocation) { Size aAllocation(rAllocation); Point aChildPos; WindowImpl* pWindowImpl = ImplGetWindowImpl(); //The label widget is the last (of two) children vcl::Window *pChild = get_child(); vcl::Window *pLabel = pChild != pWindowImpl->mpLastChild.get() ? pWindowImpl->mpLastChild.get() : nullptr; Size aButtonSize = getLayoutRequisition(*m_pDisclosureButton); Size aLabelSize; Size aExpanderSize = aButtonSize; if (pLabel && pLabel->IsVisible()) { aLabelSize = getLayoutRequisition(*pLabel); aExpanderSize.setHeight( std::max(aExpanderSize.Height(), aLabelSize.Height()) ); aExpanderSize.AdjustWidth(aLabelSize.Width() ); } aExpanderSize.setHeight( std::min(aExpanderSize.Height(), aAllocation.Height()) ); aExpanderSize.setWidth( std::min(aExpanderSize.Width(), aAllocation.Width()) ); aButtonSize.setHeight( std::min(aButtonSize.Height(), aExpanderSize.Height()) ); aButtonSize.setWidth( std::min(aButtonSize.Width(), aExpanderSize.Width()) ); tools::Long nExtraExpanderHeight = aExpanderSize.Height() - aButtonSize.Height(); Point aButtonPos(aChildPos.X(), aChildPos.Y() + nExtraExpanderHeight/2); setLayoutAllocation(*m_pDisclosureButton, aButtonPos, aButtonSize); if (pLabel && pLabel->IsVisible()) { aLabelSize.setHeight( std::min(aLabelSize.Height(), aExpanderSize.Height()) ); aLabelSize.setWidth( std::min(aLabelSize.Width(), aExpanderSize.Width() - aButtonSize.Width()) ); tools::Long nExtraLabelHeight = aExpanderSize.Height() - aLabelSize.Height(); Point aLabelPos(aChildPos.X() + aButtonSize.Width(), aChildPos.Y() + nExtraLabelHeight/2); setLayoutAllocation(*pLabel, aLabelPos, aLabelSize); } aAllocation.AdjustHeight( -(aExpanderSize.Height()) ); aChildPos.AdjustY(aExpanderSize.Height() ); if (pChild && pChild->IsVisible()) { if (!m_pDisclosureButton->IsChecked()) aAllocation = Size(); setLayoutAllocation(*pChild, aChildPos, aAllocation); } } bool VclExpander::set_property(const OUString &rKey, const OUString &rValue) { if (rKey == "expanded") set_expanded(toBool(rValue)); else if (rKey == "resize-toplevel") m_bResizeTopLevel = toBool(rValue); else return VclBin::set_property(rKey, rValue); return true; } void VclExpander::StateChanged(StateChangedType nType) { VclBin::StateChanged( nType ); if (nType == StateChangedType::InitShow) { vcl::Window *pChild = get_child(); if (pChild) pChild->Show(m_pDisclosureButton->IsChecked()); } } const vcl::Window *VclExpander::get_label_widget() const { return m_pDisclosureButton; } vcl::Window *VclExpander::get_label_widget() { return const_cast(const_cast(this)->get_label_widget()); } void VclExpander::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter) { VclContainer::DumpAsPropertyTree(rJsonWriter); rJsonWriter.put("type", "expander"); } FactoryFunction VclExpander::GetUITestFactory() const { return ExpanderUIObject::create; } IMPL_LINK( VclExpander, ClickHdl, CheckBox&, rBtn, void ) { vcl::Window *pChild = get_child(); if (pChild) { pChild->Show(rBtn.IsChecked()); queue_resize(); Dialog* pResizeDialog = m_bResizeTopLevel ? GetParentDialog() : nullptr; if (pResizeDialog) pResizeDialog->setOptimalLayoutSize(true); } maExpandedHdl.Call(*this); } VclScrolledWindow::VclScrolledWindow(vcl::Window *pParent) : VclBin(pParent, WB_HIDE | WB_CLIPCHILDREN | WB_AUTOHSCROLL | WB_AUTOVSCROLL | WB_TABSTOP) , m_bUserManagedScrolling(false) , m_eDrawFrameStyle(DrawFrameStyle::NONE) , m_eDrawFrameFlags(DrawFrameFlags::WindowBorder) , m_pVScroll(VclPtr::Create(this, WB_HIDE | WB_VERT)) , m_pHScroll(VclPtr::Create(this, WB_HIDE | WB_HORZ)) , m_aScrollBarBox(VclPtr::Create(this, WB_HIDE)) { SetType(WindowType::SCROLLWINDOW); AllSettings aAllSettings = GetSettings(); StyleSettings aStyle = aAllSettings.GetStyleSettings(); aStyle.SetMonoColor(aStyle.GetShadowColor()); aAllSettings.SetStyleSettings(aStyle); GetOutDev()->SetSettings(aAllSettings); Link aLink( LINK( this, VclScrolledWindow, ScrollBarHdl ) ); m_pVScroll->SetScrollHdl(aLink); m_pHScroll->SetScrollHdl(aLink); m_nBorderWidth = CalcBorderWidth(); } int VclScrolledWindow::CalcBorderWidth() const { if (m_eDrawFrameStyle == DrawFrameStyle::NONE) return 0; const tools::Rectangle aRect(tools::Rectangle(Point(0, 0), Size(100, 100))); DecorationView aDecoView(const_cast(GetOutDev())); // don't actually draw anything, just measure what size it would be and the diff is the desired border size to reserve const tools::Rectangle aContentRect = aDecoView.DrawFrame(aRect, m_eDrawFrameStyle, m_eDrawFrameFlags | DrawFrameFlags::NoDraw); const auto nBorderWidth = (aRect.GetWidth() - aContentRect.GetWidth()) / 2; return std::max(nBorderWidth, 1); } void VclScrolledWindow::dispose() { m_pVScroll.disposeAndClear(); m_pHScroll.disposeAndClear(); m_aScrollBarBox.disposeAndClear(); VclBin::dispose(); } IMPL_LINK_NOARG(VclScrolledWindow, ScrollBarHdl, ScrollBar*, void) { vcl::Window *pChild = get_child(); if (!pChild) return; assert(dynamic_cast(pChild) && "scrolledwindow child should be a Viewport"); pChild = pChild->GetWindow(GetWindowType::FirstChild); if (!pChild) return; Point aWinPos(-m_pHScroll->GetThumbPos(), -m_pVScroll->GetThumbPos()); pChild->SetPosPixel(aWinPos); } const vcl::Window *VclScrolledWindow::get_child() const { const WindowImpl* pWindowImpl = ImplGetWindowImpl(); assert(GetChildCount() == 4 || pWindowImpl->mbInDispose); return pWindowImpl->mpLastChild; } vcl::Window *VclScrolledWindow::get_child() { return const_cast(const_cast(this)->get_child()); } Size VclScrolledWindow::calculateRequisition() const { Size aRet(0, 0); const vcl::Window *pChild = get_child(); if (pChild && pChild->IsVisible()) aRet = getLayoutRequisition(*pChild); if (GetStyle() & WB_VSCROLL) aRet.AdjustWidth(getLayoutRequisition(*m_pVScroll).Width() ); if (GetStyle() & WB_HSCROLL) aRet.AdjustHeight(getLayoutRequisition(*m_pHScroll).Height() ); aRet.AdjustHeight(2 * m_nBorderWidth); aRet.AdjustWidth(2 * m_nBorderWidth); return aRet; } void VclScrolledWindow::InitScrollBars(const Size &rRequest) { const vcl::Window *pChild = get_child(); if (!pChild || !pChild->IsVisible()) return; Size aOutSize(getVisibleChildSize()); m_pVScroll->SetRangeMax(rRequest.Height()); m_pVScroll->SetVisibleSize(aOutSize.Height()); m_pVScroll->SetPageSize(16); m_pHScroll->SetRangeMax(rRequest.Width()); m_pHScroll->SetVisibleSize(aOutSize.Width()); m_pHScroll->SetPageSize(16); m_pVScroll->Scroll(); m_pHScroll->Scroll(); } void VclScrolledWindow::doSetAllocation(const Size &rAllocation, bool bRetryOnFailure) { Size aChildReq; vcl::Window *pChild = get_child(); if (pChild && pChild->IsVisible()) aChildReq = getLayoutRequisition(*pChild); tools::Long nAvailHeight = rAllocation.Height() - 2 * m_nBorderWidth; tools::Long nAvailWidth = rAllocation.Width() - 2 * m_nBorderWidth; // vert. ScrollBar bool bShowVScroll; if (GetStyle() & WB_AUTOVSCROLL) bShowVScroll = nAvailHeight < aChildReq.Height(); else bShowVScroll = (GetStyle() & WB_VSCROLL) != 0; if (bShowVScroll) nAvailWidth -= getLayoutRequisition(*m_pVScroll).Width(); // horz. ScrollBar bool bShowHScroll; if (GetStyle() & WB_AUTOHSCROLL) { bShowHScroll = nAvailWidth < aChildReq.Width(); if (bShowHScroll) nAvailHeight -= getLayoutRequisition(*m_pHScroll).Height(); if (GetStyle() & WB_AUTOVSCROLL) bShowVScroll = nAvailHeight < aChildReq.Height(); } else bShowHScroll = (GetStyle() & WB_HSCROLL) != 0; if (m_pHScroll->IsVisible() != bShowHScroll) m_pHScroll->Show(bShowHScroll); if (m_pVScroll->IsVisible() != bShowVScroll) m_pVScroll->Show(bShowVScroll); Size aInnerSize(rAllocation); aInnerSize.AdjustWidth(-2 * m_nBorderWidth); aInnerSize.AdjustHeight(-2 * m_nBorderWidth); bool bBothVisible = m_pVScroll->IsVisible() && m_pHScroll->IsVisible(); auto nScrollBarWidth = getLayoutRequisition(*m_pVScroll).Width(); auto nScrollBarHeight = getLayoutRequisition(*m_pHScroll).Height(); if (m_pVScroll->IsVisible()) { Point aScrollPos(rAllocation.Width() - nScrollBarWidth - m_nBorderWidth, m_nBorderWidth); Size aScrollSize(nScrollBarWidth, rAllocation.Height() - 2 * m_nBorderWidth); if (bBothVisible) aScrollSize.AdjustHeight(-nScrollBarHeight); setLayoutAllocation(*m_pVScroll, aScrollPos, aScrollSize); aInnerSize.AdjustWidth( -nScrollBarWidth ); } if (m_pHScroll->IsVisible()) { Point aScrollPos(m_nBorderWidth, rAllocation.Height() - nScrollBarHeight); Size aScrollSize(rAllocation.Width() - 2 * m_nBorderWidth, nScrollBarHeight); if (bBothVisible) aScrollSize.AdjustWidth(-nScrollBarWidth); setLayoutAllocation(*m_pHScroll, aScrollPos, aScrollSize); aInnerSize.AdjustHeight( -nScrollBarHeight ); } if (bBothVisible) { Point aBoxPos(aInnerSize.Width() + m_nBorderWidth, aInnerSize.Height() + m_nBorderWidth); m_aScrollBarBox->SetPosSizePixel(aBoxPos, Size(nScrollBarWidth, nScrollBarHeight)); m_aScrollBarBox->Show(); } else { m_aScrollBarBox->Hide(); } if (pChild && pChild->IsVisible()) { assert(dynamic_cast(pChild) && "scrolledwindow child should be a Viewport"); WinBits nOldBits = (GetStyle() & (WB_AUTOVSCROLL | WB_VSCROLL | WB_AUTOHSCROLL | WB_HSCROLL)); setLayoutAllocation(*pChild, Point(m_nBorderWidth, m_nBorderWidth), aInnerSize); // tdf#128758 if the layout allocation triggered some callback that // immediately invalidates the layout by adding scrollbars then // normally this would simply retrigger layout and another toplevel // attempt is made later. But the initial layout attempt blocks // relayouts, so just make another single effort here. WinBits nNewBits = (GetStyle() & (WB_AUTOVSCROLL | WB_VSCROLL | WB_AUTOHSCROLL | WB_HSCROLL)); if (nOldBits != nNewBits && bRetryOnFailure) { doSetAllocation(rAllocation, false); return; } } if (!m_bUserManagedScrolling) InitScrollBars(aChildReq); } void VclScrolledWindow::setAllocation(const Size &rAllocation) { doSetAllocation(rAllocation, true); } Size VclScrolledWindow::getVisibleChildSize() const { Size aRet(GetSizePixel()); if (m_pVScroll->IsVisible()) aRet.AdjustWidth( -(m_pVScroll->GetSizePixel().Width()) ); if (m_pHScroll->IsVisible()) aRet.AdjustHeight( -(m_pHScroll->GetSizePixel().Height()) ); aRet.AdjustHeight(-2 * m_nBorderWidth); aRet.AdjustWidth(-2 * m_nBorderWidth); return aRet; } bool VclScrolledWindow::set_property(const OUString &rKey, const OUString &rValue) { if (rKey == "shadow-type" || rKey == "name") { if (rKey == "shadow-type") { // despite the style names, this looks like the best mapping if (rValue == "in") m_eDrawFrameStyle = DrawFrameStyle::Out; else if (rValue == "out") m_eDrawFrameStyle = DrawFrameStyle::In; else if (rValue == "etched-in") m_eDrawFrameStyle = DrawFrameStyle::DoubleOut; else if (rValue == "etched-out") m_eDrawFrameStyle = DrawFrameStyle::DoubleIn; else if (rValue == "none") m_eDrawFrameStyle = DrawFrameStyle::NONE; } else if (rKey == "name") { m_eDrawFrameFlags = DrawFrameFlags::WindowBorder; if (rValue == "monoborder") m_eDrawFrameFlags |= DrawFrameFlags::Mono; } auto nBorderWidth = CalcBorderWidth(); if (m_nBorderWidth != nBorderWidth) { m_nBorderWidth = nBorderWidth; queue_resize(); } return true; } bool bRet = VclBin::set_property(rKey, rValue); m_pVScroll->Show((GetStyle() & WB_VSCROLL) != 0); m_pHScroll->Show((GetStyle() & WB_HSCROLL) != 0); return bRet; } bool VclScrolledWindow::EventNotify(NotifyEvent& rNEvt) { bool bDone = false; if ( rNEvt.GetType() == NotifyEventType::COMMAND ) { const CommandEvent& rCEvt = *rNEvt.GetCommandEvent(); if ( rCEvt.GetCommand() == CommandEventId::Wheel ) { const CommandWheelData* pData = rCEvt.GetWheelData(); if( !pData->GetModifier() && ( pData->GetMode() == CommandWheelMode::SCROLL ) ) { // tdf#140537 only handle scroll commands in the valid shown scrollbars bDone = HandleScrollCommand(rCEvt, m_pHScroll->IsVisible() ? m_pHScroll : nullptr, m_pVScroll->IsVisible() ? m_pVScroll : nullptr); } } else if (rCEvt.GetCommand() == CommandEventId::GesturePan) { bDone = HandleScrollCommand(rCEvt, m_pHScroll->IsVisible() ? m_pHScroll : nullptr, m_pVScroll->IsVisible() ? m_pVScroll : nullptr); } } return bDone || VclBin::EventNotify( rNEvt ); } void VclScrolledWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) { VclBin::Paint(rRenderContext, rRect); if (m_eDrawFrameStyle == DrawFrameStyle::NONE) return; const tools::Rectangle aRect(tools::Rectangle(Point(0,0), GetSizePixel())); DecorationView aDecoView(&rRenderContext); const tools::Rectangle aContentRect = aDecoView.DrawFrame(aRect, m_eDrawFrameStyle, m_eDrawFrameFlags); const auto nBorderWidth = (aRect.GetWidth() - aContentRect.GetWidth()) / 2; SAL_WARN_IF(nBorderWidth > m_nBorderWidth, "vcl.layout", "desired border at paint " << nBorderWidth << " is larger than expected " << m_nBorderWidth); } namespace { void lcl_dumpScrollbar(::tools::JsonWriter& rJsonWriter, ScrollBar& rScrollBar) { rJsonWriter.put("lower", rScrollBar.GetRangeMin()); rJsonWriter.put("upper", rScrollBar.GetRangeMax()); rJsonWriter.put("step_increment", rScrollBar.GetLineSize()); rJsonWriter.put("page_increment", rScrollBar.GetPageSize()); rJsonWriter.put("value", rScrollBar.GetThumbPos()); rJsonWriter.put("page_size", rScrollBar.GetVisibleSize()); } }; void VclScrolledWindow::DumpAsPropertyTree(::tools::JsonWriter& rJsonWriter) { VclBin::DumpAsPropertyTree(rJsonWriter); rJsonWriter.put("user_managed_scrolling", m_bUserManagedScrolling); { auto aVertical = rJsonWriter.startNode("vertical"); ScrollBar& rScrollBar = getVertScrollBar(); lcl_dumpScrollbar(rJsonWriter, rScrollBar); WinBits nWinBits = GetStyle(); if (nWinBits & WB_VSCROLL) rJsonWriter.put("policy", "always"); else if (nWinBits & WB_AUTOVSCROLL) rJsonWriter.put("policy", "auto"); else rJsonWriter.put("policy", "never"); } { auto aHorizontal = rJsonWriter.startNode("horizontal"); ScrollBar& rScrollBar = getHorzScrollBar(); lcl_dumpScrollbar(rJsonWriter, rScrollBar); WinBits nWinBits = GetStyle(); if (nWinBits & WB_HSCROLL) rJsonWriter.put("policy", "always"); else if (nWinBits & WB_AUTOHSCROLL) rJsonWriter.put("policy", "auto"); else rJsonWriter.put("policy", "never"); } } void VclViewport::setAllocation(const Size &rAllocation) { vcl::Window *pChild = get_child(); if (!(pChild && pChild->IsVisible())) return; Size aReq(getLayoutRequisition(*pChild)); aReq.setWidth( std::max(aReq.Width(), rAllocation.Width()) ); aReq.setHeight( std::max(aReq.Height(), rAllocation.Height()) ); Point aKeepPos(pChild->GetPosPixel()); if (m_bInitialAllocation) { aKeepPos = Point(0, 0); m_bInitialAllocation = false; } setLayoutAllocation(*pChild, aKeepPos, aReq); } const vcl::Window *VclEventBox::get_child() const { const WindowImpl* pWindowImpl = ImplGetWindowImpl(); assert(pWindowImpl->mpFirstChild.get() == m_aEventBoxHelper.get()); return pWindowImpl->mpFirstChild->GetWindow(GetWindowType::Next); } vcl::Window *VclEventBox::get_child() { return const_cast(const_cast(this)->get_child()); } void VclEventBox::setAllocation(const Size& rAllocation) { Point aChildPos(0, 0); for (vcl::Window *pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next)) { if (!pChild->IsVisible()) continue; setLayoutAllocation(*pChild, aChildPos, rAllocation); } } Size VclEventBox::calculateRequisition() const { Size aRet(0, 0); for (const vcl::Window* pChild = get_child(); pChild; pChild = pChild->GetWindow(GetWindowType::Next)) { if (!pChild->IsVisible()) continue; Size aChildSize = getLayoutRequisition(*pChild); aRet.setWidth( std::max(aRet.Width(), aChildSize.Width()) ); aRet.setHeight( std::max(aRet.Height(), aChildSize.Height()) ); } return aRet; } void VclEventBox::Command(const CommandEvent&) { //discard events by default to block them reaching children } VclEventBox::~VclEventBox() { disposeOnce(); } void VclEventBox::dispose() { m_aEventBoxHelper.disposeAndClear(); VclBin::dispose(); } void VclSizeGroup::trigger_queue_resize() { //sufficient to trigger one widget to trigger all of them if (!m_aWindows.empty()) { (*m_aWindows.begin())->queue_resize(); } } void VclSizeGroup::set_ignore_hidden(bool bIgnoreHidden) { if (bIgnoreHidden != m_bIgnoreHidden) { m_bIgnoreHidden = bIgnoreHidden; trigger_queue_resize(); } } void VclSizeGroup::set_mode(VclSizeGroupMode eMode) { if (eMode != m_eMode) { m_eMode = eMode; trigger_queue_resize(); } } void VclSizeGroup::set_property(const OUString &rKey, const OUString &rValue) { if (rKey == "ignore-hidden") set_ignore_hidden(toBool(rValue)); else if (rKey == "mode") { VclSizeGroupMode eMode = VclSizeGroupMode::Horizontal; if (rValue == "none") eMode = VclSizeGroupMode::NONE; else if (rValue == "horizontal") eMode = VclSizeGroupMode::Horizontal; else if (rValue == "vertical") eMode = VclSizeGroupMode::Vertical; else if (rValue == "both") eMode = VclSizeGroupMode::Both; else { SAL_WARN("vcl.layout", "unknown size group mode" << rValue); } set_mode(eMode); } else { SAL_INFO("vcl.layout", "unhandled property: " << rKey); } } void MessageDialog::create_message_area() { setDeferredProperties(); if (m_pGrid) return; VclContainer *pContainer = get_content_area(); assert(pContainer); m_pGrid.set( VclPtr::Create(pContainer) ); m_pGrid->reorderWithinParent(0); m_pGrid->set_column_spacing(12); m_pMessageBox.set(VclPtr::Create(m_pGrid)); m_pMessageBox->set_grid_left_attach(1); m_pMessageBox->set_grid_top_attach(0); m_pMessageBox->set_spacing(GetTextHeight()); m_pImage = VclPtr::Create(m_pGrid, WB_CENTER | WB_VCENTER | WB_3DLOOK); switch (m_eMessageType) { case VclMessageType::Info: m_pImage->SetImage(GetStandardInfoBoxImage()); break; case VclMessageType::Warning: m_pImage->SetImage(GetStandardWarningBoxImage()); break; case VclMessageType::Question: m_pImage->SetImage(GetStandardQueryBoxImage()); break; case VclMessageType::Error: m_pImage->SetImage(GetStandardErrorBoxImage()); break; case VclMessageType::Other: break; } m_pImage->set_grid_left_attach(0); m_pImage->set_grid_top_attach(0); m_pImage->set_valign(VclAlign::Start); m_pImage->Show(m_eMessageType != VclMessageType::Other); WinBits nWinStyle = WB_CLIPCHILDREN | WB_LEFT | WB_VCENTER | WB_NOLABEL | WB_NOTABSTOP; bool bHasSecondaryText = !m_sSecondaryString.isEmpty(); m_pPrimaryMessage = VclPtr::Create(m_pMessageBox, nWinStyle); m_pPrimaryMessage->SetPaintTransparent(true); m_pPrimaryMessage->EnableCursor(false); m_pPrimaryMessage->set_hexpand(true); m_pPrimaryMessage->SetText(m_sPrimaryString); m_pPrimaryMessage->Show(!m_sPrimaryString.isEmpty()); m_pSecondaryMessage = VclPtr::Create(m_pMessageBox, nWinStyle); m_pSecondaryMessage->SetPaintTransparent(true); m_pSecondaryMessage->EnableCursor(false); m_pSecondaryMessage->set_hexpand(true); m_pSecondaryMessage->SetText(m_sSecondaryString); m_pSecondaryMessage->Show(bHasSecondaryText); MessageDialog::SetMessagesWidths(this, m_pPrimaryMessage, bHasSecondaryText ? m_pSecondaryMessage.get() : nullptr); VclButtonBox *pButtonBox = get_action_area(); assert(pButtonBox); VclPtr pBtn; short nDefaultResponse = get_default_response(); switch (m_eButtonsType) { case VclButtonsType::NONE: break; case VclButtonsType::Ok: pBtn.set( VclPtr::Create(pButtonBox) ); pBtn->SetStyle(pBtn->GetStyle() & WB_DEFBUTTON); pBtn->Show(); pBtn->set_id(u"ok"_ustr); add_button(pBtn, RET_OK, true); nDefaultResponse = RET_OK; break; case VclButtonsType::Close: pBtn.set( VclPtr::Create(pButtonBox) ); pBtn->SetStyle(pBtn->GetStyle() & WB_DEFBUTTON); pBtn->Show(); pBtn->set_id(u"close"_ustr); add_button(pBtn, RET_CLOSE, true); nDefaultResponse = RET_CLOSE; break; case VclButtonsType::Cancel: pBtn.set( VclPtr::Create(pButtonBox) ); pBtn->SetStyle(pBtn->GetStyle() & WB_DEFBUTTON); pBtn->Show(); pBtn->set_id(u"cancel"_ustr); add_button(pBtn, RET_CANCEL, true); nDefaultResponse = RET_CANCEL; break; case VclButtonsType::YesNo: pBtn = VclPtr::Create(pButtonBox); pBtn->SetText(GetStandardText(StandardButtonType::Yes)); pBtn->Show(); pBtn->set_id(u"yes"_ustr); add_button(pBtn, RET_YES, true); pBtn.set( VclPtr::Create(pButtonBox) ); pBtn->SetText(GetStandardText(StandardButtonType::No)); pBtn->Show(); pBtn->set_id(u"no"_ustr); add_button(pBtn, RET_NO, true); nDefaultResponse = RET_NO; break; case VclButtonsType::OkCancel: pBtn.set( VclPtr::Create(pButtonBox) ); pBtn->Show(); pBtn->set_id(u"ok"_ustr); add_button(pBtn, RET_OK, true); pBtn.set( VclPtr::Create(pButtonBox) ); pBtn->Show(); pBtn->set_id(u"cancel"_ustr); add_button(pBtn, RET_CANCEL, true); nDefaultResponse = RET_CANCEL; break; } set_default_response(nDefaultResponse); sort_native_button_order(*pButtonBox); m_pMessageBox->Show(); m_pGrid->Show(); } void MessageDialog::create_owned_areas() { #if defined _WIN32 set_border_width(3); #else set_border_width(12); #endif m_pOwnedContentArea.set(VclPtr::Create(this, false, 24)); set_content_area(m_pOwnedContentArea); m_pOwnedContentArea->Show(); m_pOwnedActionArea.set( VclPtr::Create(m_pOwnedContentArea) ); set_action_area(m_pOwnedActionArea); m_pOwnedActionArea->Show(); } MessageDialog::MessageDialog(vcl::Window* pParent, WinBits nStyle) : Dialog(pParent, nStyle) , m_eButtonsType(VclButtonsType::NONE) , m_eMessageType(VclMessageType::Info) , m_pOwnedContentArea(nullptr) , m_pOwnedActionArea(nullptr) , m_pGrid(nullptr) , m_pMessageBox(nullptr) , m_pImage(nullptr) , m_pPrimaryMessage(nullptr) , m_pSecondaryMessage(nullptr) { SetType(WindowType::MESSBOX); } MessageDialog::MessageDialog(vcl::Window* pParent, OUString aMessage, VclMessageType eMessageType, VclButtonsType eButtonsType) : Dialog(pParent, WB_MOVEABLE | WB_3DLOOK | WB_CLOSEABLE) , m_eButtonsType(eButtonsType) , m_eMessageType(eMessageType) , m_pGrid(nullptr) , m_pMessageBox(nullptr) , m_pImage(nullptr) , m_pPrimaryMessage(nullptr) , m_pSecondaryMessage(nullptr) , m_sPrimaryString(std::move(aMessage)) { SetType(WindowType::MESSBOX); create_owned_areas(); create_message_area(); switch (m_eMessageType) { case VclMessageType::Info: SetText(GetStandardInfoBoxText()); break; case VclMessageType::Warning: SetText(GetStandardWarningBoxText()); break; case VclMessageType::Question: SetText(GetStandardQueryBoxText()); break; case VclMessageType::Error: SetText(GetStandardErrorBoxText()); SetTaskBarState(VclTaskBarStates::Error); break; case VclMessageType::Other: SetText(Application::GetDisplayName()); break; } } void MessageDialog::dispose() { SetTaskBarState(VclTaskBarStates::Normal); disposeOwnedButtons(); m_pPrimaryMessage.disposeAndClear(); m_pSecondaryMessage.disposeAndClear(); m_pImage.disposeAndClear(); m_pMessageBox.disposeAndClear(); m_pGrid.disposeAndClear(); m_pOwnedActionArea.disposeAndClear(); m_pOwnedContentArea.disposeAndClear(); Dialog::dispose(); } MessageDialog::~MessageDialog() { disposeOnce(); } void MessageDialog::SetMessagesWidths(vcl::Window const *pParent, VclMultiLineEdit *pPrimaryMessage, VclMultiLineEdit *pSecondaryMessage) { if (pSecondaryMessage) { assert(pPrimaryMessage); vcl::Font aFont = pParent->GetSettings().GetStyleSettings().GetLabelFont(); aFont.SetFontSize(Size(0, aFont.GetFontSize().Height() * 1.2)); aFont.SetWeight(WEIGHT_BOLD); pPrimaryMessage->SetControlFont(aFont); pPrimaryMessage->SetMaxTextWidth(pPrimaryMessage->approximate_char_width() * 44); pSecondaryMessage->SetMaxTextWidth(pSecondaryMessage->approximate_char_width() * 60); } else pPrimaryMessage->SetMaxTextWidth(pPrimaryMessage->approximate_char_width() * 60); } OUString const & MessageDialog::get_primary_text() const { const_cast(this)->setDeferredProperties(); return m_sPrimaryString; } OUString const & MessageDialog::get_secondary_text() const { const_cast(this)->setDeferredProperties(); return m_sSecondaryString; } bool MessageDialog::set_property(const OUString &rKey, const OUString &rValue) { if (rKey == "text") set_primary_text(rValue); else if (rKey == "secondary-text") set_secondary_text(rValue); else if (rKey == "message-type") { VclMessageType eMode = VclMessageType::Info; if (rValue == "info") eMode = VclMessageType::Info; else if (rValue == "warning") eMode = VclMessageType::Warning; else if (rValue == "question") eMode = VclMessageType::Question; else if (rValue == "error") eMode = VclMessageType::Error; else if (rValue == "other") eMode = VclMessageType::Other; else { SAL_WARN("vcl.layout", "unknown message type mode" << rValue); } m_eMessageType = eMode; } else if (rKey == "buttons") { VclButtonsType eMode = VclButtonsType::NONE; if (rValue == "none") eMode = VclButtonsType::NONE; else if (rValue == "ok") eMode = VclButtonsType::Ok; else if (rValue == "cancel") eMode = VclButtonsType::Cancel; else if (rValue == "close") eMode = VclButtonsType::Close; else if (rValue == "yes-no") eMode = VclButtonsType::YesNo; else if (rValue == "ok-cancel") eMode = VclButtonsType::OkCancel; else { SAL_WARN("vcl.layout", "unknown buttons type mode" << rValue); } m_eButtonsType = eMode; } else return Dialog::set_property(rKey, rValue); return true; } void MessageDialog::set_primary_text(const OUString &rPrimaryString) { m_sPrimaryString = rPrimaryString; if (m_pPrimaryMessage) { m_pPrimaryMessage->SetText(m_sPrimaryString); m_pPrimaryMessage->Show(!m_sPrimaryString.isEmpty()); MessageDialog::SetMessagesWidths(this, m_pPrimaryMessage, !m_sSecondaryString.isEmpty() ? m_pSecondaryMessage.get() : nullptr); } } void MessageDialog::set_secondary_text(const OUString &rSecondaryString) { m_sSecondaryString = rSecondaryString; if (m_pSecondaryMessage) { m_pSecondaryMessage->SetText("\n" + m_sSecondaryString); m_pSecondaryMessage->Show(!m_sSecondaryString.isEmpty()); MessageDialog::SetMessagesWidths(this, m_pPrimaryMessage, !m_sSecondaryString.isEmpty() ? m_pSecondaryMessage.get() : nullptr); } } void MessageDialog::StateChanged(StateChangedType nType) { Dialog::StateChanged(nType); if (nType == StateChangedType::InitShow) { // MessageBox should be at least as wide as to see the title auto nTitleWidth = CalcTitleWidth(); // Extra-Width for Close button nTitleWidth += mpWindowImpl->mnTopBorder; if (get_preferred_size().Width() < nTitleWidth) { set_width_request(nTitleWidth); DoInitialLayout(); } } } VclPaned::VclPaned(vcl::Window *pParent, bool bVertical) : VclContainer(pParent, WB_HIDE | WB_CLIPCHILDREN) , m_pSplitter(VclPtr::Create(this, bVertical ? WB_VSCROLL : WB_HSCROLL)) , m_nPosition(-1) { m_pSplitter->SetBackground(Wallpaper(Application::GetSettings().GetStyleSettings().GetFaceColor())); m_pSplitter->Show(); } void VclPaned::dispose() { m_pSplitter.disposeAndClear(); VclContainer::dispose(); } VclVPaned::VclVPaned(vcl::Window *pParent) : VclPaned(pParent, true) { m_pSplitter->SetSplitHdl(LINK(this, VclVPaned, SplitHdl)); } IMPL_LINK(VclVPaned, SplitHdl, Splitter*, pSplitter, void) { tools::Long nSize = pSplitter->GetSplitPosPixel(); Size aSplitterSize(m_pSplitter->GetSizePixel()); Size aAllocation(GetSizePixel()); arrange(aAllocation, nSize, aAllocation.Height() - nSize - aSplitterSize.Height()); } void VclVPaned::arrange(const Size& rAllocation, tools::Long nFirstHeight, tools::Long nSecondHeight) { Size aSplitterSize(rAllocation.Width(), getLayoutRequisition(*m_pSplitter).Height()); Size aFirstChildSize(rAllocation.Width(), nFirstHeight); Size aSecondChildSize(rAllocation.Width(), nSecondHeight); int nElement = 0; for (vcl::Window* pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next)) { if (!pChild->IsVisible()) continue; if (nElement == 0) { Point aSplitterPos(0, aFirstChildSize.Height()); setLayoutAllocation(*m_pSplitter, aSplitterPos, aSplitterSize); m_nPosition = aSplitterPos.Y() + aSplitterSize.Height() / 2; } else if (nElement == 1) { Point aChildPos(0, 0); setLayoutAllocation(*pChild, aChildPos, aFirstChildSize); } else if (nElement == 2) { Point aChildPos(0, aFirstChildSize.Height() + aSplitterSize.Height()); setLayoutAllocation(*pChild, aChildPos, aSecondChildSize); } ++nElement; } } void VclVPaned::set_position(tools::Long nPosition) { VclPaned::set_position(nPosition); Size aAllocation(GetSizePixel()); Size aSplitterSize(m_pSplitter->GetSizePixel()); nPosition -= aSplitterSize.Height() / 2; arrange(aAllocation, nPosition, aAllocation.Height() - nPosition - aSplitterSize.Height()); } void VclVPaned::setAllocation(const Size& rAllocation) { //supporting "shrink" could be done by adjusting the allowed drag rectangle m_pSplitter->SetDragRectPixel(tools::Rectangle(Point(0, 0), rAllocation)); Size aSplitterSize(rAllocation.Width(), getLayoutRequisition(*m_pSplitter).Height()); const tools::Long nHeight = rAllocation.Height() - aSplitterSize.Height(); tools::Long nFirstHeight = 0; tools::Long nSecondHeight = 0; bool bFirstCanResize = true; bool bSecondCanResize = true; const bool bInitialAllocation = get_position() < 0; int nElement = 0; for (const vcl::Window* pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next)) { if (!pChild->IsVisible()) continue; if (nElement == 1) { if (bInitialAllocation) nFirstHeight = getLayoutRequisition(*pChild).Height(); else nFirstHeight = pChild->GetSizePixel().Height() + pChild->get_margin_top() + pChild->get_margin_bottom(); bFirstCanResize = pChild->get_expand(); } else if (nElement == 2) { if (bInitialAllocation) nSecondHeight = getLayoutRequisition(*pChild).Height(); else nSecondHeight = pChild->GetSizePixel().Height() + pChild->get_margin_top() + pChild->get_margin_bottom(); bSecondCanResize = pChild->get_expand(); } ++nElement; } tools::Long nHeightRequest = nFirstHeight + nSecondHeight; tools::Long nHeightDiff = nHeight - nHeightRequest; if (bFirstCanResize == bSecondCanResize) nFirstHeight += nHeightDiff/2; else if (bFirstCanResize) nFirstHeight += nHeightDiff; arrange(rAllocation, nFirstHeight, rAllocation.Height() - nFirstHeight - aSplitterSize.Height()); } Size VclVPaned::calculateRequisition() const { Size aRet(0, 0); for (const vcl::Window* pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next)) { if (!pChild->IsVisible()) continue; Size aChildSize = getLayoutRequisition(*pChild); aRet.setWidth( std::max(aRet.Width(), aChildSize.Width()) ); aRet.AdjustHeight(aChildSize.Height() ); } return aRet; } VclHPaned::VclHPaned(vcl::Window *pParent) : VclPaned(pParent, false) { m_pSplitter->SetSplitHdl(LINK(this, VclHPaned, SplitHdl)); } IMPL_LINK(VclHPaned, SplitHdl, Splitter*, pSplitter, void) { tools::Long nSize = pSplitter->GetSplitPosPixel(); Size aSplitterSize(m_pSplitter->GetSizePixel()); Size aAllocation(GetSizePixel()); arrange(aAllocation, nSize, aAllocation.Width() - nSize - aSplitterSize.Width()); } void VclHPaned::arrange(const Size& rAllocation, tools::Long nFirstWidth, tools::Long nSecondWidth) { Size aSplitterSize(getLayoutRequisition(*m_pSplitter).Width(), rAllocation.Height()); Size aFirstChildSize(nFirstWidth, rAllocation.Height()); Size aSecondChildSize(nSecondWidth, rAllocation.Height()); int nElement = 0; for (vcl::Window* pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next)) { if (!pChild->IsVisible()) continue; if (nElement == 0) { Point aSplitterPos(aFirstChildSize.Width(), 0); setLayoutAllocation(*m_pSplitter, aSplitterPos, aSplitterSize); m_nPosition = aSplitterPos.X() + aSplitterSize.Width() / 2; } else if (nElement == 1) { Point aChildPos(0, 0); setLayoutAllocation(*pChild, aChildPos, aFirstChildSize); } else if (nElement == 2) { Point aChildPos(aFirstChildSize.Width() + aSplitterSize.Width(), 0); setLayoutAllocation(*pChild, aChildPos, aSecondChildSize); } ++nElement; } } void VclHPaned::set_position(tools::Long nPosition) { VclPaned::set_position(nPosition); Size aAllocation(GetSizePixel()); Size aSplitterSize(m_pSplitter->GetSizePixel()); nPosition -= aSplitterSize.Width() / 2; arrange(aAllocation, nPosition, aAllocation.Width() - nPosition - aSplitterSize.Width()); } void VclHPaned::setAllocation(const Size& rAllocation) { //supporting "shrink" could be done by adjusting the allowed drag rectangle m_pSplitter->SetDragRectPixel(tools::Rectangle(Point(0, 0), rAllocation)); Size aSplitterSize(getLayoutRequisition(*m_pSplitter).Width(), rAllocation.Height()); const tools::Long nWidth = rAllocation.Width() - aSplitterSize.Width(); tools::Long nFirstWidth = 0; tools::Long nSecondWidth = 0; bool bFirstCanResize = true; bool bSecondCanResize = true; const bool bInitialAllocation = get_position() < 0; int nElement = 0; for (const vcl::Window* pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next)) { if (!pChild->IsVisible()) continue; if (nElement == 1) { if (bInitialAllocation) nFirstWidth = getLayoutRequisition(*pChild).Width(); else nFirstWidth = pChild->GetSizePixel().Width() + pChild->get_margin_start() + pChild->get_margin_end(); bFirstCanResize = pChild->get_expand(); } else if (nElement == 2) { if (bInitialAllocation) nSecondWidth = getLayoutRequisition(*pChild).Width(); else nSecondWidth = pChild->GetSizePixel().Width() + pChild->get_margin_start() + pChild->get_margin_end(); bSecondCanResize = pChild->get_expand(); } ++nElement; } tools::Long nWidthRequest = nFirstWidth + nSecondWidth; tools::Long nWidthDiff = nWidth - nWidthRequest; if (bFirstCanResize == bSecondCanResize) nFirstWidth += nWidthDiff/2; else if (bFirstCanResize) nFirstWidth += nWidthDiff; arrange(rAllocation, nFirstWidth, rAllocation.Width() - nFirstWidth - aSplitterSize.Width()); } Size VclHPaned::calculateRequisition() const { Size aRet(0, 0); for (const vcl::Window* pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next)) { if (!pChild->IsVisible()) continue; Size aChildSize = getLayoutRequisition(*pChild); aRet.setHeight( std::max(aRet.Height(), aChildSize.Height()) ); aRet.AdjustWidth(aChildSize.Width() ); } return aRet; } Size getLegacyBestSizeForChildren(const vcl::Window &rWindow) { tools::Rectangle aBounds; for (const vcl::Window* pChild = rWindow.GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next)) { if (!pChild->IsVisible()) continue; tools::Rectangle aChildBounds(pChild->GetPosPixel(), pChild->GetSizePixel()); aBounds.Union(aChildBounds); } if (aBounds.IsEmpty()) return rWindow.GetSizePixel(); Size aRet(aBounds.GetSize()); Point aTopLeft(aBounds.TopLeft()); aRet.AdjustWidth(aTopLeft.X()*2 ); aRet.AdjustHeight(aTopLeft.Y()*2 ); return aRet; } vcl::Window* getNonLayoutParent(vcl::Window *pWindow) { while (pWindow) { pWindow = pWindow->GetParent(); if (!pWindow || !isContainerWindow(*pWindow)) break; } return pWindow; } bool isVisibleInLayout(const vcl::Window *pWindow) { bool bVisible = true; while (bVisible) { bVisible = pWindow->IsVisible(); pWindow = pWindow->GetParent(); if (!pWindow || !isContainerWindow(*pWindow)) break; } return bVisible; } bool isEnabledInLayout(const vcl::Window *pWindow) { bool bEnabled = true; while (bEnabled) { bEnabled = pWindow->IsEnabled(); pWindow = pWindow->GetParent(); if (!pWindow || !isContainerWindow(*pWindow)) break; } return bEnabled; } bool isLayoutEnabled(const vcl::Window *pWindow) { //Child is a container => we're layout enabled const vcl::Window *pChild = pWindow ? pWindow->GetWindow(GetWindowType::FirstChild) : nullptr; return pChild && isContainerWindow(*pChild) && !pChild->GetWindow(GetWindowType::Next); } void VclDrawingArea::RequestHelp(const HelpEvent& rHelpEvent) { if (!(rHelpEvent.GetMode() & (HelpEventMode::QUICK | HelpEventMode::BALLOON))) return; Point aPos(ScreenToOutputPixel(rHelpEvent.GetMousePosPixel())); tools::Rectangle aHelpArea(aPos.X(), aPos.Y()); OUString sHelpTip = m_aQueryTooltipHdl.Call(aHelpArea); if (sHelpTip.isEmpty()) return; Point aPt = OutputToScreenPixel(aHelpArea.TopLeft()); aHelpArea.SetLeft(aPt.X()); aHelpArea.SetTop(aPt.Y()); aPt = OutputToScreenPixel(aHelpArea.BottomRight()); aHelpArea.SetRight(aPt.X()); aHelpArea.SetBottom(aPt.Y()); // tdf#125369 recover newline support of tdf#101779 QuickHelpFlags eHelpWinStyle = sHelpTip.indexOf('\n') != -1 ? QuickHelpFlags::TipStyleBalloon : QuickHelpFlags::NONE; Help::ShowQuickHelp(this, aHelpArea, sHelpTip, eHelpWinStyle); } void VclDrawingArea::StartDrag(sal_Int8, const Point&) { if (m_aStartDragHdl.Call(this)) return; rtl::Reference xContainer = m_xTransferHelper; if (!m_xTransferHelper.is()) return; xContainer->StartDrag(this, m_nDragAction); } OUString VclDrawingArea::GetSurroundingText() const { if (!m_aGetSurroundingHdl.IsSet()) return Control::GetSurroundingText(); OUString sSurroundingText; m_aGetSurroundingHdl.Call(sSurroundingText); return sSurroundingText; } Selection VclDrawingArea::GetSurroundingTextSelection() const { if (!m_aGetSurroundingHdl.IsSet()) return Control::GetSurroundingTextSelection(); OUString sSurroundingText; int nCursor = m_aGetSurroundingHdl.Call(sSurroundingText); return Selection(nCursor, nCursor); } bool VclDrawingArea::DeleteSurroundingText(const Selection& rSelection) { if (!m_aDeleteSurroundingHdl.IsSet()) return Control::DeleteSurroundingText(rSelection); return m_aDeleteSurroundingHdl.Call(rSelection); } VclHPaned::~VclHPaned() { } VclVPaned::~VclVPaned() { } VclPaned::~VclPaned() { disposeOnce(); } VclScrolledWindow::~VclScrolledWindow() { disposeOnce(); } void VclDrawingArea::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter) { Control::DumpAsPropertyTree(rJsonWriter); rJsonWriter.put("type", "drawingarea"); ScopedVclPtrInstance pDevice; OutputDevice* pRefDevice = GetOutDev(); Size aRenderSize(pRefDevice->PixelToLogic(GetOutputSizePixel())); Size aOutputSize = GetSizePixel(); pDevice->SetOutputSize(aRenderSize); tools::Rectangle aRect(Point(0,0), aRenderSize); // Dark mode support pDevice->DrawWallpaper(aRect, pRefDevice->GetBackground()); Paint(*pDevice, aRect); BitmapEx aImage = pDevice->GetBitmapEx(Point(0,0), aRenderSize); aImage.Scale(aOutputSize); rJsonWriter.put("imagewidth", aRenderSize.Width()); rJsonWriter.put("imageheight", aRenderSize.Height()); SvMemoryStream aOStm(65535, 65535); if(GraphicConverter::Export(aOStm, aImage, ConvertDataFormat::PNG) == ERRCODE_NONE) { css::uno::Sequence aSeq( static_cast(aOStm.GetData()), aOStm.Tell()); OStringBuffer aBuffer("data:image/png;base64,"); ::comphelper::Base64::encode(aBuffer, aSeq); rJsonWriter.put("image", aBuffer); } rJsonWriter.put("text", GetQuickHelpText()); } FactoryFunction VclDrawingArea::GetUITestFactory() const { if (m_pFactoryFunction) return m_pFactoryFunction; return DrawingAreaUIObject::create; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */