include/vcl/weld/Entry.hxx | 6 -- include/vcl/weld/TextWidget.hxx | 5 ++ vcl/inc/qt5/QtInstanceTextView.hxx | 4 + vcl/inc/salvtables.hxx | 32 ++++++++----- vcl/qt5/QtInstanceTextView.cxx | 24 ++++++++++ vcl/source/app/salvtables.cxx | 86 ++++++++++++++++++------------------- vcl/source/weld/Entry.cxx | 7 --- vcl/source/weld/TextWidget.cxx | 7 +++ vcl/unx/gtk3/gtkinst.cxx | 18 +++++++ 9 files changed, 120 insertions(+), 69 deletions(-)
New commits: commit 45c8bb632502384d5f95bf7e98034e65f001de75 Author: Michael Weghorn <[email protected]> AuthorDate: Sat Jan 17 19:36:15 2026 +0100 Commit: Michael Weghorn <[email protected]> CommitDate: Sun Jan 18 09:02:59 2026 +0100 weld: Add API to get/set TextView cursor position So far, only weld::Entry had API to get/set the current cursor position, but weld::TextView didn't. It however has API to be notified about cursor position changes, via the base class weld::TextWidget::connect_cursor_position. Also implement the API to get/set the cursor position, by moving it from weld::Entry to the shared weld::TextWidget base class, and implementing it for the toolkit-specific weld::TextView subclasses. This is one step to further align the weld::Entry and weld::TextView APIs. Add new implementations for the GTK and Qt variants, using the underlying native widgets. The vcl/SalInstanceTextView implementation can reuse the existing SalInstanceEntry implementation, so move that one to the shared SalInstanceTextWidget base class. Quickly for all 3 implementations with the "Insert" -> "OLE Object" -> "QR and Bardcode..." dialog and this additional local change in place: diff --git a/cui/source/dialogs/QrCodeGenDialog.cxx b/cui/source/dialogs/QrCodeGenDialog.cxx index 6454e407bbf5..d2457df63bec 100644 --- a/cui/source/dialogs/QrCodeGenDialog.cxx +++ b/cui/source/dialogs/QrCodeGenDialog.cxx @@ -197,6 +197,8 @@ QrCodeGenDialog::QrCodeGenDialog(weld::Widget* pParent, Reference<XModel> xModel if (xSelection.is()) m_xEdittext->set_text(xSelection->getString()); } + m_xEdittext->set_text("Something random"); + m_xEdittext->set_position(4); return; } (Cursor was positioned after the 4th character, i.e. after "Some" for all implementations as expected.) Change-Id: Ia099ca560ee477bf78edb57f96e410784a2a944c Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197513 Reviewed-by: Michael Weghorn <[email protected]> Tested-by: Jenkins diff --git a/include/vcl/weld/Entry.hxx b/include/vcl/weld/Entry.hxx index 8fe36b7673b6..a2f740066c20 100644 --- a/include/vcl/weld/Entry.hxx +++ b/include/vcl/weld/Entry.hxx @@ -27,18 +27,12 @@ protected: void signal_activated(); - virtual void do_set_position(int nCursorPos) = 0; - public: virtual void set_width_chars(int nChars) = 0; virtual int get_width_chars() const = 0; // The maximum length of the entry. Use 0 for no maximum virtual void set_max_length(int nChars) = 0; - // nCursorPos can be -1 to set to the end - void set_position(int nCursorPos); - - virtual int get_position() const = 0; virtual void set_visibility(bool bVisible) = 0; virtual void set_message_type(EntryMessageType eType) = 0; virtual void set_placeholder_text(const OUString& rText) = 0; diff --git a/include/vcl/weld/TextWidget.hxx b/include/vcl/weld/TextWidget.hxx index 3449e5850db1..6f48f2d9e145 100644 --- a/include/vcl/weld/TextWidget.hxx +++ b/include/vcl/weld/TextWidget.hxx @@ -26,6 +26,7 @@ protected: void signal_cursor_position(); virtual void do_set_text(const OUString& rText) = 0; + virtual void do_set_position(int nCursorPos) = 0; virtual void do_select_region(int nStartPos, int nEndPos) = 0; virtual void do_replace_selection(const OUString& rText) = 0; @@ -33,6 +34,10 @@ public: void set_text(const OUString& rText); virtual OUString get_text() const = 0; + // nCursorPos can be -1 to set to the end + void set_position(int nCursorPos); + virtual int get_position() const = 0; + // if nStartPos or nEndPos is -1 the max available text pos will be used void select_region(int nStartPos, int nEndPos); diff --git a/vcl/inc/qt5/QtInstanceTextView.hxx b/vcl/inc/qt5/QtInstanceTextView.hxx index 8ded0693864e..3fef3b6697cd 100644 --- a/vcl/inc/qt5/QtInstanceTextView.hxx +++ b/vcl/inc/qt5/QtInstanceTextView.hxx @@ -26,6 +26,7 @@ public: virtual void do_set_text(const OUString& rText) override; virtual OUString get_text() const override; + virtual int get_position() const override; virtual void do_select_region(int nStartPos, int nEndPos) override; virtual bool get_selection_bounds(int& rStartPos, int& rEndPos) override; virtual void do_replace_selection(const OUString& rText) override; @@ -44,6 +45,9 @@ public: virtual int vadjustment_get_page_size() const override; virtual void vadjustment_set_value(int nValue) override; +protected: + virtual void do_set_position(int nCursorPos) override; + private Q_SLOTS: void handleCursorPositionChanged(); void handleTextChanged(); diff --git a/vcl/inc/salvtables.hxx b/vcl/inc/salvtables.hxx index 90f571812fd8..b53458a64be9 100644 --- a/vcl/inc/salvtables.hxx +++ b/vcl/inc/salvtables.hxx @@ -616,6 +616,11 @@ public: virtual ~SalInstanceTextWidget() override; virtual void connect_cursor_position(const Link<TextWidget&, void>& rLink) override; + + virtual int get_position() const override; + +protected: + virtual void do_set_position(int nCursorPos) override; }; class SalInstanceEntry : public SalInstanceTextWidget, public virtual weld::Entry @@ -647,10 +652,6 @@ public: virtual void do_replace_selection(const OUString& rText) override; - virtual void do_set_position(int nCursorPos) override; - - virtual int get_position() const override; - virtual void set_editable(bool bEditable) override; virtual bool get_editable() const override; diff --git a/vcl/qt5/QtInstanceTextView.cxx b/vcl/qt5/QtInstanceTextView.cxx index 37de3790249d..bf01bc4acf55 100644 --- a/vcl/qt5/QtInstanceTextView.cxx +++ b/vcl/qt5/QtInstanceTextView.cxx @@ -45,6 +45,30 @@ OUString QtInstanceTextView::get_text() const return sText; } +void QtInstanceTextView::do_set_position(int nCursorPos) +{ + SolarMutexGuard g; + + GetQtInstance().RunInMainThread([&] { + if (nCursorPos == -1) + nCursorPos = m_pTextEdit->toPlainText().length(); + + QTextCursor aCursor = m_pTextEdit->textCursor(); + aCursor.setPosition(nCursorPos); + m_pTextEdit->setTextCursor(aCursor); + }); +} + +int QtInstanceTextView::get_position() const +{ + SolarMutexGuard g; + + int nCursorPos = -1; + GetQtInstance().RunInMainThread([&] { nCursorPos = m_pTextEdit->textCursor().position(); }); + + return nCursorPos; +} + void QtInstanceTextView::do_select_region(int nStartPos, int nEndPos) { SolarMutexGuard g; diff --git a/vcl/source/app/salvtables.cxx b/vcl/source/app/salvtables.cxx index 266be8045b08..00e17ab47ca6 100644 --- a/vcl/source/app/salvtables.cxx +++ b/vcl/source/app/salvtables.cxx @@ -3312,6 +3312,16 @@ void SalInstanceTextWidget::connect_cursor_position(const Link<TextWidget&, void weld::TextWidget::connect_cursor_position(rLink); } +void SalInstanceTextWidget::do_set_position(int nCursorPos) +{ + if (nCursorPos < 0) + m_pEntry->SetCursorAtLast(); + else + m_pEntry->SetSelection(Selection(nCursorPos, nCursorPos)); +} + +int SalInstanceTextWidget::get_position() const { return m_pEntry->GetSelection().Max(); } + SalInstanceEntry::SalInstanceEntry(Edit* pEntry, SalInstanceBuilder* pBuilder, bool bTakeOwnership) : SalInstanceTextWidget(pEntry, pBuilder, bTakeOwnership) , m_xEntry(pEntry) @@ -3352,16 +3362,6 @@ void SalInstanceEntry::do_replace_selection(const OUString& rText) m_xEntry->ReplaceSelected(rText); } -void SalInstanceEntry::do_set_position(int nCursorPos) -{ - if (nCursorPos < 0) - m_xEntry->SetCursorAtLast(); - else - m_xEntry->SetSelection(Selection(nCursorPos, nCursorPos)); -} - -int SalInstanceEntry::get_position() const { return m_xEntry->GetSelection().Max(); } - void SalInstanceEntry::set_editable(bool bEditable) { m_xEntry->SetReadOnly(!bEditable); } bool SalInstanceEntry::get_editable() const { return !m_xEntry->IsReadOnly(); } diff --git a/vcl/source/weld/Entry.cxx b/vcl/source/weld/Entry.cxx index 3af623586223..fa02f0d6131a 100644 --- a/vcl/source/weld/Entry.cxx +++ b/vcl/source/weld/Entry.cxx @@ -24,13 +24,6 @@ void Entry::signal_activated() return; m_aActivateHdl.Call(*this); } - -void Entry::set_position(int nCursorPos) -{ - disable_notify_events(); - do_set_position(nCursorPos); - enable_notify_events(); -} } /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/vcl/source/weld/TextWidget.cxx b/vcl/source/weld/TextWidget.cxx index 7c61bd4726f3..3a576f595c1b 100644 --- a/vcl/source/weld/TextWidget.cxx +++ b/vcl/source/weld/TextWidget.cxx @@ -28,6 +28,13 @@ void TextWidget::set_text(const OUString& rText) enable_notify_events(); } +void TextWidget::set_position(int nCursorPos) +{ + disable_notify_events(); + do_set_position(nCursorPos); + enable_notify_events(); +} + void TextWidget::select_region(int nStartPos, int nEndPos) { disable_notify_events(); diff --git a/vcl/unx/gtk3/gtkinst.cxx b/vcl/unx/gtk3/gtkinst.cxx index 77108fb7e20d..da332771a38d 100644 --- a/vcl/unx/gtk3/gtkinst.cxx +++ b/vcl/unx/gtk3/gtkinst.cxx @@ -17700,6 +17700,24 @@ public: return sRet; } + virtual void do_set_position(int nCursorPos) override + { + if (nCursorPos == -1) + nCursorPos = gtk_text_buffer_get_char_count(m_pTextBuffer); + + GtkTextIter aTextIter; + gtk_text_buffer_get_iter_at_offset(m_pTextBuffer, &aTextIter, nCursorPos); + gtk_text_buffer_place_cursor(m_pTextBuffer, &aTextIter); + } + + virtual int get_position() const override + { + GtkTextIter aTextIter; + GtkTextMark* pInsertionMark = gtk_text_buffer_get_insert(m_pTextBuffer); + gtk_text_buffer_get_iter_at_mark(m_pTextBuffer, &aTextIter, pInsertionMark); + return gtk_text_iter_get_offset(&aTextIter); + } + virtual void do_replace_selection(const OUString& rText) override { disable_notify_events(); commit 6f9408497d56a45ef888873155927c1c08165a8f Author: Michael Weghorn <[email protected]> AuthorDate: Sat Jan 17 18:53:58 2026 +0100 Commit: Michael Weghorn <[email protected]> CommitDate: Sun Jan 18 09:02:49 2026 +0100 vcl weld: Deduplicate logic to notify about cursor pos changes Following commit 18112de4f47624458008f6be7f2bff61046795ba Author: Michael Weghorn <[email protected]> Date: Sat Jan 17 09:07:25 2026 +0100 weld: Move cursor position change signalling to TextWidget base , also deduplicate related code in SalInstanceEntry and SalInstanceTextView by moving the logic to notify about cursor position changes that is implemented the same way in both classes to a new shared base class SalInstanceTextWidget (which subclasses the abstract weld::TextWidget). Change-Id: Ic0d231af1c7ae7c93610d5a67ed8017067ff46d2 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197512 Reviewed-by: Michael Weghorn <[email protected]> Tested-by: Jenkins diff --git a/vcl/inc/salvtables.hxx b/vcl/inc/salvtables.hxx index d90c9416b057..90f571812fd8 100644 --- a/vcl/inc/salvtables.hxx +++ b/vcl/inc/salvtables.hxx @@ -605,13 +605,25 @@ public: virtual OUString filter(const OUString& rText) override; }; -class SalInstanceEntry : public SalInstanceWidget, public virtual weld::Entry +class SalInstanceTextWidget : public SalInstanceWidget, public virtual weld::TextWidget +{ + VclPtr<::Edit> m_pEntry; + + DECL_LINK(CursorListener, VclWindowEvent&, void); + +public: + SalInstanceTextWidget(Edit* pEntry, SalInstanceBuilder* pBuilder, bool bTakeOwnership); + virtual ~SalInstanceTextWidget() override; + + virtual void connect_cursor_position(const Link<TextWidget&, void>& rLink) override; +}; + +class SalInstanceEntry : public SalInstanceTextWidget, public virtual weld::Entry { private: VclPtr<::Edit> m_xEntry; DECL_LINK(ChangeHdl, Edit&, void); - DECL_LINK(CursorListener, VclWindowEvent&, void); DECL_LINK(ActivateHdl, Edit&, bool); WeldTextFilter m_aTextFilter; @@ -655,8 +667,6 @@ public: virtual void set_font_color(const Color& rColor) override; - virtual void connect_cursor_position(const Link<TextWidget&, void>& rLink) override; - virtual void set_placeholder_text(const OUString& rText) override; Edit& getEntry(); @@ -1413,7 +1423,7 @@ public: virtual ~SalInstanceToolbar() override; }; -class SalInstanceTextView : public SalInstanceWidget, public virtual weld::TextView +class SalInstanceTextView : public SalInstanceTextWidget, public virtual weld::TextView { private: VclPtr<VclMultiLineEdit> m_xTextView; @@ -1421,7 +1431,6 @@ private: DECL_LINK(ChangeHdl, Edit&, void); DECL_LINK(VscrollHdl, ScrollBar*, void); - DECL_LINK(CursorListener, VclWindowEvent&, void); public: SalInstanceTextView(VclMultiLineEdit* pTextView, SalInstanceBuilder* pBuilder, @@ -1447,8 +1456,6 @@ public: virtual void set_font_color(const Color& rColor) override; - virtual void connect_cursor_position(const Link<TextWidget&, void>& rLink) override; - virtual bool can_move_cursor_with_up() const override; virtual bool can_move_cursor_with_down() const override; diff --git a/vcl/source/app/salvtables.cxx b/vcl/source/app/salvtables.cxx index 841425ef52ed..266be8045b08 100644 --- a/vcl/source/app/salvtables.cxx +++ b/vcl/source/app/salvtables.cxx @@ -3282,8 +3282,38 @@ OUString WeldTextFilter::filter(const OUString& rText) return sText; } -SalInstanceEntry::SalInstanceEntry(Edit* pEntry, SalInstanceBuilder* pBuilder, bool bTakeOwnership) +SalInstanceTextWidget::SalInstanceTextWidget(Edit* pEntry, SalInstanceBuilder* pBuilder, + bool bTakeOwnership) : SalInstanceWidget(pEntry, pBuilder, bTakeOwnership) + , m_pEntry(pEntry) +{ +} + +SalInstanceTextWidget::~SalInstanceTextWidget() +{ + if (m_pEntry->isDisposed()) + return; + + if (m_aCursorPositionHdl.IsSet()) + m_pEntry->RemoveEventListener(LINK(this, SalInstanceTextWidget, CursorListener)); +} + +IMPL_LINK(SalInstanceTextWidget, CursorListener, VclWindowEvent&, rEvent, void) +{ + if (rEvent.GetId() == VclEventId::EditSelectionChanged + || rEvent.GetId() == VclEventId::EditCaretChanged) + signal_cursor_position(); +} + +void SalInstanceTextWidget::connect_cursor_position(const Link<TextWidget&, void>& rLink) +{ + assert(!m_aCursorPositionHdl.IsSet()); + m_pEntry->AddEventListener(LINK(this, SalInstanceTextWidget, CursorListener)); + weld::TextWidget::connect_cursor_position(rLink); +} + +SalInstanceEntry::SalInstanceEntry(Edit* pEntry, SalInstanceBuilder* pBuilder, bool bTakeOwnership) + : SalInstanceTextWidget(pEntry, pBuilder, bTakeOwnership) , m_xEntry(pEntry) , m_aTextFilter(m_aInsertTextHdl) { @@ -3393,13 +3423,6 @@ void SalInstanceEntry::set_font_color(const Color& rColor) m_xEntry->SetControlForeground(rColor); } -void SalInstanceEntry::connect_cursor_position(const Link<TextWidget&, void>& rLink) -{ - assert(!m_aCursorPositionHdl.IsSet()); - m_xEntry->AddEventListener(LINK(this, SalInstanceEntry, CursorListener)); - weld::Entry::connect_cursor_position(rLink); -} - void SalInstanceEntry::set_placeholder_text(const OUString& rText) { m_xEntry->SetPlaceholderText(rText); @@ -3450,8 +3473,6 @@ void SalInstanceEntry::set_alignment(TxtAlign eXAlign) { ::set_alignment(*m_xEnt SalInstanceEntry::~SalInstanceEntry() { - if (m_aCursorPositionHdl.IsSet()) - m_xEntry->RemoveEventListener(LINK(this, SalInstanceEntry, CursorListener)); m_xEntry->SetTextFilter(nullptr); m_xEntry->SetActivateHdl(Link<Edit&, bool>()); m_xEntry->SetModifyHdl(Link<Edit&, void>()); @@ -3459,13 +3480,6 @@ SalInstanceEntry::~SalInstanceEntry() IMPL_LINK_NOARG(SalInstanceEntry, ChangeHdl, Edit&, void) { signal_changed(); } -IMPL_LINK(SalInstanceEntry, CursorListener, VclWindowEvent&, rEvent, void) -{ - if (rEvent.GetId() == VclEventId::EditSelectionChanged - || rEvent.GetId() == VclEventId::EditCaretChanged) - signal_cursor_position(); -} - IMPL_LINK_NOARG(SalInstanceEntry, ActivateHdl, Edit&, bool) { return m_aActivateHdl.Call(*this); } static SalInstanceTreeView* g_DragSource; @@ -5597,7 +5611,7 @@ void SalInstanceLabel::set_font(const vcl::Font& rFont) SalInstanceTextView::SalInstanceTextView(VclMultiLineEdit* pTextView, SalInstanceBuilder* pBuilder, bool bTakeOwnership) - : SalInstanceWidget(pTextView, pBuilder, bTakeOwnership) + : SalInstanceTextWidget(pTextView, pBuilder, bTakeOwnership) , m_xTextView(pTextView) { // tdf#150397 don't select text when receiving keyboard focus, @@ -5667,13 +5681,6 @@ void SalInstanceTextView::set_font(const vcl::Font& rFont) m_xTextView->Invalidate(); } -void SalInstanceTextView::connect_cursor_position(const Link<TextWidget&, void>& rLink) -{ - assert(!m_aCursorPositionHdl.IsSet()); - m_xTextView->AddEventListener(LINK(this, SalInstanceTextView, CursorListener)); - weld::TextView::connect_cursor_position(rLink); -} - bool SalInstanceTextView::can_move_cursor_with_up() const { bool bNoSelection = !m_xTextView->GetSelection(); @@ -5728,8 +5735,6 @@ SalInstanceTextView::~SalInstanceTextView() { if (!m_xTextView->isDisposed()) { - if (m_aCursorPositionHdl.IsSet()) - m_xTextView->RemoveEventListener(LINK(this, SalInstanceTextView, CursorListener)); m_xTextView->SetModifyHdl(Link<Edit&, void>()); ScrollBar& rVertScrollBar = m_xTextView->GetVScrollBar(); rVertScrollBar.SetScrollHdl(m_aOrigVScrollHdl); @@ -5744,13 +5749,6 @@ IMPL_LINK(SalInstanceTextView, VscrollHdl, ScrollBar*, pScrollBar, void) IMPL_LINK_NOARG(SalInstanceTextView, ChangeHdl, Edit&, void) { signal_changed(); } -IMPL_LINK(SalInstanceTextView, CursorListener, VclWindowEvent&, rEvent, void) -{ - if (rEvent.GetId() == VclEventId::EditSelectionChanged - || rEvent.GetId() == VclEventId::EditCaretChanged) - signal_cursor_position(); -} - SalInstanceExpander::SalInstanceExpander(VclExpander* pExpander, SalInstanceBuilder* pBuilder, bool bTakeOwnership) : SalInstanceWidget(pExpander, pBuilder, bTakeOwnership)
