include/vcl/weld.hxx                           |    3 
 svx/source/tbxctrls/tbunosearchcontrollers.cxx |   29 ++++---
 vcl/inc/salvtables.hxx                         |    7 +
 vcl/source/app/salvtables.cxx                  |   81 ++++++++++++++++++++++
 vcl/unx/gtk3/gtkinst.cxx                       |   92 +++++++++++++++++++++++++
 5 files changed, 201 insertions(+), 11 deletions(-)

New commits:
commit d1da1c59d196b7f6037b7e0820b81fc527d56a4c
Author:     Caolán McNamara <caol...@redhat.com>
AuthorDate: Thu Apr 14 20:13:17 2022 +0100
Commit:     Caolán McNamara <caol...@redhat.com>
CommitDate: Fri Apr 15 20:44:49 2022 +0200

    tdf#148349 add a way to call the user's attention to a widget
    
    Change-Id: I2846155a44f3e51ddd8cc1acd81e84a38b4d3934
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133030
    Tested-by: Jenkins
    Reviewed-by: Caolán McNamara <caol...@redhat.com>

diff --git a/include/vcl/weld.hxx b/include/vcl/weld.hxx
index bdef5b22b9ca..226e2cb6c21a 100644
--- a/include/vcl/weld.hxx
+++ b/include/vcl/weld.hxx
@@ -320,6 +320,9 @@ public:
 
     virtual VclPtr<VirtualDevice> create_virtual_device() const = 0;
 
+    //do something transient to attract the attention of the user to the widget
+    virtual void call_attention_to() = 0;
+
     //make this widget look like a page in a notebook
     virtual void set_stack_background() = 0;
     //make this widget look like it has a highlighted background
diff --git a/svx/source/tbxctrls/tbunosearchcontrollers.cxx 
b/svx/source/tbxctrls/tbunosearchcontrollers.cxx
index 6deb930c18f2..b168462baf2d 100644
--- a/svx/source/tbxctrls/tbunosearchcontrollers.cxx
+++ b/svx/source/tbxctrls/tbunosearchcontrollers.cxx
@@ -295,22 +295,29 @@ IMPL_LINK(FindTextFieldControl, KeyInputHdl, const 
KeyEvent&, rKeyEvent, bool)
             }
         }
     }
-    // Select text in the search box when Ctrl-F pressed
-    else if ( bMod1 && nCode == KEY_F )
-        m_xWidget->select_entry_region(0, -1);
-
-    // Execute the search when Ctrl-G, F3 and Shift-RETURN pressed (in 
addition to ActivateHdl condition which handles bare RETURN)
-    else if ( (bMod1 && KEY_G == nCode) || (bShift && KEY_RETURN == nCode) || 
(KEY_F3 == nCode) )
-    {
-        ActivateFind(bShift);
-        bRet = true;
-    }
     else
     {
         auto awtKey = 
svt::AcceleratorExecute::st_VCLKey2AWTKey(rKeyEvent.GetKeyCode());
         const OUString aCommand(m_pAcc->findCommand(awtKey));
-        if (aCommand == ".uno:SearchDialog")
+
+        // Select text in the search box when Ctrl-F pressed
+        if ( bMod1 && nCode == KEY_F )
+            m_xWidget->select_entry_region(0, -1);
+        // Execute the search when Ctrl-G, F3 and Shift-RETURN pressed (in 
addition to ActivateHdl condition which handles bare RETURN)
+        else if ( (bMod1 && KEY_G == nCode) || (bShift && KEY_RETURN == nCode) 
|| (KEY_F3 == nCode) )
+        {
+            ActivateFind(bShift);
+            bRet = true;
+        }
+        else if (aCommand == ".uno:SearchDialog")
             bRet = m_pAcc->execute(awtKey);
+
+        // find-shortcut called with focus already in find
+        if (aCommand == "vnd.sun.star.findbar:FocusToFindbar")
+        {
+            m_xWidget->call_attention_to();
+            bRet = true;
+        }
     }
 
     return bRet || ChildKeyInput(rKeyEvent);
diff --git a/vcl/inc/salvtables.hxx b/vcl/inc/salvtables.hxx
index 5f8ceb6b9ae3..3f574237444b 100644
--- a/vcl/inc/salvtables.hxx
+++ b/vcl/inc/salvtables.hxx
@@ -167,10 +167,13 @@ public:
     virtual ~SalInstanceMenu() override;
 };
 
+class SalFlashAttention;
+
 class SalInstanceWidget : public virtual weld::Widget
 {
 protected:
     VclPtr<vcl::Window> m_xWidget;
+    std::unique_ptr<SalFlashAttention> m_xFlashAttention;
     SalInstanceBuilder* m_pBuilder;
 
 private:
@@ -367,6 +370,8 @@ public:
 
     virtual void get_property_tree(tools::JsonWriter& rJsonWriter) override;
 
+    virtual void call_attention_to() override;
+
     virtual void set_stack_background() override;
 
     virtual void set_title_background() override;
@@ -1000,6 +1005,8 @@ public:
 
     virtual void HandleEventListener(VclWindowEvent& rEvent) override;
 
+    virtual void call_attention_to() override;
+
     virtual ~SalInstanceComboBoxWithEdit() override;
 };
 
diff --git a/vcl/source/app/salvtables.cxx b/vcl/source/app/salvtables.cxx
index 263c29ff5eea..a8fdaf2a7978 100644
--- a/vcl/source/app/salvtables.cxx
+++ b/vcl/source/app/salvtables.cxx
@@ -559,6 +559,79 @@ VclPtr<VirtualDevice> 
SalInstanceWidget::create_virtual_device() const
                                          DeviceFormat::DEFAULT);
 }
 
+class SalFlashAttention
+{
+private:
+    VclPtr<vcl::Window> m_xWidget;
+    Timer m_aFlashTimer;
+    Color m_aOrigControlBackground;
+    Wallpaper m_aOrigBackground;
+    bool m_bOrigControlBackground;
+    int m_nFlashCount;
+
+    void SetFlash()
+    {
+        Color 
aColor(Application::GetSettings().GetStyleSettings().GetHighlightColor());
+        m_xWidget->SetControlBackground(aColor);
+    }
+
+    void ClearFlash()
+    {
+        if (m_bOrigControlBackground)
+            m_xWidget->SetControlBackground(m_aOrigControlBackground);
+        else
+            m_xWidget->SetControlBackground();
+    }
+
+    void Flash()
+    {
+        constexpr int FlashesWanted = 1;
+
+        if (m_nFlashCount % 2 == 0)
+            ClearFlash();
+        else
+            SetFlash();
+
+        if (m_nFlashCount == FlashesWanted * 2)
+            return;
+
+        ++m_nFlashCount;
+
+        m_aFlashTimer.Start();
+    }
+
+    DECL_LINK(FlashTimeout, Timer*, void);
+
+public:
+    SalFlashAttention(VclPtr<vcl::Window> xWidget)
+        : m_xWidget(xWidget)
+        , m_aFlashTimer("SalFlashAttention")
+        , m_bOrigControlBackground(false)
+        , m_nFlashCount(1)
+    {
+        m_aFlashTimer.SetTimeout(150);
+        m_aFlashTimer.SetInvokeHandler(LINK(this, SalFlashAttention, 
FlashTimeout));
+    }
+
+    void Start()
+    {
+        m_bOrigControlBackground = m_xWidget->IsControlBackground();
+        if (m_bOrigControlBackground)
+            m_aOrigControlBackground = m_xWidget->GetControlBackground();
+        m_aFlashTimer.Start();
+    }
+
+    ~SalFlashAttention() { ClearFlash(); }
+};
+
+IMPL_LINK_NOARG(SalFlashAttention, FlashTimeout, Timer*, void) { Flash(); }
+
+void SalInstanceWidget::call_attention_to()
+{
+    m_xFlashAttention.reset(new SalFlashAttention(m_xWidget));
+    m_xFlashAttention->Start();
+}
+
 css::uno::Reference<css::datatransfer::dnd::XDropTarget> 
SalInstanceWidget::get_drop_target()
 {
     return m_xWidget->GetDropTarget();
@@ -6356,6 +6429,14 @@ 
SalInstanceComboBoxWithEdit::SalInstanceComboBoxWithEdit(::ComboBox* pComboBox,
 
 bool SalInstanceComboBoxWithEdit::has_entry() const { return true; }
 
+void SalInstanceComboBoxWithEdit::call_attention_to()
+{
+    Edit* pEdit = m_xComboBox->GetSubEdit();
+    assert(pEdit);
+    m_xFlashAttention.reset(new SalFlashAttention(pEdit));
+    m_xFlashAttention->Start();
+}
+
 bool SalInstanceComboBoxWithEdit::changed_by_direct_pick() const
 {
     return m_bInSelect && !m_xComboBox->IsModifyByKeyboard() && 
!m_xComboBox->IsTravelSelect();
diff --git a/vcl/unx/gtk3/gtkinst.cxx b/vcl/unx/gtk3/gtkinst.cxx
index 50e3b7949877..65c618eeae13 100644
--- a/vcl/unx/gtk3/gtkinst.cxx
+++ b/vcl/unx/gtk3/gtkinst.cxx
@@ -2494,6 +2494,92 @@ 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:
@@ -2789,6 +2875,7 @@ 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;
@@ -4153,6 +4240,11 @@ public:
         //not implemented for the gtk variant
     }
 
+    virtual void call_attention_to() override
+    {
+        m_xFlashAttention.reset(new FlashAttention(m_pWidget));
+    }
+
     virtual void set_stack_background() override
     {
         
do_set_background(Application::GetSettings().GetStyleSettings().GetWindowColor());

Reply via email to