mirror of
https://github.com/genodelabs/genode.git
synced 2025-04-08 03:45:24 +00:00
window_layouter: drag windows between displays
This patch allows the user to drag windows from one target area (i.e., display) to another whereas the resizing of windows is restricted to the window's original target area. The latter point is important to ensure that the window's resize handles remain reachable at all times. Issue #5390
This commit is contained in:
parent
1638ee00c3
commit
8496d5b02a
@ -44,6 +44,8 @@ class Window_layouter::Assign : public List_model<Assign>::Element
|
||||
|
||||
using Label = String<80>;
|
||||
|
||||
Target::Name target_name { };
|
||||
|
||||
private:
|
||||
|
||||
Registry<Member> _members { };
|
||||
@ -52,8 +54,6 @@ class Window_layouter::Assign : public List_model<Assign>::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<Assign>::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<Assign>::Element
|
||||
fn(_members);
|
||||
}
|
||||
|
||||
Target::Name target_name() const { return _target_name; }
|
||||
|
||||
/**
|
||||
* Used to generate <assign> nodes of windows captured via wildcard
|
||||
*/
|
||||
@ -200,7 +198,7 @@ class Window_layouter::Assign : public List_model<Assign>::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
|
||||
|
@ -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); });
|
||||
}
|
||||
|
||||
|
@ -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<Main> _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));
|
||||
}
|
||||
|
||||
|
@ -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 <typename FN>
|
||||
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_ */
|
||||
|
@ -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_ */
|
||||
|
@ -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
|
||||
|
@ -227,12 +227,18 @@ class Window_layouter::Window : public List_model<Window>::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<Window>::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<Window>::Element
|
||||
*/
|
||||
bool matches(Xml_node const &node) const
|
||||
{
|
||||
return node.attribute_value("id", 0U) == _id;
|
||||
return node.attribute_value("id", 0U) == _id.value;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user