Skip to content

Commit

Permalink
window_layouter: restrict focus to visible windows
Browse files Browse the repository at this point in the history
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 genodelabs#5390
  • Loading branch information
nfeske committed Dec 3, 2024
1 parent a7d843a commit 9b2908c
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 44 deletions.
117 changes: 75 additions & 42 deletions repos/gems/src/app/window_layouter/focus_history.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ class Window_layouter::Focus_history
{
public:

struct Action : Interface, Noncopyable
{
virtual bool visible(Window_id) = 0;
};

struct Entry : List<Entry>::Element
{
Focus_history &focus_history;
Expand All @@ -36,72 +41,100 @@ class Window_layouter::Focus_history

private:

Action &_action;

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 e.next() ? e.next() : _entries.first();
}

return nullptr;
Entry const *_prev_in_cycle(Entry const &e) const
{
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();
}

void _remove_if_present(Entry &entry)
Window_id _any_visible_or_none() const
{
_entries.remove(&entry);
for (Entry const *ptr = _entries.first(); ptr; ptr = ptr->next())
if (_action.visible(ptr->window_id))
return ptr->window_id;
return Window_id();
}

Window_id _visible_neighbor(Window_id window_id, 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_visible_or_none();

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_visible_or_none();

if (next_ptr && _action.visible(next_ptr->window_id))
return next_ptr->window_id;
}
return Window_id();
}

public:

Focus_history(Action &action) : _action(action) { }

void focus(Window_id window_id)
{
Entry * const entry = _lookup(window_id);
if (!entry) {
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) 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 _visible_neighbor(id, [&] (Entry const &e) { return _next_in_cycle(e); });
}

Window_id prev(Window_id window_id)
Window_id prev(Window_id id) 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 _visible_neighbor(id, [&] (Entry const &e) { return _prev_in_cycle(e); });
}
};

Expand All @@ -117,7 +150,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_ */
27 changes: 25 additions & 2 deletions repos/gems/src/app/window_layouter/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ namespace Window_layouter { struct Main; }

struct Window_layouter::Main : User_state::Action,
Layout_rules::Action,
Window_list::Action
Window_list::Action,
Focus_history::Action
{
Env &_env;

Expand All @@ -58,7 +59,7 @@ struct Window_layouter::Main : User_state::Action,

unsigned _to_front_cnt = 1;

Focus_history _focus_history { };
Focus_history _focus_history { *this };

Layout_rules _layout_rules { _env, _heap, *this };

Expand Down Expand Up @@ -124,6 +125,12 @@ 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())) {
_user_state.focused_window_id(_focus_history.next({}));
_gen_focus();
}

_gen_window_layout();

if (_assign_list.matching_wildcards())
Expand Down Expand Up @@ -164,6 +171,22 @@ struct Window_layouter::Main : User_state::Action,
User_state _user_state { *this, _focus_history };


/**
* Focus_history::Action interface
*/
bool visible(Window_id const id) override
{
bool result = false;
_target_list.for_each([&] (Target const &target) {
if (target.visible)
_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 **
**********************************/
Expand Down

0 comments on commit 9b2908c

Please sign in to comment.