include/svtools/ctrlbox.hxx | 10 +- sc/qa/uitest/calc_tests/formatCells.py | 13 ++- svtools/source/control/ctrlbox.cxx | 131 +++++++++++++++++++++++++++------ svtools/uiconfig/ui/linewindow.ui | 20 ++++- svx/source/tbxctrls/tbcontrl.cxx | 8 ++ vcl/jsdialog/executor.cxx | 23 ++++- 6 files changed, 162 insertions(+), 43 deletions(-)
New commits: commit 92da7d3c6e8de077b35c20ff748752f32e1a8590 Author: Parth Raiyani <[email protected]> AuthorDate: Mon Dec 29 18:02:40 2025 +0530 Commit: Szymon Kłos <[email protected]> CommitDate: Mon Jan 5 10:44:18 2026 +0100 iconview: Replaces valueset with iconview widget in line pop-up - Updated the UI layout to use GtkIconView - Replaced valueset logic with relevant iconview logic - Added tooltip support - Fixed potential LibreOffice Kit crash by disabling unnecessary tooltip for popup dialog in Libreoffit Kit - Fixed relevant test cases Change-Id: I3c89c88e435fb8803b863db6b1295d8c27a2a8cb Signed-off-by: Parth Raiyani <[email protected]> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196150 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Szymon Kłos <[email protected]> diff --git a/include/svtools/ctrlbox.hxx b/include/svtools/ctrlbox.hxx index 1ea54f44308d..1ea6d06187a3 100644 --- a/include/svtools/ctrlbox.hxx +++ b/include/svtools/ctrlbox.hxx @@ -197,8 +197,6 @@ inline Color sameDistColor( Color /*rMain*/, Color rDefault ) return rDefault; } -class ValueSet; - class SVT_DLLPUBLIC SvtLineListBox final : public WeldToolbarPopup { public: @@ -253,7 +251,8 @@ private: Color nColor1, Color nColor2, Color nColorDist, SvxBorderLineStyle nStyle, BitmapEx& rBmp ); - DECL_DLLPRIVATE_LINK(ValueSelectHdl, ValueSet*, void); + DECL_DLLPRIVATE_LINK(SelectionChangedHdl, weld::IconView&, void); + DECL_DLLPRIVATE_LINK(QueryTooltipHdl, const weld::TreeIter&, OUString); DECL_DLLPRIVATE_LINK(ToggleHdl, weld::Toggleable&, void); DECL_DLLPRIVATE_LINK(NoneHdl, weld::Button&, void); DECL_DLLPRIVATE_LINK(StyleUpdatedHdl, weld::Widget&, void); @@ -261,14 +260,15 @@ private: void UpdateEntries(); void UpdatePreview(); + static VclPtr<VirtualDevice> GetVirtualDevice(const BitmapEx& rBmp); + ImpLineListData* getBorderLineData(sal_Int32 nId); SvtLineListBox( const SvtLineListBox& ) = delete; SvtLineListBox& operator =( const SvtLineListBox& ) = delete; std::unique_ptr<weld::MenuButton> m_xControl; std::unique_ptr<weld::Button> m_xNoneButton; - std::unique_ptr<ValueSet> m_xLineSet; - std::unique_ptr<weld::CustomWeld> m_xLineSetWin; + std::unique_ptr<weld::IconView> m_xLineIV; std::vector<std::unique_ptr<ImpLineListData>> m_vLineList; tools::Long m_nWidth; diff --git a/sc/qa/uitest/calc_tests/formatCells.py b/sc/qa/uitest/calc_tests/formatCells.py index e00f6d0fe01a..dd11acae25e3 100644 --- a/sc/qa/uitest/calc_tests/formatCells.py +++ b/sc/qa/uitest/calc_tests/formatCells.py @@ -261,21 +261,23 @@ class formatCell(UITestCase): select_pos(xTabs, "5") #tab Borders linewidthmf = xDialog.getChild("linewidthmf") - xLineSet = xDialog.getChild('lineset') + xLineIconView = xDialog.getChild('iconview_lines') # check line-width for default solid line - self.assertEqual('0', get_state_as_dict(xLineSet)['SelectedItemPos']) + self.assertEqual('0', get_state_as_dict(xLineIconView)['SelectedItemPos']) widthVal = get_state_as_dict(linewidthmf)["Text"] self.assertEqual(widthVal, '0.75 pt') # set line style to "double" (minimal width is taken) - xLineSet.executeAction("CHOOSE", mkPropertyValues({"POS": '16'})) + element6 = xLineIconView.getChild('6') + element6.executeAction("SELECT", mkPropertyValues({})) widthVal = get_state_as_dict(linewidthmf)["Text"] # minimum predefined width is Medium (1.50 pt) self.assertEqual(widthVal, '1.50 pt') # set line style to "solid" - xLineSet.executeAction("CHOOSE", mkPropertyValues({"POS": "1"})) + element1 = xLineIconView.getChild('0') + element1.executeAction("SELECT", mkPropertyValues({})) widthVal = get_state_as_dict(linewidthmf)["Text"] self.assertEqual(widthVal, '1.50 pt') @@ -287,7 +289,8 @@ class formatCell(UITestCase): self.assertEqual(widthVal, '2.25 pt') # set line style to "double" (minimal width is not taken) - xLineSet.executeAction("CHOOSE", mkPropertyValues({"POS": "8"})) + element8 = xLineIconView.getChild('6') + element8.executeAction("SELECT", mkPropertyValues({})) widthVal = get_state_as_dict(linewidthmf)["Text"] self.assertEqual(widthVal, '2.25 pt') diff --git a/svtools/source/control/ctrlbox.cxx b/svtools/source/control/ctrlbox.cxx index 736de5be4850..8789b9066d9d 100644 --- a/svtools/source/control/ctrlbox.cxx +++ b/svtools/source/control/ctrlbox.cxx @@ -47,7 +47,6 @@ #include <svtools/ctrlbox.hxx> #include <svtools/ctrltool.hxx> #include <svtools/borderhelper.hxx> -#include <svtools/valueset.hxx> #include <basegfx/polygon/b2dpolygon.hxx> #include <basegfx/polygon/b2dpolygontools.hxx> @@ -1415,9 +1414,10 @@ int FontSizeBox::get_value() const SvxBorderLineStyle SvtLineListBox::GetSelectEntryStyle() const { - if (m_xLineSet->IsNoSelection()) + if (m_xLineIV->get_selected_id().isEmpty()) return SvxBorderLineStyle::NONE; - auto nId = m_xLineSet->GetSelectedItemId(); + + auto nId = m_xLineIV->get_selected_id().toInt32(); return static_cast<SvxBorderLineStyle>(nId - 1); } @@ -1489,17 +1489,20 @@ SvtLineListBox::SvtLineListBox(std::unique_ptr<weld::MenuButton> pControl) : WeldToolbarPopup(css::uno::Reference<css::frame::XFrame>(), pControl.get(), u"svt/ui/linewindow.ui"_ustr, u"line_popup_window"_ustr) , m_xControl(std::move(pControl)) , m_xNoneButton(m_xBuilder->weld_button(u"none_line_button"_ustr)) - , m_xLineSet(new ValueSet(nullptr)) - , m_xLineSetWin(new weld::CustomWeld(*m_xBuilder, u"lineset"_ustr, *m_xLineSet)) + , m_xLineIV(m_xBuilder->weld_icon_view(u"iconview_lines"_ustr)) , m_nWidth( 5 ) , aVirDev(VclPtr<VirtualDevice>::Create()) , aColor(COL_BLACK) { - const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); - m_xLineSet->SetStyle(WinBits(WB_FLATVALUESET | WB_NO_DIRECTSELECT | WB_TABSTOP)); - m_xLineSet->SetItemHeight(rStyleSettings.GetListBoxPreviewDefaultPixelSize().Height() + 1); - m_xLineSet->SetColCount(1); - m_xLineSet->SetSelectHdl(LINK(this, SvtLineListBox, ValueSelectHdl)); + m_xLineIV->connect_selection_changed(LINK(this, SvtLineListBox, SelectionChangedHdl)); + + // Avoid LibreOffice Kit crash: tooltip handlers cause segfault during JSDialog + // serialization when popup widgets are destroyed/recreated during character formatting resets. + // Tooltip event binding is not needed for LibreOffice Kit + if (!comphelper::LibreOfficeKit::isActive()) + { + m_xLineIV->connect_query_tooltip(LINK(this, SvtLineListBox, QueryTooltipHdl)); + } m_xNoneButton->connect_clicked(LINK(this, SvtLineListBox, NoneHdl)); @@ -1528,7 +1531,7 @@ void SvtLineListBox::GrabFocus() if (GetSelectEntryStyle() == SvxBorderLineStyle::NONE) m_xNoneButton->grab_focus(); else - m_xLineSet->GrabFocus(); + m_xLineIV->grab_focus(); } IMPL_LINK(SvtLineListBox, ToggleHdl, weld::Toggleable&, rButton, void) @@ -1546,7 +1549,7 @@ IMPL_LINK_NOARG(SvtLineListBox, StyleUpdatedHdl, weld::Widget&, void) IMPL_LINK_NOARG(SvtLineListBox, NoneHdl, weld::Button&, void) { SelectEntry(SvxBorderLineStyle::NONE); - ValueSelectHdl(nullptr); + SelectionChangedHdl(*m_xLineIV); } SvtLineListBox::~SvtLineListBox() @@ -1570,9 +1573,26 @@ OUString SvtLineListBox::GetLineStyleName(SvxBorderLineStyle eStyle) void SvtLineListBox::SelectEntry(SvxBorderLineStyle nStyle) { if (nStyle == SvxBorderLineStyle::NONE) - m_xLineSet->SetNoSelection(); + m_xLineIV->unselect_all(); else - m_xLineSet->SelectItem(static_cast<sal_Int16>(nStyle) + 1); + { + for (int i = 0; i < m_xLineIV->n_children(); i++) + { + OUString sId = m_xLineIV->get_id(i); + + if (!sId.isEmpty()) + { + sal_Int32 nId = sId.toUInt32(); + SvxBorderLineStyle eIdStyle = static_cast<SvxBorderLineStyle>(nId - 1); + + if (eIdStyle == nStyle) + { + m_xLineIV->select(i); + break; + } + } + } + } UpdatePreview(); } @@ -1589,7 +1609,7 @@ void SvtLineListBox::UpdateEntries() SvxBorderLineStyle eSelected = GetSelectEntryStyle(); // Remove the old entries - m_xLineSet->Clear(); + m_xLineIV->clear(); const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); Color aFieldColor = rSettings.GetFieldColor(); @@ -1608,17 +1628,39 @@ void SvtLineListBox::UpdateEntries() pData->GetColorLine2(aColor), pData->GetColorDist(aColor, aFieldColor), pData->GetStyle(), aBmp ); - sal_Int16 nItemId = static_cast<sal_Int16>(pData->GetStyle()) + 1; - m_xLineSet->InsertItem(nItemId, Image(aBmp), GetLineStyleName(pData->GetStyle())); + sal_Int16 nPos = static_cast<sal_Int16>(pData->GetStyle()); + VclPtr<VirtualDevice> pVDev = GetVirtualDevice(aBmp); + + OUString sId = OUString::number(nPos + 1); + m_xLineIV->append(sId, GetLineStyleName(pData->GetStyle()), pVDev.get()); if (pData->GetStyle() == eSelected) - m_xLineSet->SelectItem(nItemId); + m_xLineIV->select(n); n++; } +} + +VclPtr<VirtualDevice> SvtLineListBox::GetVirtualDevice(const BitmapEx& rBmp) +{ + constexpr tools::Long nMarginTopBottom = 5; + constexpr tools::Long nMarginLeftRight = 2; + + Size aBmpSize = rBmp.GetSizePixel(); + + Size aVDSize( + aBmpSize.Width() + 2 * nMarginLeftRight, + aBmpSize.Height() + 2 * nMarginTopBottom + ); - m_xLineSet->SetOptimalSize(); + VclPtr<VirtualDevice> pVDev = VclPtr<VirtualDevice>::Create(); + pVDev->SetOutputSizePixel(aVDSize); + + Point aDrawPos(nMarginLeftRight, nMarginTopBottom); + pVDev->DrawBitmapEx(aDrawPos, rBmp); + + return pVDev; } -IMPL_LINK_NOARG(SvtLineListBox, ValueSelectHdl, ValueSet*, void) +IMPL_LINK_NOARG(SvtLineListBox, SelectionChangedHdl, weld::IconView&, void) { maSelectHdl.Call(*this); UpdatePreview(); @@ -1626,6 +1668,34 @@ IMPL_LINK_NOARG(SvtLineListBox, ValueSelectHdl, ValueSet*, void) m_xControl->set_active(false); } +IMPL_LINK(SvtLineListBox, QueryTooltipHdl, const weld::TreeIter&, rIter, OUString) +{ + OUString sId = m_xLineIV->get_id(rIter); + if (sId.isEmpty()) + return OUString(); + + sal_Int32 nId = sId.toUInt32(); + + ImpLineListData* pData = getBorderLineData(nId - 1); + if (pData) + return GetLineStyleName(pData->GetStyle()); + + return OUString(); +} + +ImpLineListData* SvtLineListBox::getBorderLineData(sal_Int32 nId) +{ + SvxBorderLineStyle eStyle = static_cast<SvxBorderLineStyle>(nId); + + for (auto& pData : m_vLineList) + { + if (pData->GetStyle() == eStyle) + return pData.get(); + } + + return nullptr; +} + void SvtLineListBox::UpdatePreview() { SvxBorderLineStyle eStyle = GetSelectEntryStyle(); @@ -1645,13 +1715,28 @@ void SvtLineListBox::UpdatePreview() } else { - Image aImage(m_xLineSet->GetItemImage(m_xLineSet->GetSelectedItemId())); + const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); + Color aFieldColor = rSettings.GetFieldColor(); + + auto nId = m_xLineIV->get_selected_id().toInt32(); + ImpLineListData* pData = getBorderLineData(nId - 1); + + if(!pData) return; + + BitmapEx aBmp; + ImpGetLine( pData->GetLine1ForWidth( m_nWidth ), + pData->GetLine2ForWidth( m_nWidth ), + pData->GetDistForWidth( m_nWidth ), + pData->GetColorLine1(aColor), + pData->GetColorLine2(aColor), + pData->GetColorDist(aColor, aFieldColor), + pData->GetStyle(), aBmp ); + Image aImage(aBmp); m_xControl->set_label(u""_ustr); const auto nPos = (aVirDev->GetOutputSizePixel().Height() - aImage.GetSizePixel().Height()) / 2; aVirDev->Push(vcl::PushFlags::MAPMODE); aVirDev->SetMapMode(MapMode(MapUnit::MapPixel)); - const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); - aVirDev->SetBackground(rSettings.GetFieldColor()); + aVirDev->SetBackground(aFieldColor); aVirDev->Erase(); aVirDev->DrawImage(Point(0, nPos), aImage); m_xControl->set_image(aVirDev.get()); diff --git a/svtools/uiconfig/ui/linewindow.ui b/svtools/uiconfig/ui/linewindow.ui index 932642998118..eb71c2bb25a8 100644 --- a/svtools/uiconfig/ui/linewindow.ui +++ b/svtools/uiconfig/ui/linewindow.ui @@ -7,6 +7,14 @@ <property name="can-focus">False</property> <property name="icon-name">cmd/sc_square_unfilled.png</property> </object> + <object class="GtkTreeStore" id="liststore1"> + <columns> + <!-- column-name pixbuf --> + <column type="GdkPixbuf"/> + <!-- column-name id --> + <column type="gchararray"/> + </columns> + </object> <object class="GtkPopover" id="line_popup_window"> <property name="can-focus">False</property> <property name="no-show-all">True</property> @@ -37,13 +45,19 @@ </packing> </child> <child> - <object class="GtkDrawingArea" id="lineset"> + <object class="GtkIconView" id="iconview_lines"> <property name="visible">True</property> <property name="can-focus">True</property> - <property name="receives-default">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> + <property name="model">liststore1</property> + <property name="pixbuf-column">0</property> + <property name="selection-mode">single</property> + <property name="columns">1</property> + <property name="activate-on-single-click">True</property> + <property name="item-padding">2</property> + <property name="row-spacing">2</property> + <property name="margin">2</property> </object> <packing> <property name="expand">False</property> commit 7501228f69a492278fb3e982b7abd87c759f3c28 Author: Parth Raiyani <[email protected]> AuthorDate: Mon Jan 5 12:31:35 2026 +0530 Commit: Szymon Kłos <[email protected]> CommitDate: Mon Jan 5 10:44:15 2026 +0100 Enhance ExecuteAction to handle mouse events and key modifiers in IconView - There are some iconviews that needs to know about shift or ctrl keys press along with selection/activation action to behavior differently - i.e. sidebar -> border popup dialog overwrite borders if shift key is pressed while activating an item and normally applies the border if not key is pressed - To handle this behavior, we need these information(mousepressed, shiftkey, ctrlkey) from libreofficekit instance - This change parse this information and trigger mouse press to achieve that behavior Change-Id: I9568eb81e329d60ee647488fe751043a755eb817 Signed-off-by: Parth Raiyani <[email protected]> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/194688 Reviewed-by: Szymon Kłos <[email protected]> Tested-by: Jenkins CollaboraOffice <[email protected]> diff --git a/svx/source/tbxctrls/tbcontrl.cxx b/svx/source/tbxctrls/tbcontrl.cxx index 3d1fa2f92ff4..b9c99fd425ac 100644 --- a/svx/source/tbxctrls/tbcontrl.cxx +++ b/svx/source/tbxctrls/tbcontrl.cxx @@ -481,6 +481,7 @@ private: DECL_LINK( MousePressHdl, const MouseEvent&, bool ); DECL_LINK( MouseReleaseHdl, const MouseEvent&, bool ); DECL_LINK(KeyPressHdl, const KeyEvent&, bool); + DECL_LINK(KeyReleaseHdl, const KeyEvent&, bool); DECL_LINK(QueryTooltipHdl, const weld::TreeIter&, OUString); OUString GetBorderTypeTooltip(sal_uInt16 nBorderType) const; @@ -2606,6 +2607,7 @@ SvxFrameWindow_Impl::SvxFrameWindow_Impl(SvxFrameToolBoxControl* pControl, weld: mxFrameIV->connect_mouse_press(LINK(this, SvxFrameWindow_Impl, MousePressHdl)); mxFrameIV->connect_mouse_release(LINK(this, SvxFrameWindow_Impl, MouseReleaseHdl)); mxFrameIV->connect_key_press(LINK(this, SvxFrameWindow_Impl, KeyPressHdl)); + mxFrameIV->connect_key_release(LINK(this, SvxFrameWindow_Impl, KeyReleaseHdl)); mxFrameIV->connect_item_activated( LINK( this, SvxFrameWindow_Impl, SelectHdl ) ); // Avoid LibreOffice Kit crash: tooltip handlers cause segfault during JSDialog @@ -2846,6 +2848,12 @@ IMPL_LINK(SvxFrameWindow_Impl, KeyPressHdl, const KeyEvent&, rKEvt, bool) return false; } +IMPL_LINK(SvxFrameWindow_Impl, KeyReleaseHdl, const KeyEvent&, /*rKEvt*/, bool) +{ + nModifier = 0; + return true; +} + VclPtr<VirtualDevice> SvxFrameWindow_Impl::GetVirtualDevice(BitmapEx aPreviewBitmap) { VclPtr<VirtualDevice> pVDev = VclPtr<VirtualDevice>::Create(); diff --git a/vcl/jsdialog/executor.cxx b/vcl/jsdialog/executor.cxx index 080e93d82a6f..e53fd13ea7d3 100644 --- a/vcl/jsdialog/executor.cxx +++ b/vcl/jsdialog/executor.cxx @@ -687,10 +687,23 @@ bool ExecuteAction(const OUString& nWindowId, const OUString& rWidget, const Str auto pIconView = dynamic_cast<weld::IconView*>(pWidget); if (pIconView) { - if (sAction == "select") + sal_Int32 nPos = o3tl::toInt32(rData.at(u"data"_ustr)); + if (sAction == "keypress") + { + sal_uInt32 nKeyNo = rData.at(u"data"_ustr).toUInt32(); + LOKTrigger::trigger_key_press(*pIconView, + KeyEvent(nKeyNo, vcl::KeyCode(nKeyNo))); + return true; + } + else if (sAction == "keyrelease") + { + sal_uInt32 nKeyNo = rData.at(u"data"_ustr).toUInt32(); + LOKTrigger::trigger_key_release(*pIconView, + KeyEvent(nKeyNo, vcl::KeyCode(nKeyNo))); + return true; + } + else if (sAction == "select") { - sal_Int32 nPos = o3tl::toInt32(rData.at(u"data"_ustr)); - pIconView->select(nPos); LOKTrigger::trigger_changed(*pIconView); @@ -698,8 +711,6 @@ bool ExecuteAction(const OUString& nWindowId, const OUString& rWidget, const Str } else if (sAction == "activate") { - sal_Int32 nPos = o3tl::toInt32(rData.at(u"data"_ustr)); - pIconView->select(nPos); LOKTrigger::trigger_changed(*pIconView); LOKTrigger::trigger_item_activated(*pIconView); @@ -708,8 +719,6 @@ bool ExecuteAction(const OUString& nWindowId, const OUString& rWidget, const Str } else if (sAction == "contextmenu") { - sal_Int32 nPos = o3tl::toInt32(rData.at(u"data"_ustr)); - tools::Rectangle aRect = pIconView->get_rect(nPos); Point aPoint = aRect.Center(); assert(aPoint.getX() >= 0 && aPoint.getY() >= 0);
