diff --git a/data/cartridges.gresource.xml.in b/data/cartridges.gresource.xml.in
index 472f6fb..4704591 100644
--- a/data/cartridges.gresource.xml.in
+++ b/data/cartridges.gresource.xml.in
@@ -1,6 +1,7 @@
+ @APP_ID@.metainfo.xml
gtk/window.ui
gtk/help-overlay.ui
gtk/game.ui
@@ -11,4 +12,14 @@
library_placeholder.svg
library_placeholder_small.svg
+
+ icons/sources/bottles-source-symbolic.svg
+ icons/sources/flatpak-source-symbolic.svg
+ icons/sources/heroic-source-symbolic.svg
+ icons/sources/itch-source-symbolic.svg
+ icons/sources/legendary-source-symbolic.svg
+ icons/sources/lutris-source-symbolic.svg
+ icons/sources/retroarch-source-symbolic.svg
+ icons/sources/steam-source-symbolic.svg
+
diff --git a/data/gtk/details-window.blp b/data/gtk/details-window.blp
index 6efa0f0..2bef55d 100644
--- a/data/gtk/details-window.blp
+++ b/data/gtk/details-window.blp
@@ -2,159 +2,151 @@ using Gtk 4.0;
using Adw 1;
template $DetailsWindow : Adw.Window {
- default-width: 480; // Same as Nautilus' properties window
- default-height: -1;
- modal: true;
+ default-width: 480; // Same as Nautilus' properties window
+ default-height: -1;
+ modal: true;
- ShortcutController {
- Shortcut {
- trigger: "Escape";
- action: "action(window.close)";
- }
- }
-
- Box {
- orientation: vertical;
-
- Adw.HeaderBar HeaderBar {
- show-start-title-buttons: false;
- show-end-title-buttons: false;
-
- [start]
- Button cancel_button {
- label: _("Cancel");
- action-name: "window.close";
- }
-
- [end]
- Button apply_button {
- styles [
- "suggested-action",
- ]
- }
+ ShortcutController {
+ Shortcut {
+ trigger: "Escape";
+ action: "action(window.close)";
+ }
}
- Adw.PreferencesPage {
- vexpand: true;
+ Adw.ToolbarView {
- Adw.PreferencesGroup cover_group {
- Adw.Clamp cover_clamp {
- maximum-size: 200;
+ [top]
+ Adw.HeaderBar HeaderBar {
+ show-start-title-buttons: false;
+ show-end-title-buttons: false;
- Overlay {
- [overlay]
- Spinner spinner {
- margin-start: 72;
- margin-end: 72;
+ [start]
+ Button cancel_button {
+ label: _("Cancel");
+ action-name: "window.close";
}
- Overlay cover_overlay {
- halign: center;
- valign: center;
-
- [overlay]
- Button cover_button_edit {
- icon-name: "document-edit-symbolic";
- tooltip-text: _("New Cover");
- halign: end;
- valign: end;
- margin-bottom: 6;
- margin-end: 6;
-
+ [end]
+ Button apply_button {
styles [
- "circular",
- "osd",
+ "suggested-action"
]
- }
+ }
+ }
- [overlay]
- Revealer cover_button_delete_revealer {
- transition-type: crossfade;
- margin-end: 40;
+ Adw.PreferencesPage {
+ Adw.PreferencesGroup cover_group {
+ Adw.Clamp cover_clamp {
+ maximum-size: 200;
+ Overlay {
+ [overlay]
+ Spinner spinner {
+ margin-start: 72;
+ margin-end: 72;
+ }
- Button cover_button_delete {
- icon-name: "user-trash-symbolic";
- tooltip-text: _("Delete Cover");
- halign: end;
- valign: end;
- margin-bottom: 6;
- margin-end: 6;
+ Overlay cover_overlay {
+ halign: center;
+ valign: center;
- styles [
- "circular",
- "osd",
- ]
+ [overlay]
+ Button cover_button_edit {
+ icon-name: "document-edit-symbolic";
+ tooltip-text: _("New Cover");
+ halign: end;
+ valign: end;
+ margin-bottom: 6;
+ margin-end: 6;
+
+ styles [
+ "circular", "osd"
+ ]
+ }
+
+ [overlay]
+ Revealer cover_button_delete_revealer {
+ transition-type: crossfade;
+ margin-end: 40;
+
+ Button cover_button_delete {
+ icon-name: "user-trash-symbolic";
+ tooltip-text: _("Delete Cover");
+ halign: end;
+ valign: end;
+ margin-bottom: 6;
+ margin-end: 6;
+
+ styles [
+ "circular", "osd"
+ ]
+ }
+ }
+
+ Picture cover {
+ width-request: 200;
+ height-request: 300;
+
+ styles [
+ "card"
+ ]
+ }
+ }
+ }
}
- }
-
- Picture cover {
- width-request: 200;
- height-request: 300;
-
- styles [
- "card",
- ]
- }
- }
- }
- }
- }
-
- Adw.PreferencesGroup {
- Adw.EntryRow name {
- title: _("Title");
- }
-
- Adw.EntryRow developer {
- title: _("Developer (optional)");
- }
- }
-
- Adw.PreferencesGroup {
- Adw.EntryRow executable {
- title: _("Executable");
-
- [suffix]
- Button file_chooser_button {
- valign: center;
- icon-name: "document-open-symbolic";
- tooltip-text: _("Select File");
-
- styles [
- "flat",
- ]
- }
-
- [suffix]
- MenuButton exec_info_button {
- valign: center;
- icon-name: "help-about-symbolic";
- tooltip-text: _("More Info");
- popover:
- Popover exec_info_popover {
- focusable: true;
-
- Label exec_info_label {
- use-markup: true;
- wrap: true;
- max-width-chars: 50;
- halign: center;
- valign: center;
- margin-top: 6;
- margin-bottom: 6;
- margin-start: 6;
- margin-end: 6;
- }
}
- ;
+ Adw.PreferencesGroup {
+ Adw.EntryRow name {
+ title: _("Title");
+ }
+ Adw.EntryRow developer {
+ title: _("Developer (optional)");
+ }
+ }
+ Adw.PreferencesGroup {
+ Adw.EntryRow executable {
+ title: _("Executable");
- styles [
- "flat",
- ]
- }
+ [suffix]
+ Button file_chooser_button {
+ valign: center;
+ icon-name: "document-open-symbolic";
+ tooltip-text: _("Select File");
+
+ styles [
+ "flat",
+ ]
+ }
+
+ [suffix]
+ MenuButton exec_info_button {
+ valign: center;
+ icon-name: "help-about-symbolic";
+ tooltip-text: _("More Info");
+
+ popover: Popover exec_info_popover {
+ focusable: true;
+
+ Label exec_info_label {
+ use-markup: true;
+ wrap: true;
+ max-width-chars: 50;
+ halign: center;
+ valign: center;
+ margin-top: 6;
+ margin-bottom: 6;
+ margin-start: 6;
+ margin-end: 6;
+ }
+ };
+
+ styles [
+ "flat"
+ ]
+ }
+
+ }
+ }
}
- }
}
- }
-}
+}
\ No newline at end of file
diff --git a/data/gtk/preferences.blp b/data/gtk/preferences.blp
index 9722cae..11b65fc 100644
--- a/data/gtk/preferences.blp
+++ b/data/gtk/preferences.blp
@@ -2,7 +2,6 @@ using Gtk 4.0;
using Adw 1;
template $PreferencesWindow : Adw.PreferencesWindow {
- default-height: 500;
Adw.PreferencesPage general_page {
name: "general";
@@ -12,37 +11,22 @@ template $PreferencesWindow : Adw.PreferencesWindow {
Adw.PreferencesGroup behavior_group {
title: _("Behavior");
- Adw.ActionRow {
+ Adw.SwitchRow exit_after_launch_switch {
title: _("Exit After Launching Games");
- activatable-widget: exit_after_launch_switch;
-
- Switch exit_after_launch_switch {
- valign: center;
- }
}
- Adw.ActionRow {
+ Adw.SwitchRow cover_launches_game_switch {
title: _("Cover Image Launches Game");
subtitle: _("Swaps the behavior of the cover image and the play button");
- activatable-widget: cover_launches_game_switch;
-
- Switch cover_launches_game_switch {
- valign: center;
- }
}
}
Adw.PreferencesGroup images_group {
title: _("Images");
- Adw.ActionRow {
+ Adw.SwitchRow high_quality_images_switch {
title: _("High Quality Images");
subtitle: _("Save game covers losslessly at the cost of storage");
- activatable-widget: high_quality_images_switch;
-
- Switch high_quality_images_switch {
- valign: center;
- }
}
}
@@ -87,13 +71,8 @@ template $PreferencesWindow : Adw.PreferencesWindow {
Adw.PreferencesGroup import_behavior_group {
title: _("Behavior");
- Adw.ActionRow {
+ Adw.SwitchRow remove_missing_switch {
title: _("Remove Uninstalled Games");
- activatable-widget: remove_missing_switch;
-
- Switch remove_missing_switch {
- valign: center;
- }
}
}
@@ -145,22 +124,12 @@ template $PreferencesWindow : Adw.PreferencesWindow {
}
}
- Adw.ActionRow {
+ Adw.SwitchRow lutris_import_steam_switch {
title: _("Import Steam Games");
- activatable-widget: lutris_import_steam_switch;
-
- Switch lutris_import_steam_switch {
- valign: center;
- }
}
- Adw.ActionRow {
+ Adw.SwitchRow lutris_import_flatpak_switch {
title: _("Import Flatpak Games");
- activatable-widget: lutris_import_flatpak_switch;
-
- Switch lutris_import_flatpak_switch {
- valign: center;
- }
}
}
@@ -180,40 +149,20 @@ template $PreferencesWindow : Adw.PreferencesWindow {
}
}
- Adw.ActionRow {
+ Adw.SwitchRow heroic_import_epic_switch {
title: _("Import Epic Games");
- activatable-widget: heroic_import_epic_switch;
-
- Switch heroic_import_epic_switch {
- valign: center;
- }
}
- Adw.ActionRow {
+ Adw.SwitchRow heroic_import_gog_switch {
title: _("Import GOG Games");
- activatable-widget: heroic_import_gog_switch;
-
- Switch heroic_import_gog_switch {
- valign: center;
- }
}
- Adw.ActionRow {
+ Adw.SwitchRow heroic_import_amazon_switch {
title: _("Import Amazon Games");
- activatable-widget: heroic_import_amazon_switch;
-
- Switch heroic_import_amazon_switch {
- valign: center;
- }
}
- Adw.ActionRow {
+ Adw.SwitchRow heroic_import_sideload_switch {
title: _("Import Sideloaded Games");
- activatable-widget: heroic_import_sideload_switch;
-
- Switch heroic_import_sideload_switch {
- valign: center;
- }
}
}
@@ -301,23 +250,13 @@ template $PreferencesWindow : Adw.PreferencesWindow {
}
}
- Adw.ActionRow flatpak_import_launchers_row {
+ Adw.SwitchRow flatpak_import_launchers_switch {
title: _("Import Game Launchers");
- activatable-widget: flatpak_import_launchers_switch;
-
- Switch flatpak_import_launchers_switch {
- valign: center;
- }
}
}
- Adw.ActionRow {
+ Adw.SwitchRow desktop_switch {
title: _("Desktop Entries");
- activatable-widget: desktop_switch;
-
- Switch desktop_switch {
- valign: center;
- }
}
}
}
@@ -338,32 +277,17 @@ template $PreferencesWindow : Adw.PreferencesWindow {
Adw.PreferencesGroup sgdb_behavior_group {
title: _("Behavior");
- Adw.ActionRow sgdb_switch_row {
+ Adw.SwitchRow sgdb_switch {
title: _("Use SteamGridDB");
subtitle: _("Download images when adding or importing games");
- activatable-widget: sgdb_switch;
-
- Switch sgdb_switch {
- valign: center;
- }
}
- Adw.ActionRow {
+ Adw.SwitchRow sgdb_prefer_switch {
title: _("Prefer Over Official Images");
- activatable-widget: sgdb_prefer_switch;
-
- Switch sgdb_prefer_switch {
- valign: center;
- }
}
- Adw.ActionRow {
+ Adw.SwitchRow sgdb_animated_switch {
title: _("Prefer Animated Images");
- activatable-widget: sgdb_animated_switch;
-
- Switch sgdb_animated_switch {
- valign: center;
- }
}
}
}
diff --git a/data/gtk/window.blp b/data/gtk/window.blp
index c68e1ee..3d00340 100644
--- a/data/gtk/window.blp
+++ b/data/gtk/window.blp
@@ -45,339 +45,422 @@ Adw.StatusPage hidden_notice_empty {
template $CartridgesWindow : Adw.ApplicationWindow {
title: _("Cartridges");
+ width-request: 281;
+ height-request: 100;
+
+ Adw.Breakpoint {
+ condition ("max-width: 564px")
+ setters {
+ overlay_split_view.collapsed: true;
+ details_view_box.orientation: vertical;
+ details_view_box.margin-top: 12;
+ details_view_box.margin-start: 12;
+ details_view_box.margin-end: 12;
+ details_view_details_box.margin-start: 0;
+ details_view_details_box.margin-end: 0;
+ details_view_title.margin-top: 30;
+ details_view_title.halign: center;
+ details_view_developer.halign: center;
+ details_view_date_box.halign: center;
+ details_view_toolbar.halign: center;
+ details_view_toolbar.orientation: vertical;
+ details_view_play_button.halign: center;
+ details_view_toolbar_buttons.margin-start: 0;
+ }
+ }
Adw.ToastOverlay toast_overlay {
- Stack stack {
- visible-child: library_view;
- transition-type: over_left;
+ Adw.NavigationView navigation_view {
+ Adw.NavigationPage library_page {
+ title: _("All Games");
- Overlay details_view {
- name: "details_view";
-
- [overlay]
- Box details_view_box {
- orientation: vertical;
-
- Adw.HeaderBar {
- [start]
- Button back_button {
- tooltip-text: _("Back");
- action-name: "win.go_back";
- icon-name: "go-previous-symbolic";
- }
-
- [title]
- Adw.WindowTitle details_view_header_bar_title {
- title: _("Game Details");
- }
-
- styles [
- "flat",
- ]
- }
-
- Adw.Bin {
- hexpand: true;
- vexpand: true;
-
- Box {
- halign: center;
- valign: center;
- margin-start: 24;
- margin-end: 24;
- margin-top: 24;
- margin-bottom: 24;
-
- Adw.Clamp {
- maximum-size: 200;
-
- Overlay {
- [overlay]
- Spinner details_view_spinner {
- margin-start: 72;
- margin-end: 72;
+ Adw.OverlaySplitView overlay_split_view {
+ [sidebar]
+ Adw.NavigationPage {
+ title: _("Cartridges");
+ Adw.ToolbarView {
+ [top]
+ Adw.HeaderBar {
+ [start]
+ Button {
+ icon-name: "sidebar-show-symbolic";
+ action-name: "win.show_sidebar";
+ tooltip-text: _("Toggle Sidebar");
}
+ }
- Picture details_view_cover {
- halign: end;
+ ListBox sidebar {
+ Box all_games_row_box {
+ margin-top: 12;
+ margin-bottom: 12;
+ margin-start: 6;
+ margin-end: 6;
+ spacing: 12;
+
+ Image {
+ icon-name: "view-grid-symbolic";
+ }
+ Label {
+ halign: start;
+ label: _("All Games");
+ }
+ Label all_games_no_label {
+ hexpand: true;
+ halign: end;
+
+ styles ["dim-label"]
+ }
+ }
+ Box added_row_box {
+ margin-top: 12;
+ margin-bottom: 12;
+ margin-start: 6;
+ spacing: 12;
+
+ Image {
+ icon-name: "list-add-symbolic";
+ }
+ Label {
+ halign: start;
+ label: _("Added");
+ margin-end: 6;
+ }
+ Label added_games_no_label {
+ hexpand: true;
+ halign: end;
+ margin-end: 6;
+
+ styles ["dim-label"]
+ }
+ }
+ ListBoxRow {
+ selectable: false;
+ activatable: false;
+ Label {
+ label: _("Imported");
+ styles ["heading"]
+ halign: start;
+ }
+ }
+ styles ["navigation-sidebar"]
+ }
+ }
+ }
+
+ Adw.ToolbarView library_view {
+
+ [top]
+ Adw.HeaderBar header_bar {
+
+ [start]
+ Revealer {
+ transition-type: slide_right;
+ reveal-child: bind overlay_split_view.show-sidebar inverted;
+ Button show_sidebar_button {
+ icon-name: "sidebar-show-symbolic";
+ action-name: "win.show_sidebar";
+ tooltip-text: _("Toggle Sidebar");
+ }
+ }
+
+ [start]
+ MenuButton {
+ tooltip-text: _("Add Game");
+ icon-name: "list-add-symbolic";
+ menu-model: add_games;
+ }
+
+ [end]
+ MenuButton primary_menu_button {
+ tooltip-text: _("Main Menu");
+ icon-name: "open-menu-symbolic";
+ menu-model: primary_menu;
+ }
+
+ [end]
+ ToggleButton search_button {
+ tooltip-text: _("Search");
+ icon-name: "system-search-symbolic";
+ action-name: "win.toggle_search";
+ }
+ }
+
+ [top]
+ SearchBar search_bar {
+ search-mode-enabled: bind search_button.active bidirectional;
+ key-capture-widget: navigation_view;
+
+ Adw.Clamp {
+ maximum-size: 500;
+ tightening-threshold: 500;
+
+ SearchEntry search_entry {
+ hexpand: true;
+ }
+ }
+ }
+
+ Overlay library_overlay {
+ ScrolledWindow scrolledwindow {
+ FlowBox library {
+ homogeneous: true;
+ halign: center;
valign: start;
- width-request: 200;
- height-request: 300;
-
- styles [
- "card",
- ]
+ column-spacing: 12;
+ row-spacing: 12;
+ margin-top: 15;
+ margin-bottom: 15;
+ margin-start: 15;
+ margin-end: 15;
+ selection-mode: none;
}
}
}
+ }
+ }
+ }
+ }
+ }
+ }
- Box {
- orientation: vertical;
- margin-start: 48;
- vexpand: true;
+Adw.NavigationPage hidden_library_page {
+ title: _("Hidden Games");
+
+ Adw.ToolbarView hidden_library_view {
+ [top]
+ Adw.HeaderBar hidden_header_bar {
+ [end]
+ MenuButton hidden_primary_menu_button {
+ tooltip-text: _("Main Menu");
+ icon-name: "open-menu-symbolic";
+ menu-model: primary_menu;
+ }
+
+ [end]
+ ToggleButton hidden_search_button {
+ tooltip-text: _("Search");
+ icon-name: "system-search-symbolic";
+ action-name: "win.toggle_search";
+ }
+ }
+
+ [top]
+ SearchBar hidden_search_bar {
+ search-mode-enabled: bind hidden_search_button.active bidirectional;
+ key-capture-widget: hidden_library_view;
+
+ Adw.Clamp {
+ maximum-size: 500;
+ tightening-threshold: 500;
+
+ SearchEntry hidden_search_entry {
+ hexpand: true;
+ }
+ }
+ }
+
+ Overlay hidden_library_overlay {
+ ScrolledWindow hidden_scrolledwindow {
+ FlowBox hidden_library {
+ homogeneous: true;
+ halign: center;
+ valign: start;
+ column-spacing: 12;
+ row-spacing: 12;
+ margin-top: 15;
+ margin-bottom: 15;
+ margin-start: 15;
+ margin-end: 15;
+ selection-mode: none;
+ }
+ }
+ }
+
+ styles [
+ "background",
+ ]
+ }
+}
+
+Adw.NavigationPage details_page {
+ title: _("Game Details");
+
+ Overlay details_view {
+ name: "details_view";
+
+ [overlay]
+ Adw.ToolbarView details_view_toolbar_view {
+
+ [top]
+ Adw.HeaderBar {
+ }
+
+ ScrolledWindow {
+ Box details_view_box {
+ halign: center;
+ valign: center;
+ margin-start: 24;
+ margin-end: 24;
+ margin-top: 24;
+ margin-bottom: 24;
+
+ Adw.Clamp {
+ maximum-size: 200;
+
+ Overlay {
+ [overlay]
+ Spinner details_view_spinner {
+ margin-start: 72;
+ margin-end: 72;
+ }
+
+ Picture details_view_cover {
+ halign: end;
+ valign: start;
+ width-request: 200;
+ height-request: 300;
+
+ styles [
+ "card",
+ ]
+ }
+ }
+ }
+
+ Box details_view_details_box {
+ orientation: vertical;
+ margin-start: 48;
+ vexpand: true;
+ valign: center;
+
+ Label details_view_title {
+ label: _("Game Title");
+ hexpand: true;
+ halign: start;
+ max-width-chars: 24;
+ wrap: true;
+ wrap-mode: word_char;
+ natural-wrap-mode: word;
+
+ styles [
+ "title-1",
+ ]
+ }
+
+ Label details_view_developer {
+ margin-top: 6;
+ hexpand: true;
+ halign: start;
+ max-width-chars: 36;
+ wrap: true;
+ wrap-mode: word_char;
+ natural-wrap-mode: word;
+
+ styles [
+ "heading",
+ ]
+ }
+
+ Box details_view_date_box {
+ orientation: horizontal;
+ margin-top: 15;
+ hexpand: true;
+ halign: start;
+
+ Label details_view_added {
+ wrap: true;
+ wrap-mode: word_char;
+ natural-wrap-mode: word;
+ justify: center;
+ }
+
+ Label details_view_last_played {
+ margin-start: 12;
+ wrap: true;
+ wrap-mode: word_char;
+ natural-wrap-mode: word;
+ justify: center;
+ }
+ }
+
+ Box details_view_toolbar {
+ hexpand: true;
+ vexpand: true;
+ valign: center;
+
+ Button details_view_play_button {
+ name: "details_view_play_button";
+ action-name: "app.launch_game";
+ label: _("Play");
+ halign: start;
+ margin-top: 24;
+
+ styles [
+ "opaque",
+ "pill",
+ ]
+ }
+
+ Box details_view_toolbar_buttons {
+ halign: start;
valign: center;
+ margin-top: 24;
+ margin-start: 9;
- Label details_view_title {
- label: _("Game Title");
- hexpand: true;
- halign: start;
- max-width-chars: 24;
- wrap: true;
- wrap-mode: word_char;
- natural-wrap-mode: word;
+ Button {
+ icon-name: "document-edit-symbolic";
+ action-name: "app.edit_game";
+ tooltip-text: _("Edit");
styles [
- "title-1",
+ "raised",
+ "circular",
]
}
- Label details_view_developer {
- margin-top: 6;
- hexpand: true;
- halign: start;
- max-width-chars: 36;
- wrap: true;
- wrap-mode: word_char;
- natural-wrap-mode: word;
+ Button details_view_hide_button {
+ action-name: "app.hide_game";
styles [
- "heading",
+ "raised",
+ "circular",
]
}
- Box {
- orientation: horizontal;
- margin-top: 15;
- hexpand: true;
- halign: start;
+ Button {
+ icon-name: "user-trash-symbolic";
+ action-name: "app.remove_game";
+ tooltip-text: _("Remove");
- Label details_view_added {
- wrap: true;
- wrap-mode: word_char;
- natural-wrap-mode: word;
- }
-
- Label details_view_last_played {
- margin-start: 12;
- wrap: true;
- wrap-mode: word_char;
- natural-wrap-mode: word;
- }
+ styles [
+ "raised",
+ "circular",
+ ]
}
- Box {
- hexpand: true;
- vexpand: true;
- valign: center;
+ MenuButton {
+ icon-name: "system-search-symbolic";
+ menu-model: search;
+ tooltip-text: _("Search");
- Button details_view_play_button {
- name: "details_view_play_button";
- action-name: "app.launch_game";
- label: _("Play");
- halign: start;
- margin-top: 24;
-
- styles [
- "opaque",
- "pill",
- ]
- }
-
- Box {
- halign: start;
- valign: center;
- margin-top: 24;
- margin-start: 9;
-
- Button {
- icon-name: "document-edit-symbolic";
- action-name: "app.edit_game";
- tooltip-text: _("Edit");
-
- styles [
- "raised",
- "circular",
- ]
- }
-
- Button details_view_hide_button {
- action-name: "app.hide_game";
-
- styles [
- "raised",
- "circular",
- ]
- }
-
- Button {
- icon-name: "user-trash-symbolic";
- action-name: "app.remove_game";
- tooltip-text: _("Remove");
-
- styles [
- "raised",
- "circular",
- ]
- }
-
- MenuButton {
- icon-name: "system-search-symbolic";
- menu-model: search;
- tooltip-text: _("Search");
-
- styles [
- "raised",
- "circular",
- ]
- }
-
- styles [
- "toolbar",
- ]
- }
+ styles [
+ "raised",
+ "circular",
+ ]
}
+
+ styles [
+ "toolbar",
+ ]
}
}
}
}
-
- Picture details_view_blurred_cover {
- keep-aspect-ratio: false;
- }
}
+ }
- Box library_view {
- orientation: vertical;
-
- Adw.HeaderBar header_bar {
- [start]
- MenuButton {
- tooltip-text: _("Add Game");
- icon-name: "list-add-symbolic";
- menu-model: add_games;
- }
-
- [end]
- MenuButton primary_menu_button {
- tooltip-text: _("Main Menu");
- icon-name: "open-menu-symbolic";
- menu-model: primary_menu;
- }
-
- [end]
- ToggleButton search_button {
- tooltip-text: _("Search");
- icon-name: "system-search-symbolic";
- action-name: "win.toggle_search";
- }
- }
-
- SearchBar search_bar {
- search-mode-enabled: bind-property search_button.active bidirectional;
- key-capture-widget: library_view;
-
- Adw.Clamp {
- maximum-size: 500;
- tightening-threshold: 500;
-
- SearchEntry search_entry {
- placeholder-text: _("Search games");
- hexpand: true;
- }
- }
- }
-
- Adw.Bin library_bin {
- ScrolledWindow scrolledwindow {
- hexpand: true;
- vexpand: true;
-
- FlowBox library {
- homogeneous: true;
- halign: center;
- valign: start;
- column-spacing: 12;
- row-spacing: 12;
- margin-top: 15;
- margin-bottom: 15;
- margin-start: 15;
- margin-end: 15;
- selection-mode: none;
- }
- }
- }
- }
-
- Box hidden_library_view {
- orientation: vertical;
-
- Adw.HeaderBar hidden_header_bar {
- [start]
- Button hidden_back_button {
- tooltip-text: _("Back");
- action-name: "win.go_back";
- icon-name: "go-previous-symbolic";
- }
-
- [title]
- Adw.WindowTitle {
- title: _("Hidden Games");
- }
-
- [end]
- MenuButton hidden_primary_menu_button {
- tooltip-text: _("Main Menu");
- icon-name: "open-menu-symbolic";
- menu-model: primary_menu;
- }
-
- [end]
- ToggleButton hidden_search_button {
- tooltip-text: _("Search");
- icon-name: "system-search-symbolic";
- action-name: "win.toggle_search";
- }
- }
-
- SearchBar hidden_search_bar {
- search-mode-enabled: bind-property hidden_search_button.active bidirectional;
- key-capture-widget: hidden_library_view;
-
- Adw.Clamp {
- maximum-size: 500;
- tightening-threshold: 500;
-
- SearchEntry hidden_search_entry {
- placeholder-text: _("Search hidden games");
- hexpand: true;
- }
- }
- }
-
- Adw.Bin hidden_library_bin {
- ScrolledWindow hidden_scrolledwindow {
- hexpand: true;
- vexpand: true;
-
- FlowBox hidden_library {
- homogeneous: true;
- halign: center;
- valign: start;
- column-spacing: 12;
- row-spacing: 12;
- margin-top: 15;
- margin-bottom: 15;
- margin-start: 15;
- margin-end: 15;
- selection-mode: none;
- }
- }
- }
-
- styles [
- "background",
- ]
- }
+ Picture details_view_blurred_cover {
+ keep-aspect-ratio: false;
}
}
}
diff --git a/data/hu.kramo.Cartridges.gschema.xml.in b/data/hu.kramo.Cartridges.gschema.xml.in
index 4e1f3e2..8480e57 100644
--- a/data/hu.kramo.Cartridges.gschema.xml.in
+++ b/data/hu.kramo.Cartridges.gschema.xml.in
@@ -108,10 +108,10 @@
- 1110
+ 1170
- 820
+ 795
false
@@ -126,6 +126,9 @@
"a-z"
+
+ false
+
"[]"
diff --git a/data/hu.kramo.Cartridges.metainfo.xml.in b/data/hu.kramo.Cartridges.metainfo.xml.in
index 8f73e56..f6823a4 100644
--- a/data/hu.kramo.Cartridges.metainfo.xml.in
+++ b/data/hu.kramo.Cartridges.metainfo.xml.in
@@ -22,7 +22,7 @@
touch
- 545
+ 280
diff --git a/data/icons/sources/bottles-source-symbolic.svg b/data/icons/sources/bottles-source-symbolic.svg
new file mode 100644
index 0000000..fc654c5
--- /dev/null
+++ b/data/icons/sources/bottles-source-symbolic.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/data/icons/sources/flatpak-source-symbolic.svg b/data/icons/sources/flatpak-source-symbolic.svg
new file mode 100644
index 0000000..87800a3
--- /dev/null
+++ b/data/icons/sources/flatpak-source-symbolic.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/data/icons/sources/heroic-source-symbolic.svg b/data/icons/sources/heroic-source-symbolic.svg
new file mode 100644
index 0000000..63975d1
--- /dev/null
+++ b/data/icons/sources/heroic-source-symbolic.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/data/icons/sources/itch-source-symbolic.svg b/data/icons/sources/itch-source-symbolic.svg
new file mode 100644
index 0000000..ff343b3
--- /dev/null
+++ b/data/icons/sources/itch-source-symbolic.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/data/icons/sources/legendary-source-symbolic.svg b/data/icons/sources/legendary-source-symbolic.svg
new file mode 100644
index 0000000..3dcacfb
--- /dev/null
+++ b/data/icons/sources/legendary-source-symbolic.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/data/icons/sources/lutris-source-symbolic.svg b/data/icons/sources/lutris-source-symbolic.svg
new file mode 100644
index 0000000..a9166c2
--- /dev/null
+++ b/data/icons/sources/lutris-source-symbolic.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/data/icons/sources/retroarch-source-symbolic.svg b/data/icons/sources/retroarch-source-symbolic.svg
new file mode 100644
index 0000000..0bceb2a
--- /dev/null
+++ b/data/icons/sources/retroarch-source-symbolic.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/data/icons/sources/steam-source-symbolic.svg b/data/icons/sources/steam-source-symbolic.svg
new file mode 100644
index 0000000..f66d4cb
--- /dev/null
+++ b/data/icons/sources/steam-source-symbolic.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/flatpak/hu.kramo.Cartridges.Devel.json b/flatpak/hu.kramo.Cartridges.Devel.json
index effe704..78737df 100644
--- a/flatpak/hu.kramo.Cartridges.Devel.json
+++ b/flatpak/hu.kramo.Cartridges.Devel.json
@@ -1,7 +1,7 @@
{
"id" : "hu.kramo.Cartridges.Devel",
"runtime" : "org.gnome.Platform",
- "runtime-version" : "44",
+ "runtime-version" : "45",
"sdk" : "org.gnome.Sdk",
"command" : "cartridges",
"finish-args" : [
diff --git a/po/cartridges.pot b/po/cartridges.pot
index a0c0fb4..92d9987 100644
--- a/po/cartridges.pot
+++ b/po/cartridges.pot
@@ -1,14 +1,14 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR kramo
-# This file is distributed under the same license as the Cartridges~ package.
+# This file is distributed under the same license as the Cartridges package.
# FIRST AUTHOR , YEAR.
#
#, fuzzy
msgid ""
msgstr ""
-"Project-Id-Version: Cartridges~\n"
+"Project-Id-Version: Cartridges\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-08-30 10:28+0200\n"
+"POT-Creation-Date: 2023-08-30 10:43+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -19,7 +19,7 @@ msgstr ""
#: data/hu.kramo.Cartridges.desktop.in:3
#: data/hu.kramo.Cartridges.metainfo.xml.in:6 data/gtk/window.blp:47
-#: src/main.py:176
+#: data/gtk/window.blp:80
msgid "Cartridges"
msgstr ""
@@ -53,14 +53,14 @@ msgstr ""
msgid "Edit Game Details"
msgstr ""
-#: data/hu.kramo.Cartridges.metainfo.xml.in:38 data/gtk/window.blp:71
+#: data/hu.kramo.Cartridges.metainfo.xml.in:38 data/gtk/window.blp:286
#: src/details_window.py:71
msgid "Game Details"
msgstr ""
-#: data/hu.kramo.Cartridges.metainfo.xml.in:42 data/gtk/window.blp:430
-#: src/details_window.py:265 src/importer/importer.py:304
-#: src/importer/importer.py:355
+#: data/hu.kramo.Cartridges.metainfo.xml.in:42 data/gtk/window.blp:513
+#: src/details_window.py:271 src/importer/importer.py:308
+#: src/importer/importer.py:359
msgid "Preferences"
msgstr ""
@@ -68,52 +68,52 @@ msgstr ""
msgid "Cancel"
msgstr ""
-#: data/gtk/details-window.blp:58
+#: data/gtk/details-window.blp:55
msgid "New Cover"
msgstr ""
-#: data/gtk/details-window.blp:77
+#: data/gtk/details-window.blp:73
msgid "Delete Cover"
msgstr ""
-#: data/gtk/details-window.blp:105 data/gtk/game.blp:80
+#: data/gtk/details-window.blp:100 data/gtk/game.blp:80
msgid "Title"
msgstr ""
-#: data/gtk/details-window.blp:109
+#: data/gtk/details-window.blp:103
msgid "Developer (optional)"
msgstr ""
-#: data/gtk/details-window.blp:115
+#: data/gtk/details-window.blp:108
msgid "Executable"
msgstr ""
-#: data/gtk/details-window.blp:121
+#: data/gtk/details-window.blp:114
msgid "Select File"
msgstr ""
-#: data/gtk/details-window.blp:132
+#: data/gtk/details-window.blp:125
msgid "More Info"
msgstr ""
-#: data/gtk/game.blp:102 data/gtk/game.blp:121 data/gtk/window.blp:195
+#: data/gtk/game.blp:102 data/gtk/game.blp:121 data/gtk/window.blp:413
msgid "Edit"
msgstr ""
-#: data/gtk/game.blp:107 src/window.py:190
+#: data/gtk/game.blp:107 src/window.py:348
msgid "Hide"
msgstr ""
-#: data/gtk/game.blp:112 data/gtk/game.blp:131 data/gtk/preferences.blp:56
-#: data/gtk/window.blp:215
+#: data/gtk/game.blp:112 data/gtk/game.blp:131 data/gtk/preferences.blp:40
+#: data/gtk/window.blp:433
msgid "Remove"
msgstr ""
-#: data/gtk/game.blp:126 src/window.py:192
+#: data/gtk/game.blp:126 src/window.py:350
msgid "Unhide"
msgstr ""
-#: data/gtk/help-overlay.blp:11 data/gtk/preferences.blp:9
+#: data/gtk/help-overlay.blp:11 data/gtk/preferences.blp:8
msgid "General"
msgstr ""
@@ -121,8 +121,8 @@ msgstr ""
msgid "Quit"
msgstr ""
-#: data/gtk/help-overlay.blp:19 data/gtk/window.blp:226 data/gtk/window.blp:269
-#: data/gtk/window.blp:336
+#: data/gtk/help-overlay.blp:19 data/gtk/window.blp:182 data/gtk/window.blp:241
+#: data/gtk/window.blp:444
msgid "Search"
msgstr ""
@@ -134,8 +134,8 @@ msgstr ""
msgid "Shortcuts"
msgstr ""
-#: data/gtk/help-overlay.blp:34 src/game.py:105 src/preferences.py:124
-#: src/importer/importer.py:379
+#: data/gtk/help-overlay.blp:34 src/game.py:105 src/preferences.py:122
+#: src/importer/importer.py:383
msgid "Undo"
msgstr ""
@@ -163,155 +163,155 @@ msgstr ""
msgid "Remove game"
msgstr ""
-#: data/gtk/preferences.blp:13 data/gtk/preferences.blp:88
-#: data/gtk/preferences.blp:339
+#: data/gtk/preferences.blp:12 data/gtk/preferences.blp:72
+#: data/gtk/preferences.blp:288
msgid "Behavior"
msgstr ""
-#: data/gtk/preferences.blp:16
+#: data/gtk/preferences.blp:15
msgid "Exit After Launching Games"
msgstr ""
-#: data/gtk/preferences.blp:25
+#: data/gtk/preferences.blp:19
msgid "Cover Image Launches Game"
msgstr ""
-#: data/gtk/preferences.blp:26
+#: data/gtk/preferences.blp:20
msgid "Swaps the behavior of the cover image and the play button"
msgstr ""
-#: data/gtk/preferences.blp:36 src/details_window.py:85
+#: data/gtk/preferences.blp:25 src/details_window.py:85
msgid "Images"
msgstr ""
-#: data/gtk/preferences.blp:39
+#: data/gtk/preferences.blp:28
msgid "High Quality Images"
msgstr ""
-#: data/gtk/preferences.blp:40
+#: data/gtk/preferences.blp:29
msgid "Save game covers losslessly at the cost of storage"
msgstr ""
-#: data/gtk/preferences.blp:50
+#: data/gtk/preferences.blp:34
msgid "Danger Zone"
msgstr ""
-#: data/gtk/preferences.blp:53
+#: data/gtk/preferences.blp:37
msgid "Remove All Games"
msgstr ""
-#: data/gtk/preferences.blp:84 data/gtk/window.blp:27 data/gtk/window.blp:456
+#: data/gtk/preferences.blp:68 data/gtk/window.blp:27 data/gtk/window.blp:539
msgid "Import"
msgstr ""
-#: data/gtk/preferences.blp:91
+#: data/gtk/preferences.blp:75
msgid "Remove Uninstalled Games"
msgstr ""
-#: data/gtk/preferences.blp:101
+#: data/gtk/preferences.blp:80
msgid "Sources"
msgstr ""
-#: data/gtk/preferences.blp:104 src/importer/sources/steam_source.py:114
+#: data/gtk/preferences.blp:83 src/importer/sources/steam_source.py:114
msgid "Steam"
msgstr ""
-#: data/gtk/preferences.blp:108 data/gtk/preferences.blp:125
-#: data/gtk/preferences.blp:172 data/gtk/preferences.blp:225
-#: data/gtk/preferences.blp:242 data/gtk/preferences.blp:259
-#: data/gtk/preferences.blp:276 data/gtk/preferences.blp:293
+#: data/gtk/preferences.blp:87 data/gtk/preferences.blp:104
+#: data/gtk/preferences.blp:141 data/gtk/preferences.blp:174
+#: data/gtk/preferences.blp:191 data/gtk/preferences.blp:208
+#: data/gtk/preferences.blp:225 data/gtk/preferences.blp:242
msgid "Install Location"
msgstr ""
-#: data/gtk/preferences.blp:121 src/importer/sources/lutris_source.py:92
+#: data/gtk/preferences.blp:100 src/importer/sources/lutris_source.py:92
msgid "Lutris"
msgstr ""
-#: data/gtk/preferences.blp:137
+#: data/gtk/preferences.blp:116
msgid "Cache Location"
msgstr ""
-#: data/gtk/preferences.blp:149
+#: data/gtk/preferences.blp:128
msgid "Import Steam Games"
msgstr ""
-#: data/gtk/preferences.blp:158
+#: data/gtk/preferences.blp:132
msgid "Import Flatpak Games"
msgstr ""
-#: data/gtk/preferences.blp:168 src/importer/sources/heroic_source.py:355
+#: data/gtk/preferences.blp:137 src/importer/sources/heroic_source.py:355
msgid "Heroic"
msgstr ""
-#: data/gtk/preferences.blp:184
+#: data/gtk/preferences.blp:153
msgid "Import Epic Games"
msgstr ""
-#: data/gtk/preferences.blp:193
+#: data/gtk/preferences.blp:157
msgid "Import GOG Games"
msgstr ""
-#: data/gtk/preferences.blp:202
+#: data/gtk/preferences.blp:161
msgid "Import Amazon Games"
msgstr ""
-#: data/gtk/preferences.blp:211
+#: data/gtk/preferences.blp:165
msgid "Import Sideloaded Games"
msgstr ""
-#: data/gtk/preferences.blp:221 src/importer/sources/bottles_source.py:86
+#: data/gtk/preferences.blp:170 src/importer/sources/bottles_source.py:86
msgid "Bottles"
msgstr ""
-#: data/gtk/preferences.blp:238 src/importer/sources/itch_source.py:81
+#: data/gtk/preferences.blp:187 src/importer/sources/itch_source.py:81
msgid "itch"
msgstr ""
-#: data/gtk/preferences.blp:255 src/importer/sources/legendary_source.py:97
+#: data/gtk/preferences.blp:204 src/importer/sources/legendary_source.py:97
msgid "Legendary"
msgstr ""
-#: data/gtk/preferences.blp:272 src/importer/sources/retroarch_source.py:142
+#: data/gtk/preferences.blp:221 src/importer/sources/retroarch_source.py:142
msgid "RetroArch"
msgstr ""
-#: data/gtk/preferences.blp:289 src/importer/sources/flatpak_source.py:118
+#: data/gtk/preferences.blp:238 src/importer/sources/flatpak_source.py:118
msgid "Flatpak"
msgstr ""
-#: data/gtk/preferences.blp:305
+#: data/gtk/preferences.blp:254
msgid "Import Game Launchers"
msgstr ""
-#: data/gtk/preferences.blp:315 src/importer/sources/desktop_source.py:196
+#: data/gtk/preferences.blp:264 src/importer/sources/desktop_source.py:196
msgid "Desktop Entries"
msgstr ""
-#: data/gtk/preferences.blp:327
+#: data/gtk/preferences.blp:276
msgid "SteamGridDB"
msgstr ""
-#: data/gtk/preferences.blp:331
+#: data/gtk/preferences.blp:280
msgid "Authentication"
msgstr ""
-#: data/gtk/preferences.blp:334
+#: data/gtk/preferences.blp:283
msgid "API Key"
msgstr ""
-#: data/gtk/preferences.blp:342
+#: data/gtk/preferences.blp:291
msgid "Use SteamGridDB"
msgstr ""
-#: data/gtk/preferences.blp:343
+#: data/gtk/preferences.blp:292
msgid "Download images when adding or importing games"
msgstr ""
-#: data/gtk/preferences.blp:352
+#: data/gtk/preferences.blp:296
msgid "Prefer Over Official Images"
msgstr ""
-#: data/gtk/preferences.blp:361
+#: data/gtk/preferences.blp:300
msgid "Prefer Animated Images"
msgstr ""
@@ -339,90 +339,94 @@ msgstr ""
msgid "Games you hide will appear here."
msgstr ""
-#: data/gtk/window.blp:64 data/gtk/window.blp:317
-msgid "Back"
+#: data/gtk/window.blp:75 data/gtk/window.blp:105 src/main.py:166
+msgid "All Games"
msgstr ""
-#: data/gtk/window.blp:121
-msgid "Game Title"
+#: data/gtk/window.blp:88 data/gtk/window.blp:162
+msgid "Toggle Sidebar"
msgstr ""
-#: data/gtk/window.blp:176
-msgid "Play"
+#: data/gtk/window.blp:125 src/main.py:168
+msgid "Added"
msgstr ""
-#: data/gtk/window.blp:255 data/gtk/window.blp:449
+#: data/gtk/window.blp:140
+msgid "Imported"
+msgstr ""
+
+#: data/gtk/window.blp:168 data/gtk/window.blp:532
msgid "Add Game"
msgstr ""
-#: data/gtk/window.blp:262 data/gtk/window.blp:329
+#: data/gtk/window.blp:175 data/gtk/window.blp:234
msgid "Main Menu"
msgstr ""
-#: data/gtk/window.blp:284
-msgid "Search games"
-msgstr ""
-
-#: data/gtk/window.blp:324
+#: data/gtk/window.blp:227
msgid "Hidden Games"
msgstr ""
-#: data/gtk/window.blp:351
-msgid "Search hidden games"
+#: data/gtk/window.blp:337
+msgid "Game Title"
msgstr ""
-#: data/gtk/window.blp:388
+#: data/gtk/window.blp:394
+msgid "Play"
+msgstr ""
+
+#: data/gtk/window.blp:471
msgid "Sort"
msgstr ""
-#: data/gtk/window.blp:391
+#: data/gtk/window.blp:474
msgid "A-Z"
msgstr ""
-#: data/gtk/window.blp:397
+#: data/gtk/window.blp:480
msgid "Z-A"
msgstr ""
-#: data/gtk/window.blp:403
+#: data/gtk/window.blp:486
msgid "Newest"
msgstr ""
-#: data/gtk/window.blp:409
+#: data/gtk/window.blp:492
msgid "Oldest"
msgstr ""
-#: data/gtk/window.blp:415
+#: data/gtk/window.blp:498
msgid "Last Played"
msgstr ""
-#: data/gtk/window.blp:422
+#: data/gtk/window.blp:505
msgid "Show Hidden"
msgstr ""
-#: data/gtk/window.blp:435
+#: data/gtk/window.blp:518
msgid "Keyboard Shortcuts"
msgstr ""
-#: data/gtk/window.blp:440
+#: data/gtk/window.blp:523
msgid "About Cartridges"
msgstr ""
#. Translators: Replace this with your name for it to show up in the about window
-#: src/main.py:195
+#: src/main.py:207
msgid "translator_credits"
msgstr ""
#. The variable is the date when the game was added
-#: src/window.py:213
+#: src/window.py:371
msgid "Added: {}"
msgstr ""
-#: src/window.py:216
+#: src/window.py:374
msgid "Never"
msgstr ""
#. The variable is the date when the game was last played
-#: src/window.py:220
+#: src/window.py:378
msgid "Last played: {}"
msgstr ""
@@ -479,15 +483,15 @@ msgstr ""
msgid "Couldn't Add Game"
msgstr ""
-#: src/details_window.py:171 src/details_window.py:207
+#: src/details_window.py:171 src/details_window.py:213
msgid "Game title cannot be empty."
msgstr ""
-#: src/details_window.py:177 src/details_window.py:215
+#: src/details_window.py:177 src/details_window.py:221
msgid "Executable cannot be empty."
msgstr ""
-#: src/details_window.py:206 src/details_window.py:214
+#: src/details_window.py:212 src/details_window.py:220
msgid "Couldn't Apply Preferences"
msgstr ""
@@ -507,66 +511,66 @@ msgstr ""
#. The variable is the title of the game
#. The variable is the number of games removed
-#: src/game.py:172 src/importer/importer.py:376
+#: src/game.py:169 src/importer/importer.py:380
msgid "{} removed"
msgstr ""
-#: src/preferences.py:123
+#: src/preferences.py:121
msgid "All games removed"
msgstr ""
-#: src/preferences.py:172
+#: src/preferences.py:169
msgid ""
"An API key is required to use SteamGridDB. You can generate one {}here{}."
msgstr ""
-#: src/preferences.py:293
+#: src/preferences.py:296
msgid "Installation Not Found"
msgstr ""
-#: src/preferences.py:294
+#: src/preferences.py:297
msgid "Select a valid directory."
msgstr ""
-#: src/preferences.py:330 src/importer/importer.py:302
+#: src/preferences.py:333 src/importer/importer.py:306
msgid "Warning"
msgstr ""
-#: src/preferences.py:364
+#: src/preferences.py:367
msgid "Invalid Directory"
msgstr ""
-#: src/preferences.py:370
+#: src/preferences.py:373
msgid "Set Location"
msgstr ""
-#: src/utils/create_dialog.py:33 src/importer/importer.py:303
+#: src/utils/create_dialog.py:33 src/importer/importer.py:307
msgid "Dismiss"
msgstr ""
-#: src/importer/importer.py:140
+#: src/importer/importer.py:142
msgid "Importing Games…"
msgstr ""
-#: src/importer/importer.py:323
+#: src/importer/importer.py:327
msgid "The following errors occured during import:"
msgstr ""
-#: src/importer/importer.py:352
+#: src/importer/importer.py:356
msgid "No new games found"
msgstr ""
-#: src/importer/importer.py:364
+#: src/importer/importer.py:368
msgid "1 game imported"
msgstr ""
#. The variable is the number of games
-#: src/importer/importer.py:368
+#: src/importer/importer.py:372
msgid "{} games imported"
msgstr ""
#. A single game removed
-#: src/importer/importer.py:372
+#: src/importer/importer.py:376
msgid "1 removed"
msgstr ""
diff --git a/src/details_window.py b/src/details_window.py
index a178d4e..582df58 100644
--- a/src/details_window.py
+++ b/src/details_window.py
@@ -199,6 +199,12 @@ class DetailsWindow(Adw.Window):
}
)
+ if shared.win.sidebar.get_selected_row().get_child() not in (
+ shared.win.all_games_row_box,
+ shared.win.added_row_box,
+ ):
+ shared.win.sidebar.select_row(shared.win.added_row_box.get_parent())
+
else:
if final_name == "":
create_dialog(
@@ -246,7 +252,7 @@ class DetailsWindow(Adw.Window):
self.game_cover.pictures.remove(self.cover)
self.close()
- shared.win.show_details_view(self.game)
+ shared.win.show_details_page(self.game)
def update_cover_callback(self, manager: SGDBManager) -> None:
# Set the game as not loading
diff --git a/src/game.py b/src/game.py
index 8dc981e..e1e3141 100644
--- a/src/game.py
+++ b/src/game.py
@@ -25,7 +25,7 @@ from pathlib import Path
from time import time
from typing import Any, Optional
-from gi.repository import Adw, GLib, GObject, Gtk
+from gi.repository import Adw, GObject, Gtk
from src import shared
from src.game_cover import GameCover
@@ -66,8 +66,7 @@ class Game(Gtk.Box):
def __init__(self, data: dict[str, Any], **kwargs: Any) -> None:
super().__init__(**kwargs)
- self.win = shared.win
- self.app = self.win.get_application()
+ self.app = shared.win.get_application()
self.version = shared.SPEC_VERSION
self.update_values(data)
@@ -100,18 +99,19 @@ class Game(Gtk.Box):
def create_toast(self, title: str, action: Optional[str] = None) -> None:
toast = Adw.Toast.new(title.format(self.name))
toast.set_priority(Adw.ToastPriority.HIGH)
+ toast.set_use_markup(False)
if action:
toast.set_button_label(_("Undo"))
- toast.connect("button-clicked", self.win.on_undo_action, self, action)
+ toast.connect("button-clicked", shared.win.on_undo_action, self, action)
- if (self, action) in self.win.toasts.keys():
+ if (self, action) in shared.win.toasts.keys():
# Dismiss the toast if there already is one
- self.win.toasts[(self, action)].dismiss()
+ shared.win.toasts[(self, action)].dismiss()
- self.win.toasts[(self, action)] = toast
+ shared.win.toasts[(self, action)] = toast
- self.win.toast_overlay.add_toast(toast)
+ shared.win.toast_overlay.add_toast(toast)
def launch(self) -> None:
self.last_played = int(time())
@@ -144,17 +144,15 @@ class Game(Gtk.Box):
self.hidden = not self.hidden
self.save()
- if self.win.stack.get_visible_child() == self.win.details_view:
- self.win.on_go_back_action()
+ if shared.win.navigation_view.get_visible_page() == shared.win.details_page:
+ shared.win.navigation_view.pop()
self.update()
if toast:
self.create_toast(
# The variable is the title of the game
- (_("{} hidden") if self.hidden else _("{} unhidden")).format(
- GLib.markup_escape_text(self.name)
- ),
+ (_("{} hidden") if self.hidden else _("{} unhidden")).format(self.name),
"hide",
)
@@ -164,14 +162,11 @@ class Game(Gtk.Box):
self.save()
self.update()
- if self.win.stack.get_visible_child() == self.win.details_view:
- self.win.on_go_back_action()
+ if shared.win.navigation_view.get_visible_page() == shared.win.details_page:
+ shared.win.navigation_view.pop()
- self.create_toast(
- # The variable is the title of the game
- _("{} removed").format(GLib.markup_escape_text(self.name)),
- "remove",
- )
+ # The variable is the title of the game
+ self.create_toast(_("{} removed").format(self.name), "remove")
def set_loading(self, state: int) -> None:
self.loading += state
@@ -202,7 +197,7 @@ class Game(Gtk.Box):
if shared.schema.get_boolean("cover-launches-game") ^ button:
self.launch()
else:
- self.win.show_details_view(self)
+ shared.win.show_details_page(self)
def set_play_icon(self) -> None:
self.play_button.set_icon_name(
diff --git a/src/importer/importer.py b/src/importer/importer.py
index 843da70..8db2d8d 100644
--- a/src/importer/importer.py
+++ b/src/importer/importer.py
@@ -53,6 +53,8 @@ class Importer(ErrorProducer):
removed_game_ids: set[str]
imported_game_ids: set[str]
+ close_req_id: int
+
def __init__(self) -> None:
super().__init__()
@@ -105,10 +107,13 @@ class Importer(ErrorProducer):
def run(self) -> None:
"""Use several Gio.Task to import games from added sources"""
+ shared.win.get_application().state = shared.AppState.IMPORT
+
if self.__class__.summary_toast:
self.__class__.summary_toast.dismiss()
shared.win.get_application().lookup_action("import").set_enabled(False)
+ shared.win.get_application().lookup_action("add_game").set_enabled(False)
self.create_dialog()
@@ -148,6 +153,11 @@ class Importer(ErrorProducer):
transient_for=shared.win,
deletable=False,
)
+
+ self.close_req_id = self.import_dialog.connect(
+ "close-request", lambda *_: shared.win.close()
+ )
+
self.import_dialog.present()
def source_task_thread_func(self, data: tuple) -> None:
@@ -268,10 +278,15 @@ class Importer(ErrorProducer):
self.imported_game_ids = shared.store.new_game_ids
shared.store.new_game_ids = set()
shared.store.duplicate_game_ids = set()
+ # Disconnect the close-request signal that closes the main window
+ self.import_dialog.disconnect(self.close_req_id)
self.import_dialog.close()
self.__class__.summary_toast = self.create_summary_toast()
self.create_error_dialog()
shared.win.get_application().lookup_action("import").set_enabled(True)
+ shared.win.get_application().lookup_action("add_game").set_enabled(True)
+ shared.win.get_application().state = shared.AppState.DEFAULT
+ shared.win.create_source_rows()
def create_error_dialog(self) -> None:
"""Dialog containing all errors raised by importers"""
diff --git a/src/main.py b/src/main.py
index 37c2005..fb38d4c 100644
--- a/src/main.py
+++ b/src/main.py
@@ -59,6 +59,7 @@ from src.window import CartridgesWindow
class CartridgesApplication(Adw.Application):
+ state = shared.AppState.DEFAULT
win: CartridgesWindow
def __init__(self) -> None:
@@ -80,25 +81,28 @@ class CartridgesApplication(Adw.Application):
Gtk.Window.set_default_icon_name(shared.APP_ID)
# Create the main window
- self.win = self.props.active_window # pylint: disable=no-member
- if not self.win:
- shared.win = self.win = CartridgesWindow(application=self)
+ win = self.props.active_window # pylint: disable=no-member
+ if not win:
+ shared.win = win = CartridgesWindow(application=self)
# Save window geometry
shared.state_schema.bind(
- "width", self.win, "default-width", Gio.SettingsBindFlags.DEFAULT
+ "width", shared.win, "default-width", Gio.SettingsBindFlags.DEFAULT
)
shared.state_schema.bind(
- "height", self.win, "default-height", Gio.SettingsBindFlags.DEFAULT
+ "height", shared.win, "default-height", Gio.SettingsBindFlags.DEFAULT
)
shared.state_schema.bind(
- "is-maximized", self.win, "maximized", Gio.SettingsBindFlags.DEFAULT
+ "is-maximized", shared.win, "maximized", Gio.SettingsBindFlags.DEFAULT
)
# Load games from disk
shared.store.add_manager(FileManager(), False)
shared.store.add_manager(DisplayManager())
+ self.state = shared.AppState.LOAD_FROM_DISK
self.load_games_from_disk()
+ self.state = shared.AppState.DEFAULT
+ shared.win.create_source_rows()
# Add rest of the managers for game imports
shared.store.add_manager(CoverManager())
@@ -124,26 +128,28 @@ class CartridgesApplication(Adw.Application):
("protondb_search",),
("lutris_search",),
("hltb_search",),
- ("show_hidden", ("h",), self.win),
- ("go_back", ("Left",), self.win),
- ("go_to_parent", ("Up",), self.win),
- ("go_home", ("Home",), self.win),
- ("toggle_search", ("f",), self.win),
- ("escape", ("Escape",), self.win),
- ("undo", ("z",), self.win),
- ("open_menu", ("F10",), self.win),
- ("close", ("w",), self.win),
+ ("show_sidebar", ("F9",), shared.win),
+ ("show_hidden", ("h",), shared.win),
+ ("go_to_parent", ("Up",), shared.win),
+ ("go_home", ("Home",), shared.win),
+ ("toggle_search", ("f",), shared.win),
+ ("escape", ("Escape",), shared.win),
+ ("undo", ("z",), shared.win),
+ ("open_menu", ("F10",), shared.win),
+ ("close", ("w",), shared.win),
}
)
sort_action = Gio.SimpleAction.new_stateful(
"sort_by", GLib.VariantType.new("s"), GLib.Variant("s", "a-z")
)
- sort_action.connect("activate", self.win.on_sort_action)
- self.win.add_action(sort_action)
- self.win.on_sort_action(sort_action, shared.state_schema.get_value("sort-mode"))
+ sort_action.connect("activate", shared.win.on_sort_action)
+ shared.win.add_action(sort_action)
+ shared.win.on_sort_action(
+ sort_action, shared.state_schema.get_value("sort-mode")
+ )
- self.win.present()
+ shared.win.present()
def load_games_from_disk(self) -> None:
if shared.games_dir.is_dir():
@@ -155,6 +161,15 @@ class CartridgesApplication(Adw.Application):
game = Game(data)
shared.store.add_game(game, {"skip_save": True})
+ def get_source_name(self, source_id: str) -> Any:
+ if source_id == "all":
+ name = _("All Games")
+ elif source_id == "imported":
+ name = _("Added")
+ else:
+ name = globals()[f'{source_id.split("_")[0].title()}Source'].name
+ return name
+
def on_about_action(self, *_args: Any) -> None:
# Get the debug info from the log files
debug_str = ""
@@ -171,13 +186,12 @@ class CartridgesApplication(Adw.Application):
debug_str += log_file.read()
log_file.close()
- about = Adw.AboutWindow(
- transient_for=self.win,
- application_name=_("Cartridges"),
- application_icon=shared.APP_ID,
- developer_name="kramo",
- version=shared.VERSION,
- developers=[
+ about = Adw.AboutWindow.new_from_appdata(
+ shared.PREFIX + "/" + shared.APP_ID + ".metainfo.xml", shared.VERSION
+ )
+ about.set_transient_for(shared.win)
+ about.set_developers(
+ (
"kramo https://kramo.hu",
"Geoffrey Coulaud https://geoffrey-coulaud.fr",
"Rilic https://rilic.red",
@@ -185,16 +199,19 @@ class CartridgesApplication(Adw.Application):
"Paweł Lidwin https://github.com/imLinguin",
"Domenico https://github.com/Domefemia",
"Rafael Mardojai CM https://mardojai.com",
- ],
- designers=("kramo https://kramo.hu",),
- copyright="© 2022-2023 kramo",
- license_type=Gtk.License.GPL_3_0,
- issue_url="https://github.com/kra-mo/cartridges/issues/new",
- website="https://github.com/kra-mo/cartridges",
- # Translators: Replace this with your name for it to show up in the about window
- translator_credits=_("translator_credits"),
- debug_info=debug_str,
- debug_info_filename="cartridges.log",
+ )
+ )
+ about.set_designers(("kramo https://kramo.hu",))
+ about.set_copyright("© 2022-2023 kramo")
+ # Translators: Replace this with your name for it to show up in the about window
+ about.set_translator_credits = (_("translator_credits"),)
+ about.set_debug_info(debug_str)
+ about.set_debug_info_filename("cartridges.log")
+ about.add_legal_section(
+ "Steam Branding",
+ "© 2023 Valve Corporation",
+ Gtk.License.CUSTOM,
+ "Steam and the Steam logo are trademarks and/or registered trademarks of Valve Corporation in the U.S. and/or other countries.", # pylint: disable=line-too-long
)
about.present()
@@ -215,13 +232,13 @@ class CartridgesApplication(Adw.Application):
return win
def on_launch_game_action(self, *_args: Any) -> None:
- self.win.active_game.launch()
+ shared.win.active_game.launch()
def on_hide_game_action(self, *_args: Any) -> None:
- self.win.active_game.toggle_hidden()
+ shared.win.active_game.toggle_hidden()
def on_edit_game_action(self, *_args: Any) -> None:
- DetailsWindow(self.win.active_game)
+ DetailsWindow(shared.win.active_game)
def on_add_game_action(self, *_args: Any) -> None:
DetailsWindow()
@@ -259,14 +276,14 @@ class CartridgesApplication(Adw.Application):
shared.importer.run()
def on_remove_game_action(self, *_args: Any) -> None:
- self.win.active_game.remove_game()
+ shared.win.active_game.remove_game()
def on_remove_game_details_view_action(self, *_args: Any) -> None:
- if self.win.stack.get_visible_child() == self.win.details_view:
+ if shared.win.navigation_view.get_visible_page() == shared.win.details_page:
self.on_remove_game_action()
def search(self, uri: str) -> None:
- Gio.AppInfo.launch_default_for_uri(f"{uri}{self.win.active_game.name}")
+ Gio.AppInfo.launch_default_for_uri(f"{uri}{shared.win.active_game.name}")
def on_igdb_search_action(self, *_args: Any) -> None:
self.search("https://www.igdb.com/search?type=1&q=")
diff --git a/src/preferences.py b/src/preferences.py
index 3b2b8ea..d9b5008 100644
--- a/src/preferences.py
+++ b/src/preferences.py
@@ -102,7 +102,6 @@ class PreferencesWindow(Adw.PreferencesWindow):
sgdb_key_group = Gtk.Template.Child()
sgdb_key_entry_row = Gtk.Template.Child()
sgdb_switch = Gtk.Template.Child()
- sgdb_switch_row = Gtk.Template.Child()
sgdb_prefer_switch = Gtk.Template.Child()
sgdb_animated_switch = Gtk.Template.Child()
@@ -116,9 +115,8 @@ class PreferencesWindow(Adw.PreferencesWindow):
def __init__(self, **kwargs: Any) -> None:
super().__init__(**kwargs)
- self.win = shared.win
self.file_chooser = Gtk.FileDialog()
- self.set_transient_for(self.win)
+ self.set_transient_for(shared.win)
self.toast = Adw.Toast.new(_("All games removed"))
self.toast.set_button_label(_("Undo"))
@@ -140,7 +138,6 @@ class PreferencesWindow(Adw.PreferencesWindow):
if shared.PROFILE == "development":
self.reset_action_row.set_visible(True)
self.reset_button.connect("clicked", self.reset_app)
- self.set_default_size(-1, 560)
# Sources settings
for source_class in (
@@ -175,15 +172,6 @@ class PreferencesWindow(Adw.PreferencesWindow):
)
)
- def set_sgdb_sensitive(widget: Adw.EntryRow) -> None:
- if not widget.get_text():
- shared.schema.set_boolean("sgdb", False)
-
- self.sgdb_switch_row.set_sensitive(widget.get_text())
-
- self.sgdb_key_entry_row.connect("changed", set_sgdb_sensitive)
- set_sgdb_sensitive(self.sgdb_key_entry_row)
-
# Switches
self.bind_switches(
{
@@ -205,6 +193,15 @@ class PreferencesWindow(Adw.PreferencesWindow):
}
)
+ def set_sgdb_sensitive(widget: Adw.EntryRow) -> None:
+ if not widget.get_text():
+ shared.schema.set_boolean("sgdb", False)
+
+ self.sgdb_switch.set_sensitive(widget.get_text())
+
+ self.sgdb_key_entry_row.connect("changed", set_sgdb_sensitive)
+ set_sgdb_sensitive(self.sgdb_key_entry_row)
+
def get_switch(self, setting: str) -> Any:
return getattr(self, f'{setting.replace("-", "_")}_switch')
@@ -220,9 +217,10 @@ class PreferencesWindow(Adw.PreferencesWindow):
def choose_folder(
self, _widget: Any, callback: Callable, callback_data: Optional[str] = None
) -> None:
- self.file_chooser.select_folder(self.win, None, callback, callback_data)
+ self.file_chooser.select_folder(shared.win, None, callback, callback_data)
def undo_remove_all(self, *_args: Any) -> None:
+ shared.win.get_application().state = shared.AppState.UNDO_REMOVE_ALL_GAMES
for game in self.removed_games:
game.removed = False
game.save()
@@ -230,8 +228,12 @@ class PreferencesWindow(Adw.PreferencesWindow):
self.removed_games = set()
self.toast.dismiss()
+ shared.win.get_application().state = shared.AppState.DEFAULT
+ shared.win.create_source_rows()
def remove_all_games(self, *_args: Any) -> None:
+ shared.win.get_application().state = shared.AppState.REMOVE_ALL_GAMES
+ shared.win.row_selected(None, shared.win.all_games_row_box.get_parent())
for game in shared.store:
if not game.removed:
self.removed_games.add(game)
@@ -239,10 +241,12 @@ class PreferencesWindow(Adw.PreferencesWindow):
game.save()
game.update()
- if self.win.stack.get_visible_child() == self.win.details_view:
- self.win.on_go_back_action()
+ if shared.win.navigation_view.get_visible_page() == shared.win.details_page:
+ shared.win.navigation_view.pop()
self.add_toast(self.toast)
+ shared.win.get_application().state = shared.AppState.DEFAULT
+ shared.win.create_source_rows()
def reset_app(self, *_args: Any) -> None:
rmtree(shared.data_dir / "cartridges", True)
diff --git a/src/shared.py.in b/src/shared.py.in
index c3b7a3c..19c6c85 100644
--- a/src/shared.py.in
+++ b/src/shared.py.in
@@ -18,10 +18,20 @@
# SPDX-License-Identifier: GPL-3.0-or-later
import os
+from enum import IntEnum, auto
from pathlib import Path
from gi.repository import Gdk, Gio, GLib
+
+class AppState(IntEnum):
+ DEFAULT = auto()
+ LOAD_FROM_DISK = auto()
+ IMPORT = auto()
+ REMOVE_ALL_GAMES = auto()
+ UNDO_REMOVE_ALL_GAMES = auto()
+
+
APP_ID = "@APP_ID@"
VERSION = "@VERSION@"
PREFIX = "@PREFIX@"
diff --git a/src/store/managers/display_manager.py b/src/store/managers/display_manager.py
index a5005a4..0304e94 100644
--- a/src/store/managers/display_manager.py
+++ b/src/store/managers/display_manager.py
@@ -17,6 +17,7 @@
#
# SPDX-License-Identifier: GPL-3.0-or-later
+from src import shared
from src.game import Game
from src.game_cover import GameCover
from src.store.managers.manager import Manager
@@ -46,27 +47,30 @@ class DisplayManager(Manager):
"notify::visible", game.toggle_play, None
)
game.menu_button.get_popover().connect(
- "notify::visible", game.win.set_active_game, game
+ "notify::visible", shared.win.set_active_game, game
)
- if game.game_id in game.win.game_covers:
- game.game_cover = game.win.game_covers[game.game_id]
+ if game.game_id in shared.win.game_covers:
+ game.game_cover = shared.win.game_covers[game.game_id]
game.game_cover.add_picture(game.cover)
else:
game.game_cover = GameCover({game.cover}, game.get_cover_path())
- game.win.game_covers[game.game_id] = game.game_cover
+ shared.win.game_covers[game.game_id] = game.game_cover
if (
- game.win.stack.get_visible_child() == game.win.details_view
- and game.win.active_game == game
+ shared.win.navigation_view.get_visible_page() == shared.win.details_page
+ and shared.win.active_game == game
):
- game.win.show_details_view(game)
+ shared.win.show_details_page(game)
if not game.removed and not game.blacklisted:
if game.hidden:
- game.win.hidden_library.append(game)
+ shared.win.hidden_library.append(game)
else:
- game.win.library.append(game)
+ shared.win.library.append(game)
game.get_parent().set_focusable(False)
- game.win.set_library_child()
+ shared.win.set_library_child()
+
+ if shared.win.get_application().state == shared.AppState.DEFAULT:
+ shared.win.create_source_rows()
diff --git a/src/store/store.py b/src/store/store.py
index 159da90..f056db3 100644
--- a/src/store/store.py
+++ b/src/store/store.py
@@ -48,7 +48,7 @@ class Store:
"""Check if the game is present in the store with the `in` keyword"""
if not isinstance(obj, Game):
return False
- if not (source_mapping := self.source_games.get(obj.source)):
+ if not (source_mapping := self.source_games.get(obj.base_source)):
return False
return obj.game_id in source_mapping
@@ -150,9 +150,9 @@ class Store:
game.connect(signal, manager.run)
# Add the game to the store
- if not game.source in self.source_games:
- self.source_games[game.source] = {}
- self.source_games[game.source][game.game_id] = game
+ if not game.base_source in self.source_games:
+ self.source_games[game.base_source] = {}
+ self.source_games[game.base_source][game.game_id] = game
# Run the pipeline for the game
if not run_pipeline:
diff --git a/src/utils/save_cover.py b/src/utils/save_cover.py
index 1bff991..336161d 100644
--- a/src/utils/save_cover.py
+++ b/src/utils/save_cover.py
@@ -61,7 +61,6 @@ def convert_cover(
tmp_path,
save_all=True,
append_images=frames[1:],
- disposal=2,
)
else:
diff --git a/src/window.py b/src/window.py
index 5318897..4d76b07 100644
--- a/src/window.py
+++ b/src/window.py
@@ -31,25 +31,33 @@ from src.utils.relative_date import relative_date
class CartridgesWindow(Adw.ApplicationWindow):
__gtype_name__ = "CartridgesWindow"
+ overlay_split_view = Gtk.Template.Child()
+ navigation_view = Gtk.Template.Child()
+ sidebar = Gtk.Template.Child()
+ all_games_row_box = Gtk.Template.Child()
+ all_games_no_label = Gtk.Template.Child()
+ added_row_box = Gtk.Template.Child()
+ added_games_no_label = Gtk.Template.Child()
toast_overlay = Gtk.Template.Child()
primary_menu_button = Gtk.Template.Child()
- stack = Gtk.Template.Child()
+ show_sidebar_button = Gtk.Template.Child()
details_view = Gtk.Template.Child()
+ library_page = Gtk.Template.Child()
library_view = Gtk.Template.Child()
library = Gtk.Template.Child()
scrolledwindow = Gtk.Template.Child()
- library_bin = Gtk.Template.Child()
+ library_overlay = Gtk.Template.Child()
notice_empty = Gtk.Template.Child()
notice_no_results = Gtk.Template.Child()
search_bar = Gtk.Template.Child()
search_entry = Gtk.Template.Child()
search_button = Gtk.Template.Child()
- details_view_box = Gtk.Template.Child()
+ details_page = Gtk.Template.Child()
+ details_view_toolbar_view = Gtk.Template.Child()
details_view_cover = Gtk.Template.Child()
details_view_spinner = Gtk.Template.Child()
details_view_title = Gtk.Template.Child()
- details_view_header_bar_title = Gtk.Template.Child()
details_view_blurred_cover = Gtk.Template.Child()
details_view_play_button = Gtk.Template.Child()
details_view_developer = Gtk.Template.Child()
@@ -57,11 +65,12 @@ class CartridgesWindow(Adw.ApplicationWindow):
details_view_last_played = Gtk.Template.Child()
details_view_hide_button = Gtk.Template.Child()
+ hidden_library_page = Gtk.Template.Child()
hidden_primary_menu_button = Gtk.Template.Child()
hidden_library = Gtk.Template.Child()
hidden_library_view = Gtk.Template.Child()
hidden_scrolledwindow = Gtk.Template.Child()
- hidden_library_bin = Gtk.Template.Child()
+ hidden_library_overlay = Gtk.Template.Child()
hidden_notice_empty = Gtk.Template.Child()
hidden_notice_no_results = Gtk.Template.Child()
hidden_search_bar = Gtk.Template.Child()
@@ -73,14 +82,139 @@ class CartridgesWindow(Adw.ApplicationWindow):
active_game: Game
details_view_game_cover: Optional[GameCover] = None
sort_state: str = "a-z"
+ filter_state: str = "all"
+ source_rows: dict = {}
+
+ def create_source_rows(self) -> None:
+ def get_removed(source_id: str) -> Any:
+ removed = tuple(
+ game.removed or game.hidden or game.blacklisted
+ for game in shared.store.source_games[source_id].values()
+ )
+ return (
+ (count,) if (count := sum(removed)) != len(removed) else False
+ ) # Return a tuple because 0 == False and 1 == True
+
+ total_games_no = 0
+ restored = False
+
+ selected_id = (
+ self.source_rows[selected_row][0]
+ if (selected_row := self.sidebar.get_selected_row()) in self.source_rows
+ else None
+ )
+
+ if selected_row == self.added_row_box.get_parent():
+ self.sidebar.select_row(self.added_row_box.get_parent())
+ restored = True
+
+ if added_missing := (
+ not shared.store.source_games.get("imported")
+ or not (removed := get_removed("imported"))
+ ):
+ self.sidebar.select_row(self.all_games_row_box.get_parent())
+ else:
+ games_no = len(shared.store.source_games["imported"]) - removed[0]
+ self.added_games_no_label.set_label(str(games_no))
+ total_games_no += games_no
+ self.added_row_box.get_parent().set_visible(not added_missing)
+
+ self.sidebar.get_row_at_index(2).set_visible(False)
+
+ while row := self.sidebar.get_row_at_index(3):
+ self.sidebar.remove(row)
+
+ for source_id in shared.store.source_games:
+ if source_id == "imported":
+ continue
+ if not (removed := get_removed(source_id)):
+ continue
+
+ row = Gtk.Box(
+ margin_top=12,
+ margin_bottom=12,
+ margin_start=6,
+ margin_end=6,
+ spacing=12,
+ )
+ games_no = len(shared.store.source_games[source_id]) - removed[0]
+ total_games_no += games_no
+
+ row.append(
+ Gtk.Image.new_from_icon_name(
+ "user-desktop-symbolic"
+ if (split_id := source_id.split("_")[0]) == "desktop"
+ else f"{split_id}-source-symbolic"
+ )
+ )
+
+ row.append(
+ Gtk.Label(
+ label=self.get_application().get_source_name(source_id),
+ halign=Gtk.Align.START,
+ )
+ )
+
+ row.append(
+ games_no_label := Gtk.Label(
+ label=games_no,
+ hexpand=True,
+ halign=Gtk.Align.END,
+ )
+ )
+
+ games_no_label.add_css_class("dim-label")
+
+ # Order rows based on the number of games in them
+ index = 3
+ while source_row := self.sidebar.get_row_at_index(index):
+ if self.source_rows[source_row][1] < games_no:
+ self.sidebar.insert(row, index)
+ break
+ index += 1
+ if not row.get_parent():
+ self.sidebar.append(row)
+
+ self.source_rows[row.get_parent()] = (
+ source_id,
+ games_no,
+ )
+
+ if source_id == selected_id:
+ self.sidebar.select_row(row.get_parent())
+ restored = True
+
+ self.sidebar.get_row_at_index(2).set_visible(True)
+
+ self.all_games_no_label.set_label(str(total_games_no))
+
+ if not restored:
+ self.sidebar.select_row(self.all_games_row_box.get_parent())
+
+ def row_selected(self, _widget: Any, row: Gtk.ListBoxRow | None) -> None:
+ if not row:
+ return
+ match row.get_child():
+ case self.all_games_row_box:
+ value = "all"
+ case self.added_row_box:
+ value = "imported"
+ case _:
+ value = self.source_rows[row][0]
+
+ self.library_page.set_title(self.get_application().get_source_name(value))
+
+ self.filter_state = value
+ self.library.invalidate_filter()
+
+ if self.overlay_split_view.get_collapsed():
+ self.overlay_split_view.set_show_sidebar(False)
def __init__(self, **kwargs: Any) -> None:
super().__init__(**kwargs)
- self.previous_page = self.library_view
-
- self.details_view.set_measure_overlay(self.details_view_box, True)
- self.details_view.set_clip_overlay(self.details_view_box, False)
+ self.details_view.set_measure_overlay(self.details_view_toolbar_view, True)
+ self.details_view.set_clip_overlay(self.details_view_toolbar_view, False)
self.library.set_filter_func(self.filter_func)
self.hidden_library.set_filter_func(self.filter_func)
@@ -92,6 +226,12 @@ class CartridgesWindow(Adw.ApplicationWindow):
self.notice_empty.set_icon_name(shared.APP_ID + "-symbolic")
+ self.overlay_split_view.set_show_sidebar(
+ shared.state_schema.get_boolean("show-sidebar")
+ )
+
+ self.sidebar.select_row(self.all_games_row_box.get_parent())
+
if shared.PROFILE == "development":
self.add_css_class("devel")
@@ -103,12 +243,13 @@ class CartridgesWindow(Adw.ApplicationWindow):
self.search_entry.connect("search-changed", self.search_changed, False)
self.hidden_search_entry.connect("search-changed", self.search_changed, True)
- self.search_entry.connect("activate", self.show_details_view_search)
- self.hidden_search_entry.connect("activate", self.show_details_view_search)
+ self.search_entry.connect("activate", self.show_details_page_search)
+ self.hidden_search_entry.connect("activate", self.show_details_page_search)
- back_mouse_button = Gtk.GestureClick(button=8)
- (back_mouse_button).connect("pressed", self.on_go_back_action)
- self.add_controller(back_mouse_button)
+ self.navigation_view.connect("popped", self.set_show_hidden)
+ self.navigation_view.connect("pushed", self.set_show_hidden)
+
+ self.sidebar.connect("row-selected", self.row_selected)
style_manager = Adw.StyleManager.get_default()
style_manager.connect("notify::dark", self.set_details_view_opacity)
@@ -140,25 +281,38 @@ class CartridgesWindow(Adw.ApplicationWindow):
if game.removed or game.blacklisted:
continue
if game.hidden:
- if game.filtered and hidden_child != self.hidden_scrolledwindow:
+ if game.filtered and hidden_child:
hidden_child = self.hidden_notice_no_results
continue
- hidden_child = self.hidden_scrolledwindow
+ hidden_child = None
else:
- if game.filtered and child != self.scrolledwindow:
+ if game.filtered and child:
child = self.notice_no_results
continue
- child = self.scrolledwindow
+ child = None
- self.library_bin.set_child(child)
- self.hidden_library_bin.set_child(hidden_child)
+ def remove_from_overlay(widget: Gtk.Widget) -> None:
+ if isinstance(widget.get_parent(), Gtk.Overlay):
+ widget.get_parent().remove_overlay(widget)
+
+ if child:
+ self.library_overlay.add_overlay(child)
+ else:
+ remove_from_overlay(self.notice_empty)
+ remove_from_overlay(self.notice_no_results)
+
+ if hidden_child:
+ self.hidden_library_overlay.add_overlay(hidden_child)
+ else:
+ remove_from_overlay(self.hidden_notice_empty)
+ remove_from_overlay(self.hidden_notice_no_results)
def filter_func(self, child: Gtk.Widget) -> bool:
game = child.get_child()
text = (
(
self.hidden_search_entry
- if self.stack.get_visible_child() == self.hidden_library_view
+ if self.navigation_view.get_visible_page() == self.hidden_library_page
else self.search_entry
)
.get_text()
@@ -170,6 +324,12 @@ class CartridgesWindow(Adw.ApplicationWindow):
or (text in game.developer.lower() if game.developer else False)
)
+ if not filtered:
+ if self.filter_state == "all":
+ pass
+ elif game.base_source != self.filter_state:
+ filtered = True
+
game.filtered = filtered
self.set_library_child()
@@ -178,7 +338,7 @@ class CartridgesWindow(Adw.ApplicationWindow):
def set_active_game(self, _widget: Any, _pspec: Any, game: Game) -> None:
self.active_game = game
- def show_details_view(self, game: Game) -> None:
+ def show_details_page(self, game: Game) -> None:
self.active_game = game
self.details_view_cover.set_opacity(int(not game.loading))
@@ -205,7 +365,7 @@ class CartridgesWindow(Adw.ApplicationWindow):
)
self.details_view_title.set_label(game.name)
- self.details_view_header_bar_title.set_title(game.name)
+ self.details_page.set_title(game.name)
date = relative_date(game.added)
self.details_view_added.set_label(
@@ -220,14 +380,14 @@ class CartridgesWindow(Adw.ApplicationWindow):
_("Last played: {}").format(last_played_date)
)
- if self.stack.get_visible_child() != self.details_view:
- self.navigate(self.details_view)
+ if self.navigation_view.get_visible_page() != self.details_page:
+ self.navigation_view.push(self.details_page)
self.set_focus(self.details_view_play_button)
self.set_details_view_opacity()
def set_details_view_opacity(self, *_args: Any) -> None:
- if self.stack.get_visible_child() != self.details_view:
+ if self.navigation_view.get_visible_page() != self.details_page:
return
if (
@@ -262,42 +422,26 @@ class CartridgesWindow(Adw.ApplicationWindow):
return ((get_value(0) > get_value(1)) ^ order) * 2 - 1
- def navigate(self, next_page: Gtk.Widget) -> None:
- levels = (self.library_view, self.hidden_library_view, self.details_view)
- self.stack.set_transition_type(
- Gtk.StackTransitionType.UNDER_RIGHT
- if levels.index(self.stack.get_visible_child()) - levels.index(next_page)
- > 0
- else Gtk.StackTransitionType.OVER_LEFT
+ def set_show_hidden(self, navigation_view: Adw.NavigationView, *_args: Any) -> None:
+ self.lookup_action("show_hidden").set_enabled(
+ navigation_view.get_visible_page() == self.library_page
)
- if next_page in (self.library_view, self.hidden_library_view):
- self.previous_page = next_page
- self.lookup_action("show_hidden").set_enabled(
- next_page == self.library_view
- )
-
- self.stack.set_visible_child(next_page)
-
- def on_go_back_action(self, *_args: Any) -> None:
- if self.stack.get_visible_child() == self.hidden_library_view:
- self.navigate(self.library_view)
- elif self.stack.get_visible_child() == self.details_view:
- self.on_go_to_parent_action()
+ def on_show_sidebar_action(self, *_args: Any) -> None:
+ shared.state_schema.set_boolean(
+ "show-sidebar", (value := not self.overlay_split_view.get_show_sidebar())
+ )
+ self.overlay_split_view.set_show_sidebar(value)
def on_go_to_parent_action(self, *_args: Any) -> None:
- if self.stack.get_visible_child() == self.details_view:
- self.navigate(
- self.hidden_library_view
- if self.previous_page == self.hidden_library_view
- else self.library_view
- )
+ if self.navigation_view.get_visible_page() == self.details_page:
+ self.navigation_view.pop()
def on_go_home_action(self, *_args: Any) -> None:
- self.navigate(self.library_view)
+ self.navigation_view.pop_to_page(self.library_page)
def on_show_hidden_action(self, *_args: Any) -> None:
- self.navigate(self.hidden_library_view)
+ self.navigation_view.push(self.hidden_library_page)
def on_sort_action(self, action: Gio.SimpleAction, state: GLib.Variant) -> None:
action.set_state(state)
@@ -307,10 +451,10 @@ class CartridgesWindow(Adw.ApplicationWindow):
shared.state_schema.set_string("sort-mode", self.sort_state)
def on_toggle_search_action(self, *_args: Any) -> None:
- if self.stack.get_visible_child() == self.library_view:
+ if self.navigation_view.get_visible_page() == self.library_page:
search_bar = self.search_bar
search_entry = self.search_entry
- elif self.stack.get_visible_child() == self.hidden_library_view:
+ elif self.navigation_view.get_visible_page() == self.hidden_library_page:
search_bar = self.hidden_search_bar
search_entry = self.hidden_search_entry
else:
@@ -330,9 +474,9 @@ class CartridgesWindow(Adw.ApplicationWindow):
):
self.on_toggle_search_action()
else:
- self.on_go_back_action()
+ self.navigation_view.pop()
- def show_details_view_search(self, widget: Gtk.Widget) -> None:
+ def show_details_page_search(self, widget: Gtk.Widget) -> None:
library = (
self.hidden_library if widget == self.hidden_search_entry else self.library
)
@@ -343,7 +487,7 @@ class CartridgesWindow(Adw.ApplicationWindow):
break
if self.filter_func(child):
- self.show_details_view(child.get_child())
+ self.show_details_page(child.get_child())
break
index += 1
@@ -377,9 +521,9 @@ class CartridgesWindow(Adw.ApplicationWindow):
self.toasts.pop((game, undo))
def on_open_menu_action(self, *_args: Any) -> None:
- if self.stack.get_visible_child() == self.library_view:
+ if self.navigation_view.get_visible_page() == self.library_page:
self.primary_menu_button.popup()
- elif self.stack.get_visible_child() == self.hidden_library_view:
+ elif self.navigation_view.get_visible_page() == self.hidden_library_page:
self.hidden_primary_menu_button.popup()
def on_close_action(self, *_args: Any) -> None: