svx/source/tbxctrls/linectrl.cxx | 153 +++++++++++++++++++++++-------------- svx/uiconfig/ui/floatinglineend.ui | 38 ++++++--- 2 files changed, 122 insertions(+), 69 deletions(-)
New commits: commit 3d80ea1e93b73e9a93d0debee0d82cbb17f856db Author: Parth Raiyani <[email protected]> AuthorDate: Tue Aug 19 16:37:57 2025 +0530 Commit: Caolán McNamara <[email protected]> CommitDate: Wed Feb 25 09:18:09 2026 +0100 Replaces ValueSet with IconView for line end selection - Updates the UI layout to use GtkIconView with better configuration options. - Simplifies logic for filling and updating the selection view. - Adds support for tooltips and single-click activation. - Removes obsolete code related to ValueSet, including custom sizing logic. Change-Id: I2bb486cc4e062fcfaec5c478958337b4af59c47b Signed-off-by: Parth Raiyani <[email protected]> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/189880 Reviewed-by: Szymon Kłos <[email protected]> Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/200217 Reviewed-by: Caolán McNamara <[email protected]> diff --git a/svx/source/tbxctrls/linectrl.cxx b/svx/source/tbxctrls/linectrl.cxx index ef901fcfb24e..b2941241529f 100644 --- a/svx/source/tbxctrls/linectrl.cxx +++ b/svx/source/tbxctrls/linectrl.cxx @@ -247,18 +247,16 @@ class SvxLineEndWindow final : public WeldToolbarPopup private: XLineEndListRef mpLineEndList; rtl::Reference<SvxLineEndToolBoxControl> mxControl; - std::unique_ptr<ValueSet> mxLineEndSet; - std::unique_ptr<weld::CustomWeld> mxLineEndSetWin; - sal_uInt16 mnLines; + std::unique_ptr<weld::IconView> mxLineEndIV; Size maBmpSize; - DECL_LINK( SelectHdl, ValueSet*, void ); - void FillValueSet(); - void SetSize(); + DECL_LINK( ItemActivatedHdl, weld::IconView&, bool ); + DECL_LINK(QueryTooltipHdl, const weld::TreeIter&, OUString); + void FillIconView(); virtual void GrabFocus() override { - mxLineEndSet->GrabFocus(); + mxLineEndIV->grab_focus(); } public: @@ -268,17 +266,12 @@ public: } -constexpr sal_uInt16 gnCols = 2; - SvxLineEndWindow::SvxLineEndWindow(SvxLineEndToolBoxControl* pControl, weld::Widget* pParent) : WeldToolbarPopup(pControl->getFrameInterface(), pParent, u"svx/ui/floatinglineend.ui"_ustr, u"FloatingLineEnd"_ustr) , mxControl(pControl) - , mxLineEndSet(new ValueSet(m_xBuilder->weld_scrolled_window(u"valuesetwin"_ustr, true))) - , mxLineEndSetWin(new weld::CustomWeld(*m_xBuilder, u"valueset"_ustr, *mxLineEndSet)) - , mnLines(12) + , mxLineEndIV(m_xBuilder->weld_icon_view(u"floating_line_end_iconview"_ustr)) { - mxLineEndSet->SetStyle(mxLineEndSet->GetStyle() | WB_ITEMBORDER | WB_3DLOOK | WB_NO_DIRECTSELECT); - mxLineEndSet->SetHelpId(HID_POPUP_LINEEND_CTRL); + mxLineEndIV->set_help_id(HID_POPUP_LINEEND_CTRL); m_xTopLevel->set_help_id(HID_POPUP_LINEEND); SfxObjectShell* pDocSh = SfxObjectShell::Current(); @@ -290,20 +283,32 @@ SvxLineEndWindow::SvxLineEndWindow(SvxLineEndToolBoxControl* pControl, weld::Wid } DBG_ASSERT( mpLineEndList.is(), "LineEndList not found" ); - mxLineEndSet->SetSelectHdl( LINK( this, SvxLineEndWindow, SelectHdl ) ); - mxLineEndSet->SetColCount( gnCols ); + mxLineEndIV->connect_item_activated( LINK( this, SvxLineEndWindow, ItemActivatedHdl ) ); - // ValueSet fill with entries of LineEndList - FillValueSet(); + // 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()) + { + mxLineEndIV->connect_query_tooltip(LINK(this, SvxLineEndWindow, QueryTooltipHdl)); + } + + // IconView fill with entries of LineEndList + FillIconView(); AddStatusListener( u".uno:LineEndListState"_ustr); } -IMPL_LINK_NOARG(SvxLineEndWindow, SelectHdl, ValueSet*, void) +IMPL_LINK_NOARG(SvxLineEndWindow, ItemActivatedHdl, weld::IconView&, bool) { std::unique_ptr<XLineEndItem> pLineEndItem; std::unique_ptr<XLineStartItem> pLineStartItem; - sal_uInt16 nId = mxLineEndSet->GetSelectedItemId(); + + OUString sId = mxLineEndIV->get_selected_id(); + if (sId.isEmpty()) + return false; + + sal_uInt32 nId = sId.toUInt32(); if( nId == 1 ) { @@ -342,14 +347,54 @@ IMPL_LINK_NOARG(SvxLineEndWindow, SelectHdl, ValueSet*, void) /* #i33380# DR 2004-09-03 Moved the following line above the Dispatch() call. This instance may be deleted in the meantime (i.e. when a dialog is opened while in Dispatch()), accessing members will crash in this case. */ - mxLineEndSet->SetNoSelection(); + mxLineEndIV->unselect_all(); mxControl->dispatchCommand(mxControl->getCommandURL(), aArgs); mxControl->EndPopupMode(); + + return true; +} + +IMPL_LINK(SvxLineEndWindow, QueryTooltipHdl, const weld::TreeIter&, rIter, OUString) +{ + OUString sId = mxLineEndIV->get_id(rIter); + if (sId.isEmpty()) + return OUString(); + + sal_uInt16 nId = sId.toUInt32(); + + if (nId == 1 || nId == 2) + { + return SvxResId(RID_SVXSTR_NONE); + } + + if (!mpLineEndList.is()) + return OUString(); + + tools::Long nEntryIndex; + if (nId % 2) // beginning of line + { + nEntryIndex = (nId - 1) / 2 - 1; + } + else // end of line + { + nEntryIndex = nId / 2 - 2; + } + + if (nEntryIndex >= 0 && nEntryIndex < mpLineEndList->Count()) + { + const XLineEndEntry* pEntry = mpLineEndList->GetLineEnd(nEntryIndex); + if (pEntry) + { + return pEntry->GetName(); + } + } + + return OUString(); } -void SvxLineEndWindow::FillValueSet() +void SvxLineEndWindow::FillIconView() { if( !mpLineEndList.is() ) return; @@ -375,8 +420,20 @@ void SvxLineEndWindow::FillValueSet() Point aPt1( maBmpSize.Width(), 0 ); pVD->DrawBitmap( Point(), aBmp ); - mxLineEndSet->InsertItem(1, Image(pVD->GetBitmap(aPt0, maBmpSize)), pEntry->GetName()); - mxLineEndSet->InsertItem(2, Image(pVD->GetBitmap(aPt1, maBmpSize)), pEntry->GetName()); + + // First half (left side) + ScopedVclPtrInstance< VirtualDevice > pVD1; + pVD1->SetOutputSizePixel( maBmpSize, false ); + pVD1->DrawBitmap( Point(), pVD->GetBitmap(aPt0, maBmpSize) ); + Bitmap aBmp1 = pVD1->GetBitmap(Point(), maBmpSize); + mxLineEndIV->append(u"1"_ustr, pEntry->GetName(), &aBmp1); + + // Second half (right side) + ScopedVclPtrInstance< VirtualDevice > pVD2; + pVD2->SetOutputSizePixel( maBmpSize, false ); + pVD2->DrawBitmap( Point(), pVD->GetBitmap(aPt1, maBmpSize) ); + Bitmap aBmp2 = pVD2->GetBitmap(Point(), maBmpSize); + mxLineEndIV->append(u"2"_ustr, pEntry->GetName(), &aBmp2); mpLineEndList->Remove(nCount); @@ -388,15 +445,21 @@ void SvxLineEndWindow::FillValueSet() OSL_ENSURE( !aBmp.IsEmpty(), "UI bitmap was not created" ); pVD->DrawBitmap( aPt0, aBmp ); - mxLineEndSet->InsertItem(static_cast<sal_uInt16>((i+1)*2L+1), - Image(pVD->GetBitmap(aPt0, maBmpSize)), pEntry->GetName()); - mxLineEndSet->InsertItem(static_cast<sal_uInt16>((i+2)*2L), - Image(pVD->GetBitmap(aPt1, maBmpSize)), pEntry->GetName()); - } - mnLines = std::min( static_cast<sal_uInt16>(nCount + 1), sal_uInt16(MAX_LINES) ); - mxLineEndSet->SetLineCount( mnLines ); - SetSize(); + // Left half for line start + ScopedVclPtrInstance< VirtualDevice > pVDStart; + pVDStart->SetOutputSizePixel( maBmpSize, false ); + pVDStart->DrawBitmap( Point(), pVD->GetBitmap(aPt0, maBmpSize) ); + Bitmap aBmpStart = pVDStart->GetBitmap(Point(), maBmpSize); + mxLineEndIV->append(OUString::number((i+1)*2L+1), pEntry->GetName(), &aBmpStart); + + // Right half for line end + ScopedVclPtrInstance< VirtualDevice > pVDEnd; + pVDEnd->SetOutputSizePixel( maBmpSize, false ); + pVDEnd->DrawBitmap( Point(), pVD->GetBitmap(aPt1, maBmpSize) ); + Bitmap aBmpEnd = pVDEnd->GetBitmap(Point(), maBmpSize); + mxLineEndIV->append(OUString::number((i+2)*2L), pEntry->GetName(), &aBmpEnd); + } } void SvxLineEndWindow::statusChanged( const css::frame::FeatureStateEvent& rEvent ) @@ -411,33 +474,11 @@ void SvxLineEndWindow::statusChanged( const css::frame::FeatureStateEvent& rEven mpLineEndList.set( static_cast< XLineEndList* >( xWeak.get() ) ); DBG_ASSERT( mpLineEndList.is(), "LineEndList not found" ); - mxLineEndSet->Clear(); - FillValueSet(); + mxLineEndIV->clear(); + FillIconView(); } } -void SvxLineEndWindow::SetSize() -{ - sal_uInt16 nItemCount = mxLineEndSet->GetItemCount(); - sal_uInt16 nMaxLines = nItemCount / gnCols; - - WinBits nBits = mxLineEndSet->GetStyle(); - if ( mnLines == nMaxLines ) - nBits &= ~WB_VSCROLL; - else - nBits |= WB_VSCROLL; - mxLineEndSet->SetStyle( nBits ); - - Size aSize( maBmpSize ); - aSize.AdjustWidth(6 ); - aSize.AdjustHeight(6 ); - aSize = mxLineEndSet->CalcWindowSizePixel( aSize ); - if (nBits & WB_VSCROLL) - aSize.AdjustWidth(mxLineEndSet->GetScrollWidth()); - mxLineEndSet->GetDrawingArea()->set_size_request(aSize.Width(), aSize.Height()); - mxLineEndSet->SetOutputSizePixel(aSize); -} - SvxLineEndToolBoxControl::SvxLineEndToolBoxControl( const css::uno::Reference<css::uno::XComponentContext>& rContext ) : svt::PopupWindowController( rContext, nullptr, OUString() ) { diff --git a/svx/uiconfig/ui/floatinglineend.ui b/svx/uiconfig/ui/floatinglineend.ui index 237b71c6d483..bc09457f55cc 100644 --- a/svx/uiconfig/ui/floatinglineend.ui +++ b/svx/uiconfig/ui/floatinglineend.ui @@ -2,6 +2,14 @@ <!-- Generated with glade 3.22.1 --> <interface domain="svx"> <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="GtkPopover" id="FloatingLineEnd"> <property name="can_focus">False</property> <property name="no_show_all">True</property> @@ -14,26 +22,30 @@ <property name="orientation">vertical</property> <property name="spacing">6</property> <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> <property name="vexpand">True</property> - <property name="hscrollbar_policy">never</property> + <property name="hscrollbar-policy">never</property> + <property name="vscrollbar-policy">always</property> <property name="shadow_type">in</property> + <property name="height-request">264</property> <child> - <object class="GtkViewport"> + <object class="GtkIconView" id="floating_line_end_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> - </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">2</property> + <property name="selection-mode">single</property> + <property name="activate-on-single-click">True</property> + <property name="item-padding">2</property> + <property name="row-spacing">2</property> + <property name="column-spacing">2</property> + <property name="margin">2</property> </object> </child> </object>
