Add menu and fix hrop hint
This commit is contained in:
parent
36bf30e314
commit
b71db3e429
7 changed files with 144 additions and 50 deletions
|
@ -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", ["<Ctrl>q"])
|
||||
self.set_accels_for_action("win.close", ["<Ctrl>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", ["<Primary>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())
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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, __, ___):
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -3,6 +3,17 @@
|
|||
<requires lib="gtk" version="4.0"/>
|
||||
<requires lib="adw" version="1.0"/>
|
||||
|
||||
<menu id="main_menu">
|
||||
<item>
|
||||
<attribute name="label">Preferences</attribute>
|
||||
<attribute name="action">app.preferences</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label">Quit</attribute>
|
||||
<attribute name="action">app.quit</attribute>
|
||||
</item>
|
||||
</menu>
|
||||
|
||||
<template class="RecoderWindow" parent="AdwApplicationWindow">
|
||||
<property name="title">Recoder</property>
|
||||
<property name="default-width">700</property>
|
||||
|
@ -26,15 +37,21 @@
|
|||
</object>
|
||||
</child>
|
||||
<child type="end">
|
||||
<object class="GtkButton" id="btn_preferences">
|
||||
<property name="icon-name">emblem-system-symbolic</property>
|
||||
<property name="tooltip-text">Preferences</property>
|
||||
<object class="GtkMenuButton" id="menu_button">
|
||||
<property name="icon-name">open-menu-symbolic</property>
|
||||
<property name="menu-model">main_menu</property>
|
||||
<property name="tooltip-text">Menu</property>
|
||||
</object>
|
||||
</child>
|
||||
</child>
|
||||
<child type="end">
|
||||
<object class="GtkButton" id="btn_cancel">
|
||||
<property name="label">Cancel</property>
|
||||
<object class="GtkButton" id="btn_clear">
|
||||
<property name="icon-name">edit-clear-symbolic</property>
|
||||
<property name="tooltip-text">Clear</property>
|
||||
<property name="visible">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<style>
|
||||
<class name="flat"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
@ -70,15 +87,65 @@
|
|||
</child>
|
||||
|
||||
<child type="overlay">
|
||||
<object class="GtkLabel" id="drop_hint">
|
||||
<property name="label">📂 Drop files or folders here to get started</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
<object class="GtkBox" id="drop_hint">
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="halign">fill</property>
|
||||
<property name="valign">fill</property>
|
||||
<property name="spacing">48</property>
|
||||
<property name="hexpand">true</property>
|
||||
<property name="vexpand">true</property>
|
||||
|
||||
<!-- Inner box to center content -->
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="vexpand">true</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="spacing">48</property>
|
||||
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">horizontal</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="spacing">48</property>
|
||||
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="icon-name">video-x-generic-symbolic</property>
|
||||
<property name="pixel-size">48</property>
|
||||
<style>
|
||||
<class name="dim-icon"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="icon-name">folder-symbolic</property>
|
||||
<property name="pixel-size">48</property>
|
||||
<style>
|
||||
<class name="dim-icon"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
|
||||
</object>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="label">Drop video files or folders here to get started</property>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
|
||||
</object>
|
||||
</child>
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue