window_layouter: allow screen-change during drag

This patch enables the user to interactively change the assignment of
windows to screens. For screens visible side by side in a multi-monitor
setup, one can now move a window from one screen to another by dragging
the window title. When using screens as virtual desktops on one display,
a window can be moved to another screen by switching the screen (by
pressing a key matching a desired screen) while the window is dragged
with the mouse. So the user can drag the window between virtual desktops.

Issue #5390
This commit is contained in:
Norman Feske 2024-11-28 16:54:35 +01:00 committed by Christian Helmuth
parent 8496d5b02a
commit ca1b22b0aa
7 changed files with 115 additions and 47 deletions

View File

@ -45,4 +45,31 @@
<press key="KEY_9" action="screen" target="screen_9"/>
<press key="KEY_0" action="screen" target="screen_0"/>
</press>
<!-- support switching screens while dragging a window -->
<press key="BTN_LEFT">
<press key="KEY_1" action="screen" target="screen_1"/>
<press key="KEY_2" action="screen" target="screen_2"/>
<press key="KEY_3" action="screen" target="screen_3"/>
<press key="KEY_4" action="screen" target="screen_4"/>
<press key="KEY_5" action="screen" target="screen_5"/>
<press key="KEY_6" action="screen" target="screen_6"/>
<press key="KEY_7" action="screen" target="screen_7"/>
<press key="KEY_8" action="screen" target="screen_8"/>
<press key="KEY_9" action="screen" target="screen_9"/>
<press key="KEY_0" action="screen" target="screen_0"/>
<press key="KEY_SCREEN">
<press key="KEY_1" action="screen" target="screen_1"/>
<press key="KEY_2" action="screen" target="screen_2"/>
<press key="KEY_3" action="screen" target="screen_3"/>
<press key="KEY_4" action="screen" target="screen_4"/>
<press key="KEY_5" action="screen" target="screen_5"/>
<press key="KEY_6" action="screen" target="screen_6"/>
<press key="KEY_7" action="screen" target="screen_7"/>
<press key="KEY_8" action="screen" target="screen_8"/>
<press key="KEY_9" action="screen" target="screen_9"/>
<press key="KEY_0" action="screen" target="screen_0"/>
</press>
</press>
</config>

View File

@ -45,4 +45,31 @@
<press key="KEY_9" action="screen" target="screen_9"/>
<press key="KEY_0" action="screen" target="screen_0"/>
</press>
<!-- support switching screens while dragging a window -->
<press key="BTN_LEFT">
<press key="KEY_1" action="screen" target="screen_1"/>
<press key="KEY_2" action="screen" target="screen_2"/>
<press key="KEY_3" action="screen" target="screen_3"/>
<press key="KEY_4" action="screen" target="screen_4"/>
<press key="KEY_5" action="screen" target="screen_5"/>
<press key="KEY_6" action="screen" target="screen_6"/>
<press key="KEY_7" action="screen" target="screen_7"/>
<press key="KEY_8" action="screen" target="screen_8"/>
<press key="KEY_9" action="screen" target="screen_9"/>
<press key="KEY_0" action="screen" target="screen_0"/>
<press key="KEY_SCREEN">
<press key="KEY_1" action="screen" target="screen_1"/>
<press key="KEY_2" action="screen" target="screen_2"/>
<press key="KEY_3" action="screen" target="screen_3"/>
<press key="KEY_4" action="screen" target="screen_4"/>
<press key="KEY_5" action="screen" target="screen_5"/>
<press key="KEY_6" action="screen" target="screen_6"/>
<press key="KEY_7" action="screen" target="screen_7"/>
<press key="KEY_8" action="screen" target="screen_8"/>
<press key="KEY_9" action="screen" target="screen_9"/>
<press key="KEY_0" action="screen" target="screen_0"/>
</press>
</press>
</config>

View File

@ -213,8 +213,36 @@ struct Window_layouter::Main : Operations,
_gen_resize_request();
}
void screen(Target::Name const & name) override
void _with_target_change(Window_id id, Target::Name to_name, auto const &fn) const
{
_target_list.with_target(_assign_list, id, [&] (Target const &from) {
_target_list.with_target(to_name, [&] (Target const &to) {
if (&from != &to)
fn(from, to); }); });
}
void _retarget_window(Window_id id, Target const &from, Target const &to)
{
_assign_list.for_each([&] (Assign &assign) {
Window *window_ptr = nullptr;
assign.for_each_member([&] (Assign::Member &member) {
if (member.window.id() == id)
window_ptr = &member.window; });
if (window_ptr) {
assign.target_name = to.name();
window_ptr->warp(from.geometry().at - to.geometry().at);
}
});
}
void screen(Target::Name const &name) override
{
/* change of screen under the dragged window */
if (_drag.dragging())
_with_target_change(_drag.window_id, name, [&] (Target const &from, Target const &to) {
if (from.geometry() == to.geometry())
_retarget_window(_drag.window_id, from, to); });
_gen_rules_with_frontmost_screen(name);
}
@ -223,9 +251,9 @@ struct Window_layouter::Main : Operations,
if (_drag.state == Drag::State::SETTLING)
return;
_target_list.with_target_at(curr, [&] (Target const &pointed_target) {
bool const moving = (element.type == Window::Element::TITLE);
_drag = { Drag::State::DRAGGING, moving, id, curr, pointed_target.name() }; });
bool const moving = _moving(id, element);
_target_list.with_target_at(curr, [&] (Target const &pointed) {
_drag = { Drag::State::DRAGGING, moving, id, curr, pointed.geometry() }; });
to_front(id);
@ -260,6 +288,18 @@ struct Window_layouter::Main : Operations,
window.finalize_drag_operation(); });
}
bool _moving(Window_id id, Window::Element element)
{
if (element.type == Window::Element::TITLE)
return true;
/* a non-resizeable window can be moved by dragging its border */
bool resizeable = false;
_window_list.with_window(id, [&] (Window const &window) {
resizeable = window.resizeable(); });
return !resizeable && element.resize_handle();
}
void finalize_drag(Window_id id, Window::Element element, Point, Point curr) override
{
/*
@ -272,34 +312,19 @@ struct Window_layouter::Main : Operations,
_handle_hover();
_drag = { };
_target_list.with_target_at(curr, [&] (Target const &pointed_target) {
bool const moving = (element.type == Window::Element::TITLE);
_drag = { Drag::State::SETTLING, moving, id, curr, pointed_target.name() }; });
/*
* Update the target of the assign rule of the dragged window
*/
auto with_target_change = [&] (auto const fn)
{
_target_list.with_target(_assign_list, id, [&] (Target const &from) {
_target_list.with_target(_drag.target, [&] (Target const &to) {
if (&from != &to)
fn(from, to); }); });
};
if (_drag.moving) {
with_target_change([&] (Target const &from, Target const &to) {
_assign_list.for_each([&] (Assign &assign) {
Window *window_ptr = nullptr;
assign.for_each_member([&] (Assign::Member &member) {
if (member.window.id() == id)
window_ptr = &member.window; });
if (window_ptr) {
assign.target_name = to.name();
window_ptr->warp(from.geometry().at - to.geometry().at);
}
});
bool const moving = _moving(id, element);
if (moving) {
_target_list.with_target_at(curr, [&] (Target const &pointed) {
_drag = { Drag::State::SETTLING, moving, id, curr, pointed.geometry() };
_with_target_change(id, pointed.name(), [&] (Target const &from, Target const &to) {
_retarget_window(id, from, to); });
});
}
_drop_timer.trigger_once(250*1000);
}

View File

@ -147,7 +147,7 @@ class Window_layouter::Target_list
if (target.layer() >= min_layer && target.layer() <= layer)
layer = target.layer(); });
Rect const drag_origin_boundary = drag.dragging()
Rect const drag_origin_boundary = drag.dragging() && drag.moving
? target_boundary(assignments, drag.window_id)
: Rect { };
/* search target by name */
@ -159,7 +159,7 @@ class Window_layouter::Target_list
if (!target.visible())
return;
if (assignments.target_empty(target.name()) && !drag.moving_at_target(target.name()))
if (assignments.target_empty(target.name()) && !drag.moving_at_target_rect(target.geometry()))
return;
Rect const boundary = target.geometry();
@ -168,7 +168,7 @@ class Window_layouter::Target_list
generate(xml, boundary);
/* in-flux window node for the currently dragged window */
if (drag.moving_at_target(target.name()))
if (drag.moving_at_target_rect(target.geometry()))
assignments.for_each([&] (Assign const &assign) {
assign.for_each_member([&] (Assign::Member const &member) {
if (drag.moving_window(member.window.id()))

View File

@ -92,13 +92,13 @@ namespace Window_layouter {
bool moving; /* distiguish moving from resizing */
Window_id window_id;
Point curr_pos;
Name target;
Rect target_rect;
bool dragging() const { return state == State::DRAGGING; }
bool moving_at_target(Name const &name) const
bool moving_at_target_rect(Rect const &rect) const
{
return dragging() && name == target && moving;
return dragging() && rect == target_rect && moving;
}
bool moving_window(Window_id id) const

View File

@ -82,22 +82,9 @@ class Window_layouter::User_state
Focus_history &_focus_history;
/*
* Return true if key is potentially part of a key sequence
* Return true if event is potentially part of a key sequence
*/
static bool _key(Input::Keycode key) { return key != Input::BTN_LEFT; }
bool _key(Input::Event const &ev) const
{
bool relevant = false;
ev.handle_press([&] (Input::Keycode key, Codepoint) {
relevant |= _key(key); });
ev.handle_release([&] (Input::Keycode key) {
relevant |= _key(key); });
return relevant;
}
bool _key(Input::Event const &ev) const { return ev.press() || ev.release(); }
inline void _handle_event(Input::Event const &, Xml_node);

View File

@ -333,6 +333,8 @@ class Window_layouter::Window : public List_model<Window>::Element
void resizeable(bool resizeable) { _resizeable = resizeable; }
bool resizeable() const { return _resizeable; }
bool label_matches(Label const &label) const { return label == _label; }
/**