mirror of
https://github.com/genodelabs/genode.git
synced 2025-04-08 03:45:24 +00:00
window_layouter: free-arrange mode
This patch adds the feature of moving and resizing windows by clicking anywhere within a window while the global window-management key is held. Depending on the position within the window, the click is interpreted as click on the title (when clicking at inner 50% of the window, or as a click on the border (when clicking at an area nearby the window boundary). This mode of interaction requires more flexibility of the handling of key sequences. The formerly hard-wired handling of the drag and drop as response to BTN_LEFT events does not suffice. Therefore, this patch moves the driving of the drag-and-drop state to the config level by introducing the actions "drag" and "drop" Fixes #5403
This commit is contained in:
parent
7fc060438a
commit
30b3fa45f1
@ -65,6 +65,23 @@
|
||||
<press key="KEY_ENTER" action="toggle_fullscreen"/>
|
||||
</press>
|
||||
|
||||
<press key="KEY_SCREEN" action="free_arrange">
|
||||
<press key="BTN_LEFT" action="drag">
|
||||
<release key="BTN_LEFT" action="drop"/>
|
||||
<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"/>
|
||||
</press>
|
||||
<release key="KEY_SCREEN" action="strict_arrange"/>
|
||||
</press>
|
||||
|
||||
<press key="BTN_LEFT" action="drag">
|
||||
<release key="BTN_LEFT" action="drop"/>
|
||||
</press>
|
||||
|
@ -65,6 +65,23 @@
|
||||
<press key="KEY_ENTER" action="toggle_fullscreen"/>
|
||||
</press>
|
||||
|
||||
<press key="KEY_SCREEN" action="free_arrange">
|
||||
<press key="BTN_LEFT" action="drag">
|
||||
<release key="BTN_LEFT" action="drop"/>
|
||||
<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"/>
|
||||
</press>
|
||||
<release key="KEY_SCREEN" action="strict_arrange"/>
|
||||
</press>
|
||||
|
||||
<press key="BTN_LEFT" action="drag">
|
||||
<release key="BTN_LEFT" action="drop"/>
|
||||
</press>
|
||||
|
@ -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;
|
||||
|
@ -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 = { };
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user