diff --git a/repos/gems/src/app/window_layouter/assign.h b/repos/gems/src/app/window_layouter/assign.h index 2f6e6e9a59..afcdb9edeb 100644 --- a/repos/gems/src/app/window_layouter/assign.h +++ b/repos/gems/src/app/window_layouter/assign.h @@ -44,6 +44,8 @@ class Window_layouter::Assign : public List_model::Element using Label = String<80>; + Target::Name target_name { }; + private: Registry _members { }; @@ -52,8 +54,6 @@ class Window_layouter::Assign : public List_model::Element Label const _label_prefix; Label const _label_suffix; - Target::Name _target_name { }; - bool _pos_defined = false; bool _xpos_any = false; bool _ypos_any = false; @@ -75,7 +75,7 @@ class Window_layouter::Assign : public List_model::Element void update(Xml_node assign) { - _target_name = assign.attribute_value("target", Target::Name()); + target_name = assign.attribute_value("target", Target::Name()); _pos_defined = assign.has_attribute("xpos") && assign.has_attribute("ypos"); _size_defined = assign.has_attribute("width") && assign.has_attribute("height"); _maximized = assign.attribute_value("maximized", false); @@ -160,8 +160,6 @@ class Window_layouter::Assign : public List_model::Element fn(_members); } - Target::Name target_name() const { return _target_name; } - /** * Used to generate nodes of windows captured via wildcard */ @@ -200,7 +198,7 @@ class Window_layouter::Assign : public List_model::Element if (_label_prefix.valid()) xml.attribute("label_prefix", _label_prefix); if (_label_suffix.valid()) xml.attribute("label_suffix", _label_suffix); - xml.attribute("target", _target_name); + xml.attribute("target", target_name); } void gen_geometry_attr(Xml_generator &xml) const diff --git a/repos/gems/src/app/window_layouter/assign_list.h b/repos/gems/src/app/window_layouter/assign_list.h index 7ad88647f0..22aa53dc8b 100644 --- a/repos/gems/src/app/window_layouter/assign_list.h +++ b/repos/gems/src/app/window_layouter/assign_list.h @@ -96,7 +96,7 @@ class Window_layouter::Assign_list : Noncopyable void for_each_visible(auto const &target_name, auto const &fn) const { for_each([&] (Assign const &assign) { - if (assign.visible() && target_name == assign.target_name()) + if (assign.visible() && target_name == assign.target_name) fn(assign); }); } diff --git a/repos/gems/src/app/window_layouter/main.cc b/repos/gems/src/app/window_layouter/main.cc index 653e69d0c2..c524b7d640 100644 --- a/repos/gems/src/app/window_layouter/main.cc +++ b/repos/gems/src/app/window_layouter/main.cc @@ -48,9 +48,7 @@ struct Window_layouter::Main : Operations, Timer::Connection _drop_timer { _env }; - enum class Drag_state { IDLE, DRAGGING, SETTLING }; - - Drag_state _drag_state { Drag_state::IDLE }; + Drag _drag { }; Signal_handler
_drop_timer_handler { _env.ep(), *this, &Main::_handle_drop_timer }; @@ -106,7 +104,7 @@ struct Window_layouter::Main : Operations, _assign_list.for_each([&] (Assign &assign) { _target_list.for_each([&] (Target const &target) { - if (target.name() != assign.target_name()) + if (target.name() != assign.target_name) return; assign.for_each_member([&] (Assign::Member &member) { @@ -222,10 +220,12 @@ struct Window_layouter::Main : Operations, void drag(Window_id id, Window::Element element, Point clicked, Point curr) override { - if (_drag_state == Drag_state::SETTLING) + if (_drag.state == Drag::State::SETTLING) return; - _drag_state = Drag_state::DRAGGING; + _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() }; }); to_front(id); @@ -252,7 +252,7 @@ struct Window_layouter::Main : Operations, void _handle_drop_timer() { - _drag_state = Drag_state::IDLE; + _drag = { }; _gen_rules(); @@ -260,7 +260,7 @@ struct Window_layouter::Main : Operations, window.finalize_drag_operation(); }); } - void finalize_drag(Window_id, Window::Element, Point, Point) override + void finalize_drag(Window_id id, Window::Element element, Point, Point curr) override { /* * Update window layout because highlighting may have changed after the @@ -271,8 +271,35 @@ struct Window_layouter::Main : Operations, */ _handle_hover(); - _drag_state = Drag_state::SETTLING; + _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); + } + }); + }); + } _drop_timer.trigger_once(250*1000); } @@ -417,7 +444,7 @@ void Window_layouter::Main::_gen_window_layout() }); _window_layout_reporter.generate([&] (Xml_generator &xml) { - _target_list.gen_layout(xml, _assign_list); }); + _target_list.gen_layout(xml, _assign_list, _drag); }); } @@ -470,7 +497,7 @@ void Window_layouter::Main::_gen_rules_assignments(Xml_generator &xml, FN const xml.node("assign", [&] () { xml.attribute("label", member.window.label()); - xml.attribute("target", assign.target_name()); + xml.attribute("target", assign.target_name); gen_window_geometry(xml, assign, member.window); }); }; @@ -608,7 +635,7 @@ void Window_layouter::Main::_handle_hover() try { Xml_node const hover_window_xml = _hover.xml().sub_node("window"); - _user_state.hover(hover_window_xml.attribute_value("id", 0U), + _user_state.hover({ hover_window_xml.attribute_value("id", 0U) }, _element_from_hover_model(hover_window_xml)); } diff --git a/repos/gems/src/app/window_layouter/target_list.h b/repos/gems/src/app/window_layouter/target_list.h index b65fa2d476..d7f20806fc 100644 --- a/repos/gems/src/app/window_layouter/target_list.h +++ b/repos/gems/src/app/window_layouter/target_list.h @@ -138,7 +138,8 @@ class Window_layouter::Target_list * \return layer that was processed by the method */ unsigned _gen_top_most_layer(Xml_generator &xml, unsigned min_layer, - Assign_list const &assignments) const + Assign_list const &assignments, + Drag const &drag) const { /* search targets for next matching layer */ unsigned layer = MAX_LAYER; @@ -146,6 +147,9 @@ class Window_layouter::Target_list if (target.layer() >= min_layer && target.layer() <= layer) layer = target.layer(); }); + Rect const drag_origin_boundary = drag.dragging() + ? target_boundary(assignments, drag.window_id) + : Rect { }; /* search target by name */ _targets.for_each([&] (Target const &target) { @@ -155,7 +159,7 @@ class Window_layouter::Target_list if (!target.visible()) return; - if (assignments.target_empty(target.name())) + if (assignments.target_empty(target.name()) && !drag.moving_at_target(target.name())) return; Rect const boundary = target.geometry(); @@ -163,11 +167,18 @@ class Window_layouter::Target_list xml.attribute("name", target.name()); generate(xml, boundary); - /* visit all windows on the layer */ + /* in-flux window node for the currently dragged window */ + if (drag.moving_at_target(target.name())) + assignments.for_each([&] (Assign const &assign) { + assign.for_each_member([&] (Assign::Member const &member) { + if (drag.moving_window(member.window.id())) + member.window.generate(xml, drag_origin_boundary); }); }); + + /* visit all windows on the layer, except for the dragged one */ assignments.for_each_visible(target.name(), [&] (Assign const &assign) { assign.for_each_member([&] (Assign::Member const &member) { - member.window.generate(xml, boundary); }); - }); + if (!drag.moving_window(member.window.id())) + member.window.generate(xml, boundary); }); }); }); }); @@ -214,7 +225,8 @@ class Window_layouter::Target_list }); } - void gen_layout(Xml_generator &xml, Assign_list const &assignments) const + void gen_layout(Xml_generator &xml, Assign_list const &assignments, + Drag const &drag) const { unsigned min_layer = 0; @@ -222,7 +234,7 @@ class Window_layouter::Target_list for (;;) { unsigned const layer = - _gen_top_most_layer(xml, min_layer, assignments); + _gen_top_most_layer(xml, min_layer, assignments, drag); if (layer == MAX_LAYER) break; @@ -273,8 +285,51 @@ class Window_layouter::Target_list }); } - template - void for_each(FN const &fn) const { _targets.for_each(fn); } + void for_each(auto const &fn) const { _targets.for_each(fn); } + + void with_target(Name const &name, auto const &fn) const + { + Target const *ptr = nullptr; + for_each([&] (Target const &target) { + if (target.name() == name) + ptr = ⌖ }); + if (ptr) + fn(*ptr); + } + + void with_target_at(Point const at, auto const &fn) const + { + Target const *ptr = nullptr; + for_each([&] (Target const &target) { + if (target.visible() && target.geometry().contains(at)) + ptr = ⌖ }); + if (ptr) + fn(*ptr); + } + + void with_target(Assign_list const &assignments, Window_id id, auto const &fn) const + { + Target const *ptr = nullptr; + _targets.for_each([&] (Target const &target) { + assignments.for_each_visible(target.name(), [&] (Assign const &assign) { + assign.for_each_member([&] (Assign::Member const &member) { + if (member.window.id() == id) + ptr = ⌖ }); }); }); + if (ptr) + fn(*ptr); + } + + /** + * Return the boundary of the target that displays the given window + */ + Rect target_boundary(Assign_list const &assignments, Window_id id) const + { + Rect result { }; + with_target(assignments, id, [&] (Target const &target) { + result = target.geometry(); }); + return result; + } + }; #endif /* _TARGET_LIST_H_ */ diff --git a/repos/gems/src/app/window_layouter/types.h b/repos/gems/src/app/window_layouter/types.h index d647949928..0383aeb1f0 100644 --- a/repos/gems/src/app/window_layouter/types.h +++ b/repos/gems/src/app/window_layouter/types.h @@ -28,10 +28,7 @@ namespace Window_layouter { struct Window_id { - unsigned value = 0; - - Window_id() { } - Window_id(unsigned value) : value(value) { } + unsigned value; bool valid() const { return value != 0; } @@ -86,6 +83,29 @@ namespace Window_layouter { xml.attribute("width", rect.w()); xml.attribute("height", rect.h()); } + + struct Drag + { + enum class State { IDLE, DRAGGING, SETTLING }; + + State state; + bool moving; /* distiguish moving from resizing */ + Window_id window_id; + Point curr_pos; + Name target; + + bool dragging() const { return state == State::DRAGGING; } + + bool moving_at_target(Name const &name) const + { + return dragging() && name == target && moving; + } + + bool moving_window(Window_id id) const + { + return dragging() && id == window_id && moving; + } + }; } #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 edb2d8cf8e..1d826390d3 100644 --- a/repos/gems/src/app/window_layouter/user_state.h +++ b/repos/gems/src/app/window_layouter/user_state.h @@ -284,9 +284,8 @@ void Window_layouter::User_state::_handle_event(Input::Event const &e, /* detect end of drag operation */ if (e.release() && _key_cnt == 0) { - _drag_state = false; - - if (_dragged_window_id.valid()) { + if (_drag_state && _dragged_window_id.valid()) { + _drag_state = false; /* * Issue resize to 0x0 when releasing the the window closer diff --git a/repos/gems/src/app/window_layouter/window.h b/repos/gems/src/app/window_layouter/window.h index 7d9601ab7f..d421f924e4 100644 --- a/repos/gems/src/app/window_layouter/window.h +++ b/repos/gems/src/app/window_layouter/window.h @@ -227,12 +227,18 @@ class Window_layouter::Window : public List_model::Element int x1 = _orig_geometry.x1(), y1 = _orig_geometry.y1(), x2 = _orig_geometry.x2(), y2 = _orig_geometry.y2(); - if (_drag_left_border) x1 = min(x1 + offset.x, x2); - if (_drag_right_border) x2 = max(x2 + offset.x, x1); - if (_drag_top_border) y1 = min(y1 + offset.y, y2); - if (_drag_bottom_border) y2 = max(y2 + offset.y, y1); + /* restrict resizing to the window's target area */ + Rect const outer { { }, _target_area }; + Rect const inner = _decorator_margins.inner_geometry(outer); - _drag_geometry = Rect::compound(Point(x1, y1), Point(x2, y2)); + auto clamped = [] (int v, int lowest, int highest) { return min(max(v, lowest), highest); }; + + if (_drag_left_border) x1 = clamped(min(x1 + offset.x, x2), inner.x1(), outer.x2()); + if (_drag_right_border) x2 = clamped(max(x2 + offset.x, x1), outer.x1(), inner.x2()); + if (_drag_top_border) y1 = clamped(min(y1 + offset.y, y2), inner.y1(), outer.y2()); + if (_drag_bottom_border) y2 = clamped(max(y2 + offset.y, y1), outer.y1(), inner.y2()); + + _drag_geometry = Rect::compound(Point { x1, y1 }, Point { x2, y2 }); _dragged_size = _drag_geometry.area; } @@ -480,6 +486,13 @@ class Window_layouter::Window : public List_model::Element void target_area(Area area) { _target_area = area; }; + void warp(Point const rel) + { + _geometry.at = _geometry.at + rel; + _orig_geometry.at = _orig_geometry.at + rel; + _drag_geometry.at = _drag_geometry.at + rel; + } + bool maximized() const { return _maximized; } void maximized(bool maximized) { _maximized = maximized; } @@ -505,7 +518,7 @@ class Window_layouter::Window : public List_model::Element */ bool matches(Xml_node const &node) const { - return node.attribute_value("id", 0U) == _id; + return node.attribute_value("id", 0U) == _id.value; } /** diff --git a/repos/gems/src/app/window_layouter/window_list.h b/repos/gems/src/app/window_layouter/window_list.h index 4506e1ef8d..3f5a310921 100644 --- a/repos/gems/src/app/window_layouter/window_list.h +++ b/repos/gems/src/app/window_layouter/window_list.h @@ -54,11 +54,12 @@ class Window_layouter::Window_list [&] (Xml_node const &node) -> Window & { - unsigned const id = node.attribute_value("id", 0U); - Area const initial_size = Area::from_xml(node); + Window_id const id { node.attribute_value("id", 0U) }; + + Area const initial_size = Area::from_xml(node); Window::Label const label = - node.attribute_value("label",Window::Label()); + node.attribute_value("label", Window::Label()); return *new (_alloc) Window(id, label, initial_size,