mirror of
https://github.com/genodelabs/genode.git
synced 2025-04-08 03:45:24 +00:00
window_layouter: restrict focus to visible windows
This patch restricts the focus switching via the keyboard (Super-Tab) to windows located at visible screens. Should the currently focused window become invible, switch the focus to the most recently focused visible window. Issue #5390
This commit is contained in:
parent
dbcc21c109
commit
d9e49a5c03
@ -38,70 +38,97 @@ class Window_layouter::Focus_history
|
||||
|
||||
List<Entry> _entries { };
|
||||
|
||||
Entry *_lookup(Window_id window_id)
|
||||
Entry const *_next_in_cycle(Entry const &e) const
|
||||
{
|
||||
for (Entry *e = _entries.first(); e; e = e->next())
|
||||
if (e->window_id == window_id)
|
||||
return e;
|
||||
|
||||
return nullptr;
|
||||
return e.next() ? e.next() : _entries.first();
|
||||
}
|
||||
|
||||
void _remove_if_present(Entry &entry)
|
||||
Entry const *_prev_in_cycle(Entry const &e) const
|
||||
{
|
||||
_entries.remove(&entry);
|
||||
auto last_ptr = [&]
|
||||
{
|
||||
Entry const *ptr = _entries.first();
|
||||
for ( ; ptr && ptr->next(); ptr = ptr->next());
|
||||
return ptr;
|
||||
};
|
||||
|
||||
auto prev_ptr = [&] (Entry const &e) -> Entry const *
|
||||
{
|
||||
for (Entry const *ptr = _entries.first(); ptr; ptr = ptr->next())
|
||||
if (ptr->next() == &e)
|
||||
return ptr;
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
Entry const * const ptr = prev_ptr(e);
|
||||
return ptr ? ptr : last_ptr();
|
||||
}
|
||||
|
||||
Window_id _any_suitable_or_none(auto const &cond_fn) const
|
||||
{
|
||||
for (Entry const *ptr = _entries.first(); ptr; ptr = ptr->next())
|
||||
if (cond_fn(ptr->window_id))
|
||||
return ptr->window_id;
|
||||
return Window_id();
|
||||
}
|
||||
|
||||
Window_id _neighbor(Window_id window_id, auto const &cond_fn, auto const &next_fn) const
|
||||
{
|
||||
auto entry_ptr_for_window = [&] () -> Entry const *
|
||||
{
|
||||
for (Entry const *e = _entries.first(); e; e = e->next())
|
||||
if (e->window_id == window_id)
|
||||
return e;
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
Entry const * const anchor_ptr = entry_ptr_for_window();
|
||||
if (!anchor_ptr)
|
||||
return _any_suitable_or_none(cond_fn);
|
||||
|
||||
Entry const *next_ptr = nullptr;
|
||||
for (Entry const *ptr = anchor_ptr; ptr; ptr = next_ptr) {
|
||||
next_ptr = next_fn(*ptr);
|
||||
|
||||
bool const cycle_complete = (next_ptr == anchor_ptr);
|
||||
if (cycle_complete)
|
||||
return _any_suitable_or_none(cond_fn);
|
||||
|
||||
if (next_ptr && cond_fn(next_ptr->window_id))
|
||||
return next_ptr->window_id;
|
||||
}
|
||||
return Window_id();
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
void focus(Window_id window_id)
|
||||
{
|
||||
Entry * const entry = _lookup(window_id);
|
||||
if (!entry) {
|
||||
if (window_id.value == 0)
|
||||
return;
|
||||
|
||||
Entry *ptr = _entries.first();
|
||||
for (; ptr && ptr->window_id != window_id; ptr = ptr->next());
|
||||
|
||||
if (!ptr) {
|
||||
warning("unexpected lookup failure for focus history entry");
|
||||
return;
|
||||
}
|
||||
|
||||
_remove_if_present(*entry);
|
||||
_entries.remove(ptr);
|
||||
|
||||
/* insert entry at the beginning (most recently focused) */
|
||||
_entries.insert(entry);
|
||||
_entries.insert(ptr);
|
||||
}
|
||||
|
||||
Window_id next(Window_id window_id)
|
||||
Window_id next(Window_id id, auto const &cond_fn) const
|
||||
{
|
||||
Entry * const first = _entries.first();
|
||||
if (!first)
|
||||
return Window_id();
|
||||
|
||||
Entry * const entry = _lookup(window_id);
|
||||
if (!entry)
|
||||
return first->window_id;
|
||||
|
||||
Entry * const next = entry->next();
|
||||
return next ? next->window_id : first->window_id;
|
||||
return _neighbor(id, cond_fn, [&] (Entry const &e) { return _next_in_cycle(e); });
|
||||
}
|
||||
|
||||
Window_id prev(Window_id window_id)
|
||||
Window_id prev(Window_id id, auto const &cond_fn) const
|
||||
{
|
||||
Entry *curr = _entries.first();
|
||||
if (!curr)
|
||||
return Window_id();
|
||||
|
||||
/* if argument refers to the first window, cycle to the last one */
|
||||
if (curr->window_id == window_id) {
|
||||
|
||||
/* determine last list element */
|
||||
for (; curr->next(); curr = curr->next());
|
||||
return curr->window_id;
|
||||
}
|
||||
|
||||
/* traverse list, looking for the predecessor of the window */
|
||||
for (; curr->next(); curr = curr->next())
|
||||
if (curr->next()->window_id == window_id)
|
||||
return curr->window_id;
|
||||
|
||||
return Window_id();
|
||||
return _neighbor(id, cond_fn, [&] (Entry const &e) { return _prev_in_cycle(e); });
|
||||
}
|
||||
};
|
||||
|
||||
@ -117,7 +144,7 @@ Window_layouter::Focus_history::Entry::Entry(Focus_history &focus_history,
|
||||
|
||||
Window_layouter::Focus_history::Entry::~Entry()
|
||||
{
|
||||
focus_history._remove_if_present(*this);
|
||||
focus_history._entries.remove(this);
|
||||
}
|
||||
|
||||
#endif /* _FOCUS_HISTORY_H_ */
|
||||
|
@ -125,6 +125,13 @@ struct Window_layouter::Main : User_state::Action,
|
||||
_assign_list.for_each_wildcard_assigned_window([&] (Window &window) {
|
||||
_to_front(window); });
|
||||
|
||||
/* update focus if focused window became invisible */
|
||||
if (!visible(_user_state.focused_window_id())) {
|
||||
auto window_visible = [&] (Window_id id) { return visible(id); };
|
||||
_user_state.focused_window_id(_focus_history.next({}, window_visible));
|
||||
_gen_focus();
|
||||
}
|
||||
|
||||
_gen_window_layout();
|
||||
|
||||
if (_assign_list.matching_wildcards())
|
||||
@ -163,6 +170,26 @@ struct Window_layouter::Main : User_state::Action,
|
||||
|
||||
User_state _user_state { *this, _focus_history };
|
||||
|
||||
bool _visible(Window_id const id, auto const &target_cond_fn)
|
||||
{
|
||||
bool result = false;
|
||||
_target_list.for_each([&] (Target const &target) {
|
||||
if (target_cond_fn(target))
|
||||
_assign_list.for_each_visible(target.name, [&] (Assign const &assign) {
|
||||
assign.for_each_member([&] (Assign::Member const &member) {
|
||||
if (member.window.id == id)
|
||||
result = true; }); }); });
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* User_state::Action interface
|
||||
*/
|
||||
bool visible(Window_id const id) override
|
||||
{
|
||||
return _visible(id, [&] (Target const &target) { return target.visible; });
|
||||
}
|
||||
|
||||
|
||||
/**********************************
|
||||
** User_state::Action interface **
|
||||
@ -242,6 +269,21 @@ struct Window_layouter::Main : User_state::Action,
|
||||
if (from.rect == to.rect)
|
||||
_retarget_window(_drag.window_id, from, to); });
|
||||
|
||||
/* repeated activation of screen moves focus to the screen */
|
||||
bool already_visible = false;
|
||||
_target_list.with_target(name, [&] (Target const &target) {
|
||||
already_visible = target.visible; });
|
||||
|
||||
auto visible_on_screen = [&] (Window_id id)
|
||||
{
|
||||
return _visible(id, [&] (Target const &target) { return target.name == name; });
|
||||
};
|
||||
|
||||
if (already_visible && !_drag.dragging()) {
|
||||
_user_state.focused_window_id(_focus_history.next({}, visible_on_screen));
|
||||
_gen_focus();
|
||||
}
|
||||
|
||||
_gen_rules_with_frontmost_screen(name);
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,7 @@ class Window_layouter::User_state
|
||||
|
||||
struct Action : Interface
|
||||
{
|
||||
virtual bool visible(Window_id) = 0;
|
||||
virtual void close(Window_id) = 0;
|
||||
virtual void toggle_fullscreen(Window_id) = 0;
|
||||
virtual void focus(Window_id) = 0;
|
||||
@ -296,6 +297,8 @@ void Window_layouter::User_state::_handle_event(Input::Event const &e,
|
||||
if (e.press() && _key_cnt == 1)
|
||||
_key_sequence_tracker.reset();
|
||||
|
||||
auto visible = [&] (Window_id id) { return _action.visible(id); };
|
||||
|
||||
_key_sequence_tracker.apply(e, config, [&] (Command const &command) {
|
||||
|
||||
switch (command.type) {
|
||||
@ -309,12 +312,12 @@ void Window_layouter::User_state::_handle_event(Input::Event const &e,
|
||||
return;
|
||||
|
||||
case Command::NEXT_WINDOW:
|
||||
_focused_window_id = _focus_history.next(_focused_window_id);
|
||||
_focused_window_id = _focus_history.next(_focused_window_id, visible);
|
||||
_action.focus(_focused_window_id);
|
||||
return;
|
||||
|
||||
case Command::PREV_WINDOW:
|
||||
_focused_window_id = _focus_history.prev(_focused_window_id);
|
||||
_focused_window_id = _focus_history.prev(_focused_window_id, visible);
|
||||
_action.focus(_focused_window_id);
|
||||
return;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user