This commit is contained in:
kramo
2023-04-18 02:31:44 +02:00
parent 741245b415
commit f8bb111939
7 changed files with 108 additions and 163 deletions

View File

@@ -43,6 +43,7 @@ class Game(Gtk.Box):
hidden_game_options = Gtk.Template.Child() hidden_game_options = Gtk.Template.Child()
loading = 0 loading = 0
filtered = False
def __init__(self, win, data, **kwargs): def __init__(self, win, data, **kwargs):
super().__init__(**kwargs) super().__init__(**kwargs)
@@ -60,33 +61,36 @@ class Game(Gtk.Box):
self.removed = "removed" in data self.removed = "removed" in data
self.blacklisted = "blacklisted" in data self.blacklisted = "blacklisted" in data
self.title.set_label(self.name)
if self.game_id in self.win.game_covers: if self.game_id in self.win.game_covers:
self.win.game_covers[self.game_id].add_picture(self.cover) self.win.game_covers[self.game_id].add_picture(self.cover)
else: else:
game_cover = GameCover({self.cover}, self.get_cover_path()) game_cover = GameCover({self.cover}, self.get_cover_path())
self.win.game_covers[self.game_id] = game_cover self.win.game_covers[self.game_id] = game_cover
self.event_contoller_motion = Gtk.EventControllerMotion.new()
self.add_controller(self.event_contoller_motion)
self.overlay.set_measure_overlay(self.play_revealer, True)
self.set_play_label()
self.cover_button.connect("clicked", self.cover_button_clicked)
self.play_button.connect("clicked", self.play_button_clicked)
self.event_contoller_motion.connect("enter", self.show_play)
self.event_contoller_motion.connect("leave", self.hide_play)
self.win.schema.connect("changed", self.schema_changed)
if self.hidden: if self.hidden:
self.menu_button.set_menu_model(self.hidden_game_options) self.menu_button.set_menu_model(self.hidden_game_options)
else: else:
self.menu_button.set_menu_model(self.game_options) self.menu_button.set_menu_model(self.game_options)
self.title.set_label(self.name)
self.set_play_label()
self.overlay.set_measure_overlay(self.play_revealer, True)
self.event_contoller_motion = Gtk.EventControllerMotion.new()
self.add_controller(self.event_contoller_motion)
self.event_contoller_motion.connect("enter", self.show_play)
self.event_contoller_motion.connect("leave", self.hide_play)
self.cover_button.connect("clicked", self.cover_button_clicked)
self.play_button.connect("clicked", self.play_button_clicked)
self.menu_button.get_popover().connect("notify::visible", self.hide_play) self.menu_button.get_popover().connect("notify::visible", self.hide_play)
self.menu_button.get_popover().connect(
"notify::visible", self.win.set_active_game, self.game_id
)
self.win.schema.connect("changed", self.schema_changed)
def launch(self): def launch(self):
# Generate launch arguments, either list (no shell) or a string (for shell). # Generate launch arguments, either list (no shell) or a string (for shell).

View File

@@ -154,7 +154,7 @@ class CartridgesApplication(Adw.Application):
game_id = self.win.active_game_id game_id = self.win.active_game_id
last_played = int(time.time()) last_played = int(time.time())
data = get_games(self.win, [game_id])[game_id] data = get_games(self.win, {game_id})[game_id]
data["last_played"] = last_played data["last_played"] = last_played
save_game(self.win, data) save_game(self.win, data)
@@ -180,7 +180,7 @@ class CartridgesApplication(Adw.Application):
if self.win.stack.get_visible_child() == self.win.details_view: if self.win.stack.get_visible_child() == self.win.details_view:
self.win.on_go_back_action(None, None) self.win.on_go_back_action(None, None)
self.win.games[game_id].toggle_hidden() self.win.games[game_id].toggle_hidden()
self.win.update_games([game_id]) self.win.update_games({game_id})
if not toast: if not toast:
return return
@@ -237,11 +237,11 @@ class CartridgesApplication(Adw.Application):
# Add "removed=True" to the game properties so it can be deleted on next init # Add "removed=True" to the game properties so it can be deleted on next init
game_id = self.win.active_game_id game_id = self.win.active_game_id
data = get_games(self.win, [game_id])[game_id] data = get_games(self.win, {game_id})[game_id]
data["removed"] = True data["removed"] = True
save_game(self.win, data) save_game(self.win, data)
self.win.update_games([game_id]) self.win.update_games({game_id})
if self.win.stack.get_visible_child() == self.win.details_view: if self.win.stack.get_visible_child() == self.win.details_view:
self.win.on_go_back_action(None, None) self.win.on_go_back_action(None, None)

View File

@@ -19,7 +19,6 @@
import os import os
from pathlib import Path from pathlib import Path
from shutil import move
from gi.repository import Adw, Gio, GLib, Gtk from gi.repository import Adw, Gio, GLib, Gtk
@@ -139,7 +138,7 @@ class PreferencesWindow(Adw.PreferencesWindow):
) )
) )
self.add_controller(shortcut_controller) self.add_controller(shortcut_controller)
self.removed_games = [] self.removed_games = set()
# General # General
self.schema.bind( self.schema.bind(
@@ -335,43 +334,23 @@ class PreferencesWindow(Adw.PreferencesWindow):
self.file_chooser.select_folder(self.win, None, function, None) self.file_chooser.select_folder(self.win, None, function, None)
def undo_remove_all(self, _widget, _unused): def undo_remove_all(self, _widget, _unused):
deleted_covers_dir = self.win.cache_dir / "cartridges" / "deleted_covers"
for game_id in self.removed_games: for game_id in self.removed_games:
data = get_games(self.win, [game_id])[game_id] data = get_games(self.win, {game_id})[game_id]
if "removed" in data: if "removed" in data:
data.pop("removed") data.pop("removed")
save_game(self.win, data) save_game(self.win, data)
cover_path = deleted_covers_dir / f"{game_id}.tiff"
if not cover_path.is_file():
cover_path = deleted_covers_dir / f"{game_id}.gif"
if not cover_path.is_file():
continue
move(cover_path, self.win.covers_dir / cover_path.name)
self.win.update_games(self.removed_games) self.win.update_games(self.removed_games)
self.removed_games = [] self.removed_games = set()
self.toast.dismiss() self.toast.dismiss()
def remove_all_games(self, _widget): def remove_all_games(self, _widget):
deleted_covers_dir = self.win.cache_dir / "cartridges" / "deleted_covers"
deleted_covers_dir.mkdir(parents=True, exist_ok=True)
for game in get_games(self.win).values(): for game in get_games(self.win).values():
if "removed" not in game: if "removed" not in game:
self.removed_games.append(game["game_id"]) self.removed_games.add(game["game_id"])
game["removed"] = True game["removed"] = True
save_game(self.win, game) save_game(self.win, game)
cover_path = self.win.games[game["game_id"]].get_cover_path()
if not cover_path:
continue
if cover_path.is_file():
move(cover_path, deleted_covers_dir / cover_path.name)
self.win.update_games(self.win.games) self.win.update_games(self.win.games)
if self.win.stack.get_visible_child() == self.win.details_view: if self.win.stack.get_visible_child() == self.win.details_view:
self.win.on_go_back_action(None, None) self.win.on_go_back_action(None, None)

View File

@@ -332,7 +332,7 @@ def create_details_window(win, game_id=None):
else: else:
save_game(win, values) save_game(win, values)
win.update_games([game_id]) win.update_games({game_id})
if not game_cover.get_pixbuf(): if not game_cover.get_pixbuf():
SGDBSave(win, {(game_id, values["name"])}) SGDBSave(win, {(game_id, values["name"])})

View File

@@ -23,17 +23,15 @@ import json
def get_games(win, game_ids=None): def get_games(win, game_ids=None):
games = {} games = {}
if not win.games_dir.exists():
return {}
game_files = ( game_files = (
[win.games_dir / f"{game_id}.json" for game_id in game_ids] {win.games_dir / f"{game_id}.json" for game_id in game_ids}
if game_ids if game_ids
else win.games_dir.iterdir() else win.games_dir.iterdir()
) )
for open_file in game_files: for open_file in game_files:
data = json.load(open_file.open()) if open_file.exists():
games[data["game_id"]] = data data = json.load(open_file.open())
games[data["game_id"]] = data
return games return games

View File

@@ -112,7 +112,7 @@ class SGDBSave:
).connect("response", self.response) ).connect("response", self.response)
game_id = result.propagate_value()[1] game_id = result.propagate_value()[1]
self.win.update_games([game_id]) self.win.update_games({game_id})
def response(self, _widget, response): def response(self, _widget, response):
if response == "open_preferences": if response == "open_preferences":

View File

@@ -21,7 +21,6 @@ import datetime
import os import os
from struct import unpack_from from struct import unpack_from
from pathlib import Path from pathlib import Path
from shutil import rmtree
from gi.repository import Adw, Gdk, GdkPixbuf, Gio, GLib, Gtk from gi.repository import Adw, Gdk, GdkPixbuf, Gio, GLib, Gtk
@@ -71,10 +70,6 @@ class CartridgesWindow(Adw.ApplicationWindow):
games = {} games = {}
game_covers = {} game_covers = {}
visible_widgets = {}
hidden_widgets = {}
filtered = {}
hidden_filtered = {}
toasts = {} toasts = {}
active_game_id = None active_game_id = None
scaled_pixbuf = None scaled_pixbuf = None
@@ -119,13 +114,11 @@ class CartridgesWindow(Adw.ApplicationWindow):
(self.covers_dir / f"{game_id}.tiff").unlink(missing_ok=True) (self.covers_dir / f"{game_id}.tiff").unlink(missing_ok=True)
(self.covers_dir / f"{game_id}.gif").unlink(missing_ok=True) (self.covers_dir / f"{game_id}.gif").unlink(missing_ok=True)
rmtree(self.cache_dir / "cartridges" / "deleted_covers", True)
self.details_view.set_measure_overlay(self.details_view_box, True) 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_clip_overlay(self.details_view_box, False)
self.library.set_filter_func(self.search_filter) self.library.set_filter_func(self.search_filter)
self.hidden_library.set_filter_func(self.hidden_search_filter) self.hidden_library.set_filter_func(self.search_filter)
self.library.set_sort_func(self.sort_func) self.library.set_sort_func(self.sort_func)
self.hidden_library.set_sort_func(self.sort_func) self.hidden_library.set_sort_func(self.sort_func)
@@ -148,126 +141,99 @@ class CartridgesWindow(Adw.ApplicationWindow):
) )
def update_games(self, games): def update_games(self, games):
current_games = get_games(self)
for game_id in games: for game_id in games:
if game_id in self.visible_widgets: if game_id in self.games and self.games[game_id].get_parent():
self.library.remove(self.visible_widgets[game_id]) self.games[game_id].get_parent().get_parent().remove(
self.filtered.pop(self.visible_widgets[game_id]) self.games[game_id]
self.visible_widgets.pop(game_id) )
elif game_id in self.hidden_widgets:
self.hidden_library.remove(self.hidden_widgets[game_id])
self.hidden_filtered.pop(self.hidden_widgets[game_id])
self.hidden_widgets.pop(game_id)
current_game = current_games[game_id] entry = Game(self, get_games(self, {game_id})[game_id])
self.games[game_id] = entry
entry = Game(self, current_game) if entry.removed or entry.blacklisted:
self.games[current_game["game_id"]] = entry
if entry.removed:
continue
if entry.blacklisted:
continue continue
if not self.games[game_id].hidden: if entry.hidden:
self.visible_widgets[game_id] = entry
self.library.append(entry)
else:
self.hidden_widgets[game_id] = entry
self.hidden_library.append(entry) self.hidden_library.append(entry)
else:
self.library.append(entry)
entry.menu_button.get_popover().connect(
"notify::visible", self.set_active_game, game_id
)
entry.get_parent().set_focusable(False) entry.get_parent().set_focusable(False)
if not self.visible_widgets: self.library_bin.set_child(
self.library_bin.set_child(self.notice_empty) self.scrolledwindow
else: if any(not game.hidden for game in self.games.values())
self.library_bin.set_child(self.scrolledwindow) else self.notice_empty
)
if not self.hidden_widgets: self.hidden_library_bin.set_child(
self.hidden_library_bin.set_child(self.hidden_notice_empty) self.hidden_scrolledwindow
else: if any(game.hidden for game in self.games.values())
self.hidden_library_bin.set_child(self.hidden_scrolledwindow) else self.hidden_notice_empty
)
if self.stack.get_visible_child() == self.details_view: if self.stack.get_visible_child() == self.details_view:
self.show_details_view(None, self.active_game_id) self.show_details_view(None, self.active_game_id)
self.library.invalidate_filter()
self.hidden_library.invalidate_filter()
def search_changed(self, _widget, hidden): def search_changed(self, _widget, hidden):
# Refresh search filter on keystroke in search box # Refresh search filter on keystroke in search box
if not hidden: if hidden:
self.library.invalidate_filter()
else:
self.hidden_library.invalidate_filter() self.hidden_library.invalidate_filter()
else:
self.library.invalidate_filter()
def search_filter(self, child): def search_filter(self, child):
# Only show games matching the contents of the search box hidden = self.stack.get_visible_child() == self.hidden_library_view
text = self.search_entry.get_text().lower() text = (
if text == "": (self.hidden_search_entry if hidden else self.search_entry)
filtered = True .get_text()
elif ( .lower()
)
filtered = text != "" and not (
text in child.get_first_child().name.lower() text in child.get_first_child().name.lower()
or text in child.get_first_child().developer.lower() or text in child.get_first_child().developer.lower()
if child.get_first_child().developer if child.get_first_child().developer
else None else None
): )
filtered = True
else:
filtered = False
# Add filtered entry to dict of filtered widgets child.get_first_child().filtered = filtered
self.filtered[child.get_first_child()] = filtered
if True not in self.filtered.values(): (self.hidden_library_bin if hidden else self.library_bin).set_child(
self.library_bin.set_child(self.notice_no_results) (self.hidden_scrolledwindow if hidden else self.scrolledwindow)
else: if any(
self.library_bin.set_child(self.scrolledwindow) not game.filtered
return filtered for game in self.games.values()
if not (
game.removed
or game.blacklisted
or (not game.hidden if hidden else game.hidden)
)
)
else self.notice_no_results
)
def hidden_search_filter(self, child): return not filtered
text = self.hidden_search_entry.get_text().lower()
if text == "":
filtered = True
elif (
text in child.get_first_child().name.lower()
or text in child.get_first_child().developer.lower()
if child.get_first_child().developer
else None
):
filtered = True
else:
filtered = False
self.hidden_filtered[child.get_first_child()] = filtered
if True not in self.hidden_filtered.values():
self.hidden_library_bin.set_child(self.notice_no_results)
else:
self.hidden_library_bin.set_child(self.hidden_scrolledwindow)
return filtered
def set_active_game(self, _widget, _unused, game_id): def set_active_game(self, _widget, _unused, game_id):
self.active_game_id = game_id self.active_game_id = game_id
def get_time(self, timestamp): def get_time(self, timestamp):
date = datetime.datetime.fromtimestamp(timestamp) date = datetime.datetime.fromtimestamp(timestamp)
days_no = (datetime.datetime.today() - date).days
if (datetime.datetime.today() - date).days == 0: if days_no == 0:
return _("Today") return _("Today")
if (datetime.datetime.today() - date).days == 1: if days_no == 1:
return _("Yesterday") return _("Yesterday")
if (datetime.datetime.today() - date).days < 8: if days_no < 8:
return GLib.DateTime.new_from_unix_utc(timestamp).format("%A") return GLib.DateTime.new_from_unix_utc(timestamp).format("%A")
if (datetime.datetime.today() - date).days < 335: if days_no < 335:
return GLib.DateTime.new_from_unix_utc(timestamp).format("%B") return GLib.DateTime.new_from_unix_utc(timestamp).format("%B")
return GLib.DateTime.new_from_unix_utc(timestamp).format("%Y") return GLib.DateTime.new_from_unix_utc(timestamp).format("%Y")
def show_details_view(self, _widget, game_id): def show_details_view(self, _widget, game_id):
self.active_game_id = game_id
current_game = self.games[game_id] current_game = self.games[game_id]
self.details_view_cover.set_visible(not current_game.loading) self.details_view_cover.set_visible(not current_game.loading)
@@ -286,8 +252,6 @@ class CartridgesWindow(Adw.ApplicationWindow):
self.details_view_hide_button.set_icon_name("view-conceal-symbolic") self.details_view_hide_button.set_icon_name("view-conceal-symbolic")
self.details_view_hide_button.set_tooltip_text(_("Hide")) self.details_view_hide_button.set_tooltip_text(_("Hide"))
self.active_game_id = game_id
if self.details_view_game_cover: if self.details_view_game_cover:
self.details_view_game_cover.pictures.remove(self.details_view_cover) self.details_view_game_cover.pictures.remove(self.details_view_cover)
self.details_view_game_cover = self.game_covers[game_id] self.details_view_game_cover = self.game_covers[game_id]
@@ -333,12 +297,14 @@ class CartridgesWindow(Adw.ApplicationWindow):
self.details_view_blurred_cover.set_opacity(0.3) self.details_view_blurred_cover.set_opacity(0.3)
return return
pixels = self.scaled_pixbuf.get_pixels() colors = {
channels = self.scaled_pixbuf.get_n_channels() unpack_from(
colors = set() "BBBB",
self.scaled_pixbuf.get_pixels(),
for index in range(6): offset=index * self.scaled_pixbuf.get_n_channels(),
colors.add(unpack_from("BBBB", pixels, offset=index * channels)) )
for index in range(6)
}
dark_theme = style_manager.get_dark() dark_theme = style_manager.get_dark()
@@ -348,19 +314,17 @@ class CartridgesWindow(Adw.ApplicationWindow):
# https://en.wikipedia.org/wiki/Relative_luminance # https://en.wikipedia.org/wiki/Relative_luminance
luminance = red * 0.2126 + green * 0.7152 + blue * 0.0722 luminance = red * 0.2126 + green * 0.7152 + blue * 0.0722
if dark_theme: luminances.append(
luminances.append((luminance * alpha) / 255**2) (luminance * alpha) / 255**2
else: if dark_theme
luminances.append((alpha * (luminance - 255)) / 255**2 + 1) else (alpha * (luminance - 255)) / 255**2 + 1
)
if dark_theme: self.details_view_blurred_cover.set_opacity(
self.details_view_blurred_cover.set_opacity( 1.3 - (sum(luminances) / len(luminances) + max(luminances)) / 2
1.3 - (sum(luminances) / len(luminances) + max(luminances)) / 2 if dark_theme
) else 0.2 + (sum(luminances) / len(luminances) + min(luminances)) / 2
else: )
self.details_view_blurred_cover.set_opacity(
0.2 + (sum(luminances) / len(luminances) + min(luminances)) / 2
)
def sort_func(self, child1, child2): def sort_func(self, child1, child2):
games = (child1.get_first_child(), child2.get_first_child()) games = (child1.get_first_child(), child2.get_first_child())
@@ -473,10 +437,10 @@ class CartridgesWindow(Adw.ApplicationWindow):
) )
elif undo == "remove": elif undo == "remove":
data = get_games(self, [game_id])[game_id] data = get_games(self, {game_id})[game_id]
data.pop("removed", None) data.pop("removed", None)
save_game(self, data) save_game(self, data)
self.update_games([game_id]) self.update_games({game_id})
self.toasts[(game_id, undo)].dismiss() self.toasts[(game_id, undo)].dismiss()
self.toasts.pop((game_id, undo)) self.toasts.pop((game_id, undo))