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