vcl/unx/gtk3/gtkinst.cxx |  145 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 145 insertions(+)

New commits:
commit 9220716bed8ff4337453f06788233aa73463d63b
Author:     Caolán McNamara <caol...@redhat.com>
AuthorDate: Wed Sep 7 12:24:44 2022 +0100
Commit:     Caolán McNamara <caol...@redhat.com>
CommitDate: Wed Sep 7 16:06:29 2022 +0200

    tdf#150810 get visible placeholder text in GtkEntry with focus
    
    in gtk3 focus causes placeholders to be not rendered
    https://gitlab.gnome.org/GNOME/gtk/-/issues/378
    https://bugzilla.gnome.org/show_bug.cgi?id=657613
    
    FWIW, currently in gtk4 they are shown when focused
    
    Change-Id: I623c81dd71f651d8b24a717a63fe428bc9d6a505
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/139592
    Tested-by: Jenkins
    Reviewed-by: Caolán McNamara <caol...@redhat.com>

diff --git a/vcl/unx/gtk3/gtkinst.cxx b/vcl/unx/gtk3/gtkinst.cxx
index 326c1aa9ee27..499215667252 100644
--- a/vcl/unx/gtk3/gtkinst.cxx
+++ b/vcl/unx/gtk3/gtkinst.cxx
@@ -13331,16 +13331,161 @@ public:
 
 class GtkInstanceEntry : public GtkInstanceEditable
 {
+private:
+#if !GTK_CHECK_VERSION(4, 0, 0)
+    GtkEntry* m_pEntry;
+    GtkOverlay* m_pPlaceHolderReplacement;
+    GtkLabel* m_pPlaceHolderLabel;
+    gulong m_nEntryFocusInSignalId;
+    gulong m_nEntryFocusOutSignalId;
+    gulong m_nEntryTextLengthSignalId;
+    gulong m_nEntryScrollOffsetSignalId;
+    guint m_nUpdatePlaceholderReplacementIdle;
+
+    static gboolean do_update_placeholder_replacement(gpointer widget)
+    {
+        GtkInstanceEntry* pThis = static_cast<GtkInstanceEntry*>(widget);
+        pThis->update_placeholder_replacement();
+        return false;
+    }
+
+    void update_placeholder_replacement()
+    {
+        m_nUpdatePlaceholderReplacementIdle = 0;
+
+        const char* placeholder_text = 
gtk_entry_get_placeholder_text(m_pEntry);
+        const bool bShow = placeholder_text && 
!gtk_entry_get_text_length(m_pEntry) &&
+                           gtk_widget_has_focus(GTK_WIDGET(m_pEntry));
+        if (bShow)
+        {
+            GdkRectangle text_area;
+            gtk_entry_get_text_area(m_pEntry, &text_area);
+            gint x;
+            gtk_entry_get_layout_offsets(m_pEntry, &x, nullptr);
+            gtk_widget_set_margin_start(GTK_WIDGET(m_pPlaceHolderLabel), x);
+            gtk_widget_set_margin_end(GTK_WIDGET(m_pPlaceHolderLabel), x);
+            gtk_label_set_text(m_pPlaceHolderLabel, placeholder_text);
+            gtk_widget_show(GTK_WIDGET(m_pPlaceHolderLabel));
+        }
+        else
+            gtk_widget_hide(GTK_WIDGET(m_pPlaceHolderLabel));
+    }
+
+    void launch_update_placeholder_replacement()
+    {
+        // do it in the next event cycle so the GtkEntry has done its layout
+        // and gtk_entry_get_layout_offsets returns the right results
+        if (m_nUpdatePlaceholderReplacementIdle)
+            return;
+        // G_PRIORITY_LOW so gtk's idles are run before this
+        m_nUpdatePlaceholderReplacementIdle = g_idle_add_full(G_PRIORITY_LOW, 
do_update_placeholder_replacement, this, nullptr);
+    }
+
+    static gboolean signalEntryFocusIn(GtkWidget*, GdkEvent*, gpointer widget)
+    {
+        GtkInstanceEntry* pThis = static_cast<GtkInstanceEntry*>(widget);
+        pThis->launch_update_placeholder_replacement();
+        return false;
+    }
+
+    static gboolean signalEntryFocusOut(GtkWidget*, GdkEvent*, gpointer widget)
+    {
+        GtkInstanceEntry* pThis = static_cast<GtkInstanceEntry*>(widget);
+        pThis->launch_update_placeholder_replacement();
+        return false;
+    }
+
+    static void signalEntryTextLength(void*, GParamSpec*, gpointer widget)
+    {
+        GtkInstanceEntry* pThis = static_cast<GtkInstanceEntry*>(widget);
+        pThis->launch_update_placeholder_replacement();
+    }
+
+    static void signalEntryScrollOffset(void*, GParamSpec*, gpointer widget)
+    {
+        // this property affects the x-position of the text area
+        GtkInstanceEntry* pThis = static_cast<GtkInstanceEntry*>(widget);
+        pThis->launch_update_placeholder_replacement();
+    }
+
+#endif
+
 public:
     GtkInstanceEntry(GtkEntry* pEntry, GtkInstanceBuilder* pBuilder, bool 
bTakeOwnership)
         : GtkInstanceEditable(GTK_WIDGET(pEntry), pBuilder, bTakeOwnership)
+#if !GTK_CHECK_VERSION(4, 0, 0)
+        , m_pEntry(pEntry)
+        , m_pPlaceHolderReplacement(nullptr)
+        , m_pPlaceHolderLabel(nullptr)
+        , m_nEntryFocusInSignalId(0)
+        , m_nEntryFocusOutSignalId(0)
+        , m_nEntryTextLengthSignalId(0)
+        , m_nEntryScrollOffsetSignalId(0)
+        , m_nUpdatePlaceholderReplacementIdle(0)
+#endif
     {
+#if !GTK_CHECK_VERSION(4, 0, 0)
+        // tdf#150810 fake getting placeholders visible even when GtkEntry has 
focus in gtk3.
+        // In gtk4 this works out of the box, for gtk3 fake it by having a 
GtkLabel in an
+        // overlay and show that label if the placeholder would be shown if 
there was
+        // no focus
+        const char* pPlaceHolderText = 
gtk_entry_get_placeholder_text(m_pEntry);
+        if (pPlaceHolderText ? strlen(pPlaceHolderText) : 0)
+        {
+            m_pPlaceHolderReplacement = GTK_OVERLAY(gtk_overlay_new());
+            m_pPlaceHolderLabel = GTK_LABEL(gtk_label_new(nullptr));
+
+            GtkStyleContext *pStyleContext = 
gtk_widget_get_style_context(GTK_WIDGET(m_pEntry));
+            GdkRGBA fg = { 0.5, 0.5, 0.5, 0.0 };
+            gtk_style_context_lookup_color(pStyleContext, 
"placeholder_text_color", &fg);
+
+            auto red = std::clamp(fg.red * 65535 + 0.5, 0.0, 65535.0);
+            auto green = std::clamp(fg.green * 65535 + 0.5, 0.0, 65535.0);
+            auto blue = std::clamp(fg.blue * 65535 + 0.5, 0.0, 65535.0);
+
+            PangoAttribute *pAttr = pango_attr_foreground_new(red, green, 
blue);
+            pAttr->start_index = 0;
+            pAttr->end_index = G_MAXINT;
+            PangoAttrList* pAttrList = pango_attr_list_new();
+            pango_attr_list_insert(pAttrList, pAttr);
+            gtk_label_set_attributes(m_pPlaceHolderLabel, pAttrList);
+            pango_attr_list_unref(pAttrList);
+
+            // The GtkEntry will have the placeholder as the text to analyze 
here, assumes there is no initial text, just placeholder
+            const bool bRTL = PANGO_DIRECTION_RTL == 
pango_context_get_base_dir(pango_layout_get_context(gtk_entry_get_layout(m_pEntry)));
+            SAL_WARN_IF(gtk_entry_get_text_length(m_pEntry), "vcl.gtk", "don't 
have a placeholder set, but also initial text");
+            gtk_label_set_xalign(m_pPlaceHolderLabel, bRTL ? 1.0 : 0.0);
+
+            gtk_overlay_add_overlay(m_pPlaceHolderReplacement, 
GTK_WIDGET(m_pPlaceHolderLabel));
+            insertAsParent(GTK_WIDGET(m_pEntry), 
GTK_WIDGET(m_pPlaceHolderReplacement));
+            m_nEntryFocusInSignalId = g_signal_connect_after(m_pEntry, 
"focus-in-event", G_CALLBACK(signalEntryFocusIn), this);
+            m_nEntryFocusOutSignalId = g_signal_connect_after(m_pEntry, 
"focus-out-event", G_CALLBACK(signalEntryFocusOut), this);
+            m_nEntryTextLengthSignalId = g_signal_connect(m_pEntry, 
"notify::text-length", G_CALLBACK(signalEntryTextLength), this);
+            m_nEntryScrollOffsetSignalId = g_signal_connect(m_pEntry, 
"notify::scroll-offset", G_CALLBACK(signalEntryScrollOffset), this);
+        }
+#endif
     }
 
     virtual void set_font(const vcl::Font& rFont) override
     {
         m_aCustomFont.use_custom_font(&rFont, u"entry");
     }
+
+#if !GTK_CHECK_VERSION(4, 0, 0)
+    virtual ~GtkInstanceEntry() override
+    {
+        if (m_nUpdatePlaceholderReplacementIdle)
+            g_source_remove(m_nUpdatePlaceholderReplacementIdle);
+        if (m_nEntryFocusInSignalId)
+            g_signal_handler_disconnect(m_pEntry, m_nEntryFocusInSignalId);
+        if (m_nEntryFocusOutSignalId)
+            g_signal_handler_disconnect(m_pEntry, m_nEntryFocusOutSignalId);
+        if (m_nEntryTextLengthSignalId)
+            g_signal_handler_disconnect(m_pEntry, m_nEntryTextLengthSignalId);
+        if (m_nEntryScrollOffsetSignalId)
+            g_signal_handler_disconnect(m_pEntry, 
m_nEntryScrollOffsetSignalId);
+    }
+#endif
 };
 
 }

Reply via email to