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()
loading = 0
filtered = False
def __init__(self, win, data, **kwargs):
super().__init__(**kwargs)
@@ -60,33 +61,36 @@ class Game(Gtk.Box):
self.removed = "removed" in data
self.blacklisted = "blacklisted" in data
self.title.set_label(self.name)
if self.game_id in self.win.game_covers:
self.win.game_covers[self.game_id].add_picture(self.cover)
else:
game_cover = GameCover({self.cover}, self.get_cover_path())
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:
self.menu_button.set_menu_model(self.hidden_game_options)
else:
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.win.set_active_game, self.game_id
)
self.win.schema.connect("changed", self.schema_changed)
def launch(self):
# 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
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
save_game(self.win, data)
@@ -180,7 +180,7 @@ class CartridgesApplication(Adw.Application):
if self.win.stack.get_visible_child() == self.win.details_view:
self.win.on_go_back_action(None, None)
self.win.games[game_id].toggle_hidden()
self.win.update_games([game_id])
self.win.update_games({game_id})
if not toast:
return
@@ -237,11 +237,11 @@ class CartridgesApplication(Adw.Application):
# Add "removed=True" to the game properties so it can be deleted on next init
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
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:
self.win.on_go_back_action(None, None)

View File

@@ -19,7 +19,6 @@
import os
from pathlib import Path
from shutil import move
from gi.repository import Adw, Gio, GLib, Gtk
@@ -139,7 +138,7 @@ class PreferencesWindow(Adw.PreferencesWindow):
)
)
self.add_controller(shortcut_controller)
self.removed_games = []
self.removed_games = set()
# General
self.schema.bind(
@@ -335,43 +334,23 @@ class PreferencesWindow(Adw.PreferencesWindow):
self.file_chooser.select_folder(self.win, None, function, None)
def undo_remove_all(self, _widget, _unused):
deleted_covers_dir = self.win.cache_dir / "cartridges" / "deleted_covers"
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:
data.pop("removed")
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.removed_games = []
self.removed_games = set()
self.toast.dismiss()
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():
if "removed" not in game:
self.removed_games.append(game["game_id"])
self.removed_games.add(game["game_id"])
game["removed"] = True
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)
if self.win.stack.get_visible_child() == self.win.details_view:
self.win.on_go_back_action(None, None)

View File

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

View File

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

View File

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

View File

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