include/svx/svxbmpnumiconview.hxx | 73 ++++ solenv/clang-format/excludelist | 2 svx/Library_svx.mk | 1 svx/source/dialog/svxbmpnumiconview.cxx | 542 +++++++++++++++++++++++++++++++ svx/source/tbxctrls/bulletsnumbering.cxx | 188 ++++++---- svx/uiconfig/ui/numberingwindow.ui | 66 ++- sw/inc/strings.hrc | 10 sw/source/ui/frmdlg/column.cxx | 140 ++++---- sw/source/uibase/inc/column.hxx | 29 - sw/uiconfig/swriter/ui/columnpage.ui | 41 +- 10 files changed, 902 insertions(+), 190 deletions(-)
New commits: commit 745ccfb8ef7c6cffbe57fb2196185b3ea6f19417 Author: Parth Raiyani <[email protected]> AuthorDate: Fri Aug 22 12:56:38 2025 +0530 Commit: Caolán McNamara <[email protected]> CommitDate: Tue Feb 24 18:00:41 2026 +0100 Switch to IconView in bullets numbering popup for improved UI handling - Replaced SvxNumValueSet with weld::IconView in the bullets numbering popup - Updated UI in numberingwindow.ui to include GtkIconView and GtkTreeStore - Refactors related logic to accommodate IconView, including preview generation, selection handling, and tooltip support Change-Id: I0b9b2fa5e9612488662548edc1fb78bf7eaa95e0 Signed-off-by: Parth Raiyani <[email protected]> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/189605 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Szymon Kłos <[email protected]> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/200196 Reviewed-by: Caolán McNamara <[email protected]> diff --git a/include/svx/svxbmpnumiconview.hxx b/include/svx/svxbmpnumiconview.hxx new file mode 100644 index 000000000000..853ad3ae29a1 --- /dev/null +++ b/include/svx/svxbmpnumiconview.hxx @@ -0,0 +1,73 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <svx/numvset.hxx> + +using namespace com::sun::star::uno; +using namespace com::sun::star::beans; +using namespace com::sun::star::lang; +using namespace com::sun::star::text; +using namespace com::sun::star::container; +using namespace com::sun::star::style; + +class SVX_DLLPUBLIC SvxBmpNumIconView +{ +public: + static vcl::Font& GetDefaultBulletFont(); + + static void PopulateIconView( + weld::IconView* pIconView, + NumberingPageType ePageType, + Size previewSize, + const Sequence<Sequence<PropertyValue>>& rNumSettings = Sequence<Sequence<PropertyValue>>(), + const Sequence<Reference<XIndexAccess>>& rOutlineSettings = Sequence<Reference<XIndexAccess>>(), + Reference<XNumberingFormatter> const& xFormatter = nullptr, + const Locale& rLocale = Locale()); + + static VclPtr<VirtualDevice> CreatePreviewFromUserDraw( + NumberingPageType ePageType, + sal_Int32 nIndex, + Size previewSize, + const Sequence<Sequence<PropertyValue>>& rNumSettings = Sequence<Sequence<PropertyValue>>(), + const Sequence<Reference<XIndexAccess>>& rOutlineSettings = Sequence<Reference<XIndexAccess>>(), + Reference<XNumberingFormatter> const& xFormatter = nullptr, + const Locale& rLocale = Locale(), + const std::vector<std::pair<OUString, OUString>>& rCustomBullets = std::vector<std::pair<OUString, OUString>>()); + + static VclPtr<VirtualDevice> CreateCustomBulletPreview(const OUString& rBulletChar, const OUString& rFontName); + static OUString GetNumberingDescription(NumberingPageType ePageType, sal_Int32 nIndex); + + static void SetNumberingSettings( + weld::IconView* mxIconView, + Size previewSize, + const Sequence<Sequence<PropertyValue>>& aNum, + Reference<XNumberingFormatter> const& xFormat, + const Locale& rLocale, + std::vector<std::pair<OUString, OUString>> maCustomBullets = std::vector<std::pair<OUString, OUString>>()); + + static void SetOutlineNumberingSettings( + weld::IconView* mxIconView, + Size previewSize, + const Sequence<Reference<XIndexAccess>>& rOutline, + Reference<XNumberingFormatter> const& xFormat, + const Locale& rLocale, + std::vector<std::pair<OUString, OUString>> maCustomBullets = std::vector<std::pair<OUString, OUString>>()); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/solenv/clang-format/excludelist b/solenv/clang-format/excludelist index a05aa28bdd2e..1a45aca5b71f 100644 --- a/solenv/clang-format/excludelist +++ b/solenv/clang-format/excludelist @@ -5573,6 +5573,7 @@ include/svx/ParseContext.hxx include/svx/ShapeTypeHandler.hxx include/svx/SmartTagItem.hxx include/svx/SmartTagMgr.hxx +include/svx/svxbmpnumiconview.hxx include/svx/SvxColorChildWindow.hxx include/svx/SvxPresetListBox.hxx include/svx/algitem.hxx @@ -11180,6 +11181,7 @@ svx/source/dialog/searchcharmap.cxx svx/source/dialog/spacinglistbox.cxx svx/source/dialog/srchctrl.cxx svx/source/dialog/srchdlg.cxx +svx/source/dialog/svxbmpnumiconview.cxx svx/source/dialog/svxbmpnumvalueset.cxx svx/source/dialog/svxgraphicitem.cxx svx/source/dialog/svxruler.cxx diff --git a/svx/Library_svx.mk b/svx/Library_svx.mk index a22e57876e07..4a31c95deafa 100644 --- a/svx/Library_svx.mk +++ b/svx/Library_svx.mk @@ -172,6 +172,7 @@ $(eval $(call gb_Library_add_exception_objects,svx,\ svx/source/dialog/srchdlg \ svx/source/dialog/strarray \ svx/source/dialog/svxbmpnumvalueset \ + svx/source/dialog/svxbmpnumiconview \ svx/source/dialog/svxgraphicitem \ svx/source/dialog/svxruler \ svx/source/dialog/swframeexample \ diff --git a/svx/source/dialog/svxbmpnumiconview.cxx b/svx/source/dialog/svxbmpnumiconview.cxx new file mode 100644 index 000000000000..5175789ee89d --- /dev/null +++ b/svx/source/dialog/svxbmpnumiconview.cxx @@ -0,0 +1,542 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/text/DefaultNumberingProvider.hpp> +#include <com/sun/star/text/XNumberingFormatter.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/graphic/XGraphic.hpp> + +#include <vcl/virdev.hxx> +#include <i18nlangtag/mslangid.hxx> +#include <editeng/svxenum.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <svx/dialmgr.hxx> +#include <svx/strings.hrc> +#include <officecfg/Office/Common.hxx> + +#include <svx/svxbmpnumiconview.hxx> + +using namespace com::sun::star::uno; +using namespace com::sun::star::beans; +using namespace com::sun::star::lang; +using namespace com::sun::star::text; +using namespace com::sun::star::container; +using namespace com::sun::star::style; + +const TranslateId OUTLINE_DESCRIPTIONS[] = { + RID_SVXSTR_OUTLINENUM_DESCRIPTION_0, + RID_SVXSTR_OUTLINENUM_DESCRIPTION_1, + RID_SVXSTR_OUTLINENUM_DESCRIPTION_2, + RID_SVXSTR_OUTLINENUM_DESCRIPTION_3, + RID_SVXSTR_OUTLINENUM_DESCRIPTION_4, + RID_SVXSTR_OUTLINENUM_DESCRIPTION_5, + RID_SVXSTR_OUTLINENUM_DESCRIPTION_6, + RID_SVXSTR_OUTLINENUM_DESCRIPTION_7 +}; + +const TranslateId SINGLENUM_DESCRIPTIONS[] = { + RID_SVXSTR_SINGLENUM_DESCRIPTION_0, + RID_SVXSTR_SINGLENUM_DESCRIPTION_1, + RID_SVXSTR_SINGLENUM_DESCRIPTION_2, + RID_SVXSTR_SINGLENUM_DESCRIPTION_3, + RID_SVXSTR_SINGLENUM_DESCRIPTION_4, + RID_SVXSTR_SINGLENUM_DESCRIPTION_5, + RID_SVXSTR_SINGLENUM_DESCRIPTION_6, + RID_SVXSTR_SINGLENUM_DESCRIPTION_7 +}; + +vcl::Font& SvxBmpNumIconView::GetDefaultBulletFont() +{ + static vcl::Font aDefBulletFont = []() + { + static vcl::Font tmp(u"OpenSymbol"_ustr, u""_ustr, Size(0, 14)); + tmp.SetCharSet( RTL_TEXTENCODING_SYMBOL ); + tmp.SetFamily( FAMILY_DONTKNOW ); + tmp.SetPitch( PITCH_DONTKNOW ); + tmp.SetWeight( WEIGHT_DONTKNOW ); + tmp.SetTransparent( true ); + return tmp; + }(); + return aDefBulletFont; +} + +void SvxBmpNumIconView::PopulateIconView( + weld::IconView* pIconView, + NumberingPageType ePageType, + Size previewSize, + const Sequence<Sequence<PropertyValue>>& rNumSettings, + const Sequence<Reference<XIndexAccess>>& rOutlineSettings, + Reference<XNumberingFormatter> const& xFormatter, + const Locale& rLocale) +{ + if (!pIconView) + return; + + pIconView->clear(); + + if (ePageType == NumberingPageType::BULLET) + { + Sequence<OUString> aBulletSymbols = + officecfg::Office::Common::BulletsNumbering::DefaultBullets::get(); + + for (sal_Int32 i = 0; i < std::min(aBulletSymbols.getLength(), sal_Int32(8)); ++i) + { + VclPtr<VirtualDevice> pVDev = CreatePreviewFromUserDraw(NumberingPageType::BULLET, i, previewSize, rNumSettings, rOutlineSettings, xFormatter, rLocale); + Bitmap aBmp = pVDev->GetBitmap(Point(), pVDev->GetOutputSizePixel()); + OUString sId = OUString::number(i); + OUString sText = GetNumberingDescription(ePageType, i); + pIconView->insert(-1, &sText, &sId, &aBmp, nullptr); + } + } +} + +VclPtr<VirtualDevice> SvxBmpNumIconView::CreatePreviewFromUserDraw( + NumberingPageType ePageType, + sal_Int32 nIndex, + Size previewSize, + const Sequence<Sequence<PropertyValue>>& rNumSettings, + const Sequence<Reference<XIndexAccess>>& rOutlineSettings, + Reference<XNumberingFormatter> const& xFormatter, + const Locale& rLocale, + const std::vector<std::pair<OUString, OUString>>& rCustomBullets) +{ + static const sal_uInt16 aLinesArr[] = + { + 15, 10, + 20, 30, + 25, 50, + 30, 70, + 35, 90, // up to here line positions + 5, 10, // character positions + 10, 30, + 15, 50, + 20, 70, + 25, 90, + }; + + const Color aBackColor(COL_WHITE); + const Color aTextColor(COL_BLACK); + + VclPtr<VirtualDevice> pVDev = VclPtr<VirtualDevice>::Create(); + pVDev->SetOutputSizePixel(previewSize); + + + tools::Long nRectWidth = previewSize.Width(); + tools::Long nRectHeight = previewSize.Height(); + Point aBLPos(0, 0); + + vcl::Font aFont(OutputDevice::GetDefaultFont( + DefaultFontType::UI_SANS, MsLangId::getConfiguredSystemLanguage(), GetDefaultFontFlags::OnlyOne)); + + Size aSize = aFont.GetFontSize(); + + vcl::Font aRuleFont( GetDefaultBulletFont() ); + Sequence<OUString> aBulletSymbols; + + if(ePageType == NumberingPageType::BULLET || ePageType == NumberingPageType::DOCBULLET) + { + if(ePageType == NumberingPageType::BULLET) + { + aBulletSymbols = officecfg::Office::Common::BulletsNumbering::DefaultBullets::get(); + Sequence< OUString > aBulletFonts = + officecfg::Office::Common::BulletsNumbering::DefaultBulletsFonts::get(); + if (nIndex < aBulletFonts.getLength()) + { + aRuleFont.SetFamilyName(aBulletFonts[nIndex]); + } + } + else // DOCBULLET + { + if (o3tl::make_unsigned(nIndex) < rCustomBullets.size()) + { + aRuleFont.SetFamilyName(rCustomBullets[nIndex].second); + } + } + aSize.setHeight(nRectHeight/6); + aRuleFont.SetFontSize(aSize); + aRuleFont.SetColor(aTextColor); + aRuleFont.SetFillColor(aBackColor); + aFont = aRuleFont; + } + else if(ePageType == NumberingPageType::OUTLINE) + { + aSize.setHeight( nRectHeight/8 ); + } + else // SINGLENUM + { + aSize.setHeight(nRectHeight/6); + } + + aFont.SetColor(aTextColor); + aFont.SetFillColor(aBackColor); + aFont.SetFontSize( aSize ); + pVDev->SetFont(aFont); + + pVDev->SetFillColor(aBackColor); + pVDev->SetLineColor(aTextColor); + pVDev->DrawRect(tools::Rectangle(Point(0,0), previewSize)); + + // Draw background lines for non-outline types + if(ePageType != NumberingPageType::OUTLINE) + { + pVDev->SetLineColor(COL_LIGHTGRAY); + Point aStart(aBLPos.X() + nRectWidth * 25 / 100, 0); + Point aEnd(aBLPos.X() + nRectWidth * 9 / 10, 0); + for(sal_uInt16 i = 11; i < 100; i += 33) + { + aStart.setY(aBLPos.Y() + nRectHeight * i / 100); + aEnd.setY(aStart.Y()); + pVDev->DrawLine(aStart, aEnd); + aStart.setY(aBLPos.Y() + nRectHeight * (i + 11) / 100); + aEnd.setY(aStart.Y()); + pVDev->DrawLine(aStart, aEnd); + } + } + + // Render content based on page type + if(ePageType == NumberingPageType::SINGLENUM || + ePageType == NumberingPageType::BULLET || + ePageType == NumberingPageType::DOCBULLET) + { + Point aStart(aBLPos.X() + nRectWidth / 9, 0); + for(sal_uInt16 i = 0; i < 3; i++) + { + sal_uInt16 nY = 11 + i * 33; + aStart.setY(aBLPos.Y() + nRectHeight * nY / 100); + OUString sText; + + if(ePageType == NumberingPageType::BULLET) + { + if (nIndex < aBulletSymbols.getLength()) + sText = aBulletSymbols[nIndex]; + aStart.AdjustY(-(pVDev->GetTextHeight()/2)); + aStart.setX(aBLPos.X() + 5); + } + else if(ePageType == NumberingPageType::DOCBULLET) + { + if (o3tl::make_unsigned(nIndex) < rCustomBullets.size()) + sText = rCustomBullets[nIndex].first; + aStart.AdjustY(-(pVDev->GetTextHeight()/2)); + aStart.setX(aBLPos.X() + 5); + } + else // SINGLENUM + { + if(xFormatter.is() && rNumSettings.getLength() > nIndex) + { + Sequence<PropertyValue> aLevel = rNumSettings.getConstArray()[nIndex]; + try + { + aLevel.realloc(aLevel.getLength() + 1); + PropertyValue& rValue = aLevel.getArray()[aLevel.getLength() - 1]; + rValue.Name = "Value"; + rValue.Value <<= static_cast<sal_Int32>(i + 1); + sText = xFormatter->makeNumberingString(aLevel, rLocale); + } + catch(Exception&) + { + sText = OUString::number(i + 1); + } + } + aStart.setX(aBLPos.X() + 2); + aStart.AdjustY(-(pVDev->GetTextHeight()/2)); + } + pVDev->DrawText(aStart, sText); + } + } + else if(ePageType == NumberingPageType::OUTLINE) + { + tools::Long nStartX = 0; + tools::Long nStartY = 0; + + if(xFormatter.is() && rOutlineSettings.getLength() > nIndex) + { + Reference<XIndexAccess> xLevel = rOutlineSettings.getConstArray()[nIndex]; + try + { + OUString sLevelTexts[5]; + OUString sFontNames[5]; + OUString sBulletChars[5]; + sal_Int16 aNumberingTypes[5]; + OUString sPrefixes[5]; + OUString sSuffixes[5]; + sal_Int16 aParentNumberings[5]; + + sal_Int32 nLevelCount = xLevel->getCount(); + if(nLevelCount > 5) + nLevelCount = 5; + + for(sal_Int32 i = 0; i < nLevelCount; i++) + { + tools::Long nTop = nStartY + nRectHeight * (aLinesArr[2 * i + 11])/100; + Point aLeft(nStartX + nRectWidth * (aLinesArr[2 * i + 10])/100, nTop); + + Any aLevelAny = xLevel->getByIndex(i); + Sequence<PropertyValue> aLevel; + aLevelAny >>= aLevel; + + aNumberingTypes[i] = 0; + aParentNumberings[i] = 0; + + for (const PropertyValue& rProp : aLevel) + { + if (rProp.Name == "NumberingType") + rProp.Value >>= aNumberingTypes[i]; + else if (rProp.Name == "BulletFontName") + rProp.Value >>= sFontNames[i]; + else if (rProp.Name == "BulletChar") + rProp.Value >>= sBulletChars[i]; + else if (rProp.Name == "Prefix") + rProp.Value >>= sPrefixes[i]; + else if (rProp.Name == "Suffix") + rProp.Value >>= sSuffixes[i]; + else if (rProp.Name == "ParentNumbering") + rProp.Value >>= aParentNumberings[i]; + } + + // Generate numbering text using formatter + Sequence<PropertyValue> aProperties(2); + PropertyValue* pProperties = aProperties.getArray(); + pProperties[0].Name = "NumberingType"; + pProperties[0].Value <<= aNumberingTypes[i]; + pProperties[1].Name = "Value"; + pProperties[1].Value <<= sal_Int32(1); + + try + { + sLevelTexts[i] = xFormatter->makeNumberingString(aProperties, rLocale); + } + catch(Exception&) + { + sLevelTexts[i] = "1"; + } + + aLeft.AdjustY(-(pVDev->GetTextHeight()/2)); + + // Draw prefix + if(!sPrefixes[i].isEmpty() && sPrefixes[i] != " ") + { + pVDev->SetFont(aFont); + pVDev->DrawText(aLeft, sPrefixes[i]); + aLeft.AdjustX(pVDev->GetTextWidth(sPrefixes[i])); + } + + // Draw parent numbering + if(aParentNumberings[i]) + { + sal_Int32 nStartLevel = std::min(static_cast<sal_Int32>(aParentNumberings[i]), i); + for(sal_Int32 nParentLevel = i - nStartLevel; nParentLevel < i; nParentLevel++) + { + OUString sTmp = sLevelTexts[nParentLevel] + "."; + + if(aNumberingTypes[nParentLevel] == SVX_NUM_CHAR_SPECIAL && !sBulletChars[nParentLevel].isEmpty()) + { + if(!sFontNames[nParentLevel].isEmpty()) + { + vcl::Font aBulletFont(sFontNames[nParentLevel], aSize); + aBulletFont.SetColor(aTextColor); + pVDev->SetFont(aBulletFont); + } + pVDev->DrawText(aLeft, sBulletChars[nParentLevel]); + aLeft.AdjustX(pVDev->GetTextWidth(sBulletChars[nParentLevel])); + } + else + { + pVDev->SetFont(aFont); + pVDev->DrawText(aLeft, sTmp); + aLeft.AdjustX(pVDev->GetTextWidth(sTmp)); + } + } + } + + // Draw current level + if(aNumberingTypes[i] == SVX_NUM_CHAR_SPECIAL && !sBulletChars[i].isEmpty()) + { + if(!sFontNames[i].isEmpty()) + { + vcl::Font aBulletFont(sFontNames[i], aSize); + aBulletFont.SetColor(aTextColor); + pVDev->SetFont(aBulletFont); + } + pVDev->DrawText(aLeft, sBulletChars[i]); + aLeft.AdjustX(pVDev->GetTextWidth(sBulletChars[i])); + } + else + { + pVDev->SetFont(aFont); + pVDev->DrawText(aLeft, sLevelTexts[i]); + aLeft.AdjustX(pVDev->GetTextWidth(sLevelTexts[i])); + } + + // Draw suffix + if(!sSuffixes[i].isEmpty() && !sSuffixes[i].startsWith(" ")) + { + pVDev->SetFont(aFont); + pVDev->DrawText(aLeft, sSuffixes[i]); + aLeft.AdjustX(pVDev->GetTextWidth(sSuffixes[i])); + } + + // Draw horizontal line + tools::Long nLineTop = nStartY + nRectHeight * aLinesArr[2 * i + 1]/100; + Point aLineLeft(aLeft.X(), nLineTop); + Point aLineRight(nStartX + nRectWidth * 90/100, nLineTop); + pVDev->SetLineColor(COL_LIGHTGRAY); + pVDev->DrawLine(aLineLeft, aLineRight); + } + + } + #ifdef DBG_UTIL + catch(Exception&) + { + static bool bAssert = false; + if(!bAssert) + { + TOOLS_WARN_EXCEPTION("svx.dialog", ""); + bAssert = true; + } + } +#else + catch(Exception&) + { + } +#endif + } + } + + return pVDev; +} + +VclPtr<VirtualDevice> SvxBmpNumIconView::CreateCustomBulletPreview(const OUString& rBulletChar, const OUString& rFontName) +{ + VclPtr<VirtualDevice> pVDev = VclPtr<VirtualDevice>::Create(); + Size aSize(80, 100); + pVDev->SetOutputSizePixel(aSize); + + const Color aBackColor(COL_WHITE); + const Color aTextColor(COL_BLACK); + + pVDev->SetFillColor(aBackColor); + pVDev->SetLineColor(aTextColor); + pVDev->DrawRect(tools::Rectangle(Point(0,0), aSize)); + + // Add horizontal lines + pVDev->SetLineColor(COL_LIGHTGRAY); + Point aStart(aSize.Width() * 25 / 100, 0); + Point aEnd(aSize.Width() * 9 / 10, 0); + for (sal_uInt16 i = 11; i < 100; i += 33) + { + aStart.setY(aSize.Height() * i / 100); + aEnd.setY(aStart.Y()); + pVDev->DrawLine(aStart, aEnd); + aStart.setY(aSize.Height() * (i + 11) / 100); + aEnd.setY(aStart.Y()); + pVDev->DrawLine(aStart, aEnd); + } + + vcl::Font aRuleFont(rFontName, Size(0, aSize.Height()/6)); + aRuleFont.SetCharSet(RTL_TEXTENCODING_SYMBOL); + aRuleFont.SetColor(aTextColor); + aRuleFont.SetFillColor(aBackColor); + pVDev->SetFont(aRuleFont); + + for (sal_uInt16 i = 0; i < 3; i++) // Show 3 sample lines + { + sal_uInt16 nY = 11 + i * 33; + Point pStart(5, aSize.Height() * nY / 100); + pStart.AdjustY(-(pVDev->GetTextHeight()/2)); + pVDev->DrawText(pStart, rBulletChar); + } + + return pVDev; +} + +OUString SvxBmpNumIconView::GetNumberingDescription(NumberingPageType ePageType, sal_Int32 nIndex) +{ + if (nIndex < 0) + return OUString(); + + switch (ePageType) + { + case NumberingPageType::OUTLINE: { + constexpr auto size = std::size(OUTLINE_DESCRIPTIONS); + if (nIndex < static_cast<sal_Int32>(size)) + return SvxResId(OUTLINE_DESCRIPTIONS[nIndex]); + break; + } + case NumberingPageType::SINGLENUM: { + constexpr auto size = std::size(SINGLENUM_DESCRIPTIONS); + if (nIndex < static_cast<sal_Int32>(size)) + return SvxResId(SINGLENUM_DESCRIPTIONS[nIndex]); + break; + } + case NumberingPageType::BULLET: + return u"Bullet " + OUString::number(nIndex + 1); // convert and display 1-based index + default: + break; + } + + return OUString(); +} + +void SvxBmpNumIconView::SetNumberingSettings( + weld::IconView* mxIconView, + Size previewSize, + const Sequence<Sequence<PropertyValue>>& aNum, + Reference<XNumberingFormatter> const& xFormat, + const Locale& rLocale, + std::vector<std::pair<OUString, OUString>> maCustomBullets) +{ + mxIconView->clear(); + + for (sal_Int32 i = 0; i < aNum.getLength(); ++i) + { + VclPtr<VirtualDevice> pVDev = CreatePreviewFromUserDraw( + NumberingPageType::SINGLENUM, i, previewSize, aNum, Sequence<Reference<XIndexAccess>>(), + xFormat, rLocale, maCustomBullets); + + Bitmap aBmp = pVDev->GetBitmap(Point(), pVDev->GetOutputSizePixel()); + OUString sId = OUString::number(i); + OUString sText = GetNumberingDescription(NumberingPageType::SINGLENUM, i); + mxIconView->insert(-1, &sText, &sId, &aBmp, nullptr); + } +} + +void SvxBmpNumIconView::SetOutlineNumberingSettings( + weld::IconView* mxIconView, + Size previewSize, + const Sequence<Reference<XIndexAccess>>& rOutline, + Reference<XNumberingFormatter> const& xFormat, + const Locale& rLocale, + std::vector<std::pair<OUString, OUString>> maCustomBullets) +{ + mxIconView->clear(); + + for (sal_Int32 i = 0; i < rOutline.getLength(); ++i) + { + VclPtr<VirtualDevice> pVDev = CreatePreviewFromUserDraw( + NumberingPageType::OUTLINE, i, previewSize, Sequence<Sequence<PropertyValue>>(), rOutline, + xFormat, rLocale, maCustomBullets); + + Bitmap aBmp = pVDev->GetBitmap(Point(), pVDev->GetOutputSizePixel()); + OUString sId = OUString::number(i); + OUString sText = GetNumberingDescription(NumberingPageType::OUTLINE, i); + mxIconView->insert(-1, &sText, &sId, &aBmp, nullptr); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/tbxctrls/bulletsnumbering.cxx b/svx/source/tbxctrls/bulletsnumbering.cxx index 90a7b0c5e7a7..994c8440405f 100644 --- a/svx/source/tbxctrls/bulletsnumbering.cxx +++ b/svx/source/tbxctrls/bulletsnumbering.cxx @@ -21,6 +21,18 @@ #include <vcl/toolbox.hxx> #include <vcl/settings.hxx> #include <vcl/svapp.hxx> +#include <vcl/virdev.hxx> +#include <i18nlangtag/mslangid.hxx> +#include <editeng/svxenum.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <svx/svxbmpnumiconview.hxx> + +using namespace com::sun::star::uno; +using namespace com::sun::star::beans; +using namespace com::sun::star::lang; +using namespace com::sun::star::text; +using namespace com::sun::star::container; +using namespace com::sun::star::style; namespace { @@ -30,19 +42,23 @@ class NumberingPopup : public WeldToolbarPopup { NumberingPageType mePageType; NumberingToolBoxControl& mrController; - std::unique_ptr<SvxNumValueSet> mxValueSet; - std::unique_ptr<weld::CustomWeld> mxValueSetWin; - std::unique_ptr<SvxNumValueSet> mxValueSetDoc; - std::unique_ptr<weld::CustomWeld> mxValueSetWinDoc; + std::unique_ptr<weld::IconView> mxIconView; + std::unique_ptr<weld::IconView> mxIconViewDoc; std::unique_ptr<weld::Button> mxMoreButton; std::unique_ptr<weld::Label> mxBulletsLabel; std::unique_ptr<weld::Label> mxDocBulletsLabel; - DECL_LINK(VSSelectValueSetHdl, ValueSet*, void); - DECL_LINK(VSSelectValueSetDocHdl, ValueSet*, void); + DECL_LINK(ItemActivatedHdl, weld::IconView&, bool); + DECL_LINK(QueryTooltipHdl, const weld::TreeIter&, OUString); + + DECL_LINK(DocItemActivatedHdl, weld::IconView&, bool); DECL_LINK(VSButtonClickSetHdl, weld::Button&, void); virtual void GrabFocus() override; +private: + std::vector<std::pair<OUString, OUString>> maCustomBullets; + Size aPreviewSize; + public: NumberingPopup(NumberingToolBoxControl& rController, weld::Widget* pParent, NumberingPageType ePageType); @@ -54,7 +70,7 @@ class NumberingToolBoxControl : public svt::PopupWindowController NumberingPageType mePageType; public: - explicit NumberingToolBoxControl( const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + explicit NumberingToolBoxControl( const Reference< XComponentContext >& rxContext ); virtual VclPtr<vcl::Window> createVclPopupWindow( vcl::Window* pParent ) override; std::unique_ptr<WeldToolbarPopup> weldPopupWindow() override; @@ -63,7 +79,7 @@ public: // XServiceInfo virtual OUString SAL_CALL getImplementationName() override; - virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override; }; } @@ -73,59 +89,43 @@ NumberingPopup::NumberingPopup(NumberingToolBoxControl& rController, : WeldToolbarPopup(rController.getFrameInterface(), pParent, u"svx/ui/numberingwindow.ui"_ustr, u"NumberingWindow"_ustr) , mePageType(ePageType) , mrController(rController) - , mxValueSet(new SvxNumValueSet(m_xBuilder->weld_scrolled_window(u"valuesetwin"_ustr, true))) - , mxValueSetWin(new weld::CustomWeld(*m_xBuilder, u"valueset"_ustr, *mxValueSet)) - , mxValueSetDoc(new SvxNumValueSet(m_xBuilder->weld_scrolled_window(u"valuesetwin_doc"_ustr, true))) - , mxValueSetWinDoc(new weld::CustomWeld(*m_xBuilder, u"valueset_doc"_ustr, *mxValueSetDoc)) + , mxIconView(m_xBuilder->weld_icon_view(u"numbering_window_iconview"_ustr)) + , mxIconViewDoc(m_xBuilder->weld_icon_view(u"numbering_window_iconview_doc"_ustr)) , mxMoreButton(m_xBuilder->weld_button(u"more"_ustr)) , mxBulletsLabel(m_xBuilder->weld_label(u"label_default"_ustr)) , mxDocBulletsLabel(m_xBuilder->weld_label(u"label_doc"_ustr)) + , aPreviewSize(80, 100) { - mxValueSet->SetStyle(WB_MENUSTYLEVALUESET | WB_FLATVALUESET | WB_NO_DIRECTSELECT); - mxValueSetDoc->SetStyle(WB_MENUSTYLEVALUESET | WB_FLATVALUESET | WB_NO_DIRECTSELECT); - mxValueSet->init(mePageType); - mxValueSetDoc->init(NumberingPageType::DOCBULLET); - mxValueSetDoc->Hide(); + SvxBmpNumIconView::PopulateIconView(mxIconView.get(), mePageType, aPreviewSize); + SvxBmpNumIconView::PopulateIconView(mxIconViewDoc.get(), NumberingPageType::DOCBULLET, aPreviewSize); + + mxIconViewDoc->hide(); mxDocBulletsLabel->hide(); if ( mePageType != NumberingPageType::BULLET ) { mxBulletsLabel->hide(); - css::uno::Reference< css::text::XDefaultNumberingProvider > xDefNum = css::text::DefaultNumberingProvider::create( mrController.getContext() ); + Reference< XDefaultNumberingProvider > xDefNum = DefaultNumberingProvider::create( mrController.getContext() ); if ( xDefNum.is() ) { - css::lang::Locale aLocale = Application::GetSettings().GetLanguageTag().getLocale(); - css::uno::Reference< css::text::XNumberingFormatter > xFormat( xDefNum, css::uno::UNO_QUERY ); + Locale aLocale = Application::GetSettings().GetLanguageTag().getLocale(); + Reference< XNumberingFormatter > xFormat( xDefNum, UNO_QUERY ); if ( mePageType == NumberingPageType::SINGLENUM ) { - css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > > aNumberings( + Sequence< Sequence< PropertyValue > > aNumberings( xDefNum->getDefaultContinuousNumberingLevels( aLocale ) ); - mxValueSet->SetNumberingSettings( aNumberings, xFormat, aLocale ); + SvxBmpNumIconView::SetNumberingSettings( mxIconView.get(), aPreviewSize, aNumberings, xFormat, aLocale ); } else if ( mePageType == NumberingPageType::OUTLINE ) { - css::uno::Sequence< css::uno::Reference< css::container::XIndexAccess > > aOutline( + Sequence< Reference< XIndexAccess > > aOutline( xDefNum->getDefaultOutlineNumberings( aLocale ) ); - mxValueSet->SetOutlineNumberingSettings( aOutline, xFormat, aLocale ); + SvxBmpNumIconView::SetOutlineNumberingSettings( mxIconView.get(), aPreviewSize, aOutline, xFormat, aLocale ); } } } - weld::DrawingArea* pDrawingArea = mxValueSet->GetDrawingArea(); - weld::DrawingArea* pDrawingAreaDoc = mxValueSetDoc->GetDrawingArea(); - OutputDevice& rRefDevice = pDrawingArea->get_ref_device(); - Size aItemSize(rRefDevice.LogicToPixel(Size(30, 42), MapMode(MapUnit::MapAppFont))); - mxValueSet->SetExtraSpacing( 2 ); - mxValueSetDoc->SetExtraSpacing( 2 ); - Size aSize(mxValueSet->CalcWindowSizePixel(aItemSize)); - pDrawingArea->set_size_request(aSize.Width(), aSize.Height()); - pDrawingAreaDoc->set_size_request(aSize.Width(), aSize.Height()); - mxValueSet->SetOutputSizePixel(aSize); - mxValueSetDoc->SetOutputSizePixel(aSize); - mxValueSet->SetColor(Application::GetSettings().GetStyleSettings().GetFieldColor()); - mxValueSetDoc->SetColor(Application::GetSettings().GetStyleSettings().GetFieldColor()); - if ( mePageType == NumberingPageType::BULLET ) { AddStatusListener( u".uno:CurrentBulletListType"_ustr ); @@ -140,17 +140,19 @@ NumberingPopup::NumberingPopup(NumberingToolBoxControl& rController, mxMoreButton->set_image(xImage); mxMoreButton->connect_clicked(LINK(this, NumberingPopup, VSButtonClickSetHdl)); - mxValueSet->SetSelectHdl(LINK(this, NumberingPopup, VSSelectValueSetHdl)); - mxValueSetDoc->SetSelectHdl(LINK(this, NumberingPopup, VSSelectValueSetDocHdl)); + mxIconView->connect_item_activated(LINK(this, NumberingPopup, ItemActivatedHdl)); + mxIconView->connect_query_tooltip(LINK(this, NumberingPopup, QueryTooltipHdl)); + + mxIconViewDoc->connect_item_activated(LINK(this, NumberingPopup, DocItemActivatedHdl)); } namespace { bool lcl_BulletIsDefault(std::u16string_view aSymbol, std::u16string_view aFont) { - css::uno::Sequence<OUString> aBulletSymbols + Sequence<OUString> aBulletSymbols = officecfg::Office::Common::BulletsNumbering::DefaultBullets::get(); - css::uno::Sequence<OUString> aBulletFonts + Sequence<OUString> aBulletFonts = officecfg::Office::Common::BulletsNumbering::DefaultBulletsFonts::get(); for (sal_Int32 i = 0; i < aBulletSymbols.getLength(); i++) { @@ -165,12 +167,12 @@ void NumberingPopup::statusChanged( const css::frame::FeatureStateEvent& rEvent { if (rEvent.FeatureURL.Complete == ".uno:DocumentBulletList") { - css::uno::Sequence<OUString> aSeq; + Sequence<OUString> aSeq; if (rEvent.State >>= aSeq) { std::vector<std::pair<OUString, OUString>> aList; - mxValueSetDoc->Clear(); - int i = 1; + mxIconViewDoc->clear(); + // The string contains the bullet as first character, and then the font name for (const OUString& sBulletFont : aSeq) { @@ -178,80 +180,114 @@ void NumberingPopup::statusChanged( const css::frame::FeatureStateEvent& rEvent OUString sFont(sBulletFont.copy(1, sBulletFont.getLength() - 1)); if (lcl_BulletIsDefault(sBullet, sFont)) continue; - mxValueSetDoc->InsertItem(i, sBullet, i); + + VclPtr<VirtualDevice> pVDev = SvxBmpNumIconView::CreateCustomBulletPreview(sBullet, sFont); + Bitmap aBmp = pVDev->GetBitmap(Point(), pVDev->GetOutputSizePixel()); + OUString sId = OUString::number(aList.size()); + mxIconViewDoc->insert(-1, nullptr, &sId, &aBmp, nullptr); aList.emplace_back(sBullet, sFont); - i++; } + if (!aList.empty()) { - mxValueSetDoc->Show(); mxDocBulletsLabel->show(); - mxValueSetDoc->SetCustomBullets(aList); + mxIconViewDoc->show(); + maCustomBullets = aList; } else { - mxValueSetDoc->Hide(); mxDocBulletsLabel->hide(); + mxIconViewDoc->hide(); } } } else { - mxValueSet->SetNoSelection(); - sal_Int32 nSelItem; - if ( rEvent.State >>= nSelItem ) - mxValueSet->SelectItem( nSelItem ); + OUString sId = mxIconView->get_selected_id(); + sal_Int32 nSelItem = !sId.isEmpty() ? sId.toInt32() : -1; + if(nSelItem == -1) { + if ( rEvent.State >>= nSelItem ) + { + nSelItem--; // convert to 0-based index for iconview + if(nSelItem > -1 && nSelItem < mxIconView->n_children()) + mxIconView->select( nSelItem ); + } + } } } -IMPL_LINK_NOARG(NumberingPopup, VSSelectValueSetHdl, ValueSet*, void) +IMPL_LINK(NumberingPopup, ItemActivatedHdl, weld::IconView&, rIconView, bool) { - sal_uInt16 nSelItem = mxValueSet->GetSelectedItemId(); + OUString sId = rIconView.get_selected_id(); + if (sId.isEmpty()) + return false; + + sal_Int32 nId = sId.toInt32(); + if ( mePageType == NumberingPageType::BULLET ) { - auto aArgs( comphelper::InitPropertySequence( { { "BulletIndex", css::uno::Any( nSelItem ) } } ) ); + auto aArgs( comphelper::InitPropertySequence( { { "BulletIndex", Any( nId + 1 ) } } ) ); mrController.dispatchCommand( u".uno:SetBullet"_ustr, aArgs ); } else if ( mePageType == NumberingPageType::SINGLENUM ) { - auto aArgs( comphelper::InitPropertySequence( { { "SetNumber", css::uno::Any( nSelItem ) } } ) ); + auto aArgs( comphelper::InitPropertySequence( { { "SetNumber", Any( nId + 1 ) } } ) ); mrController.dispatchCommand( u".uno:SetNumber"_ustr, aArgs ); } else { - auto aArgs( comphelper::InitPropertySequence( { { "SetOutline", css::uno::Any( nSelItem ) } } ) ); + auto aArgs( comphelper::InitPropertySequence( { { "SetOutline", Any( nId + 1 ) } } ) ); mrController.dispatchCommand( u".uno:SetOutline"_ustr, aArgs ); } mrController.EndPopupMode(); + return true; } -IMPL_LINK_NOARG(NumberingPopup, VSSelectValueSetDocHdl, ValueSet*, void) +IMPL_LINK(NumberingPopup, DocItemActivatedHdl, weld::IconView&, rIconView, bool) { - sal_uInt16 nSelItem = mxValueSetDoc->GetSelectedItemId() - 1; - auto aCustomBullets = mxValueSetDoc->GetCustomBullets(); - OUString nChar(aCustomBullets[nSelItem].first); - OUString sFont(aCustomBullets[nSelItem].second); - auto aArgs(comphelper::InitPropertySequence( - { { "BulletChar", css::uno::Any(nChar) }, { "BulletFont", css::uno::Any(sFont) } })); - mrController.dispatchCommand(u".uno:SetBullet"_ustr, aArgs); - mrController.EndPopupMode(); + OUString sId = rIconView.get_selected_id(); + if (sId.isEmpty()) + return false; + + sal_Int32 nId = sId.toInt32(); + + if (nId >= 0 && nId < static_cast<sal_Int32>(maCustomBullets.size())) + { + OUString nChar(maCustomBullets[nId].first); + OUString sFont(maCustomBullets[nId].second); + auto aArgs(comphelper::InitPropertySequence( + { { "BulletChar", Any(nChar) }, { "BulletFont", Any(sFont) } })); + mrController.dispatchCommand(u".uno:SetBullet"_ustr, aArgs); + mrController.EndPopupMode(); + } + return true; +} + +IMPL_LINK(NumberingPopup, QueryTooltipHdl, const weld::TreeIter&, rIter, OUString) +{ + const OUString sId = mxIconView->get_id(rIter); + if (sId.isEmpty()) + return OUString(); + + sal_Int32 nIndex = sId.toInt32(); + return SvxBmpNumIconView::GetNumberingDescription(mePageType, nIndex); } void NumberingPopup::GrabFocus() { - mxValueSet->GrabFocus(); + mxIconView->grab_focus(); } IMPL_LINK_NOARG(NumberingPopup, VSButtonClickSetHdl, weld::Button&, void) { - auto aArgs( comphelper::InitPropertySequence( { { "Page", css::uno::Any( u"customize"_ustr ) } } ) ); + auto aArgs( comphelper::InitPropertySequence( { { "Page", Any( u"customize"_ustr ) } } ) ); mrController.dispatchCommand( u".uno:OutlineBullet"_ustr, aArgs ); mrController.EndPopupMode(); } -NumberingToolBoxControl::NumberingToolBoxControl( const css::uno::Reference< css::uno::XComponentContext >& rxContext ): - svt::PopupWindowController( rxContext, css::uno::Reference< css::frame::XFrame >(), OUString() ), +NumberingToolBoxControl::NumberingToolBoxControl( const Reference< XComponentContext >& rxContext ): + svt::PopupWindowController( rxContext, Reference< css::frame::XFrame >(), OUString() ), mePageType( NumberingPageType::SINGLENUM ) { } @@ -303,15 +339,15 @@ OUString SAL_CALL NumberingToolBoxControl::getImplementationName() return u"com.sun.star.comp.svx.NumberingToolBoxControl"_ustr; } -css::uno::Sequence< OUString > SAL_CALL NumberingToolBoxControl::getSupportedServiceNames() +Sequence< OUString > SAL_CALL NumberingToolBoxControl::getSupportedServiceNames() { return { u"com.sun.star.frame.ToolbarController"_ustr }; } -extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +extern "C" SAL_DLLPUBLIC_EXPORT XInterface * com_sun_star_comp_svx_NumberingToolBoxControl_get_implementation( - css::uno::XComponentContext *rxContext, - css::uno::Sequence<css::uno::Any> const & ) + XComponentContext *rxContext, + Sequence<Any> const & ) { return cppu::acquire( new NumberingToolBoxControl( rxContext ) ); } diff --git a/svx/uiconfig/ui/numberingwindow.ui b/svx/uiconfig/ui/numberingwindow.ui index 15693097e038..406b5cbf64a2 100644 --- a/svx/uiconfig/ui/numberingwindow.ui +++ b/svx/uiconfig/ui/numberingwindow.ui @@ -13,6 +13,22 @@ <property name="border-width">4</property> <property name="constrain-to">none</property> <child> + <object class="GtkTreeStore" id="liststore1"> + <columns> + <!-- column-name pixbuf --> + <column type="GdkPixbuf"/> + <!-- column-name id --> + <column type="gchararray"/> + </columns> + </object> + <object class="GtkTreeStore" id="liststore2"> + <columns> + <!-- column-name pixbuf --> + <column type="GdkPixbuf"/> + <!-- column-name id --> + <column type="gchararray"/> + </columns> + </object> <object class="GtkBox" id="container"> <property name="visible">True</property> <property name="can-focus">False</property> @@ -24,7 +40,7 @@ <property name="can-focus">False</property> <property name="label" translatable="yes" context="numberingwindow|label_default">Bullet Library</property> <accessibility> - <relation type="label-for" target="valueset"/> + <relation type="label-for" target="numbering_window_iconview"/> </accessibility> </object> <packing> @@ -34,7 +50,7 @@ </packing> </child> <child> - <object class="GtkScrolledWindow" id="valuesetwin"> + <object class="GtkScrolledWindow"> <property name="visible">True</property> <property name="can-focus">True</property> <property name="hexpand">True</property> @@ -43,21 +59,20 @@ <property name="vscrollbar-policy">never</property> <property name="shadow-type">in</property> <child> - <object class="GtkViewport"> + <object class="GtkIconView" id="numbering_window_iconview"> <property name="visible">True</property> - <property name="can-focus">False</property> - <child> - <object class="GtkDrawingArea" id="valueset"> - <property name="visible">True</property> - <property name="can-focus">True</property> - <property name="events">GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_STRUCTURE_MASK</property> - <property name="hexpand">True</property> - <property name="vexpand">True</property> - <accessibility> - <relation type="labelled-by" target="label_default"/> - </accessibility> - </object> - </child> + <property name="can-focus">True</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="model">liststore1</property> + <property name="pixbuf-column">0</property> + <property name="columns">4</property> + <property name="item-width">80</property> + <property name="selection-mode">single</property> + <property name="activate-on-single-click">True</property> + <accessibility> + <relation type="labelled-by" target="label_default"/> + </accessibility> </object> </child> </object> @@ -73,7 +88,7 @@ <property name="can-focus">False</property> <property name="label" translatable="yes" context="numberingwindow|label_doc">Document Bullets</property> <accessibility> - <relation type="label-for" target="valueset_doc"/> + <relation type="label-for" target="numbering_window_iconview_doc"/> </accessibility> </object> <packing> @@ -83,7 +98,7 @@ </packing> </child> <child> - <object class="GtkScrolledWindow" id="valuesetwin_doc"> + <object class="GtkScrolledWindow"> <property name="visible">True</property> <property name="can-focus">True</property> <property name="hexpand">True</property> @@ -92,23 +107,22 @@ <property name="vscrollbar-policy">never</property> <property name="shadow-type">in</property> <child> - <object class="GtkViewport"> - <property name="visible">True</property> - <property name="can-focus">False</property> - <child> - <object class="GtkDrawingArea" id="valueset_doc"> + <object class="GtkIconView" id="numbering_window_iconview_doc"> <property name="visible">True</property> <property name="can-focus">False</property> - <property name="events">GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_STRUCTURE_MASK</property> <property name="hexpand">True</property> <property name="vexpand">True</property> + <property name="model">liststore2</property> + <property name="pixbuf-column">0</property> + <property name="columns">4</property> + <property name="item-width">80</property> + <property name="selection-mode">single</property> + <property name="activate-on-single-click">True</property> <accessibility> <relation type="labelled-by" target="label_doc"/> </accessibility> </object> </child> - </object> - </child> </object> <packing> <property name="expand">False</property> commit f0d594322888d25a421f547f8773fb3a10cde7e0 Author: Parth Raiyani <[email protected]> AuthorDate: Thu Aug 7 11:28:08 2025 +0530 Commit: Caolán McNamara <[email protected]> CommitDate: Tue Feb 24 18:00:33 2026 +0100 Switch to IconView from ValueSet for column layout for improved UI handling - Updated column page UI from ValueSet to IconView - Added tooltip support - Removed unused ValueSet related logic - Replaced ValueSet based column names with IconView based names Change-Id: I9dbd46162f8373ded78bb3dd207fb94cd9567a3a Signed-off-by: Parth Raiyani <[email protected]> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/188954 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Szymon Kłos <[email protected]> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/200192 Tested-by: Caolán McNamara <[email protected]> Reviewed-by: Caolán McNamara <[email protected]> diff --git a/sw/inc/strings.hrc b/sw/inc/strings.hrc index 7fb72a1d2840..0ae00d5e9470 100644 --- a/sw/inc/strings.hrc +++ b/sw/inc/strings.hrc @@ -229,11 +229,11 @@ #define STR_POOLNUMRULE_BUL4 NC_("STR_POOLNUMRULE_BUL4", "Bullet ➢") //Bullet \u2717 #define STR_POOLNUMRULE_BUL5 NC_("STR_POOLNUMRULE_BUL5", "Bullet ✗") -#define STR_COLUMN_VALUESET_ITEM0 NC_("STR_COLUMN_VALUESET_ITEM0", "1 column") -#define STR_COLUMN_VALUESET_ITEM1 NC_("STR_COLUMN_VALUESET_ITEM1", "2 columns with equal size") -#define STR_COLUMN_VALUESET_ITEM2 NC_("STR_COLUMN_VALUESET_ITEM2", "3 columns with equal size") -#define STR_COLUMN_VALUESET_ITEM3 NC_("STR_COLUMN_VALUESET_ITEM3", "2 columns with different size (left > right)") -#define STR_COLUMN_VALUESET_ITEM4 NC_("STR_COLUMN_VALUESET_ITEM4", "2 columns with different size (left < right)") +#define STR_COLUMN_ICONVIEW_ITEM0 NC_("STR_COLUMN_ICONVIEW_ITEM0", "1 column") +#define STR_COLUMN_ICONVIEW_ITEM1 NC_("STR_COLUMN_ICONVIEW_ITEM1", "2 columns with equal size") +#define STR_COLUMN_ICONVIEW_ITEM2 NC_("STR_COLUMN_ICONVIEW_ITEM2", "3 columns with equal size") +#define STR_COLUMN_ICONVIEW_ITEM3 NC_("STR_COLUMN_ICONVIEW_ITEM3", "2 columns with different size (left > right)") +#define STR_COLUMN_ICONVIEW_ITEM4 NC_("STR_COLUMN_ICONVIEW_ITEM4", "2 columns with different size (left < right)") // Table styles, Writer internal, others are taken from Svx #define STR_TABSTYLE_DEFAULT NC_("STR_TABSTYLE_DEFAULT", "Default Table Style") diff --git a/sw/source/ui/frmdlg/column.cxx b/sw/source/ui/frmdlg/column.cxx index ed703b5c1cf2..bb2443ee9ceb 100644 --- a/sw/source/ui/frmdlg/column.cxx +++ b/sw/source/ui/frmdlg/column.cxx @@ -402,7 +402,7 @@ SwColumnPage::SwColumnPage(weld::Container* pPage, weld::DialogController* pCont , m_xEd3(new SwPercentField(m_xBuilder->weld_metric_spin_button(u"width3mf"_ustr, FieldUnit::CM))) , m_xDistEd1(new SwPercentField(m_xBuilder->weld_metric_spin_button(u"spacing1mf"_ustr, FieldUnit::CM))) , m_xDistEd2(new SwPercentField(m_xBuilder->weld_metric_spin_button(u"spacing2mf"_ustr, FieldUnit::CM))) - , m_xDefaultVS(new weld::CustomWeld(*m_xBuilder, u"valueset"_ustr, m_aDefaultVS)) + , m_xDefaultIV(m_xBuilder->weld_icon_view(u"column-iconview"_ustr)) , m_xPgeExampleWN(new weld::CustomWeld(*m_xBuilder, u"pageexample"_ustr, m_aPgeExampleWN)) , m_xFrameExampleWN(new weld::CustomWeld(*m_xBuilder, u"frameexample"_ustr, m_aFrameExampleWN)) , m_xApplyToFT(m_xBuilder->weld_label(u"applytoft"_ustr)) @@ -420,34 +420,11 @@ SwColumnPage::SwColumnPage(weld::Container* pPage, weld::DialogController* pCont SetExchangeSupport(); - m_aDefaultVS.SetColCount(5); + // Initialize column layout items + InitColumnLayouts(); - for (int i = 0; i < 5; ++i) - //Set accessible name one by one - { - OUString aItemText; - switch( i ) - { - case 0: - aItemText = SwResId( STR_COLUMN_VALUESET_ITEM0 ) ; - break; - case 1: - aItemText = SwResId( STR_COLUMN_VALUESET_ITEM1 ) ; - break; - case 2: - aItemText = SwResId( STR_COLUMN_VALUESET_ITEM2 ) ; - break; - case 3: - aItemText = SwResId( STR_COLUMN_VALUESET_ITEM3 ); - break; - default: - aItemText = SwResId( STR_COLUMN_VALUESET_ITEM4 ); - break; - } - m_aDefaultVS.InsertItem( i + 1, aItemText, i ); - } - - m_aDefaultVS.SetSelectHdl(LINK(this, SwColumnPage, SetDefaultsHdl)); + m_xDefaultIV->connect_item_activated(LINK(this, SwColumnPage, SetDefaultsHdl)); + m_xDefaultIV->connect_query_tooltip(LINK(this, SwColumnPage, QueryTooltipHdl)); Link<weld::SpinButton&,void> aCLNrLk = LINK(this, SwColumnPage, ColModify); m_xCLNrEdt->connect_value_changed(aCLNrLk); @@ -499,7 +476,7 @@ SwColumnPage::~SwColumnPage() { m_xFrameExampleWN.reset(); m_xPgeExampleWN.reset(); - m_xDefaultVS.reset(); + m_xDefaultIV.reset(); m_xDistEd2.reset(); m_xDistEd1.reset(); m_xEd3.reset(); @@ -510,6 +487,53 @@ SwColumnPage::~SwColumnPage() m_xTextDirectionLB.reset(); } +void SwColumnPage::InitColumnLayouts() +{ + m_xDefaultIV->freeze(); + + for (int i = 0; i < 5; ++i) + { + OUString aItemText = GetColumnLayoutText(i); + OUString aItemId = OUString::number(i); + + VclPtr<VirtualDevice> aColumnVDev = CreateColumnLayoutVDev(i + 1); + Bitmap aBmp = aColumnVDev->GetBitmap(Point(), aColumnVDev->GetOutputSizePixel()); + m_xDefaultIV->insert(i, &aItemText, &aItemId, &aBmp, nullptr); + } + + m_xDefaultIV->thaw(); +} + +OUString SwColumnPage::GetColumnLayoutText(sal_Int32 nId) { + OUString aItemText; + switch( nId ) + { + case 0: + aItemText = SwResId( STR_COLUMN_ICONVIEW_ITEM0 ); + break; + case 1: + aItemText = SwResId( STR_COLUMN_ICONVIEW_ITEM1 ); + break; + case 2: + aItemText = SwResId( STR_COLUMN_ICONVIEW_ITEM2 ); + break; + case 3: + aItemText = SwResId( STR_COLUMN_ICONVIEW_ITEM3 ); + break; + default: + aItemText = SwResId( STR_COLUMN_ICONVIEW_ITEM4 ); + break; + } + + return aItemText; +} + +IMPL_LINK(SwColumnPage, QueryTooltipHdl, const weld::TreeIter&, iter, OUString) +{ + const OUString sId = m_xDefaultIV->get_id(iter); + return !sId.isEmpty() ? GetColumnLayoutText(sId.toInt32()) : OUString(); +} + void SwColumnPage::SetPageWidth(tools::Long nPageWidth) { tools::Long nNewMaxWidth = static_cast< tools::Long >(m_xEd1->NormalizePercent(nPageWidth)); @@ -916,7 +940,7 @@ void SwColumnPage::ColModify(bool bForceColReset) return; if (!bForceColReset) - m_aDefaultVS.SetNoSelection(); + m_xDefaultIV->unselect_all(); tools::Long nDist = static_cast< tools::Long >(m_xDistEd1->DenormalizePercent(m_xDistEd1->get_value(FieldUnit::TWIP))); m_xColMgr->SetCount(m_nCols, o3tl::narrowing<sal_uInt16>(nDist)); for(sal_uInt16 i = 0; i < m_nCols; i++) @@ -1258,12 +1282,16 @@ DeactivateRC SwColumnPage::DeactivatePage(SfxItemSet *_pSet) return DeactivateRC::LeavePage; } -IMPL_LINK(SwColumnPage, SetDefaultsHdl, ValueSet *, pVS, void) +IMPL_LINK(SwColumnPage, SetDefaultsHdl, weld::IconView&, rIconView, bool) { - const sal_uInt16 nItem = pVS->GetSelectedItemId(); - if( nItem < 4 ) + OUString sSelectedId = rIconView.get_selected_id(); + if (sSelectedId.isEmpty()) + return false; + + const sal_Int32 nItem = sSelectedId.toInt32(); + if( nItem < 3 ) { - m_xCLNrEdt->set_value(nItem); + m_xCLNrEdt->set_value(nItem + 1); m_xAutoWidthBox->set_active(true); m_xDistEd1->set_value(50, FieldUnit::CM); ColModify(/*bForceColReset=*/true); @@ -1277,7 +1305,7 @@ IMPL_LINK(SwColumnPage, SetDefaultsHdl, ValueSet *, pVS, void) ColModify(/*bForceColReset=*/true); // now set the width ratio to 2 : 1 or 1 : 2 respectively const tools::Long nSmall = static_cast< tools::Long >(m_xColMgr->GetActualSize() / 3); - if(nItem == 4) + if(nItem == 3) { m_xEd2->set_value(m_xEd2->NormalizePercent(nSmall), FieldUnit::TWIP); m_pModifiedField = m_xEd2.get(); @@ -1291,6 +1319,8 @@ IMPL_LINK(SwColumnPage, SetDefaultsHdl, ValueSet *, pVS, void) Timeout(); } + + return true; } void SwColumnPage::SetFrameMode(bool bMod) @@ -1307,27 +1337,30 @@ void SwColumnPage::SetInSection(bool bSet) m_xTextDirectionLB->set_visible(bSet); } -void ColumnValueSet::UserDraw(const UserDrawEvent& rUDEvt) +VclPtr<VirtualDevice> SwColumnPage::CreateColumnLayoutVDev(sal_uInt16 nItemId) { - vcl::RenderContext* pDev = rUDEvt.GetRenderContext(); + VclPtr<VirtualDevice> pVDev = VclPtr<VirtualDevice>::Create(); + const Size aSize(30, 30); + pVDev->SetOutputSizePixel(aSize); + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); - tools::Rectangle aRect = rUDEvt.GetRect(); - const sal_uInt16 nItemId = rUDEvt.GetItemId(); + pVDev->SetFillColor(rStyleSettings.GetFieldColor()); + pVDev->SetLineColor(rStyleSettings.GetFieldTextColor()); + + tools::Rectangle aRect(Point(0, 0), aSize); tools::Long nRectWidth = aRect.GetWidth(); tools::Long nRectHeight = aRect.GetHeight(); Point aBLPos = aRect.TopLeft(); - auto popIt = pDev->ScopedPush(vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR); - pDev->SetFillColor(rStyleSettings.GetFieldColor()); - pDev->SetLineColor(rStyleSettings.GetFieldTextColor()); - tools::Long nStep = std::abs(std::abs(nRectHeight * 95 /100) / 11); - tools::Long nTop = (nRectHeight - 11 * nStep ) / 2; + tools::Long nStep = std::abs(std::abs(nRectHeight * 95 / 100) / 11); + tools::Long nTop = (nRectHeight - 11 * nStep) / 2; sal_uInt16 nCols = 0; tools::Long nStarts[3]; tools::Long nEnds[3]; nStarts[0] = nRectWidth * 10 / 100; + switch( nItemId ) { case 1: @@ -1357,24 +1390,19 @@ void ColumnValueSet::UserDraw(const UserDrawEvent& rUDEvt) nEnds[1] = nRectWidth * 9 / 10; break; } - for(sal_uInt16 j = 0; j < nCols; j++ ) + + for(sal_uInt16 j = 0; j < nCols; j++) { Point aStart(aBLPos.X() + nStarts[j], 0); Point aEnd(aBLPos.X() + nEnds[j], 0); - for( sal_uInt16 i = 0; i < 12; i ++) + for(sal_uInt16 i = 0; i < 12; i++) { - aStart.setY( aBLPos.Y() + nTop + i * nStep); - aEnd.setY( aStart.Y() ); - pDev->DrawLine(aStart, aEnd); + aStart.setY(aBLPos.Y() + nTop + i * nStep); + aEnd.setY(aStart.Y()); + pVDev->DrawLine(aStart, aEnd); } } -} -void ColumnValueSet::StyleUpdated() -{ - SetFormat(); - Invalidate(); - ValueSet::StyleUpdated(); + return pVDev; } - /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/column.hxx b/sw/source/uibase/inc/column.hxx index 6eabca9db70c..ed2c6971f90f 100644 --- a/sw/source/uibase/inc/column.hxx +++ b/sw/source/uibase/inc/column.hxx @@ -19,11 +19,11 @@ #pragma once #include <svtools/ctrlbox.hxx> -#include <svtools/valueset.hxx> #include <sfx2/basedlgs.hxx> #include <sfx2/tabdlg.hxx> #include <svx/colorbox.hxx> #include <svx/frmdirlbox.hxx> +#include <vcl/virdev.hxx> #include <map> #include "colex.hxx" #include "prcntfld.hxx" @@ -64,22 +64,6 @@ public: virtual ~SwColumnDlg() override; }; -class ColumnValueSet final : public ValueSet -{ -public: - ColumnValueSet() - : ValueSet(nullptr) - { - } - virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override - { - ValueSet::SetDrawingArea(pDrawingArea); - SetStyle(WB_TABSTOP | WB_ITEMBORDER | WB_DOUBLEBORDER); - } - virtual void UserDraw(const UserDrawEvent& rUDEvt) override; - virtual void StyleUpdated() override; -}; - // column dialog now as TabPage class SwColumnPage final : public SfxTabPage { @@ -98,7 +82,6 @@ class SwColumnPage final : public SfxTabPage bool m_bHtmlMode; bool m_bLockUpdate; - ColumnValueSet m_aDefaultVS; SwColExample m_aPgeExampleWN; SwColumnOnlyExample m_aFrameExampleWN; @@ -127,7 +110,7 @@ class SwColumnPage final : public SfxTabPage std::unique_ptr<SwPercentField> m_xEd3; std::unique_ptr<SwPercentField> m_xDistEd1; std::unique_ptr<SwPercentField> m_xDistEd2; - std::unique_ptr<weld::CustomWeld> m_xDefaultVS; + std::unique_ptr<weld::IconView> m_xDefaultIV; // Example std::unique_ptr<weld::CustomWeld> m_xPgeExampleWN; std::unique_ptr<weld::CustomWeld> m_xFrameExampleWN; @@ -141,7 +124,8 @@ class SwColumnPage final : public SfxTabPage DECL_LINK(GapModify, weld::MetricSpinButton&, void); DECL_LINK(EdModify, weld::MetricSpinButton&, void); DECL_LINK(AutoWidthHdl, weld::Toggleable&, void ); - DECL_LINK(SetDefaultsHdl, ValueSet *, void); + DECL_LINK(SetDefaultsHdl, weld::IconView&, bool); + DECL_LINK(QueryTooltipHdl, const weld::TreeIter&, OUString); DECL_LINK(Up, weld::Button&, void); DECL_LINK(Down, weld::Button&, void); @@ -162,6 +146,11 @@ class SwColumnPage final : public SfxTabPage void connectPercentField(SwPercentField &rWrap); + void InitColumnLayouts(); + static OUString GetColumnLayoutText(sal_Int32 nId); + static VclPtr<VirtualDevice> CreateColumnLayoutVDev(sal_uInt16 nItemId); + + bool isLineNotNone() const; static const WhichRangesContainer s_aPageRg; diff --git a/sw/uiconfig/swriter/ui/columnpage.ui b/sw/uiconfig/swriter/ui/columnpage.ui index 67fbff149158..dc4e0e88bb4a 100644 --- a/sw/uiconfig/swriter/ui/columnpage.ui +++ b/sw/uiconfig/swriter/ui/columnpage.ui @@ -658,6 +658,14 @@ <property name="shadow-type">none</property> <child> <!-- n-columns=2 n-rows=2 --> + <object class="GtkTreeStore" id="liststore1"> + <columns> + <!-- column-name pixbuf --> + <column type="GdkPixbuf"/> + <!-- column-name id --> + <column type="gchararray"/> + </columns> + </object> <object class="GtkGrid" id="grid4"> <property name="visible">True</property> <property name="can-focus">False</property> @@ -830,17 +838,36 @@ </packing> </child> <child> - <object class="GtkDrawingArea" id="valueset"> + <object class="GtkScrolledWindow"> <property name="width-request">160</property> <property name="height-request">35</property> <property name="visible">True</property> <property name="can-focus">True</property> - <property name="events">GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_STRUCTURE_MASK</property> - <property name="hexpand">True</property> - <property name="vexpand">True</property> - <child internal-child="accessible"> - <object class="AtkObject" id="valueset-atkobject"> - <property name="AtkObject::accessible-description" translatable="yes" context="columnpage|extended_tip|valueset">Enter the number of columns that you want in the page, frame, or section.</property> + <property name="hscrollbar-policy">never</property> + <property name="vscrollbar-policy">never</property> + <property name="shadow-type">in</property> + <child> + <object class="GtkIconView" id="column-iconview"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="hexpand">False</property> + <property name="vexpand">False</property> + <property name="model">liststore1</property> + <property name="pixbuf-column">0</property> + <property name="selection-mode">single</property> + <property name="columns">5</property> + <property name="item-width">32</property> + <property name="item-padding">2</property> + <property name="spacing">2</property> + <property name="row-spacing">2</property> + <property name="column-spacing">2</property> + <property name="margin">2</property> + <property name="activate-on-single-click">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="column-iconview-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="columnpage|extended_tip|column-iconview">Enter the number of columns that you want in the page, frame, or section.</property> + </object> + </child> </object> </child> </object>
