mirror of
https://github.com/genodelabs/genode.git
synced 2025-03-03 13:14:45 +00:00
window_layouter: handle drag/drop as actions
This patch moves the formerly hard-wired drag-and-drop handling to the configuration level by introducing the actions "drag" and "drop". To aid the robust handling of release events matching their corresponding press events, the patch refines the policy-matching of the current combination of keys against the hierarchy of <press> and <release> nodes. If no policy for a concrete combination exists, a release event also considers the policy of its matching <press> node. This way, the regular drag-and-drop rules can be expressed as <press key="BTN_LEFT" action="drag"> <release key="BTN_LEFT" action="drop"/> </press> This also works when releasing BTN_LEFT while pressing additional keys, for which no policy exists. With this change, the layouter supports the matching of multiple key sequences instead of only one, thereby supporting multiple actions at once and allowing for decoupling different user interactions in the configuration. Issue #5403
This commit is contained in:
parent
6c7cbb2c5e
commit
7fc060438a
@ -19,19 +19,24 @@
|
||||
<assign label_prefix="" target="screen_1" xpos="any" ypos="any"/>
|
||||
</rules>
|
||||
|
||||
<press key="KEY_SCREEN" action="release_grab">
|
||||
<press key="KEY_SCREEN">
|
||||
<press key="KEY_TAB" action="next_window">
|
||||
<release key="KEY_TAB">
|
||||
<release key="KEY_SCREEN" action="raise_window"/>
|
||||
</release>
|
||||
<release key="KEY_SCREEN" action="raise_window"/>
|
||||
</press>
|
||||
<press key="KEY_LEFTSHIFT" action="pick_up">
|
||||
<press key="KEY_LEFTSHIFT">
|
||||
<press key="KEY_TAB" action="prev_window">
|
||||
<release key="KEY_TAB">
|
||||
<release key="KEY_SCREEN" action="raise_window"/>
|
||||
</release>
|
||||
</press>
|
||||
</press>
|
||||
</press>
|
||||
|
||||
<press key="KEY_SCREEN">
|
||||
<press key="KEY_LEFTSHIFT" action="pick_up">
|
||||
<press key="KEY_1" action="screen" target="screen_1"/>
|
||||
<press key="KEY_2" action="screen" target="screen_2"/>
|
||||
<press key="KEY_3" action="screen" target="screen_3"/>
|
||||
@ -44,7 +49,6 @@
|
||||
<press key="KEY_0" action="screen" target="screen_0"/>
|
||||
<release key="KEY_LEFTSHIFT" action="place_down"/>
|
||||
</press>
|
||||
<press key="KEY_ENTER" action="toggle_fullscreen"/>
|
||||
<press key="KEY_1" action="screen" target="screen_1"/>
|
||||
<press key="KEY_2" action="screen" target="screen_2"/>
|
||||
<press key="KEY_3" action="screen" target="screen_3"/>
|
||||
@ -57,6 +61,14 @@
|
||||
<press key="KEY_0" action="screen" target="screen_0"/>
|
||||
</press>
|
||||
|
||||
<press key="KEY_SCREEN" action="release_grab">
|
||||
<press key="KEY_ENTER" action="toggle_fullscreen"/>
|
||||
</press>
|
||||
|
||||
<press key="BTN_LEFT" action="drag">
|
||||
<release key="BTN_LEFT" action="drop"/>
|
||||
</press>
|
||||
|
||||
<!-- support switching screens while dragging a window -->
|
||||
<press key="BTN_LEFT">
|
||||
<press key="KEY_1" action="screen" target="screen_1"/>
|
||||
|
@ -19,19 +19,24 @@
|
||||
<assign label_prefix="" target="screen_1" xpos="any" ypos="any"/>
|
||||
</rules>
|
||||
|
||||
<press key="KEY_SCREEN" action="release_grab">
|
||||
<press key="KEY_SCREEN">
|
||||
<press key="KEY_TAB" action="next_window">
|
||||
<release key="KEY_TAB">
|
||||
<release key="KEY_SCREEN" action="raise_window"/>
|
||||
</release>
|
||||
<release key="KEY_SCREEN" action="raise_window"/>
|
||||
</press>
|
||||
<press key="KEY_LEFTSHIFT" action="pick_up">
|
||||
<press key="KEY_LEFTSHIFT">
|
||||
<press key="KEY_TAB" action="prev_window">
|
||||
<release key="KEY_TAB">
|
||||
<release key="KEY_SCREEN" action="raise_window"/>
|
||||
</release>
|
||||
</press>
|
||||
</press>
|
||||
</press>
|
||||
|
||||
<press key="KEY_SCREEN">
|
||||
<press key="KEY_LEFTSHIFT" action="pick_up">
|
||||
<press key="KEY_1" action="screen" target="screen_1"/>
|
||||
<press key="KEY_2" action="screen" target="screen_2"/>
|
||||
<press key="KEY_3" action="screen" target="screen_3"/>
|
||||
@ -44,7 +49,6 @@
|
||||
<press key="KEY_0" action="screen" target="screen_0"/>
|
||||
<release key="KEY_LEFTSHIFT" action="place_down"/>
|
||||
</press>
|
||||
<press key="KEY_ENTER" action="toggle_fullscreen"/>
|
||||
<press key="KEY_1" action="screen" target="screen_1"/>
|
||||
<press key="KEY_2" action="screen" target="screen_2"/>
|
||||
<press key="KEY_3" action="screen" target="screen_3"/>
|
||||
@ -57,6 +61,14 @@
|
||||
<press key="KEY_0" action="screen" target="screen_0"/>
|
||||
</press>
|
||||
|
||||
<press key="KEY_SCREEN" action="release_grab">
|
||||
<press key="KEY_ENTER" action="toggle_fullscreen"/>
|
||||
</press>
|
||||
|
||||
<press key="BTN_LEFT" action="drag">
|
||||
<release key="BTN_LEFT" action="drop"/>
|
||||
</press>
|
||||
|
||||
<!-- support switching screens while dragging a window -->
|
||||
<press key="BTN_LEFT">
|
||||
<press key="KEY_1" action="screen" target="screen_1"/>
|
||||
|
@ -22,7 +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 };
|
||||
NEXT_TAB, PREV_TAB, SCREEN, RELEASE_GRAB, PICK_UP, PLACE_DOWN,
|
||||
DRAG, DROP };
|
||||
|
||||
Type type;
|
||||
Target::Name target;
|
||||
@ -39,6 +40,8 @@ struct Window_layouter::Command
|
||||
if (string == "release_grab") return RELEASE_GRAB;
|
||||
if (string == "pick_up") return PICK_UP;
|
||||
if (string == "place_down") return PLACE_DOWN;
|
||||
if (string == "drag") return DRAG;
|
||||
if (string == "drop") return DROP;
|
||||
|
||||
warning("cannot convert \"", string, "\" to action type");
|
||||
return NONE;
|
||||
|
@ -110,7 +110,7 @@ class Window_layouter::Key_sequence_tracker
|
||||
|
||||
bool done = false; /* process the first match only */
|
||||
curr.for_each_sub_node(node_type, [&] (Xml_node const &node) {
|
||||
if (!done && node.attribute_value("key", Key_name()) == key) {
|
||||
if (node.attribute_value("key", Key_name()) == key) {
|
||||
fn(node);
|
||||
done = true; } });
|
||||
|
||||
@ -118,9 +118,10 @@ class Window_layouter::Key_sequence_tracker
|
||||
no_match_fn();
|
||||
}
|
||||
|
||||
void _with_match_rec(unsigned const pos, Xml_node const &node, auto const &fn) const
|
||||
void _with_match_rec(unsigned const pos, unsigned const max_pos,
|
||||
Xml_node const &node, auto const &fn) const
|
||||
{
|
||||
if (pos == _stack.pos) {
|
||||
if (pos == max_pos) {
|
||||
fn(node);
|
||||
return;
|
||||
}
|
||||
@ -129,7 +130,7 @@ class Window_layouter::Key_sequence_tracker
|
||||
_with_matching_sub_node(node, _stack.entries[pos],
|
||||
[&] (Xml_node const &sub_node) {
|
||||
if (pos < _stack.pos)
|
||||
_with_match_rec(pos + 1, sub_node, fn); },
|
||||
_with_match_rec(pos + 1, max_pos, sub_node, fn); },
|
||||
[&] { });
|
||||
};
|
||||
|
||||
@ -142,7 +143,15 @@ class Window_layouter::Key_sequence_tracker
|
||||
*/
|
||||
void _with_xml_by_path(Xml_node const &config, auto const &fn) const
|
||||
{
|
||||
_with_match_rec(0, config, fn);
|
||||
_with_match_rec(0, _stack.pos, config, fn);
|
||||
}
|
||||
|
||||
void _with_xml_at_press(Xml_node const &config, Input::Keycode key, auto const &fn) const
|
||||
{
|
||||
for (unsigned i = 0; i < _stack.pos; i++)
|
||||
if (_stack.entries[i].press && _stack.entries[i].key == key) {
|
||||
_with_match_rec(0, i + 1, config, fn);
|
||||
return; }
|
||||
}
|
||||
|
||||
/**
|
||||
@ -182,39 +191,59 @@ class Window_layouter::Key_sequence_tracker
|
||||
_stack.flush(Stack::Entry { .press = false, .key = key });
|
||||
});
|
||||
|
||||
Constructible<Stack::Entry> new_entry { };
|
||||
|
||||
_with_xml_by_path(config, [&] (Xml_node const &curr_node) {
|
||||
|
||||
ev.handle_press([&] (Input::Keycode key, Codepoint) {
|
||||
|
||||
Stack::Entry const press { .press = true, .key = key };
|
||||
|
||||
_with_matching_sub_node(curr_node, press,
|
||||
[&] (Xml_node const &node) { _execute_command(node, fn); },
|
||||
[&] (Xml_node const &node) {
|
||||
_execute_command(node, fn); },
|
||||
[&] { });
|
||||
|
||||
_stack.push(press);
|
||||
new_entry.construct(press);
|
||||
});
|
||||
|
||||
ev.handle_release([&] (Input::Keycode key) {
|
||||
|
||||
Stack::Entry const release { .press = false, .key = key };
|
||||
|
||||
/*
|
||||
* If there exists a specific path for the release event,
|
||||
* follow the path. Otherwise, we remove the released key
|
||||
* from the sequence.
|
||||
* follow the path and record the release event. Otherwise,
|
||||
* 'new_entry' will remain unconstructed so that the
|
||||
* corresponding press event gets flushed from the stack.
|
||||
*/
|
||||
Stack::Entry const release { .press = false, .key = key };
|
||||
_with_matching_sub_node(curr_node, release,
|
||||
[&] (Xml_node const &next_node) {
|
||||
_execute_command(next_node, fn);
|
||||
_stack.push(release);
|
||||
if (next_node.num_sub_nodes())
|
||||
new_entry.construct(release);
|
||||
},
|
||||
[&] /* no match */ {
|
||||
Stack::Entry const press { .press = true, .key = key };
|
||||
_stack.flush(press);
|
||||
});
|
||||
[&] /* no match */ { });
|
||||
});
|
||||
});
|
||||
|
||||
if (new_entry.constructed()) {
|
||||
_stack.push(*new_entry);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If no matching <release> node exists for the current combination
|
||||
* of keys, fall back to a <release> node declared immediately
|
||||
* inside the corresponding <press> node.
|
||||
*/
|
||||
ev.handle_release([&] (Input::Keycode key) {
|
||||
_with_xml_at_press(config, key, [&] (Xml_node const &press_node) {
|
||||
_with_matching_sub_node(press_node, { .press = false, .key = key },
|
||||
[&] (Xml_node const &next_node) {
|
||||
_execute_command(next_node, fn); },
|
||||
[&] { }); });
|
||||
|
||||
_stack.flush(Stack::Entry { .press = true, .key = key });
|
||||
_stack.flush(Stack::Entry { .press = false, .key = key });
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -243,59 +243,6 @@ void Window_layouter::User_state::_handle_event(Input::Event const &e,
|
||||
if (e.press()) _key_cnt++;
|
||||
if (e.release()) _key_cnt--;
|
||||
|
||||
/* handle pointer click */
|
||||
if (e.key_press(Input::BTN_LEFT) && _key_cnt == 1) {
|
||||
|
||||
/*
|
||||
* Initiate drag operation if possible
|
||||
*/
|
||||
_drag_state = true;
|
||||
_pointer_clicked = _pointer_curr;
|
||||
|
||||
if (_hovered_window_id.valid()) {
|
||||
|
||||
/*
|
||||
* Initiate drag operation
|
||||
*
|
||||
* If the hovered window is known at the time of the press event,
|
||||
* we can initiate the drag operation immediately. Otherwise,
|
||||
* the initiation is deferred to the next update of the hover
|
||||
* model.
|
||||
*/
|
||||
|
||||
_initiate_drag(_hovered_window_id, _hovered_element);
|
||||
|
||||
} else {
|
||||
|
||||
/*
|
||||
* If the hovering state is undefined at the time of the click,
|
||||
* we defer the drag handling until the next update of the hover
|
||||
* state. This intermediate state is captured by '_drag_init_done'.
|
||||
*/
|
||||
_drag_init_done = false;
|
||||
_dragged_window_id = Window_id();
|
||||
_dragged_element = Window::Element(Window::Element::UNDEFINED);
|
||||
}
|
||||
}
|
||||
|
||||
/* detect end of drag operation */
|
||||
if (e.release() && _key_cnt == 0) {
|
||||
|
||||
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)
|
||||
_action.close(_dragged_window_id);
|
||||
|
||||
_action.finalize_drag(_dragged_window_id, _dragged_element,
|
||||
_pointer_clicked, _pointer_curr);
|
||||
}
|
||||
}
|
||||
|
||||
/* handle key sequences */
|
||||
if (_key(e)) {
|
||||
|
||||
@ -348,6 +295,54 @@ void Window_layouter::User_state::_handle_event(Input::Event const &e,
|
||||
}
|
||||
return;
|
||||
|
||||
case Command::DRAG:
|
||||
|
||||
_drag_state = true;
|
||||
_pointer_clicked = _pointer_curr;
|
||||
|
||||
if (_hovered_window_id.valid()) {
|
||||
|
||||
/*
|
||||
* Initiate drag operation
|
||||
*
|
||||
* If the hovered window is known at the time of the press event,
|
||||
* we can initiate the drag operation immediately. Otherwise,
|
||||
* the initiation is deferred to the next update of the hover
|
||||
* model.
|
||||
*/
|
||||
|
||||
_initiate_drag(_hovered_window_id, _hovered_element);
|
||||
|
||||
} else {
|
||||
|
||||
/*
|
||||
* If the hovering state is undefined at the time of the click,
|
||||
* we defer the drag handling until the next update of the hover
|
||||
* state. This intermediate state is captured by '_drag_init_done'.
|
||||
*/
|
||||
_drag_init_done = false;
|
||||
_dragged_window_id = Window_id();
|
||||
_dragged_element = Window::Element(Window::Element::UNDEFINED);
|
||||
}
|
||||
return;
|
||||
|
||||
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)
|
||||
_action.close(_dragged_window_id);
|
||||
|
||||
_action.finalize_drag(_dragged_window_id, _dragged_element,
|
||||
_pointer_clicked, _pointer_curr);
|
||||
}
|
||||
return;
|
||||
|
||||
default:
|
||||
warning("command ", (int)command.type, " unhanded");
|
||||
}
|
||||
|
@ -31,6 +31,9 @@ proc qt5_layouter_config { } {
|
||||
<screen name="screen"/>
|
||||
<assign label_prefix="" target="screen" xpos="any" ypos="any"/>
|
||||
</rules>
|
||||
<press key="BTN_LEFT" action="drag">
|
||||
<release key="BTN_LEFT" action="drop"/>
|
||||
</press>
|
||||
</config>}
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,9 @@ proc qt6_layouter_config { } {
|
||||
<screen name="screen"/>
|
||||
<assign label_prefix="" target="screen" xpos="any" ypos="any"/>
|
||||
</rules>
|
||||
<press key="BTN_LEFT" action="drag">
|
||||
<release key="BTN_LEFT" action="drop"/>
|
||||
</press>
|
||||
</config>}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user