cui/source/inc/numpages.hxx                          |    9 +
 cui/source/tabpages/numpages.cxx                     |   86 +++++++++++--------
 cui/uiconfig/ui/pickbulletpage.ui                    |   46 +++++-----
 sw/qa/uitest/writer_tests2/formatBulletsNumbering.py |   38 +++++---
 4 files changed, 104 insertions(+), 75 deletions(-)

New commits:
commit 541a19edde3da18e4e979597cc5ed5482386b726
Author:     Parth Raiyani <[email protected]>
AuthorDate: Mon Aug 25 20:50:13 2025 +0530
Commit:     Caolán McNamara <[email protected]>
CommitDate: Tue Feb 24 20:50:51 2026 +0100

    Switch to IconView from ValueSet for pick bullet page
    
    - Replaced SvxNumValueSet with weld::IconView in numpages
    - Updated UI in pickbulletpage.ui to include GtkIconView and GtkTreeStore
    - added tooltip support
    - update relevant test cases
    
    Change-Id: I3686773e787a1d028dd5c6c116a60c9de63fd151
    Signed-off-by: Parth Raiyani <[email protected]>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/189846
    Reviewed-by: Szymon Kłos <[email protected]>
    Tested-by: Szymon Kłos <[email protected]>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/200210
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Reviewed-by: Caolán McNamara <[email protected]>

diff --git a/cui/source/inc/numpages.hxx b/cui/source/inc/numpages.hxx
index 2c82a6eece90..d1b2b80e58d6 100644
--- a/cui/source/inc/numpages.hxx
+++ b/cui/source/inc/numpages.hxx
@@ -97,13 +97,14 @@ class SvxBulletPickTabPage final : public SfxTabPage
     OUString            sBulletCharFormatName;
 
     std::unique_ptr<weld::Button> m_xBtChangeBullet;
-    std::unique_ptr<SvxNumValueSet> m_xExamplesVS;
-    std::unique_ptr<weld::CustomWeld> m_xExamplesVSWin;
+    std::unique_ptr<weld::IconView> m_xExamplesIV;
+    Size aPreviewSize;
     css::uno::Sequence<OUString> m_aBulletSymbols;
     css::uno::Sequence<OUString> m_aBulletSymbolsFonts;
 
-    DECL_LINK(NumSelectHdl_Impl, ValueSet*, void);
-    DECL_LINK(DoubleClickHdl_Impl, ValueSet*, void);
+    DECL_LINK(NumSelectHdl_Impl, weld::IconView&, void);
+    DECL_LINK(DoubleClickHdl_Impl, weld::IconView&, bool);
+    DECL_LINK(QueryTooltipHdl, const weld::TreeIter&, OUString);
     DECL_LINK(ClickAddChangeHdl_Impl, weld::Button&, void);
 public:
     SvxBulletPickTabPage(weld::Container* pPage, weld::DialogController* 
pController, const SfxItemSet& rSet);
diff --git a/cui/source/tabpages/numpages.cxx b/cui/source/tabpages/numpages.cxx
index 3d3e5083294f..90837e8b9254 100644
--- a/cui/source/tabpages/numpages.cxx
+++ b/cui/source/tabpages/numpages.cxx
@@ -79,6 +79,19 @@
 
 #include <bitmaps.hlst>
 
+#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;
+
 using namespace css;
 using namespace css::uno;
 using namespace css::beans;
@@ -132,21 +145,6 @@ static bool lcl_IsNumFmtSet(SvxNumRule const * pNum, 
sal_uInt16 nLevelMask)
     return bRet;
 }
 
-static vcl::Font& lcl_GetDefaultBulletFont()
-{
-    static vcl::Font aDefBulletFont = []()
-    {
-        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;
-}
-
 SvxSingleNumPickTabPage::SvxSingleNumPickTabPage(weld::Container* pPage, 
weld::DialogController* pController, const SfxItemSet& rSet)
     : SfxTabPage(pPage, pController, u"cui/ui/picknumberingpage.ui"_ustr, 
u"PickNumberingPage"_ustr, &rSet)
     , nActNumLvl(SAL_MAX_UINT16)
@@ -334,14 +332,15 @@ 
SvxBulletPickTabPage::SvxBulletPickTabPage(weld::Container* pPage, weld::DialogC
     , bPreset(false)
     , nNumItemId(SID_ATTR_NUMBERING_RULE)
     , m_xBtChangeBullet(m_xBuilder->weld_button(u"changeBulletBtn"_ustr))
-    , m_xExamplesVS(new 
SvxNumValueSet(m_xBuilder->weld_scrolled_window(u"valuesetwin"_ustr, true)))
-    , m_xExamplesVSWin(new weld::CustomWeld(*m_xBuilder, u"valueset"_ustr, 
*m_xExamplesVS))
+    , m_xExamplesIV(m_xBuilder->weld_icon_view(u"pick_bullet_iconview"_ustr))
+    , aPreviewSize(150, 200)
 {
     SetExchangeSupport();
     m_xBtChangeBullet->set_sensitive(false);
-    m_xExamplesVS->init(NumberingPageType::BULLET);
-    m_xExamplesVS->SetSelectHdl(LINK(this, SvxBulletPickTabPage, 
NumSelectHdl_Impl));
-    m_xExamplesVS->SetDoubleClickHdl(LINK(this, SvxBulletPickTabPage, 
DoubleClickHdl_Impl));
+    SvxBmpNumIconView::PopulateIconView(m_xExamplesIV.get(), 
NumberingPageType::BULLET, aPreviewSize);
+    m_xExamplesIV->connect_selection_changed(LINK(this, SvxBulletPickTabPage, 
NumSelectHdl_Impl));
+    m_xExamplesIV->connect_item_activated(LINK(this, SvxBulletPickTabPage, 
DoubleClickHdl_Impl));
+    m_xExamplesIV->connect_query_tooltip(LINK(this, SvxBulletPickTabPage, 
QueryTooltipHdl));
     m_xBtChangeBullet->connect_clicked(LINK(this, SvxBulletPickTabPage, 
ClickAddChangeHdl_Impl));
     m_aBulletSymbols = 
officecfg::Office::Common::BulletsNumbering::DefaultBullets::get();
     m_aBulletSymbolsFonts = 
officecfg::Office::Common::BulletsNumbering::DefaultBulletsFonts::get();
@@ -349,8 +348,7 @@ SvxBulletPickTabPage::SvxBulletPickTabPage(weld::Container* 
pPage, weld::DialogC
 
 SvxBulletPickTabPage::~SvxBulletPickTabPage()
 {
-    m_xExamplesVSWin.reset();
-    m_xExamplesVS.reset();
+    m_xExamplesIV.reset();
 }
 
 std::unique_ptr<SfxTabPage> SvxBulletPickTabPage::Create(weld::Container* 
pPage, weld::DialogController* pController,
@@ -389,13 +387,13 @@ void  SvxBulletPickTabPage::ActivatePage(const 
SfxItemSet& rSet)
     if(pActNum && *pSaveNum != *pActNum)
     {
         *pActNum = *pSaveNum;
-        m_xExamplesVS->SetNoSelection();
+        m_xExamplesIV->unselect_all();
     }
 
     if(pActNum && (!lcl_IsNumFmtSet(pActNum.get(), nActNumLvl) || bIsPreset))
     {
-        m_xExamplesVS->SelectItem(1);
-        NumSelectHdl_Impl(m_xExamplesVS.get());
+        m_xExamplesIV->select(0);
+        NumSelectHdl_Impl(*m_xExamplesIV);
         bPreset = true;
     }
     else if (pActNum)
@@ -407,7 +405,7 @@ void  SvxBulletPickTabPage::ActivatePage(const SfxItemSet& 
rSet)
             const sal_uInt16 nLevel = 
svx::sidebar::NBOTypeMgrBase::IsSingleLevel(nActNumLvl);
             SvxNumberFormat aFmt(pActNum->GetLevel(nLevel));
             if (aFmt.GetNumberingType() == SVX_NUM_CHAR_SPECIAL)
-                
m_xExamplesVS->SelectItem(pChoices->GetNBOIndexForNumRule(*pActNum, 
nActNumLvl));
+                
m_xExamplesIV->select(pChoices->GetNBOIndexForNumRule(*pActNum, nActNumLvl) - 
1);
         }
     }
 
@@ -453,7 +451,17 @@ void  SvxBulletPickTabPage::Reset( const SfxItemSet* rSet )
         *pActNum = *pSaveNum;
 }
 
-IMPL_LINK_NOARG(SvxBulletPickTabPage, NumSelectHdl_Impl, ValueSet*, void)
+IMPL_LINK(SvxBulletPickTabPage, QueryTooltipHdl, const weld::TreeIter&, rIter, 
OUString)
+{
+    const OUString sId = m_xExamplesIV->get_id(rIter);
+    if (sId.isEmpty())
+        return OUString();
+
+    sal_Int32 nIndex = sId.toInt32();
+    return 
SvxBmpNumIconView::GetNumberingDescription(NumberingPageType::BULLET, nIndex);
+}
+
+IMPL_LINK_NOARG(SvxBulletPickTabPage, NumSelectHdl_Impl, weld::IconView&, void)
 {
     if(!pActNum)
         return;
@@ -462,9 +470,10 @@ IMPL_LINK_NOARG(SvxBulletPickTabPage, NumSelectHdl_Impl, 
ValueSet*, void)
 
     bPreset = false;
     bModified = true;
-    sal_uInt16 nIndex = m_xExamplesVS->GetSelectedItemId() - 1;
+    OUString sId = m_xExamplesIV->get_selected_id();
+    sal_uInt16 nIndex = !sId.isEmpty() ? sId.toInt32() : 0;
     sal_Unicode cChar = m_aBulletSymbols[nIndex].toChar();
-    vcl::Font& rActBulletFont = lcl_GetDefaultBulletFont();
+    vcl::Font& rActBulletFont = SvxBmpNumIconView::GetDefaultBulletFont();
     rActBulletFont.SetFamilyName(m_aBulletSymbolsFonts[nIndex]);
 
     sal_uInt16 nMask = 1;
@@ -486,11 +495,16 @@ IMPL_LINK_NOARG(SvxBulletPickTabPage, NumSelectHdl_Impl, 
ValueSet*, void)
     }
 }
 
-IMPL_LINK_NOARG(SvxBulletPickTabPage, DoubleClickHdl_Impl, ValueSet*, void)
+IMPL_LINK_NOARG(SvxBulletPickTabPage, DoubleClickHdl_Impl, weld::IconView&, 
bool)
 {
-    NumSelectHdl_Impl(m_xExamplesVS.get());
+    if(m_xExamplesIV->get_selected_id().isEmpty())
+        return false;
+
+    NumSelectHdl_Impl(*m_xExamplesIV);
     weld::Button& rOk = GetDialogController()->GetOKButton();
     rOk.clicked();
+
+    return true;
 }
 
 IMPL_LINK_NOARG(SvxBulletPickTabPage, ClickAddChangeHdl_Impl, weld::Button&, 
void)
@@ -551,7 +565,8 @@ IMPL_LINK_NOARG(SvxBulletPickTabPage, 
ClickAddChangeHdl_Impl, weld::Button&, voi
     auto aBulletSymbolsListRange = asNonConstRange(aBulletSymbolsList);
     auto aBulletSymbolsFontsListRange = 
asNonConstRange(aBulletSymbolsFontsList);
 
-    sal_uInt16 nIndex = m_xExamplesVS->GetSelectedItemId() - 1;
+    OUString sId = m_xExamplesIV->get_selected_id();
+    sal_uInt16 nIndex = !sId.isEmpty() ? sId.toInt32() : 0;
     for (size_t i = 0; i < m_aBulletSymbols.size(); ++i)
     {
         if (i == nIndex)
@@ -571,8 +586,7 @@ IMPL_LINK_NOARG(SvxBulletPickTabPage, 
ClickAddChangeHdl_Impl, weld::Button&, voi
     
officecfg::Office::Common::BulletsNumbering::DefaultBulletsFonts::set(aBulletSymbolsFontsList,
 batch);
     batch->commit();
 
-    m_xExamplesVS->SetFormat();
-    m_xExamplesVS->Invalidate();
+    SvxBmpNumIconView::PopulateIconView(m_xExamplesIV.get(), 
NumberingPageType::BULLET, aPreviewSize);
 }
 
 void SvxBulletPickTabPage::PageCreated(const SfxAllItemSet& aSet)
@@ -740,7 +754,7 @@ IMPL_LINK_NOARG(SvxNumPickTabPage, NumSelectHdl_Impl, 
ValueSet*, void)
 
     SvxNumSettingsArr_Impl& rItemArr = 
aNumSettingsArrays[m_xExamplesVS->GetSelectedItemId() - 1];
 
-    const vcl::Font& rActBulletFont = lcl_GetDefaultBulletFont();
+    const vcl::Font& rActBulletFont = 
SvxBmpNumIconView::GetDefaultBulletFont();
     SvxNumSettings_Impl* pLevelSettings = nullptr;
     for(sal_uInt16 i = 0; i < pActNum->GetLevelCount(); i++)
     {
@@ -1179,7 +1193,7 @@ 
SvxNumOptionsTabPage::SvxNumOptionsTabPage(weld::Container* pPage, weld::DialogC
     m_xBulRelSizeMF->set_min(SVX_NUM_REL_SIZE_MIN, FieldUnit::PERCENT);
     m_xBulRelSizeMF->set_increments(5, 50, FieldUnit::PERCENT);
     SetExchangeSupport();
-    aActBulletFont = lcl_GetDefaultBulletFont();
+    aActBulletFont = SvxBmpNumIconView::GetDefaultBulletFont();
     // vertical alignment = fill makes the drawingarea expand the associated 
spinedits so we have to size it here
     const sal_Int16 aHeight
         = 
static_cast<sal_Int16>(std::max(int(m_xRatioCB->get_preferred_size().getHeight()
 / 2
diff --git a/cui/uiconfig/ui/pickbulletpage.ui 
b/cui/uiconfig/ui/pickbulletpage.ui
index b8fe43ca04c2..345b00c358c6 100644
--- a/cui/uiconfig/ui/pickbulletpage.ui
+++ b/cui/uiconfig/ui/pickbulletpage.ui
@@ -2,6 +2,14 @@
 <!-- Generated with glade 3.40.0 -->
 <interface domain="cui">
   <requires lib="gtk+" version="3.24"/>
+  <object class="GtkTreeStore" id="liststore1">
+    <columns>
+      <!-- column-name pixbuf -->
+      <column type="GdkPixbuf"/>
+      <!-- column-name id -->
+      <column type="gchararray"/>
+    </columns>
+  </object>
   <object class="GtkGrid" id="PickBulletPage">
     <property name="visible">True</property>
     <property name="can-focus">False</property>
@@ -13,7 +21,7 @@
     <property name="vexpand">True</property>
     <property name="row-spacing">6</property>
     <child>
-      <object class="GtkScrolledWindow" id="valusetwin">
+      <object class="GtkScrolledWindow">
         <property name="visible">True</property>
         <property name="can-focus">True</property>
         <property name="hexpand">True</property>
@@ -22,28 +30,26 @@
         <property name="vscrollbar-policy">never</property>
         <property name="shadow-type">in</property>
         <child>
-          <object class="GtkViewport">
+          <object class="GtkIconView" id="pick_bullet_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>
-                <child internal-child="accessible">
-                  <object class="AtkObject" id="valueset-atkobject">
-                    <property name="AtkObject::accessible-description" 
translatable="yes" context="pickbulletpage|extended_tip|valueset">Click the 
bullet style that you want to use.</property>
-                  </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>
+            <child internal-child="accessible">
+              <object class="AtkObject" id="pick_bullet_iconview-atkobject">
+                <property name="AtkObject::accessible-description" 
translatable="yes" 
context="pickbulletpage|extended_tip|pick_bullet_iconview">Click the bullet 
style that you want to use.</property>
               </object>
             </child>
           </object>
         </child>
       </object>
       <packing>
-        <property name="left-attach">1</property>
+        <property name="left-attach">0</property>
         <property name="top-attach">0</property>
       </packing>
     </child>
@@ -55,10 +61,10 @@
         <property name="receives-default">False</property>
         <property name="halign">end</property>
       </object>
-      <packing>
-        <property name="left-attach">1</property>
-        <property name="top-attach">2</property>
-      </packing>
+        <packing>
+          <property name="left-attach">0</property>
+          <property name="top-attach">1</property>
+        </packing>
     </child>
     <child internal-child="accessible">
       <object class="AtkObject" id="PickBulletPage-atkobject">
diff --git a/sw/qa/uitest/writer_tests2/formatBulletsNumbering.py 
b/sw/qa/uitest/writer_tests2/formatBulletsNumbering.py
index f240cfee7152..22ed41059543 100644
--- a/sw/qa/uitest/writer_tests2/formatBulletsNumbering.py
+++ b/sw/qa/uitest/writer_tests2/formatBulletsNumbering.py
@@ -26,11 +26,13 @@ class formatBulletsNumbering(UITestCase):
                 xTabs = xDialog.getChild("tabcontrol")
                 select_pos(xTabs, "0")
                 xBulletPage = xDialog.getChild("PickBulletPage")
-                xSelector = xBulletPage.getChild("valueset")
+                xSelector = xBulletPage.getChild("pick_bullet_iconview")
 
                 # Select element number 3
-                xSelector.executeAction("CHOOSE", mkPropertyValues({"POS": 
"3"}))
-                
self.assertEqual(get_state_as_dict(xSelector)["SelectedItemId"], "3")
+                element3 = xSelector.getChild("2")
+                element3.executeAction("SELECT", mkPropertyValues({}))
+                self.assertEqual(get_state_as_dict(xSelector)["VisibleCount"], 
"8")
+                
self.assertEqual(get_state_as_dict(xSelector)["SelectedItemId"], "2")
                 xChangeBulletBtn = xBulletPage.getChild("changeBulletBtn")
                 with 
self.ui_test.execute_blocking_action(xChangeBulletBtn.executeAction, 
args=('CLICK', ())) as xCharSetDialog:
                     xCharSet = xCharSetDialog.getChild("showcharset")
@@ -42,11 +44,13 @@ class formatBulletsNumbering(UITestCase):
                 xTabs = xDialog.getChild("tabcontrol")
                 select_pos(xTabs, "0")
                 xBulletPage = xDialog.getChild("PickBulletPage")
-                xSelector = xBulletPage.getChild("valueset")
+                xSelector = xBulletPage.getChild("pick_bullet_iconview")
 
                 # Select element number 3
-                xSelector.executeAction("CHOOSE", mkPropertyValues({"POS": 
"3"}))
-                
self.assertEqual(get_state_as_dict(xSelector)["SelectedItemId"], "3")
+                element3 = xSelector.getChild("2")
+                element3.executeAction("SELECT", mkPropertyValues({}))
+                self.assertEqual(get_state_as_dict(xSelector)["VisibleCount"], 
"8")
+                
self.assertEqual(get_state_as_dict(xSelector)["SelectedItemId"], "2")
                 xChangeBulletBtn = xBulletPage.getChild("changeBulletBtn")
                 with 
self.ui_test.execute_blocking_action(xChangeBulletBtn.executeAction, 
args=('CLICK', ())) as xCharSetDialog:
                     xHexText = xCharSetDialog.getChild("hexvalue")
@@ -240,16 +244,19 @@ class formatBulletsNumbering(UITestCase):
                 xTabs = xDialog.getChild("tabcontrol")
                 select_pos(xTabs, "0")
                 xBulletPage = xDialog.getChild("PickBulletPage")
-                xselector = xBulletPage.getChild("valueset")
-                self.assertEqual(get_state_as_dict(xselector)["ItemsCount"], 
"8")
+                xselector = xBulletPage.getChild("pick_bullet_iconview")
                 # Select element num 3
-                xselector.executeAction("CHOOSE", mkPropertyValues({"POS": 
"3"}))
+                element3 = xselector.getChild("2")
+                element3.executeAction("SELECT", mkPropertyValues({}))
+                self.assertEqual(get_state_as_dict(xselector)["VisibleCount"], 
"8")
                 
self.assertEqual(get_state_as_dict(xselector)["SelectedItemPos"], "2")
-                
self.assertEqual(get_state_as_dict(xselector)["SelectedItemId"], "3")
+                
self.assertEqual(get_state_as_dict(xselector)["SelectedItemId"], "2")
                 # Select element num 7
-                xselector.executeAction("CHOOSE", mkPropertyValues({"POS": 
"7"}))
+                element7 = xselector.getChild("6")
+                element7.executeAction("SELECT", mkPropertyValues({}))
+                self.assertEqual(get_state_as_dict(xselector)["VisibleCount"], 
"8")
                 
self.assertEqual(get_state_as_dict(xselector)["SelectedItemPos"], "6")
-                
self.assertEqual(get_state_as_dict(xselector)["SelectedItemId"], "7")
+                
self.assertEqual(get_state_as_dict(xselector)["SelectedItemId"], "6")
 
             # Test other Pages
             with 
self.ui_test.execute_dialog_through_command(".uno:BulletsAndNumberingDialog") 
as xDialog:
@@ -315,11 +322,12 @@ class formatBulletsNumbering(UITestCase):
                 xTabs = xDialog.getChild("tabcontrol")
                 select_pos(xTabs, "0")
                 xBulletPage = xDialog.getChild("PickBulletPage")
-                xSelector = xBulletPage.getChild("valueset")
+                xSelector = xBulletPage.getChild("pick_bullet_iconview")
 
                 # Select element number 1
-                xSelector.executeAction("CHOOSE", mkPropertyValues({"POS": 
"1"}))
-                
self.assertEqual(get_state_as_dict(xSelector)["SelectedItemId"], "1")
+                element1 = xSelector.getChild("0")
+                element1.executeAction("SELECT", mkPropertyValues({}))
+                
self.assertEqual(get_state_as_dict(xSelector)["SelectedItemId"], "0")
                 xChangeBulletBtn = xBulletPage.getChild("changeBulletBtn")
                 with 
self.ui_test.execute_blocking_action(xChangeBulletBtn.executeAction, 
args=('CLICK', ())) as xCharSetDialog:
                     # Select the DejaVu Sans font because that should contain 
the character

Reply via email to