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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
|
diff --git a/plugins/wm-actions/wm-actions.cpp b/plugins/wm-actions/wm-actions.cpp
index 5d7c2bc..61788b0 100644
--- a/plugins/wm-actions/wm-actions.cpp
+++ b/plugins/wm-actions/wm-actions.cpp
@@ -21,6 +21,8 @@
#include "wm-actions-signals.hpp"
#include "wayfire/nonstd/reverse.hpp"
+namespace
+{
class always_on_top_root_node_t : public wf::scene::output_node_t
{
public:
@@ -36,6 +38,45 @@ class wayfire_wm_actions_output_t : public wf::per_output_plugin_instance_t
{
wf::scene::floating_inner_ptr always_above;
bool showdesktop_active = false;
+ wf::wl_idle_call dialog_update;
+
+ static bool restore_dialog_parent(wayfire_view view)
+ {
+ auto toplevel = wf::toplevel_cast(view);
+ if (!toplevel || !toplevel->parent)
+ {
+ return false;
+ }
+
+ // Dialogs belong to their parent's scene tree. Repair dialogs detached
+ // by an older wm-actions instance during a live plugin upgrade too.
+ if (view->has_data("wm-actions-above") &&
+ view->get_root_node()->parent() != toplevel->parent->get_root_node().get())
+ {
+ wf::scene::readd_front(toplevel->parent->get_root_node(), view->get_root_node());
+ }
+
+ view->erase_data("wm-actions-above");
+ view->erase_data("wm-actions-floating-above");
+ view->erase_data("wm-actions-floating-manual");
+ return true;
+ }
+
+ void schedule_dialog_update()
+ {
+ // Wait for mapping/reparenting handlers before repairing dialog ownership.
+ dialog_update.run_once([this]
+ {
+ for (auto view : wf::get_core().get_all_views())
+ {
+ if (view->get_output() != output || !view->is_mapped()) continue;
+ restore_dialog_parent(view);
+ }
+ });
+ }
+
+ wf::signal::connection_t<wf::view_mapped_signal> dialog_mapped =
+ [this] (auto) { schedule_dialog_update(); };
wf::option_wrapper_t<wf::activatorbinding_t> minimize{
"wm-actions/minimize"};
@@ -61,6 +102,8 @@ class wayfire_wm_actions_output_t : public wf::per_output_plugin_instance_t
bool set_keep_above_state(wayfire_view view, bool above)
{
+ // A manual above request on a dialog applies to its whole window family.
+ view = wf::find_topmost_parent(view);
if (!view || !output->can_activate_plugin(&grab_interface))
{
return false;
@@ -73,7 +116,10 @@ class wayfire_wm_actions_output_t : public wf::per_output_plugin_instance_t
"wm-actions-above");
} else
{
- wf::scene::readd_front(output->wset()->get_node(), view->get_root_node());
+ // A tiler may already have moved the view into its own scene node.
+ // Clear the flag without taking that view out of its tiling tree.
+ if (view->get_root_node()->parent() == always_above.get())
+ wf::scene::readd_front(output->wset()->get_node(), view->get_root_node());
if (view->has_data("wm-actions-above"))
{
view->erase_data("wm-actions-above");
@@ -122,7 +168,14 @@ class wayfire_wm_actions_output_t : public wf::per_output_plugin_instance_t
wf::signal::connection_t<wf::view_moved_to_wset_signal> on_view_output_changed =
[=] (wf::view_moved_to_wset_signal *signal)
{
- if (!signal->new_wset || (signal->new_wset->get_attached_output() != output))
+ if (!signal->new_wset)
+ {
+ // A mapped independent window may have just become a dialog.
+ schedule_dialog_update();
+ return;
+ }
+
+ if (signal->new_wset->get_attached_output() != output)
{
return;
}
@@ -138,6 +191,7 @@ class wayfire_wm_actions_output_t : public wf::per_output_plugin_instance_t
{
wf::scene::readd_front(always_above, view->get_root_node());
}
+ schedule_dialog_update();
};
/**
@@ -152,10 +206,12 @@ class wayfire_wm_actions_output_t : public wf::per_output_plugin_instance_t
return;
}
- if (ev->view->has_data("wm-actions-above") && !ev->view->minimized)
+ if (!restore_dialog_parent(ev->view) &&
+ ev->view->has_data("wm-actions-above") && !ev->view->minimized)
{
wf::scene::readd_front(always_above, ev->view->get_root_node());
}
+ schedule_dialog_update();
};
void check_disable_showdesktop(wayfire_view view)
@@ -222,7 +278,7 @@ class wayfire_wm_actions_output_t : public wf::per_output_plugin_instance_t
*/
wf::activator_callback on_toggle_above = [=] (auto ev) -> bool
{
- auto view = choose_view(ev.source);
+ auto view = wf::find_topmost_parent(choose_view(ev.source));
if (view)
{
return set_keep_above_state(view, !view->has_data("wm-actions-above"));
@@ -384,17 +440,30 @@ class wayfire_wm_actions_output_t : public wf::per_output_plugin_instance_t
output->add_activator(bring_to_front, &on_bring_to_front);
output->connect(&on_set_above_state_signal);
output->connect(&on_view_minimized);
+ output->connect(&dialog_mapped);
wf::get_core().connect(&on_view_output_changed);
+ schedule_dialog_update();
}
void fini() override
{
+ dialog_update.disconnect();
+ for (auto view : wf::get_core().get_all_views())
+ {
+ if (view->get_output() == output)
+ {
+ restore_dialog_parent(view);
+ }
+ }
+
for (auto view : output->wset()->get_views())
{
if (view->has_data("wm-actions-above"))
{
set_keep_above_state(view, false);
}
+ view->erase_data("wm-actions-floating-above");
+ view->erase_data("wm-actions-floating-manual");
}
wf::scene::remove_child(always_above);
@@ -526,4 +595,5 @@ class wayfire_wm_actions_t : public wf::plugin_interface_t,
};
};
+}
DECLARE_WAYFIRE_PLUGIN(wayfire_wm_actions_t);
|