blob: 297d07541ca28b8a7968422770fe9e5bed72584d (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
|
// *****************************************************************************
// * This file is part of the FreeFileSync project. It is distributed under *
// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
// *****************************************************************************
#ifndef FOCUS_1084731021985757843
#define FOCUS_1084731021985757843
#include <wx/toplevel.h>
namespace zen
{
//pretty much the same like "bool wxWindowBase::IsDescendant(wxWindowBase* child) const" but without the obvious misnomer
inline
bool isComponentOf(const wxWindow* child, const wxWindow* top)
{
for (const wxWindow* wnd = child; wnd != nullptr; wnd = wnd->GetParent())
if (wnd == top)
return true;
return false;
}
inline
wxWindow& getRootWindow(wxWindow& child)
{
wxWindow* root = &child;
for (;;)
if (wxWindow* parent = root->GetParent())
root = parent;
else
return *root;
}
inline
wxTopLevelWindow* getTopLevelWindow(wxWindow* child)
{
for (wxWindow* wnd = child; wnd != nullptr; wnd = wnd->GetParent())
if (auto tlw = dynamic_cast<wxTopLevelWindow*>(wnd)) //why does wxWidgets use wxWindows::IsTopLevel() ??
return tlw;
return nullptr;
}
/*
Preserving input focus has to be more clever than:
wxWindow* oldFocus = wxWindow::FindFocus();
ZEN_ON_SCOPE_EXIT(if (oldFocus) oldFocus->SetFocus());
=> wxWindow::SetFocus() internally calls Win32 ::SetFocus, which calls ::SetActiveWindow, which - lord knows why - changes the foreground window to the focus window
even if the user is currently busy using a different app! More curiosity: this foreground focus stealing happens only during the *first* SetFocus() after app start!
It also can be avoided by changing focus back and forth with some other app after start => wxWidgets bug or Win32 feature???
*/
struct FocusPreserver
{
FocusPreserver()
{
if (wxWindow* win = wxWindow::FindFocus())
setFocus(win);
}
~FocusPreserver()
{
//wxTopLevelWindow::IsActive() does NOT call Win32 ::GetActiveWindow()!
//Instead it checks if ::GetFocus() is set somewhere inside the top level
//Note: Both Win32 active and focus windows are *thread-local* values, while foreground window is global! https://devblogs.microsoft.com/oldnewthing/20131016-00/?p=2913
if (oldFocusId_ != wxID_ANY)
if (wxWindow* oldFocusWin = wxWindow::FindWindowById(oldFocusId_))
if (wxTopLevelWindow* topWin = getTopLevelWindow(oldFocusWin))
if (topWin->IsActive()) //Linux/macOS: already behaves just like ::GetForegroundWindow() on Windows!
oldFocusWin->SetFocus();
}
wxWindowID getFocusId() const { return oldFocusId_; }
void setFocus(wxWindow* win)
{
oldFocusId_ = win->GetId();
assert(oldFocusId_ != wxID_ANY);
}
private:
wxWindowID oldFocusId_ = wxID_ANY;
//don't store wxWindow* which may be dangling during ~FocusPreserver()!
//test: click on delete folder pair and immediately press F5 => focus window (= FP del button) is defer-deleted during sync
};
}
#endif //FOCUS_1084731021985757843
|