rewrite ui in hy, with python astal bindings
This commit is contained in:
12
astal/gtk3/__init__.py
Normal file
12
astal/gtk3/__init__.py
Normal file
@ -0,0 +1,12 @@
|
||||
import gi
|
||||
|
||||
gi.require_version("Gtk", "3.0")
|
||||
gi.require_version("Gdk", "3.0")
|
||||
gi.require_version("Astal", "3.0")
|
||||
|
||||
from .app import App
|
||||
from .astalify import astalify
|
||||
from .widget import Widget
|
||||
|
||||
from gi.repository import Gtk, Gdk, Astal
|
||||
|
62
astal/gtk3/app.py
Normal file
62
astal/gtk3/app.py
Normal file
@ -0,0 +1,62 @@
|
||||
import gi, sys
|
||||
|
||||
from typing import Callable, Optional, Any, Dict
|
||||
|
||||
gi.require_version("Astal", "3.0")
|
||||
gi.require_version("AstalIO", "0.1")
|
||||
from gi.repository import Astal, AstalIO, Gio
|
||||
|
||||
type Config = Dict[str, Any]
|
||||
|
||||
class AstalPy(Astal.Application):
|
||||
request_handler: Optional[Callable[[str, Callable[[Any], None]], None]] = None
|
||||
|
||||
def do_astal_application_request(self, msg: str, conn: Gio.SocketConnection):
|
||||
print(msg)
|
||||
if callable(self.request_handler):
|
||||
def respond(response: Any):
|
||||
AstalIO.write_sock(conn, str(response), None, None)
|
||||
self.request_handler(msg, respond)
|
||||
else:
|
||||
AstalIO.Application.do_request(self, msg, conn)
|
||||
|
||||
def quit(self, code: int = 0):
|
||||
super().quit()
|
||||
sys.exit(code)
|
||||
|
||||
def apply_css(self, css: str, reset: bool = False):
|
||||
super().apply_css(css, reset)
|
||||
|
||||
def start(self, **config: Any):
|
||||
config.setdefault("client", lambda *_: (print(f'Astal instance "{self.get_instance_name()}" is already running'), sys.exit(1)))
|
||||
config.setdefault("hold", True)
|
||||
|
||||
self.request_handler = config.get("request_handler")
|
||||
|
||||
if "css" in config:
|
||||
self.apply_css(config["css"])
|
||||
if "icons" in config:
|
||||
self.add_icons(config["icons"])
|
||||
|
||||
for key in ["instance_name", "gtk_theme", "icon_theme", "cursor_theme"]:
|
||||
if key in config:
|
||||
self.set_property(key, config[key])
|
||||
|
||||
def on_activate(_):
|
||||
if callable(config.get("main")):
|
||||
config["main"]()
|
||||
if config["hold"]:
|
||||
self.hold()
|
||||
|
||||
self.connect("activate", on_activate)
|
||||
|
||||
try:
|
||||
self.acquire_socket()
|
||||
except Exception:
|
||||
return config["client"](lambda msg: AstalIO.send_message(self.get_instance_name(), msg), *sys.argv[1:])
|
||||
|
||||
self.run()
|
||||
|
||||
return self
|
||||
|
||||
App = AstalPy()
|
111
astal/gtk3/astalify.py
Normal file
111
astal/gtk3/astalify.py
Normal file
@ -0,0 +1,111 @@
|
||||
import gi
|
||||
from functools import partial, singledispatch
|
||||
|
||||
from typing import Callable, List
|
||||
|
||||
from astal.binding import Binding, bind
|
||||
from astal.variable import Variable
|
||||
|
||||
gi.require_version("Astal", "3.0")
|
||||
gi.require_version("AstalIO", "0.1")
|
||||
gi.require_version("Gtk", "3.0")
|
||||
gi.require_version("GObject", "2.0")
|
||||
from gi.repository import Astal, Gtk, GObject
|
||||
|
||||
def astalify(widget: Gtk.Widget):
|
||||
class Widget(widget):
|
||||
class_name = ''
|
||||
|
||||
def hook(self, object: GObject.Object, signal_or_callback: str | Callable, callback: Callable = lambda _, x: x):
|
||||
if isinstance(signal_or_callback, Callable):
|
||||
callback = signal_or_callback
|
||||
|
||||
if isinstance(object, Variable):
|
||||
unsubscribe = object.subscribe(callback)
|
||||
|
||||
else:
|
||||
if isinstance(signal_or_callback, Callable): return
|
||||
|
||||
if 'notify::' in signal_or_callback:
|
||||
id = object.connect(f'{signal_or_callback}', lambda obj, *_: callback(self, object.get_property(signal_or_callback.replace('notify::', '').replace('-', '_')) if signal_or_callback.replace('notify::', '') in [*map(lambda x: x.name, object.list_properties())] else None))
|
||||
|
||||
else:
|
||||
id = object.connect(signal_or_callback, lambda _, value, *args: callback(self, value) if not args else callback(self, value, *args))
|
||||
|
||||
unsubscribe = lambda _=None: object.disconnect(id)
|
||||
|
||||
self.connect('destroy', unsubscribe)
|
||||
|
||||
def toggle_class_name(self, name: str, state: bool | None = None):
|
||||
Astal.widget_toggle_class_name(self, name, state if state is not None else not name in Astal.widget_get_class_names(self))
|
||||
|
||||
@GObject.Property(type=str)
|
||||
def class_name(self):
|
||||
return ' '.join(Astal.widget_get_class_names(self))
|
||||
|
||||
@class_name.setter
|
||||
def class_name(self, name):
|
||||
Astal.widget_set_class_names(self, name.split(' '))
|
||||
|
||||
@GObject.Property(type=str)
|
||||
def css(self):
|
||||
return Astal.widget_get_css(self)
|
||||
|
||||
@css.setter
|
||||
def css(self, css: str):
|
||||
Astal.widget_set_css(self, css)
|
||||
|
||||
@GObject.Property(type=str)
|
||||
def cursor(self):
|
||||
return Astal.widget_get_cursor(self)
|
||||
|
||||
@cursor.setter
|
||||
def cursor(self, cursor: str):
|
||||
Astal.widget_set_cursor(self, cursor)
|
||||
|
||||
@GObject.Property(type=str)
|
||||
def click_through(self):
|
||||
return Astal.widget_get_click_through(self)
|
||||
|
||||
@click_through.setter
|
||||
def click_through(self, click_through: str):
|
||||
Astal.widget_set_click_through(self, click_through)
|
||||
|
||||
if widget == Astal.Box or widget == Gtk.Box:
|
||||
@GObject.Property()
|
||||
def children(self):
|
||||
return Astal.Box.get_children(self)
|
||||
|
||||
@children.setter
|
||||
def children(self, children):
|
||||
Astal.Box.set_children(self, children)
|
||||
|
||||
def __init__(self, **props):
|
||||
super().__init__()
|
||||
|
||||
if not 'show' in props:
|
||||
self.show()
|
||||
|
||||
else:
|
||||
if props['show']:
|
||||
self.show()
|
||||
|
||||
for prop, value in props.items():
|
||||
if isinstance(value, Binding):
|
||||
self.set_property(prop, value.get())
|
||||
unsubscribe = value.subscribe(partial(self.set_property, prop))
|
||||
self.connect('destroy', unsubscribe)
|
||||
|
||||
elif 'on_' == prop[0:3] and isinstance(value, Callable):
|
||||
self.connect(prop.replace('on_', '', 1), value)
|
||||
|
||||
elif prop.replace('_', '-') in map(lambda x: x.name, self.props):
|
||||
self.set_property(prop.replace('_', '-'), value)
|
||||
|
||||
elif prop == 'setup' and isinstance(value, Callable):
|
||||
value(self)
|
||||
|
||||
else:
|
||||
self.__setattr__(prop, value)
|
||||
|
||||
return Widget
|
33
astal/gtk3/widget.py
Normal file
33
astal/gtk3/widget.py
Normal file
@ -0,0 +1,33 @@
|
||||
import gi
|
||||
|
||||
from .astalify import astalify
|
||||
from enum import Enum
|
||||
|
||||
gi.require_version("Astal", "3.0")
|
||||
gi.require_version("Gtk", "3.0")
|
||||
|
||||
from gi.repository import Astal, Gtk
|
||||
|
||||
class CallableEnum(Enum):
|
||||
def __call__(self, *args, **kwargs):
|
||||
return self.value(*args, **kwargs)
|
||||
|
||||
class Widget(CallableEnum):
|
||||
Box = astalify(Astal.Box)
|
||||
Button = astalify(Astal.Button)
|
||||
CenterBox = astalify(Astal.CenterBox)
|
||||
CircularProgress = astalify(Astal.CircularProgress)
|
||||
DrawingArea = astalify(Gtk.DrawingArea)
|
||||
Entry = astalify(Gtk.Entry)
|
||||
EventBox = astalify(Astal.EventBox)
|
||||
Icon = astalify(Astal.Icon)
|
||||
Label = astalify(Gtk.Label)
|
||||
LevelBar = astalify(Astal.LevelBar)
|
||||
MenuButton = astalify(Gtk.MenuButton)
|
||||
Overlay = astalify(Astal.Overlay)
|
||||
Revealer = astalify(Gtk.Revealer)
|
||||
Scrollable = astalify(Astal.Scrollable)
|
||||
Slider = astalify(Astal.Slider)
|
||||
Stack = astalify(Astal.Stack)
|
||||
Switch = astalify(Gtk.Switch)
|
||||
Window = astalify(Astal.Window)
|
Reference in New Issue
Block a user