[vte] widget: Listen for toplevel focus change



commit d46756b89cfbb61929812ca2bd1d8c13a9504044
Author: Christian Persch <chpe src gnome org>
Date:   Wed Aug 3 22:58:02 2022 +0200

    widget: Listen for toplevel focus change
    
    Need to listen to notify::state on the toplevel the widget is in, so
    as to generate the correct focus notifications (DECSET 1004).
    
    https://gitlab.gnome.org/GNOME/vte/-/issues/2555

 src/vte.cc         |   9 +++++
 src/vteinternal.hh |   1 +
 src/widget.cc      | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/widget.hh      |  12 ++++++
 4 files changed, 135 insertions(+)
---
diff --git a/src/vte.cc b/src/vte.cc
index 601b89ad..e09ff384 100644
--- a/src/vte.cc
+++ b/src/vte.cc
@@ -7009,6 +7009,15 @@ Terminal::widget_focus_out()
        check_cursor_blink();
 }
 
+void
+Terminal::widget_root_focused_changed(bool focused) noexcept
+{
+        if (!widget_realized())
+                return;
+
+        maybe_feed_focus_event(focused);
+}
+
 void
 Terminal::widget_mouse_enter(vte::platform::MouseEvent const& event)
 {
diff --git a/src/vteinternal.hh b/src/vteinternal.hh
index 88464760..05e21d97 100644
--- a/src/vteinternal.hh
+++ b/src/vteinternal.hh
@@ -954,6 +954,7 @@ public:
                                   Alignment yalign,
                                   bool xfill,
                                   bool yfill) noexcept;
+        void widget_root_focused_changed(bool focused) noexcept;
 #endif /* VTE_GTK */
 
         void set_blink_settings(bool blink,
diff --git a/src/widget.cc b/src/widget.cc
index ddf22df9..1d28d905 100644
--- a/src/widget.cc
+++ b/src/widget.cc
@@ -395,6 +395,43 @@ catch (...)
         vte::log_exception();
 }
 
+static void
+root_realize_cb(GtkRoot* r,
+                vte::platform::Widget* that) noexcept
+try
+{
+        that->root_realize();
+}
+catch (...)
+{
+        vte::log_exception();
+}
+
+static void
+root_unrealize_cb(GtkRoot* r,
+                vte::platform::Widget* that) noexcept
+try
+{
+        that->root_unrealize();
+}
+catch (...)
+{
+        vte::log_exception();
+}
+
+static void
+root_surface_state_notify_cb(GdkToplevel* toplevel,
+                             GParamSpec* pspec,
+                             Widget* that) noexcept
+try
+{
+        that->root_surface_state_notify();
+}
+catch (...)
+{
+        vte::log_exception();
+}
+
 #endif /* VTE_GTK == 4 */
 
 Widget::Widget(VteTerminal* t)
@@ -765,6 +802,11 @@ Widget::event_focus_in(GdkEventFocus *event)
 {
        _vte_debug_print(VTE_DEBUG_EVENTS, "Focus In");
 
+#if VTE_GTK == 4
+        if (!root_focused())
+                return;
+#endif
+
         m_terminal->widget_focus_in();
 }
 
@@ -773,6 +815,11 @@ Widget::event_focus_out(GdkEventFocus *event)
 {
        _vte_debug_print(VTE_DEBUG_EVENTS, "Focus Out");
 
+#if VTE_GTK == 4
+        if (!root_focused())
+                return;
+#endif
+
         m_terminal->widget_focus_out();
 }
 
@@ -1660,9 +1707,68 @@ Widget::realize() noexcept
 
 #if VTE_GTK == 4
 
+void
+Widget::root_surface_state_notify()
+{
+        auto const r = gtk_widget_get_root(gtk());
+        auto const toplevel = GDK_TOPLEVEL(gtk_native_get_surface(GTK_NATIVE(r)));
+        auto const new_state = toplevel ? gdk_toplevel_get_state(toplevel) : GdkToplevelState(0);
+        auto const changed_mask = new_state ^ m_root_surface_state;
+
+        m_root_surface_state = new_state;
+
+        if (changed_mask & GDK_TOPLEVEL_STATE_FOCUSED) {
+                terminal()->widget_root_focused_changed(root_focused());
+        }
+}
+
+void
+Widget::root_realize()
+{
+        if (m_root_surface_state_notify_id != 0)
+                return;
+
+        auto const r = gtk_widget_get_root(gtk());
+        auto const toplevel = GDK_TOPLEVEL(gtk_native_get_surface(GTK_NATIVE(r)));
+        m_root_surface_state_notify_id = g_signal_connect(toplevel,
+                                                          "notify::state",
+                                                          G_CALLBACK(root_surface_state_notify_cb),
+                                                          this);
+
+        root_surface_state_notify();
+}
+
+void
+Widget::root_unrealize()
+{
+        root_surface_state_notify();
+        m_root_surface_state = GdkToplevelState(0);
+
+        if (m_root_surface_state_notify_id == 0)
+                return;
+
+        auto const r = gtk_widget_get_root(gtk());
+        auto const toplevel = GDK_TOPLEVEL(gtk_native_get_surface(GTK_NATIVE(r)));
+        g_signal_handler_disconnect(toplevel, m_root_surface_state_notify_id);
+        m_root_surface_state_notify_id = 0;
+}
+
 void
 Widget::root()
 {
+        auto const r = gtk_widget_get_root(gtk());
+        m_root_realize_id = g_signal_connect(r,
+                                             "realize",
+                                             G_CALLBACK(root_realize_cb),
+                                             this);
+        m_root_unrealize_id = g_signal_connect(r,
+                                               "unrealize",
+                                               G_CALLBACK(root_unrealize_cb),
+                                               this);
+
+        /* Already realised? */
+        if (gtk_widget_get_realized(GTK_WIDGET(r)))
+                root_realize();
 }
 
 #endif /* VTE_GTK == 4 */
@@ -2031,6 +2137,13 @@ Widget::unrealize() noexcept
 void
 Widget::unroot()
 {
+        root_unrealize();
+
+        auto const r = gtk_widget_get_root(gtk());
+        g_signal_handler_disconnect(r, m_root_realize_id);
+        m_root_realize_id = 0;
+        g_signal_handler_disconnect(r, m_root_unrealize_id);
+        m_root_unrealize_id = 0;
 }
 
 #endif /* VTE_GTK == 4 */
diff --git a/src/widget.hh b/src/widget.hh
index 308edb57..cd8ee771 100644
--- a/src/widget.hh
+++ b/src/widget.hh
@@ -318,6 +318,11 @@ public:
                      int* natural_baseline) noexcept;
         std::pair<bool, bool> compute_expand();
         void css_changed(GtkCssStyleChange* change);
+        void root_realize();
+        void root_unrealize();
+        void root_surface_state_notify();
+        void root_surface_focused_changed();
+        auto root_focused() const noexcept { return (m_root_surface_state & GDK_TOPLEVEL_STATE_FOCUSED) != 
0; }
         void system_setting_changed(GtkSystemSetting setting);
         void snapshot(GtkSnapshot* snapshot) noexcept { terminal()->widget_snapshot(snapshot); }
         bool contains(double x,
@@ -662,6 +667,13 @@ private:
         VteAlign m_yalign{VTE_ALIGN_START};
         bool m_xfill{true};
         bool m_yfill{true};
+
+#if VTE_GTK == 4
+        GdkToplevelState m_root_surface_state{GdkToplevelState(0)};
+        long m_root_realize_id{0};
+        long m_root_unrealize_id{0};
+        long m_root_surface_state_notify_id{0};
+#endif /* VTE_GTK == 4 */
 };
 
 } // namespace platform


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]