Skip to content

Commit

Permalink
window_layouter: allow screen-change during drag
Browse files Browse the repository at this point in the history
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 genodelabs#5390
  • Loading branch information
nfeske committed Dec 4, 2024
1 parent 0b13600 commit fb123c3
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 47 deletions.
79 changes: 53 additions & 26 deletions repos/gems/src/app/window_layouter/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -213,8 +213,38 @@ 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 virtual desktop under the dragged window */
if (_drag.moving)
_with_target_change(_drag.window_id, name, [&] (Target const &from, Target const &to) {
_target_list.with_target_at(_drag.curr_pos, [&] (Target const &pointed_target) {
if (&pointed_target == &from && &pointed_target != &to)
if (from.geometry() == to.geometry())
_retarget_window(_drag.window_id, from, to); }); });

_gen_rules_with_frontmost_screen(name);
}

Expand All @@ -223,9 +253,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);

Expand Down Expand Up @@ -260,6 +290,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
{
/*
Expand All @@ -272,34 +314,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);
}

Expand Down
6 changes: 3 additions & 3 deletions repos/gems/src/app/window_layouter/target_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand All @@ -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();
Expand All @@ -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()))
Expand Down
6 changes: 3 additions & 3 deletions repos/gems/src/app/window_layouter/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
17 changes: 2 additions & 15 deletions repos/gems/src/app/window_layouter/user_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
2 changes: 2 additions & 0 deletions repos/gems/src/app/window_layouter/window.h
Original file line number Diff line number Diff line change
Expand Up @@ -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; }

/**
Expand Down

0 comments on commit fb123c3

Please sign in to comment.