diff --git a/repos/gems/recipes/raw/motif_wm/layouter.config b/repos/gems/recipes/raw/motif_wm/layouter.config index 1631c5ccc9..f9a6ebd7a4 100644 --- a/repos/gems/recipes/raw/motif_wm/layouter.config +++ b/repos/gems/recipes/raw/motif_wm/layouter.config @@ -26,12 +26,23 @@ - + + + + + + + + + + + + diff --git a/repos/gems/recipes/raw/window_layouter/window_layouter.config b/repos/gems/recipes/raw/window_layouter/window_layouter.config index 1631c5ccc9..f9a6ebd7a4 100644 --- a/repos/gems/recipes/raw/window_layouter/window_layouter.config +++ b/repos/gems/recipes/raw/window_layouter/window_layouter.config @@ -26,12 +26,23 @@ - + + + + + + + + + + + + diff --git a/repos/gems/src/app/window_layouter/command.h b/repos/gems/src/app/window_layouter/command.h index 2461286175..3de2cacb57 100644 --- a/repos/gems/src/app/window_layouter/command.h +++ b/repos/gems/src/app/window_layouter/command.h @@ -22,7 +22,7 @@ namespace Window_layouter { class Command; } struct Window_layouter::Command { enum Type { NONE, NEXT_WINDOW, PREV_WINDOW, RAISE_WINDOW, TOGGLE_FULLSCREEN, - NEXT_TAB, PREV_TAB, SCREEN, RELEASE_GRAB, }; + NEXT_TAB, PREV_TAB, SCREEN, RELEASE_GRAB, PICK_UP, PLACE_DOWN }; Type type; Target::Name target; @@ -37,6 +37,8 @@ struct Window_layouter::Command if (string == "toggle_fullscreen") return TOGGLE_FULLSCREEN; if (string == "screen") return SCREEN; if (string == "release_grab") return RELEASE_GRAB; + if (string == "pick_up") return PICK_UP; + if (string == "place_down") return PLACE_DOWN; warning("cannot convert \"", string, "\" to action type"); return NONE; diff --git a/repos/gems/src/app/window_layouter/main.cc b/repos/gems/src/app/window_layouter/main.cc index 9e80fad85b..e7598678b3 100644 --- a/repos/gems/src/app/window_layouter/main.cc +++ b/repos/gems/src/app/window_layouter/main.cc @@ -48,6 +48,7 @@ struct Window_layouter::Main : User_state::Action, Timer::Connection _drop_timer { _env }; Drag _drag { }; + Pick _pick { }; Signal_handler
_drop_timer_handler { _env.ep(), *this, &Main::_handle_drop_timer }; @@ -247,7 +248,7 @@ struct Window_layouter::Main : User_state::Action, fn(from, to); }); }); } - void _retarget_window(Window_id id, Target const &from, Target const &to) + void _with_retargeted_window(Window_id id, Target const &to, auto const &fn) { _assign_list.for_each([&] (Assign &assign) { Window *window_ptr = nullptr; @@ -256,18 +257,44 @@ struct Window_layouter::Main : User_state::Action, window_ptr = &member.window; }); if (window_ptr) { assign.target_name = to.name; - window_ptr->warp(from.rect.at - to.rect.at); + fn(*window_ptr); } }); } + void _retarget_window(Window_id id, Target const &to) + { + _with_retargeted_window(id, to, [&] (Window &) { }); + } + + void _retarget_and_warp_window(Window_id id, Target const &from, Target const &to) + { + _with_retargeted_window(id, to, [&] (Window &window) { + window.warp(from.rect.at - to.rect.at); }); + } + void screen(Target::Name const &name) override { + auto with_visible_geometry = [&] (Rect orig, Area target_area, auto const &fn) + { + Area const overlap = Rect::intersect(orig, { { }, target_area }).area; + bool const visible = (overlap.w > 50) && (overlap.h > 50); + + fn(visible ? orig : Rect { { }, orig.area }); + }; + + /* change screen under picked window */ + if (_pick.picked) + _with_target_change(_pick.window_id, name, [&] (Target const &, Target const &to) { + _with_retargeted_window(_pick.window_id, to, [&] (Window &window) { + with_visible_geometry(_pick.orig_geometry, to.rect.area, [&] (Rect rect) { + window.outer_geometry(rect); }); }); }); + /* 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.rect == to.rect) - _retarget_window(_drag.window_id, from, to); }); + _retarget_window(_drag.window_id, to); }); /* repeated activation of screen moves focus to the screen */ bool already_visible = false; @@ -287,6 +314,18 @@ struct Window_layouter::Main : User_state::Action, _gen_rules_with_frontmost_screen(name); } + void pick_up(Window_id id) override + { + _window_list.with_window(id, [&] (Window const &window) { + _pick = { .picked = true, + .window_id = id, + .orig_geometry = window.outer_geometry() }; + to_front(id); + }); + } + + void place_down() override { _pick = { }; } + void drag(Window_id id, Window::Element element, Point clicked, Point curr) override { if (_drag.state == Drag::State::SETTLING) @@ -362,7 +401,7 @@ struct Window_layouter::Main : User_state::Action, _target_list.with_target_at(curr, [&] (Target const &pointed) { _drag = { Drag::State::SETTLING, moving, id, curr, pointed.rect }; _with_target_change(id, pointed.name, [&] (Target const &from, Target const &to) { - _retarget_window(id, from, to); }); + _retarget_and_warp_window(id, from, to); }); }); } diff --git a/repos/gems/src/app/window_layouter/types.h b/repos/gems/src/app/window_layouter/types.h index 205c336b0d..257769d1b8 100644 --- a/repos/gems/src/app/window_layouter/types.h +++ b/repos/gems/src/app/window_layouter/types.h @@ -106,6 +106,13 @@ namespace Window_layouter { return dragging() && id == window_id && moving; } }; + + struct Pick + { + bool picked; + Window_id window_id; + Rect orig_geometry; + }; } #endif /* _TYPES_H_ */ diff --git a/repos/gems/src/app/window_layouter/user_state.h b/repos/gems/src/app/window_layouter/user_state.h index d44343c13d..6221a86e00 100644 --- a/repos/gems/src/app/window_layouter/user_state.h +++ b/repos/gems/src/app/window_layouter/user_state.h @@ -34,6 +34,8 @@ class Window_layouter::User_state virtual void to_front(Window_id) = 0; virtual void drag(Window_id, Window::Element, Point clicked, Point curr) = 0; virtual void finalize_drag(Window_id, Window::Element, Point clicked, Point final) = 0; + virtual void pick_up(Window_id) = 0; + virtual void place_down() = 0; virtual void screen(Target::Name const &) = 0; }; @@ -76,6 +78,8 @@ class Window_layouter::User_state */ bool _drag_init_done = false; + bool _picked_up = false; + /* * Pointer position at the beginning of a drag operation */ @@ -168,7 +172,7 @@ class Window_layouter::User_state void hover(Window_id window_id, Window::Element element) { - Window_id const last_hovered_window_id = _hovered_window_id; + Window_id const orig_hovered_window_id = _hovered_window_id; _hovered_window_id = window_id; _hovered_element = element; @@ -192,10 +196,11 @@ class Window_layouter::User_state _initiate_drag(_hovered_window_id, _hovered_element); /* - * Let focus follows the pointer + * Let focus follows the pointer, except while dragging or when + * the focused window is currently picked up. */ - if (!_drag_state && _hovered_window_id.valid() - && _hovered_window_id != last_hovered_window_id) { + if (!_drag_state && !_picked_up && _hovered_window_id.valid() + && _hovered_window_id != orig_hovered_window_id) { _focused_window_id = _hovered_window_id; _focus_history.focus(_focused_window_id); @@ -329,6 +334,20 @@ void Window_layouter::User_state::_handle_event(Input::Event const &e, _action.release_grab(); return; + case Command::PICK_UP: + if (_focused_window_id.value) { + _picked_up = true; + _action.pick_up(_focused_window_id); + } + return; + + case Command::PLACE_DOWN: + if (_picked_up) { + _action.place_down(); + _picked_up = false; + } + return; + default: warning("command ", (int)command.type, " unhanded"); }