diff --git a/src/recoder/app.py b/src/recoder/app.py index f464675..445650d 100755 --- a/src/recoder/app.py +++ b/src/recoder/app.py @@ -10,15 +10,19 @@ from importlib.resources import files Adw.init() + def load_resources(): resource_path = files("recoder").joinpath("resources.gresource") resource = Gio.Resource.load(str(resource_path)) Gio.resources_register(resource) + def main(): load_resources() - from recoder.window import RecoderWindow # delayed import + # Delay imports until after resources are registered + from recoder.window import RecoderWindow + from recoder.preferences import RecoderPreferences class RecoderApp(Adw.Application): def __init__(self): @@ -27,14 +31,50 @@ def main(): flags=Gio.ApplicationFlags.FLAGS_NONE ) self.window = None + self.preferences_window = None + + def do_startup(self): + Adw.Application.do_startup(self) + + quit_action = Gio.SimpleAction.new("quit", None) + quit_action.connect("activate", lambda *_: self.quit()) + self.add_action(quit_action) + self.set_accels_for_action("app.quit", ["q"]) + self.set_accels_for_action("win.close", ["w"]) + + preferences_action = Gio.SimpleAction.new("preferences", None) + preferences_action.connect("activate", self.on_preferences_activate) + self.add_action(preferences_action) + # Add the accelerator for Preferences (Ctrl+,) + self.set_accels_for_action("app.preferences", ["comma"]) def do_activate(self): if not self.window: self.window = RecoderWindow(self) + self.window.connect("close-request", self.on_window_close) self.window.present() + def on_preferences_activate(self, action, param): + if not self.preferences_window: + self.preferences_window = RecoderPreferences() + self.preferences_window.set_transient_for(self.window) + self.preferences_window.set_modal(True) + self.preferences_window.connect("close-request", self.on_preferences_close) + + self.preferences_window.present() + + def on_preferences_close(self, window): + window.set_visible(False) + # Don't destroy, just hide + return True # stops further handlers, prevents default destruction + + def on_window_close(self, window): + self.quit() + return False # allow default handler to proceed + app = RecoderApp() return app.run(sys.argv) + if __name__ == "__main__": sys.exit(main()) diff --git a/src/recoder/app_state.py b/src/recoder/app_state.py index a87de6c..ef8b126 100644 --- a/src/recoder/app_state.py +++ b/src/recoder/app_state.py @@ -63,7 +63,7 @@ class UIStateManager: if w.drop_hint.get_parent() != w.overlay: w.overlay.add_overlay(w.drop_hint) w.btn_transcode.set_visible(False) - w.btn_cancel.set_visible(False) + w.btn_clear.set_visible(False) def _handle_files_loaded(self): w = self.window @@ -74,7 +74,7 @@ class UIStateManager: w.btn_transcode.set_sensitive(True) w.btn_transcode.set_label("Transcode") w.btn_transcode.add_css_class("suggested-action") - w.btn_cancel.set_visible(True) + w.btn_clear.set_visible(True) w.is_paused = False def _handle_transcoding(self): @@ -85,7 +85,7 @@ class UIStateManager: w.btn_transcode.set_sensitive(True) w.btn_transcode.set_label("Pause") w.btn_transcode.remove_css_class("suggested-action") - w.btn_cancel.set_visible(True) + w.btn_clear.set_visible(True) w.is_paused = False def _handle_paused(self): @@ -95,7 +95,7 @@ class UIStateManager: w.btn_transcode.set_visible(True) w.btn_transcode.set_sensitive(True) w.btn_transcode.set_label("Resume") - w.btn_cancel.set_visible(True) + w.btn_clear.set_visible(True) w.is_paused = True def _handle_done(self): @@ -105,7 +105,7 @@ class UIStateManager: w.progress_bar.set_fraction(1.0) w.btn_transcode.set_visible(False) w.btn_transcode.remove_css_class("suggested-action") - w.btn_cancel.set_visible(True) + w.btn_clear.set_visible(True) w.is_paused = False def _handle_error(self): @@ -114,5 +114,5 @@ class UIStateManager: w.drop_hint.set_visible(False) w.progress_bar.set_visible(False) w.btn_transcode.set_visible(False) - w.btn_cancel.set_visible(True) + w.btn_clear.set_visible(True) w.is_paused = False diff --git a/src/recoder/drop_handler.py b/src/recoder/drop_handler.py index 51288a2..107647a 100644 --- a/src/recoder/drop_handler.py +++ b/src/recoder/drop_handler.py @@ -37,11 +37,11 @@ class DropHandler: def on_drop_enter(self, *_): if not self._accepting: return False - self.w.overlay.add_css_class("drop-highlight") + self.w.drop_hint.add_css_class("drop-highlight") return True def on_drop_leave(self, *_): - self.w.overlay.remove_css_class("drop-highlight") + self.w.drop_hint.remove_css_class("drop-highlight") return True def on_drop(self, _, value, __, ___): diff --git a/src/recoder/file_entry_row.py b/src/recoder/file_entry_row.py index 2bf9c08..c631d29 100644 --- a/src/recoder/file_entry_row.py +++ b/src/recoder/file_entry_row.py @@ -7,7 +7,7 @@ from recoder.models import FileStatus ICONS = { FileStatus.WAITING: "network-idle-symbolic", FileStatus.PROCESSING: "network-transmit-symbolic", - FileStatus.DONE: "check-plain-symbolic", + FileStatus.DONE: "checkmark-symbolic", FileStatus.ERROR: "network-error-symbolic", } @@ -36,7 +36,6 @@ class FileEntryRow(Gtk.ListBoxRow): def update_display(self, *args): basename = self.item.file.get_basename() self.label.set_text(basename) - icon_name = ICONS.get(self.item.status, "object-select-symbolic") self.icon.set_from_icon_name(icon_name) diff --git a/src/recoder/window.py b/src/recoder/window.py index 52ee2b7..c2c9272 100644 --- a/src/recoder/window.py +++ b/src/recoder/window.py @@ -26,10 +26,9 @@ class RecoderWindow(Adw.ApplicationWindow): listbox = Gtk.Template.Child() scrolled_window = Gtk.Template.Child() btn_transcode = Gtk.Template.Child() - btn_cancel = Gtk.Template.Child() + btn_clear = Gtk.Template.Child() progress_bar = Gtk.Template.Child() folder_label = Gtk.Template.Child() - btn_preferences = Gtk.Template.Child() def __init__(self, application): super().__init__(application=application) @@ -42,6 +41,10 @@ class RecoderWindow(Adw.ApplicationWindow): self.state_settings.bind("is-maximized", self, "maximized", Gio.SettingsBindFlags.DEFAULT) self.state_settings.bind("is-fullscreen", self, "fullscreened", Gio.SettingsBindFlags.DEFAULT) + close_action = Gio.SimpleAction.new("close", None) + close_action.connect("activate", lambda *a: self.close()) + self.add_action(close_action) + self.file_items_to_process = [] self.current_folder_name = None self.transcoder = None @@ -53,8 +56,7 @@ class RecoderWindow(Adw.ApplicationWindow): self.ui_manager = UIStateManager(self, self.app_state_manager) self.btn_transcode.connect("clicked", self.on_transcode_clicked) - self.btn_cancel.connect("clicked", self.on_cancel_clicked) - self.btn_preferences.connect("clicked", self.on_preferences_clicked) + self.btn_clear.connect("clicked", self.on_clear_clicked) self.app_state_manager.state = AppState.IDLE @@ -68,22 +70,6 @@ class RecoderWindow(Adw.ApplicationWindow): Notify.init("Recoder") - self.preferences_window = None - - def on_preferences_clicked(self, button): - if self.preferences_window is None: - self.preferences_window = RecoderPreferences() - self.preferences_window.set_transient_for(self) - self.preferences_window.set_modal(True) - self.preferences_window.connect("close-request", self.on_preferences_window_close) - - self.preferences_window.present() - - def on_preferences_window_close(self, window): - window.hide() - # Don't destroy, just hide - return True # stops further handlers, prevents default destruction - def process_drop_value(self, value): folder_file = None @@ -150,7 +136,7 @@ class RecoderWindow(Adw.ApplicationWindow): self.is_paused = False self.app_state_manager.state = AppState.TRANSCODING - def on_cancel_clicked(self, button): + def on_clear_clicked(self, button): if self.transcoder and self.transcoder.is_processing: self.transcoder.stop() self.transcoder = None diff --git a/src/resources/style.css b/src/resources/style.css index 04c5d66..2d52215 100644 --- a/src/resources/style.css +++ b/src/resources/style.css @@ -1,12 +1,14 @@ .drop-highlight { - background-color: @theme_selected_bg_color; /* good: uses theme color */ - border: 2px solid @theme_selected_fg_color; /* good contrast */ - border-radius: 6px; /* soften edges */ - box-shadow: 0 0 8px @theme_selected_bg_color; /* subtle glow */ - transition: background-color 150ms ease, box-shadow 150ms ease; /* smooth */ + background: alpha(@accent_color, 0.12); + border: 2px solid alpha(@accent_color, 0.9); } .dim-label { font-size: 16px; color: @theme_unfocused_fg_color; } + +.dim-icon { + opacity: 0.3; + color: @theme_unfocused_fg_color; +} \ No newline at end of file diff --git a/src/resources/window.ui b/src/resources/window.ui index 5ec4e0d..e57978f 100644 --- a/src/resources/window.ui +++ b/src/resources/window.ui @@ -3,6 +3,17 @@ + + + Preferences + app.preferences + + + Quit + app.quit + + +