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");
}