Skip to content

Commit

Permalink
window_layouter: free-arrange mode
Browse files Browse the repository at this point in the history
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 genodelabs#5403
  • Loading branch information
nfeske committed Dec 18, 2024
1 parent 4f14c8e commit 146c341
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 18 deletions.
17 changes: 17 additions & 0 deletions repos/gems/recipes/raw/motif_wm/layouter.config
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
17 changes: 17 additions & 0 deletions repos/gems/recipes/raw/window_layouter/window_layouter.config
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
6 changes: 4 additions & 2 deletions repos/gems/src/app/window_layouter/command.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
49 changes: 49 additions & 0 deletions repos/gems/src/app/window_layouter/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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 = { };
Expand Down
82 changes: 66 additions & 16 deletions repos/gems/src/app/window_layouter/user_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
*/
Expand Down Expand Up @@ -118,16 +132,16 @@ 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;
_focus_history.focus(_focused_window_id);

_action.toggle_fullscreen(_hovered_window_id);

_hovered_element = { };
_hovered_window_id = { };
_strict_hovered_element = { };
_hovered_window_id = { };
return;
}

Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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); });

Expand Down Expand Up @@ -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;

Expand All @@ -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 {

Expand All @@ -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);
Expand Down

0 comments on commit 146c341

Please sign in to comment.