From 68f55fc9a1aa8c0f403761e39e7531ae298ea79e Mon Sep 17 00:00:00 2001 From: Vasily Melenchuk Date: Wed, 25 Oct 2023 22:26:14 +0300 Subject: [PATCH] vcl: interface for WinAPI FlashWindow() function To improve LibreOffice UX it will be great to have possibility to signal user on some event did happen, but without capuring focus and bringing LO to foreground, like it is happenings sometimes. It can be annoying. For example, if dialog window is opening slowly and user did switch to another application, this is used to inform user that there are some updates in LO window: dialog finally alive. There are somewhat confusing implementation of this feature: VCL dialog window became visible and actual Windows window should use ::FlashWindow() are very different in hierarchies, so it is somewhat challenging to find window to flash or even decide shoud we flash window or not. Change-Id: I6ca6706d2dda8902aea273ebe6e318ec9bf4beda Reviewed-on: https://gerrit.libreoffice.org/c/core/+/158472 Tested-by: Jenkins Reviewed-by: Samuel Mehrbrodt --- include/vcl/window.hxx | 3 +++ vcl/inc/brdwin.hxx | 2 ++ vcl/inc/salframe.hxx | 2 ++ vcl/inc/win/salframe.h | 1 + vcl/source/window/brdwin.cxx | 15 +++++++++++++++ vcl/source/window/window.cxx | 4 ++++ vcl/win/window/salframe.cxx | 8 ++++++++ 7 files changed, 35 insertions(+) diff --git a/include/vcl/window.hxx b/include/vcl/window.hxx index 74f1d395e9ad..f451d6f3038b 100644 --- a/include/vcl/window.hxx +++ b/include/vcl/window.hxx @@ -1119,6 +1119,9 @@ public: /// Dumps itself and potentially its children to a property tree, to be written easily to JSON. virtual void DumpAsPropertyTree(tools::JsonWriter&); + + virtual void FlashWindow() const {}; + /** @name Accessibility */ ///@{ diff --git a/vcl/inc/brdwin.hxx b/vcl/inc/brdwin.hxx index f9c8a8edb894..dbaa52ed02b2 100644 --- a/vcl/inc/brdwin.hxx +++ b/vcl/inc/brdwin.hxx @@ -169,6 +169,8 @@ public: tools::Rectangle GetMenuRect() const; virtual Size GetOptimalSize() const override; + + virtual void FlashWindow() const override; }; struct ImplBorderFrameData diff --git a/vcl/inc/salframe.hxx b/vcl/inc/salframe.hxx index f25f8de92777..a2ce50202b92 100644 --- a/vcl/inc/salframe.hxx +++ b/vcl/inc/salframe.hxx @@ -211,6 +211,8 @@ public: virtual void Beep() = 0; + virtual void FlashWindow() const {}; + // returns system data (most prominent: window handle) virtual const SystemEnvData* GetSystemData() const = 0; diff --git a/vcl/inc/win/salframe.h b/vcl/inc/win/salframe.h index de72c089b57b..b9ddea89476a 100644 --- a/vcl/inc/win/salframe.h +++ b/vcl/inc/win/salframe.h @@ -126,6 +126,7 @@ public: virtual LanguageType GetInputLanguage() override; virtual void UpdateSettings( AllSettings& rSettings ) override; virtual void Beep() override; + virtual void FlashWindow() const override; virtual const SystemEnvData* GetSystemData() const override; virtual SalPointerState GetPointerState() override; virtual KeyIndicatorState GetIndicatorState() override; diff --git a/vcl/source/window/brdwin.cxx b/vcl/source/window/brdwin.cxx index e7b569364021..d1de4daffac5 100644 --- a/vcl/source/window/brdwin.cxx +++ b/vcl/source/window/brdwin.cxx @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -2000,4 +2001,18 @@ void ImplBorderWindow::queue_resize(StateChangedType eReason) vcl::Window::queue_resize(eReason); } +void ImplBorderWindow::FlashWindow() const +{ + // We are showing top level window without focus received. Let's flash it + // Use OS features to bring user attention to this window: find topmost one and FlashWindow + vcl::Window* pMyParent = mpWindowImpl->mpParent; + while (pMyParent && pMyParent->mpWindowImpl && pMyParent->mpWindowImpl->mpParent) + { + pMyParent = pMyParent->mpWindowImpl->mpParent; + } + if (pMyParent) { + pMyParent->mpWindowImpl->mpFrame->FlashWindow(); + } +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/window/window.cxx b/vcl/source/window/window.cxx index 1e661c4151d1..62bc5029f84d 100644 --- a/vcl/source/window/window.cxx +++ b/vcl/source/window/window.cxx @@ -2322,6 +2322,10 @@ void Window::Show(bool bVisible, ShowFlags nFlags) ImplFocusToTop( ToTopFlags::NONE, false ); } + if (!HasFocus() && GetParent()) { + GetParent()->FlashWindow(); + } + // adjust mpWindowImpl->mbReallyVisible bRealVisibilityChanged = !mpWindowImpl->mbReallyVisible; ImplSetReallyVisible(); diff --git a/vcl/win/window/salframe.cxx b/vcl/win/window/salframe.cxx index 81dcf341e60d..ab20e2411e42 100644 --- a/vcl/win/window/salframe.cxx +++ b/vcl/win/window/salframe.cxx @@ -2993,6 +2993,14 @@ void WinSalFrame::Beep() MessageBeep( 0 ); } +void WinSalFrame::FlashWindow() const +{ + if (GetForegroundWindow() != mhWnd) + { + ::FlashWindow(mhWnd, TRUE); + } +} + SalFrame::SalPointerState WinSalFrame::GetPointerState() { SalPointerState aState;