1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
|
from gi.repository import Adw, GObject, Gtk, GLib, Gio, Gdk
from typing import Callable, Optional, List, Any
from enum import Enum, auto
class AppAction:
class StateType(Enum):
BOOL = auto()
RADIO = auto()
def __init__(
self,
name: str,
func: Callable,
accel: Optional[str] = None,
stateful: bool = False,
state_type: Optional[StateType] = None,
state_default: Any = None
):
self.name = name
self.func = func
self.accel = accel
self.stateful = stateful
self.state_type = state_type
self.state_default = state_default
assert (not self.stateful or self.state_default is not None)
def get_action(self):
action = None
if self.stateful:
parameter_type = None
variant = None
if self.state_type == AppAction.StateType.BOOL:
variant = GLib.Variant.new_boolean(self.state_default)
elif self.state_type == AppAction.StateType.RADIO:
parameter_type = GLib.VariantType.new('s')
variant = GLib.Variant('s', self.state_default)
action = Gio.SimpleAction.new_stateful(
self.name, parameter_type, variant
)
else:
action = Gio.SimpleAction.new(self.name, None)
action.connect('activate', self.func)
return action
class BaseApp(Adw.Application):
def __init__(
self,
app_id: str,
app_name: str,
app_actions: List[AppAction] = [],
flags: Gio.ApplicationFlags = Gio.ApplicationFlags.FLAGS_NONE,
css_resource: Optional[str] = None
):
self.app_actions = app_actions
self.css_resource = css_resource
super().__init__(application_id=app_id, flags=flags)
GLib.set_application_name(app_name)
GLib.set_prgname(app_id)
def do_startup(self):
Adw.Application.do_startup(self)
for a in self.app_actions:
action = a.get_action()
self.add_action(action)
if a.accel is not None:
self.set_accels_for_action(f'app.{a.name}', [a.accel])
def load_css(self):
if self.css_resource is None:
return
provider = Gtk.CssProvider()
provider.load_from_resource(self.css_resource)
Gtk.StyleContext.add_provider_for_display(
Gdk.Display.get_default(), provider,
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
)
def do_activate(self):
self.load_css()
class AppShortcut:
def __init__(self, keystroke: str, callback: Callable):
self.keystroke = keystroke
self.callback = callback
def bind(self, win: 'BaseWindow'):
_, key, mod = Gtk.accelerator_parse(self.keystroke)
trigger = Gtk.KeyvalTrigger.new(key, mod)
cb = Gtk.CallbackAction.new(self.callback)
shortcut = Gtk.Shortcut.new(trigger, cb)
win.shortcut_controller.add_shortcut(shortcut)
@classmethod
def create_controller(cls, widget: Gtk.Widget):
sc = Gtk.ShortcutController()
sc.set_scope(Gtk.ShortcutScope.GLOBAL)
widget.add_controller(sc)
return sc
class BaseWindow(Adw.ApplicationWindow):
shortcut_controller: Gtk.ShortcutController
__dark_mode = False
def __init__(
self,
app_name: str,
icon_name: str,
shortcuts: List[AppShortcut] = []
):
super().__init__()
self.set_title(app_name)
self.set_icon_name(icon_name)
self.shortcut_controller = AppShortcut.create_controller(self)
for shortcut in shortcuts:
shortcut.bind(self)
self.main_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
self.set_content(self.main_box)
self.append = self.main_box.append
self.prepend = self.main_box.prepend
self.remove = self.main_box.remove
@GObject.Property(type=bool, default=False)
def dark_mode(self) -> bool:
return self.__dark_mode
@dark_mode.setter
def dark_mode(self, nval: bool):
self.__dark_mode = nval
Adw.StyleManager.get_default().set_color_scheme(
Adw.ColorScheme.FORCE_DARK if nval
else Adw.ColorScheme.DEFAULT
)
|