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)

Reply via email to