diff --git a/repos/gems/recipes/raw/motif_wm/layouter.config b/repos/gems/recipes/raw/motif_wm/layouter.config
index 80bba521c84..2b1737473e2 100644
--- a/repos/gems/recipes/raw/motif_wm/layouter.config
+++ b/repos/gems/recipes/raw/motif_wm/layouter.config
@@ -65,6 +65,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/repos/gems/recipes/raw/window_layouter/window_layouter.config b/repos/gems/recipes/raw/window_layouter/window_layouter.config
index 80bba521c84..2b1737473e2 100644
--- a/repos/gems/recipes/raw/window_layouter/window_layouter.config
+++ b/repos/gems/recipes/raw/window_layouter/window_layouter.config
@@ -65,6 +65,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/repos/gems/src/app/window_layouter/command.h b/repos/gems/src/app/window_layouter/command.h
index ab0c572306a..a15ec3a1b01 100644
--- a/repos/gems/src/app/window_layouter/command.h
+++ b/repos/gems/src/app/window_layouter/command.h
@@ -22,8 +22,8 @@ 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, PICK_UP, PLACE_DOWN,
- DRAG, DROP };
+ SCREEN, RELEASE_GRAB, PICK_UP, PLACE_DOWN,
+ DRAG, DROP, FREE_ARRANGE, STRICT_ARRANGE };
Type type;
Target::Name target;
@@ -42,6 +42,8 @@ struct Window_layouter::Command
if (string == "place_down") return PLACE_DOWN;
if (string == "drag") return DRAG;
if (string == "drop") return DROP;
+ if (string == "free_arrange") return FREE_ARRANGE;
+ if (string == "strict_arrange") return STRICT_ARRANGE;
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 e7598678b37..6cd75cb9786 100644
--- a/repos/gems/src/app/window_layouter/main.cc
+++ b/repos/gems/src/app/window_layouter/main.cc
@@ -358,6 +358,55 @@ struct Window_layouter::Main : User_state::Action,
_gen_resize_request();
}
+ Window::Element free_arrange_element_at(Window_id id, Point const abs_at) override
+ {
+ using Element = Window::Element;
+ Element result { };
+
+ /* window geometry is relative to target */
+ Point at { };
+ _target_list.with_target(_assign_list, id, [&] (Target const &target) {
+ at = abs_at - target.rect.at; });
+
+ _window_list.with_window(id, [&] (Window &window) {
+ Rect const rect = window.outer_geometry();
+ if (!rect.contains(at))
+ return;
+
+ int const x_percent = (100*(at.x - rect.x1()))/rect.w(),
+ y_percent = (100*(at.y - rect.y1()))/rect.h();
+
+ auto with_rel = [&] (int rel, auto const &lo_fn, auto const &mid_fn, auto const &hi_fn)
+ {
+ if (rel > 75) hi_fn(); else if (rel > 25) mid_fn(); else lo_fn();
+ };
+
+ with_rel(x_percent,
+ [&] {
+ with_rel(y_percent,
+ [&] { result = { Element::TOP_LEFT }; },
+ [&] { result = { Element::LEFT }; },
+ [&] { result = { Element::BOTTOM_LEFT }; }); },
+ [&] {
+ with_rel(y_percent,
+ [&] { result = { Element::TOP }; },
+ [&] { result = { Element::TITLE }; },
+ [&] { result = { Element::BOTTOM }; }); },
+ [&] {
+ with_rel(y_percent,
+ [&] { result = { Element::TOP_RIGHT }; },
+ [&] { result = { Element::RIGHT }; },
+ [&] { result = { Element::BOTTOM_RIGHT }; }); }
+ );
+ });
+ return result;
+ }
+
+ void free_arrange_hover_changed() override
+ {
+ _update_window_layout();
+ }
+
void _handle_drop_timer()
{
_drag = { };
diff --git a/repos/gems/src/app/window_layouter/user_state.h b/repos/gems/src/app/window_layouter/user_state.h
index bd43f1f16df..6c4a7571e46 100644
--- a/repos/gems/src/app/window_layouter/user_state.h
+++ b/repos/gems/src/app/window_layouter/user_state.h
@@ -37,6 +37,8 @@ class Window_layouter::User_state
virtual void pick_up(Window_id) = 0;
virtual void place_down() = 0;
virtual void screen(Target::Name const &) = 0;
+ virtual void free_arrange_hover_changed() = 0;
+ virtual Window::Element free_arrange_element_at(Window_id, Point) = 0;
};
struct Hover_state
@@ -63,8 +65,9 @@ class Window_layouter::User_state
Key_sequence_tracker _key_sequence_tracker { };
- Window::Element _hovered_element { };
- Window::Element _dragged_element { };
+ Window::Element _strict_hovered_element { }; /* hovered window control */
+ Window::Element _free_hovered_element { }; /* hovered window area */
+ Window::Element _dragged_element { };
/*
* True while drag operation in progress
@@ -80,6 +83,17 @@ class Window_layouter::User_state
bool _picked_up = false;
+ /*
+ * If true, the window element is determined by the sole relation of
+ * the pointer position to the window area, ignoring window controls.
+ */
+ bool _free_arrange = false;
+
+ Window::Element _hovered_element() const
+ {
+ return _free_arrange ? _free_hovered_element : _strict_hovered_element;
+ }
+
/*
* Pointer position at the beginning of a drag operation
*/
@@ -118,7 +132,7 @@ class Window_layouter::User_state
/*
* Toggle maximized (fullscreen) state
*/
- if (_hovered_element.maximizer()) {
+ if (_strict_hovered_element.maximizer()) {
_dragged_window_id = _hovered_window_id;
_focused_window_id = _hovered_window_id;
@@ -126,8 +140,8 @@ class Window_layouter::User_state
_action.toggle_fullscreen(_hovered_window_id);
- _hovered_element = { };
- _hovered_window_id = { };
+ _strict_hovered_element = { };
+ _hovered_window_id = { };
return;
}
@@ -147,6 +161,14 @@ class Window_layouter::User_state
_pointer_clicked, _pointer_curr);
}
+ void _update_free_hovered_element()
+ {
+ _free_hovered_element = { };
+ if (_hovered_window_id.valid())
+ _free_hovered_element = _action.free_arrange_element_at(_hovered_window_id,
+ _pointer_curr);
+ }
+
public:
User_state(Action &action, Focus_history &focus_history)
@@ -174,8 +196,10 @@ class Window_layouter::User_state
{
Window_id const orig_hovered_window_id = _hovered_window_id;
- _hovered_window_id = window_id;
- _hovered_element = element;
+ _hovered_window_id = window_id;
+ _strict_hovered_element = element;
+
+ _update_free_hovered_element();
/*
* Check if we have just received an update while already being in
@@ -193,7 +217,7 @@ class Window_layouter::User_state
* operation for the now-known window.
*/
if (_drag_state && !_drag_init_done && _hovered_window_id.valid())
- _initiate_drag(_hovered_window_id, _hovered_element);
+ _initiate_drag(_hovered_window_id, _strict_hovered_element);
/*
* Let focus follows the pointer, except while dragging or when
@@ -214,21 +238,28 @@ class Window_layouter::User_state
if (_drag_state)
return;
- _hovered_element = { };
- _hovered_window_id = { };
+ _strict_hovered_element = { };
+ _hovered_window_id = { };
}
Window_id focused_window_id() const { return _focused_window_id; }
void focused_window_id(Window_id id) { _focused_window_id = id; }
- Hover_state hover_state() const { return { _hovered_window_id, _hovered_element }; }
+ Hover_state hover_state() const
+ {
+ return { .window_id = _hovered_window_id,
+ .element = _hovered_element() };
+ }
};
void Window_layouter::User_state::_handle_event(Input::Event const &e,
Xml_node config)
{
+ Point const orig_pointer_curr = _pointer_curr;
+ bool const orig_free_arrange = _free_arrange;
+
e.handle_absolute_motion([&] (int x, int y) {
_pointer_curr = Point(x, y); });
@@ -295,8 +326,20 @@ void Window_layouter::User_state::_handle_event(Input::Event const &e,
}
return;
+ case Command::FREE_ARRANGE:
+ _free_arrange = true;
+ return;
+
+ case Command::STRICT_ARRANGE:
+ _free_arrange = false;
+ return;
+
case Command::DRAG:
+ /* ignore clicks outside of a window in free-arrange mode */
+ if (_free_arrange && !_hovered_window_id.valid())
+ return;
+
_drag_state = true;
_pointer_clicked = _pointer_curr;
@@ -311,7 +354,7 @@ void Window_layouter::User_state::_handle_event(Input::Event const &e,
* model.
*/
- _initiate_drag(_hovered_window_id, _hovered_element);
+ _initiate_drag(_hovered_window_id, _hovered_element());
} else {
@@ -329,26 +372,33 @@ void Window_layouter::User_state::_handle_event(Input::Event const &e,
case Command::DROP:
if (_drag_state && _dragged_window_id.valid()) {
- _drag_state = false;
/*
* Issue resize to 0x0 when releasing the the window closer
*/
if (_dragged_element.closer())
- if (_dragged_element == _hovered_element)
+ if (_dragged_element == _hovered_element())
_action.close(_dragged_window_id);
_action.finalize_drag(_dragged_window_id, _dragged_element,
_pointer_clicked, _pointer_curr);
}
+ _drag_state = false;
return;
- default:
- warning("command ", (int)command.type, " unhanded");
+ case Command::NONE:
+ return;
}
});
}
+ if (_free_arrange && (!orig_free_arrange || orig_pointer_curr != _pointer_curr)) {
+ Window::Element const orig_free_hovered_element = _free_hovered_element;
+ _update_free_hovered_element();
+ if (orig_free_hovered_element != _free_hovered_element)
+ _action.free_arrange_hover_changed();
+ }
+
/* update focus history after key/button action is completed */
if (e.release() && _key_cnt == 0)
_focus_history.focus(_focused_window_id);