include/vcl/weld.hxx | 11 ++++ sw/source/uibase/inc/QuickFindPanel.hxx | 6 ++ sw/source/uibase/sidebar/QuickFindPanel.cxx | 75 ++++++++++++++++++++++++++++ sw/uiconfig/swriter/ui/sidebarquickfind.ui | 71 +++++++++++++++++++++++++- 4 files changed, 161 insertions(+), 2 deletions(-)
New commits: commit 8fa5f638dbc65c30020b851b3d59bec748922c00 Author: NickWingate <[email protected]> AuthorDate: Wed Sep 10 12:59:28 2025 +0100 Commit: Szymon Kłos <[email protected]> CommitDate: Tue Sep 16 10:23:28 2025 +0200 QuickFindPanel: Add navigation (up, down) buttons We add `iter_nth_from_start` to `weld::TreeView` to access the last entry in the treeview. Signed-off-by: NickWingate <[email protected]> Change-Id: Ib2f71719a84ab6f1a73fb1c844e68c3907d69c0c Reviewed-on: https://gerrit.libreoffice.org/c/core/+/190754 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Szymon Kłos <[email protected]> diff --git a/include/vcl/weld.hxx b/include/vcl/weld.hxx index fdda5b5168bb..277686c5061f 100644 --- a/include/vcl/weld.hxx +++ b/include/vcl/weld.hxx @@ -1175,6 +1175,17 @@ public: return false; return iter_nth_sibling(rIter, nChild); } + bool iter_nth_from_start(TreeIter& rIter, int nIndex) const + { + if (!get_iter_first(rIter)) + return false; + for (int i = 0; i < nIndex; ++i) + { + if (!iter_next(rIter)) + return false; + } + return true; + } virtual bool iter_parent(TreeIter& rIter) const = 0; virtual int get_iter_depth(const TreeIter& rIter) const = 0; virtual int get_iter_index_in_parent(const TreeIter& rIter) const = 0; diff --git a/sw/source/uibase/inc/QuickFindPanel.hxx b/sw/source/uibase/inc/QuickFindPanel.hxx index 39b2ad7afa6e..4cc129ead7f5 100644 --- a/sw/source/uibase/inc/QuickFindPanel.hxx +++ b/sw/source/uibase/inc/QuickFindPanel.hxx @@ -60,8 +60,11 @@ private: std::unique_ptr<weld::Toolbar> m_xFindAndReplaceToolbar; std::unique_ptr<ToolbarUnoDispatcher> m_xFindAndReplaceToolbarDispatch; std::unique_ptr<weld::Box> m_xTopbar; + std::unique_ptr<weld::Box> m_xQuickFindControls; std::unique_ptr<weld::TreeView> m_xSearchFindsList; std::unique_ptr<weld::Label> m_xSearchFindFoundTimesLabel; + std::unique_ptr<weld::Button> m_xFindNextButton; + std::unique_ptr<weld::Button> m_xFindPreviousButton; SwWrtShell* m_pWrtShell; @@ -84,7 +87,10 @@ private: DECL_LINK(SearchFindsListMousePressHandler, const MouseEvent&, bool); DECL_LINK(SearchOptionsToolbarClickedHandler, const OUString&, void); DECL_LINK(FindAndReplaceToolbarClickedHandler, const OUString&, void); + DECL_LINK(FindNextClickedHandler, weld::Button&, void); + DECL_LINK(FindPreviousClickedHandler, weld::Button&, void); + void NavigateSearchFinds(bool bNext); void FillSearchFindsList(); static OUString CreatePageEntry(sal_Int32 nPageNum); bool IsPageEntry(const weld::TreeIter& rEntry); diff --git a/sw/source/uibase/sidebar/QuickFindPanel.cxx b/sw/source/uibase/sidebar/QuickFindPanel.cxx index 7d3ca86d2b3e..50cead0d1673 100644 --- a/sw/source/uibase/sidebar/QuickFindPanel.cxx +++ b/sw/source/uibase/sidebar/QuickFindPanel.cxx @@ -145,8 +145,11 @@ QuickFindPanel::QuickFindPanel(weld::Widget* pParent, const uno::Reference<frame , m_xFindAndReplaceToolbarDispatch( new ToolbarUnoDispatcher(*m_xFindAndReplaceToolbar, *m_xBuilder, rxFrame)) , m_xTopbar(m_xBuilder->weld_box(u"topbar"_ustr)) + , m_xQuickFindControls(m_xBuilder->weld_box(u"quickfindcontrols"_ustr)) , m_xSearchFindsList(m_xBuilder->weld_tree_view(u"searchfinds"_ustr)) , m_xSearchFindFoundTimesLabel(m_xBuilder->weld_label("numberofsearchfinds")) + , m_xFindNextButton(m_xBuilder->weld_button(u"findnext"_ustr)) + , m_xFindPreviousButton(m_xBuilder->weld_button(u"findprevious"_ustr)) , m_pWrtShell(::GetActiveWrtShell()) { if (comphelper::LibreOfficeKit::isActive()) @@ -186,6 +189,11 @@ QuickFindPanel::QuickFindPanel(weld::Widget* pParent, const uno::Reference<frame LINK(this, QuickFindPanel, SearchFindsListRowActivatedHandler)); m_xSearchFindsList->connect_mouse_press( LINK(this, QuickFindPanel, SearchFindsListMousePressHandler)); + + m_xQuickFindControls->set_visible(false); + + m_xFindNextButton->connect_clicked(LINK(this, QuickFindPanel, FindNextClickedHandler)); + m_xFindPreviousButton->connect_clicked(LINK(this, QuickFindPanel, FindPreviousClickedHandler)); } IMPL_LINK_NOARG(QuickFindPanel, SearchOptionsToolbarClickedHandler, const OUString&, void) @@ -249,6 +257,7 @@ IMPL_LINK_NOARG(QuickFindPanel, SearchFindEntryChangedHandler, weld::Entry&, voi m_xSearchFindEntry->set_message_type(weld::EntryMessageType::Normal); m_xSearchFindsList->clear(); m_xSearchFindFoundTimesLabel->set_label(OUString()); + m_xQuickFindControls->set_visible(false); } IMPL_LINK_NOARG(QuickFindPanel, SearchFindEntryActivateHandler, weld::Entry&, bool) @@ -429,6 +438,8 @@ IMPL_LINK_NOARG(QuickFindPanel, SearchFindsListSelectionChangedHandler, weld::Tr sText = sText.replaceFirst("%1", OUString::number(sId.toUInt32() + 1)); sText = sText.replaceFirst("%2", OUString::number(nSearchFindFoundTimes)); m_xSearchFindFoundTimesLabel->set_label(sText); + if (nSearchFindFoundTimes > 1) + m_xQuickFindControls->set_visible(true); SwShellCursor* pShellCursor = m_pWrtShell->GetCursor_(); std::vector<basegfx::B2DRange> vRanges; @@ -454,11 +465,73 @@ IMPL_LINK_NOARG(QuickFindPanel, SearchFindsListRowActivatedHandler, weld::TreeVi return true; } +IMPL_LINK_NOARG(QuickFindPanel, FindNextClickedHandler, weld::Button&, void) +{ + NavigateSearchFinds(true); // true = next/down +} + +IMPL_LINK_NOARG(QuickFindPanel, FindPreviousClickedHandler, weld::Button&, void) +{ + NavigateSearchFinds(false); // false = previous/up +} + +void QuickFindPanel::NavigateSearchFinds(bool bNext) +{ + if (!m_xSearchFindsList || m_xSearchFindsList->n_children() == 0) + return; + + std::unique_ptr<weld::TreeIter> xEntry(m_xSearchFindsList->make_iterator()); + + bool bMoved = false; + + // no current selection, select first/last entry + if (!m_xSearchFindsList->get_cursor(xEntry.get())) + { + bMoved = m_xSearchFindsList->iter_nth_from_start( + *xEntry, bNext ? 0 : m_xSearchFindsList->n_children() - 1); + } + else + { + if (bNext) + bMoved = m_xSearchFindsList->iter_next(*xEntry); + else + bMoved = m_xSearchFindsList->iter_previous(*xEntry); + } + + if (!bMoved) + { + bMoved = m_xSearchFindsList->iter_nth_from_start( + *xEntry, bNext ? 0 : m_xSearchFindsList->n_children() - 1); + } + + // Keep going until we find a non-page entry + while (IsPageEntry(*xEntry)) + { + // skip page entry + if (bNext) + bMoved = m_xSearchFindsList->iter_next(*xEntry); + else + bMoved = m_xSearchFindsList->iter_previous(*xEntry); + + if (!bMoved) + { + bMoved = m_xSearchFindsList->iter_nth_from_start( + *xEntry, bNext ? 0 : m_xSearchFindsList->n_children() - 1); + } + } + m_xSearchFindsList->set_cursor(*xEntry); + + // todo: ideally we dont need to manually call this handler, but select disables event propagation see SalInstanceTreeView::select(const weld::TreeIter& rIter) + m_xSearchFindsList->select(*xEntry); + SearchFindsListSelectionChangedHandler(*m_xSearchFindsList); +} + void QuickFindPanel::FillSearchFindsList() { m_vPaMs.clear(); m_xSearchFindsList->clear(); m_xSearchFindFoundTimesLabel->set_label(OUString()); + m_xQuickFindControls->set_visible(false); const OUString sFindEntry = m_xSearchFindEntry->get_text(); if (sFindEntry.isEmpty()) @@ -647,6 +720,8 @@ void QuickFindPanel::FillSearchFindsList() OUString sText(SwResId(STR_SEARCH_KEY_FOUND_TIMES, nSearchFindFoundTimes)); sText = sText.replaceFirst("%1", OUString::number(nSearchFindFoundTimes)); m_xSearchFindFoundTimesLabel->set_label(sText); + if (nSearchFindFoundTimes > 1) + m_xQuickFindControls->set_visible(true); } OUString QuickFindPanel::CreatePageEntry(sal_Int32 nPageNum) diff --git a/sw/uiconfig/swriter/ui/sidebarquickfind.ui b/sw/uiconfig/swriter/ui/sidebarquickfind.ui index 7701a42005cf..9345733622dd 100644 --- a/sw/uiconfig/swriter/ui/sidebarquickfind.ui +++ b/sw/uiconfig/swriter/ui/sidebarquickfind.ui @@ -10,6 +10,18 @@ <column type="gchararray"/> </columns> </object> + <object class="GtkImage" id="prevImage"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="icon-name">pan-up-symbolic</property> + <property name="icon_size">2</property> + </object> + <object class="GtkImage" id="nextImage"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="icon-name">pan-down-symbolic</property> + <property name="icon_size">2</property> + </object> <!-- n-columns=1 n-rows=1 --> <object class="GtkGrid" id="QuickFindPanel"> <property name="visible">True</property> @@ -156,16 +168,71 @@ </packing> </child> <child> - <object class="GtkLabel" id="numberofsearchfinds"> + <object class="GtkBox" id="searchresultscontainer"> <property name="visible">True</property> <property name="can-focus">False</property> + <property name="spacing">6</property> + <child> + <object class="GtkLabel" id="numberofsearchfinds"> + <property name="visible">True</property> + <property name="can-focus">False</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkBox" id="quickfindcontrols"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="spacing">3</property> + <child> + <object class="GtkButton" id="findprevious"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + <property name="tooltip-text">Previous Result</property> <!-- TODO: add translatable --> + <property name="image">prevImage</property> + <property name="always-show-image">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkButton" id="findnext"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + <property name="tooltip-text">Next Result</property> <!-- TODO: add translatable --> + <property name="image">nextImage</property> + <property name="always-show-image">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> - <property name="position">3</property> + <property name="position">2</property> </packing> </child> + </object> <packing> <property name="left-attach">0</property>
