window_layouter: "pick_up" and "place_down" action

The new 'action' attribute values can be used to realize the
keyboard-based assignement of windows to screens. The "pick_up" action
(on a key press) selects the focused window to be held at the current
position until the "place_down" action is issued (by a key release).
While the focused window is held, "screen" actions can be executed
taking the picked up window to the selected screen.

Issue #5390
This commit is contained in:
Norman Feske 2024-12-06 13:55:47 +01:00 committed by Christian Helmuth
parent d9e49a5c03
commit 248c37784b
6 changed files with 100 additions and 11 deletions

View File

@ -26,12 +26,23 @@
</release> </release>
<release key="KEY_SCREEN" action="raise_window"/> <release key="KEY_SCREEN" action="raise_window"/>
</press> </press>
<press key="KEY_LEFTSHIFT"> <press key="KEY_LEFTSHIFT" action="pick_up">
<press key="KEY_TAB" action="prev_window"> <press key="KEY_TAB" action="prev_window">
<release key="KEY_TAB"> <release key="KEY_TAB">
<release key="KEY_SCREEN" action="raise_window"/> <release key="KEY_SCREEN" action="raise_window"/>
</release> </release>
</press> </press>
<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"/>
<press key="KEY_4" action="screen" target="screen_4"/>
<press key="KEY_5" action="screen" target="screen_5"/>
<press key="KEY_6" action="screen" target="screen_6"/>
<press key="KEY_7" action="screen" target="screen_7"/>
<press key="KEY_8" action="screen" target="screen_8"/>
<press key="KEY_9" action="screen" target="screen_9"/>
<press key="KEY_0" action="screen" target="screen_0"/>
<release key="KEY_LEFTSHIFT" action="place_down"/>
</press> </press>
<press key="KEY_ENTER" action="toggle_fullscreen"/> <press key="KEY_ENTER" action="toggle_fullscreen"/>
<press key="KEY_1" action="screen" target="screen_1"/> <press key="KEY_1" action="screen" target="screen_1"/>

View File

@ -26,12 +26,23 @@
</release> </release>
<release key="KEY_SCREEN" action="raise_window"/> <release key="KEY_SCREEN" action="raise_window"/>
</press> </press>
<press key="KEY_LEFTSHIFT"> <press key="KEY_LEFTSHIFT" action="pick_up">
<press key="KEY_TAB" action="prev_window"> <press key="KEY_TAB" action="prev_window">
<release key="KEY_TAB"> <release key="KEY_TAB">
<release key="KEY_SCREEN" action="raise_window"/> <release key="KEY_SCREEN" action="raise_window"/>
</release> </release>
</press> </press>
<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"/>
<press key="KEY_4" action="screen" target="screen_4"/>
<press key="KEY_5" action="screen" target="screen_5"/>
<press key="KEY_6" action="screen" target="screen_6"/>
<press key="KEY_7" action="screen" target="screen_7"/>
<press key="KEY_8" action="screen" target="screen_8"/>
<press key="KEY_9" action="screen" target="screen_9"/>
<press key="KEY_0" action="screen" target="screen_0"/>
<release key="KEY_LEFTSHIFT" action="place_down"/>
</press> </press>
<press key="KEY_ENTER" action="toggle_fullscreen"/> <press key="KEY_ENTER" action="toggle_fullscreen"/>
<press key="KEY_1" action="screen" target="screen_1"/> <press key="KEY_1" action="screen" target="screen_1"/>

View File

@ -22,7 +22,7 @@ namespace Window_layouter { class Command; }
struct Window_layouter::Command struct Window_layouter::Command
{ {
enum Type { NONE, NEXT_WINDOW, PREV_WINDOW, RAISE_WINDOW, TOGGLE_FULLSCREEN, 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; Type type;
Target::Name target; Target::Name target;
@ -37,6 +37,8 @@ struct Window_layouter::Command
if (string == "toggle_fullscreen") return TOGGLE_FULLSCREEN; if (string == "toggle_fullscreen") return TOGGLE_FULLSCREEN;
if (string == "screen") return SCREEN; if (string == "screen") return SCREEN;
if (string == "release_grab") return RELEASE_GRAB; 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"); warning("cannot convert \"", string, "\" to action type");
return NONE; return NONE;

View File

@ -48,6 +48,7 @@ struct Window_layouter::Main : User_state::Action,
Timer::Connection _drop_timer { _env }; Timer::Connection _drop_timer { _env };
Drag _drag { }; Drag _drag { };
Pick _pick { };
Signal_handler<Main> _drop_timer_handler { Signal_handler<Main> _drop_timer_handler {
_env.ep(), *this, &Main::_handle_drop_timer }; _env.ep(), *this, &Main::_handle_drop_timer };
@ -247,7 +248,7 @@ struct Window_layouter::Main : User_state::Action,
fn(from, to); }); }); 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) { _assign_list.for_each([&] (Assign &assign) {
Window *window_ptr = nullptr; Window *window_ptr = nullptr;
@ -256,18 +257,44 @@ struct Window_layouter::Main : User_state::Action,
window_ptr = &member.window; }); window_ptr = &member.window; });
if (window_ptr) { if (window_ptr) {
assign.target_name = to.name; 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 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 */ /* change of screen under the dragged window */
if (_drag.dragging()) if (_drag.dragging())
_with_target_change(_drag.window_id, name, [&] (Target const &from, Target const &to) { _with_target_change(_drag.window_id, name, [&] (Target const &from, Target const &to) {
if (from.rect == to.rect) 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 */ /* repeated activation of screen moves focus to the screen */
bool already_visible = false; bool already_visible = false;
@ -287,6 +314,18 @@ struct Window_layouter::Main : User_state::Action,
_gen_rules_with_frontmost_screen(name); _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 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)
@ -362,7 +401,7 @@ struct Window_layouter::Main : User_state::Action,
_target_list.with_target_at(curr, [&] (Target const &pointed) { _target_list.with_target_at(curr, [&] (Target const &pointed) {
_drag = { Drag::State::SETTLING, moving, id, curr, pointed.rect }; _drag = { Drag::State::SETTLING, moving, id, curr, pointed.rect };
_with_target_change(id, pointed.name, [&] (Target const &from, Target const &to) { _with_target_change(id, pointed.name, [&] (Target const &from, Target const &to) {
_retarget_window(id, from, to); }); _retarget_and_warp_window(id, from, to); });
}); });
} }

View File

@ -106,6 +106,13 @@ namespace Window_layouter {
return dragging() && id == window_id && moving; return dragging() && id == window_id && moving;
} }
}; };
struct Pick
{
bool picked;
Window_id window_id;
Rect orig_geometry;
};
} }
#endif /* _TYPES_H_ */ #endif /* _TYPES_H_ */

View File

@ -34,6 +34,8 @@ class Window_layouter::User_state
virtual void to_front(Window_id) = 0; virtual void to_front(Window_id) = 0;
virtual void drag(Window_id, Window::Element, Point clicked, Point curr) = 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 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; virtual void screen(Target::Name const &) = 0;
}; };
@ -76,6 +78,8 @@ class Window_layouter::User_state
*/ */
bool _drag_init_done = false; bool _drag_init_done = false;
bool _picked_up = false;
/* /*
* Pointer position at the beginning of a drag operation * 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) 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_window_id = window_id;
_hovered_element = element; _hovered_element = element;
@ -192,10 +196,11 @@ class Window_layouter::User_state
_initiate_drag(_hovered_window_id, _hovered_element); _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() if (!_drag_state && !_picked_up && _hovered_window_id.valid()
&& _hovered_window_id != last_hovered_window_id) { && _hovered_window_id != orig_hovered_window_id) {
_focused_window_id = _hovered_window_id; _focused_window_id = _hovered_window_id;
_focus_history.focus(_focused_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(); _action.release_grab();
return; 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: default:
warning("command ", (int)command.type, " unhanded"); warning("command ", (int)command.type, " unhanded");
} }