Compare commits

...

5 Commits

Author SHA1 Message Date
Zoey Ahmed
faaef4bd5e gamepad: Go to sidebar when going left of headerbar 2026-01-05 15:10:30 +00:00
Zoey Ahmed
3c18ae0964 gamepad: Cleanup MR 2026-01-05 15:10:30 +00:00
Zoey Ahmed
46a30614c3 gamepad: Select collection on activate btn pressed 2026-01-05 13:42:34 +00:00
Zoey Ahmed
afa9b4ca84 gamepad: Implement return button action for sidebar 2026-01-05 13:42:34 +00:00
Zoey Ahmed
b6d73e0efe gamepad: Implement directional nav of sidebar 2026-01-05 13:42:34 +00:00
3 changed files with 100 additions and 24 deletions

View File

@@ -112,6 +112,11 @@ class Gamepad(GObject.Object):
focus_widget.activate()
return
if self.window.sidebar.get_focus_child():
selected_item = self.window.sidebar.get_selected_item()
self.window.navigate_sidebar(self.window.sidebar, item=selected_item)
return
self.window.grid.activate_action(
"list.activate-item",
GLib.Variant.new_uint32(self._get_current_position()),
@@ -136,7 +141,20 @@ class Gamepad(GObject.Object):
open_menu.grab_focus()
return
self.window.grid.grab_focus()
grid_visible = self.window.view_stack.props.visible_child_name == "grid"
if self._is_focused_on_top_bar():
focus_widget = self.window.grid if grid_visible else self.window.sidebar
# If the grid is not visible (i.e no search results or imports)
# the searchbar is focused as a fallback.
fallback_child = (
self.window.grid
if self.window.sidebar.get_focus_child()
else self.window.sidebar
)
focus_widget = fallback_child if grid_visible else self.window.search_entry
focus_widget.grab_focus()
self.window.props.focus_visible = True
def _navigate_to_game_position(self, new_pos: int):
@@ -147,24 +165,67 @@ class Gamepad(GObject.Object):
def _move_horizontally(self, direction: Gtk.DirectionType):
if self._is_focused_on_top_bar():
if not self.window.header_bar.child_focus(direction):
self.window.header_bar.keynav_failed(direction)
if self.window.header_bar.child_focus(direction):
self.window.props.focus_visible = True
return
# The usual behaviour of child_focus() on the header bar on the
# left will result in the above child focus to fail, so
# we need to manually check the user is going left to then focus the
# sidebar.
if direction is not self._get_rtl_direction(
Gtk.DirectionType.RIGHT, Gtk.DirectionType.LEFT
):
self.window.header_bar.keynav_failed(direction)
return
self.window.sidebar.grab_focus()
self.window.props.focus_visible = True
return
if self._can_navigate_games_page():
self._navigate_to_game_position(
self._get_current_position()
+ (
-1
if direction
== self._get_rtl_direction(
Gtk.DirectionType.RIGHT, Gtk.DirectionType.LEFT
)
else 1
)
if self.window.sidebar.get_focus_child():
# The usual behaviour of child_focus() on the sidebar
# would result in the + button being focused, instead of the grid
# so we need to grab the focus of the grid if the user inputs the
# corresponding direction to the grid.
grid_direction = self._get_rtl_direction(
Gtk.DirectionType.LEFT, Gtk.DirectionType.RIGHT
)
# Focus the first game when re-entering from sidebar
if direction is grid_direction:
self.window.grid.scroll_to(0, Gtk.ListScrollFlags.FOCUS, None)
self.window.grid.grab_focus()
return
self.window.sidebar.keynav_failed(direction)
return
if self._can_navigate_games_page():
if not self._get_focused_game():
return
new_pos = self._get_current_position() + (
-1
if direction
== self._get_rtl_direction(
Gtk.DirectionType.RIGHT, Gtk.DirectionType.LEFT
)
else 1
)
# If the user is focused on the first game and tries to go
# back another game, instead of failing, the focus should
# change to the sidebar.
if new_pos < 0:
self.window.sidebar.grab_focus()
self.window.props.focus_visible = True
return
self._navigate_to_game_position(new_pos)
return
if self.window.navigation_view.props.visible_page_tag == "details":
@@ -184,6 +245,14 @@ class Gamepad(GObject.Object):
self.window.grid.grab_focus()
return
if self.window.sidebar.get_focus_child():
if self.window.sidebar.child_focus(direction):
self.window.props.focus_visible = True
return
self.window.sidebar.keynav_failed(direction)
return
if self._can_navigate_games_page():
if not (game := self._get_focused_game()):
return

View File

@@ -208,7 +208,7 @@ template $Window: Adw.ApplicationWindow {
}
content: Adw.ToastOverlay toast_overlay {
child: Adw.ViewStack {
child: Adw.ViewStack view_stack {
visible-child-name: bind $_if_else(
grid.model as <NoSelection>.n-items,
"grid",

View File

@@ -56,6 +56,7 @@ class Window(Adw.ApplicationWindow):
sort_button: Gtk.MenuButton = Gtk.Template.Child()
main_menu_button: Gtk.MenuButton = Gtk.Template.Child()
toast_overlay: Adw.ToastOverlay = Gtk.Template.Child()
view_stack: Adw.ViewStack = Gtk.Template.Child()
grid: Gtk.GridView = Gtk.Template.Child()
sorter: GameSorter = Gtk.Template.Child()
collection_filter: CollectionFilter = Gtk.Template.Child()
@@ -163,15 +164,12 @@ class Window(Adw.ApplicationWindow):
self.toast_overlay.add_toast(toast)
@Gtk.Template.Callback()
def _show_sidebar_title(self, _obj, layout: str) -> bool:
right_window_controls = layout.replace("appmenu", "").startswith(":")
return right_window_controls and not sys.platform.startswith("darwin")
@Gtk.Template.Callback()
def _navigate(self, sidebar: Adw.Sidebar, index: int): # pyright: ignore[reportAttributeAccessIssue]
item = sidebar.get_item(index)
def navigate_sidebar(self, sidebar: Adw.Sidebar, item: Adw.SidebarItem): # pyright: ignore[reportAttributeAccessIssue]
"""Select given item in the sidebar.
Item should correspond to a collection, or the all game category/
new collection buttons.
"""
match item:
case self.new_collection_item:
self._add_collection()
@@ -182,11 +180,20 @@ class Window(Adw.ApplicationWindow):
self.collection = None
if item is not self.new_collection_item:
self._selected_sidebar_item = index
self._selected_sidebar_item = sidebar.props.selected
if self.split_view.props.collapsed:
self.split_view.props.show_sidebar = False
@Gtk.Template.Callback()
def _show_sidebar_title(self, _obj, layout: str) -> bool:
right_window_controls = layout.replace("appmenu", "").startswith(":")
return right_window_controls and not sys.platform.startswith("darwin")
@Gtk.Template.Callback()
def _navigate(self, sidebar: Adw.Sidebar, index: int): # pyright: ignore[reportAttributeAccessIssue]
self.navigate_sidebar(sidebar, sidebar.get_item(index))
@Gtk.Template.Callback()
def _update_selection(self, sidebar: Adw.Sidebar, *_args): # pyright: ignore[reportAttributeAccessIssue]
if sidebar.props.selected_item is self.new_collection_item: