vcl/unx/gtk3/gtkdata.cxx |   16 +++++-
 vcl/unx/gtk3/gtkinst.cxx |  114 ++++++++++-------------------------------------
 2 files changed, 40 insertions(+), 90 deletions(-)

New commits:
commit 689d1f5dd3dc12d9f85ba98702a66c7af20849dc
Author:     Caolán McNamara <caol...@redhat.com>
AuthorDate: Sat Apr 16 12:30:01 2022 +0100
Commit:     Caolán McNamara <caol...@redhat.com>
CommitDate: Sun Apr 17 17:35:47 2022 +0200

    Related: tdf#148349 gtk[3|4] use a css effect to draw attention to the 
widget
    
    Change-Id: I96af6c87d5aeab6513ff782a0bf25554602b4953
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133105
    Tested-by: Jenkins
    Reviewed-by: Caolán McNamara <caol...@redhat.com>

diff --git a/vcl/unx/gtk3/gtkdata.cxx b/vcl/unx/gtk3/gtkdata.cxx
index 53f897420299..cc8535ff2ad2 100644
--- a/vcl/unx/gtk3/gtkdata.cxx
+++ b/vcl/unx/gtk3/gtkdata.cxx
@@ -462,7 +462,7 @@ static GtkStyleProvider* CreateStyleProvider()
 #if GTK_CHECK_VERSION(4, 0, 0)
       // we basically assumed during dialog design that the frame's were 
invisible, because
       // they used to be in the default theme during gtk3
-      "frame { border-style: none;  }"
+      "frame { border-style: none; }"
 #endif
       "notebook.overflow > header.top > tabs > tab:checked { "
       "box-shadow: none; padding: 0 0 0 0; margin: 0 0 0 0;"
@@ -470,7 +470,19 @@ static GtkStyleProvider* CreateStyleProvider()
       "background-image: none; background-color: transparent;"
       "border-radius: 0 0 0 0; border-width: 0 0 0 0;"
       "border-style: none; border-color: transparent;"
-      "opacity: 0; min-height: 0; min-width: 0; }";
+      "opacity: 0; min-height: 0; min-width: 0; }"
+      // https://css-tricks.com/restart-css-animation/
+      // This animation appears twice with two different names so we can change
+      // the class from "call_attention_1" to "call_attention_2" to restart the
+      // animation
+      "@keyframes shinkandrestore1 { 50% { margin-left: 15px; margin-right: 
15px; opacity: 0.5; } }"
+      "@keyframes shinkandrestore2 { 50% { margin-left: 15px; margin-right: 
15px; opacity: 0.5; } }"
+      " *.call_attention_1 {"
+      "animation-name: shinkandrestore1; animation-duration: 1s; "
+      "animation-timing-function: linear; animation-iteration-count: 2; }"
+      " *.call_attention_2 {"
+      "animation-name: shinkandrestore2; animation-duration: 1s; "
+      "animation-timing-function: linear; animation-iteration-count: 2; }";
     css_provider_load_from_data(pStyleProvider, data, -1);
     return GTK_STYLE_PROVIDER(pStyleProvider);
 }
diff --git a/vcl/unx/gtk3/gtkinst.cxx b/vcl/unx/gtk3/gtkinst.cxx
index 65c618eeae13..ca8c893d3915 100644
--- a/vcl/unx/gtk3/gtkinst.cxx
+++ b/vcl/unx/gtk3/gtkinst.cxx
@@ -2494,92 +2494,6 @@ void set_buildable_id(GtkBuildable* pWidget, const 
OString& rId)
 
 namespace {
 
-class FlashAttention
-{
-private:
-    GtkWidget* m_pWidget;
-    int m_nFlashCount;
-    gint m_nFlashTimeout;
-
-    static gboolean signalDraw(GtkWidget* pWidget, cairo_t* cr, gpointer self)
-    {
-        FlashAttention* pThis = static_cast<FlashAttention*>(self);
-        if (pThis->m_nFlashCount % 2 == 0)
-            return false;
-
-        GtkAllocation alloc {0, 0,
-                             gtk_widget_get_allocated_width(pWidget),
-                             gtk_widget_get_allocated_height(pWidget)};
-
-        Color 
aColor(Application::GetSettings().GetStyleSettings().GetHighlightColor());
-        cairo_set_source_rgba(cr, aColor.GetRed() / 255.0, aColor.GetGreen() / 
255.0, aColor.GetBlue() / 255.0, 0.5);
-        cairo_rectangle(cr, alloc.x + 0.5, alloc.y + 0.5, alloc.width - 1, 
alloc.height - 1);
-        cairo_fill(cr);
-
-        return false;
-    }
-
-    static void signalUnmap(gpointer self)
-    {
-        FlashAttention* pThis = static_cast<FlashAttention*>(self);
-        pThis->ClearFlash();
-    }
-
-    void ClearFlash()
-    {
-        if (m_nFlashTimeout != 0)
-        {
-            g_source_remove(m_nFlashTimeout);
-            m_nFlashTimeout = 0;
-        }
-        if (m_pWidget)
-        {
-            gtk_widget_queue_draw(m_pWidget);
-            g_signal_handlers_disconnect_by_func(m_pWidget, 
reinterpret_cast<void*>(signalDraw), this);
-            g_signal_handlers_disconnect_by_func(m_pWidget, 
reinterpret_cast<void*>(signalUnmap), this);
-            m_pWidget = nullptr;
-        }
-    }
-
-    bool QueueFlash()
-    {
-        constexpr int FlashesWanted = 1;
-
-        gtk_widget_queue_draw(m_pWidget);
-        m_nFlashCount++;
-
-        if (m_nFlashCount == FlashesWanted * 2)
-        {
-            ClearFlash();
-            return false;
-        }
-
-        return true;
-    }
-
-    static gboolean FlashTimeout(FlashAttention* pThis)
-    {
-        return pThis->QueueFlash();
-    }
-
-public:
-    FlashAttention(GtkWidget* pWidget)
-        : m_pWidget(pWidget)
-        , m_nFlashCount(1)
-    {
-        g_signal_connect_after(m_pWidget, "draw", G_CALLBACK(signalDraw), 
this);
-        g_signal_connect_swapped(m_pWidget, "unmap", G_CALLBACK(signalUnmap), 
this);
-        gtk_widget_queue_draw(m_pWidget);
-
-        m_nFlashTimeout = g_timeout_add(250, 
reinterpret_cast<GSourceFunc>(FlashTimeout), this);
-    }
-
-    ~FlashAttention()
-    {
-        ClearFlash();
-    }
-};
-
 class GtkInstanceWidget : public virtual weld::Widget
 {
 protected:
@@ -2875,7 +2789,6 @@ private:
 #if !GTK_CHECK_VERSION(4, 0, 0)
     GdkDragAction m_eDragAction;
 #endif
-    std::unique_ptr<FlashAttention> m_xFlashAttention;
     gulong m_nFocusInSignalId;
     gulong m_nMnemonicActivateSignalId;
     gulong m_nFocusOutSignalId;
@@ -4242,7 +4155,32 @@ public:
 
     virtual void call_attention_to() override
     {
-        m_xFlashAttention.reset(new FlashAttention(m_pWidget));
+        // Change the class name to restart the animation under
+        // its other name: https://css-tricks.com/restart-css-animation/
+#if GTK_CHECK_VERSION(4, 0, 0)
+        if (gtk_widget_has_css_class(m_pWidget, "call_attention_1"))
+        {
+            gtk_widget_remove_css_class(m_pWidget, "call_attention_1");
+            gtk_widget_add_css_class(m_pWidget, "call_attention_2");
+        }
+        else
+        {
+            gtk_widget_remove_css_class(m_pWidget, "call_attention_2");
+            gtk_widget_add_css_class(m_pWidget, "call_attention_1");
+        }
+#else
+        GtkStyleContext *pWidgetContext = 
gtk_widget_get_style_context(m_pWidget);
+        if (gtk_style_context_has_class(pWidgetContext, "call_attention_1"))
+        {
+            gtk_style_context_remove_class(pWidgetContext, "call_attention_1");
+            gtk_style_context_add_class(pWidgetContext, "call_attention_2");
+        }
+        else
+        {
+            gtk_style_context_remove_class(pWidgetContext, "call_attention_2");
+            gtk_style_context_add_class(pWidgetContext, "call_attention_1");
+        }
+#endif
     }
 
     virtual void set_stack_background() override

Reply via email to