Initial commit

This commit is contained in:
Surferlul 2022-07-30 23:03:15 +02:00
parent 9e9b31d49c
commit cf3d949369
13 changed files with 375 additions and 0 deletions

2
.gitignore vendored
View File

@ -127,3 +127,5 @@ dmypy.json
# Pyre type checker
.pyre/
.idea/

43
example.py Normal file
View File

@ -0,0 +1,43 @@
import native_ui
import sys
native_ui.set_runtime_platform("gnome", "org.surferlul.native-ui.ExampleApp")
from native_ui import native, abstract
def main():
window = native.Window()
window.set(
child=None
)
app = native.Application(
window=native.Window(
child=native.Container(
layout=abstract.Layout.VERTICAL,
)
)
)
cont = app.window.child
def on_pressed(button, container):
print("Hello world")
container.call("add_child",
native.Button(
label="Hello",
pressed=on_pressed,
pressed_args=[container]
))
cont.add_child(native.Button(
label="Hello",
pressed=on_pressed,
pressed_args=[cont],
))
app.build()
app.run(sys.argv)
if __name__ == "__main__":
main()

19
native_ui/__init__.py Normal file
View File

@ -0,0 +1,19 @@
from . import abstract
import importlib
native = abstract
runtime_platform = None
def set_runtime_platform(platform, *args, **kwargs):
global native
global runtime_platform
try:
native = importlib.import_module(f".{platform.lower()}", "native_ui.impl")
runtime_platform = platform.lower()
native.requirements(*args, **kwargs)
except ImportError:
raise ValueError(f"{platform} not implemented")

View File

@ -0,0 +1,82 @@
import native_ui
from typing import Callable, Any
from collections import defaultdict
from enum import Enum
class Layout(Enum):
HORIZONTAL = 0
VERTICAL = 1
def abstract_property(function: Callable):
def fget(self):
return getattr(self, f"_{function.__name__}")
def fset(self, value):
function(self, value)
self.changed(function.__name__)
return property(fget, fset)
class Abstract(object):
def __init__(self, platform: str = None):
self.is_builder = False
if not hasattr(self, "supported_platforms"):
self.supported_platforms = set()
if platform is not None:
self.supported_platforms.add(platform)
self.native = None
def set(self, **kwargs):
for prop in kwargs:
self.set_property(prop, kwargs[prop])
def set_property(self, property_name: str, value: Any, index: int = None):
if index is None:
setattr(self, property_name, value)
else:
getattr(self, property_name)[index] = value
self.changed(property_name, index=index)
def call(self, property_name: str, *args, **kwargs):
res = getattr(self, property_name)(*args, **kwargs)
self.called(property_name, res, *args, **kwargs)
return res
def native_call(self, property_name: str, *args, **kwargs):
if native_ui.runtime_platform is None:
print("No runtime platform set")
if native_ui.runtime_platform not in self.supported_platforms:
print(f"Platform {native_ui.runtime_platform} not supported by {type(self).__name__}")
else:
return getattr(self, f"{property_name}_{native_ui.runtime_platform}")(*args, **kwargs)
def changed(self, property_name: str, index: int = None):
if self.native is not None:
self.native_call("changed", property_name, index)
def called(self, property_name: str, res: Any, *args, **kwargs):
if self.native is not None:
self.native_call("called", property_name, res, *args, **kwargs)
def build(self, *args, **kwargs):
if self.native is not None:
return self.native
return self.native_call("build", *args, **kwargs)
class __metaclass__(type):
__inheritors__ = defaultdict(list)
def __new__(meta, name, bases, dct):
klass = type.__new__(meta, name, bases, dct)
for base in klass.mro()[1:-1]:
meta.__inheritors__[base].append(klass)
return klass
from .button import Button
from .container import Container
from .window import Window
from .application import Application

View File

@ -0,0 +1,14 @@
from . import Abstract, abstract_property, Window
class Application(Abstract):
def __init__(self, window: Window = None, platform: str = None):
super().__init__(platform=platform)
self._window = window
@abstract_property
def window(self, value):
self._window = value
def run(self, *args, **kwargs):
self.native_call("run", *args, **kwargs)

View File

@ -0,0 +1,45 @@
from . import Abstract, abstract_property
from typing import Callable
class Button(Abstract):
def __init__(self,
label: str = "",
pressed: Callable = lambda button: None,
pressed_args: list = None,
pressed_kwargs: dict = None,
platform: str = None):
super().__init__(platform=platform)
self._label = label
self._pressed = pressed
if pressed_args is None:
pressed_args = []
if pressed_kwargs is None:
pressed_kwargs = {}
self._pressed_args = pressed_args
self._pressed_kwargs = pressed_kwargs
@abstract_property
def label(self, value):
self._label = value
@abstract_property
def pressed(self, value):
self._pressed = value
@abstract_property
def pressed_args(self, value):
self._pressed_args = value
def add_pressed_arg(self, value):
self._pressed_args.append(value)
@abstract_property
def pressed_kwargs(self, value):
self._pressed_kwargs = value
def set_pressed_kwarg(self, key, value):
self._pressed_kwargs[key] = value
def press(self, *args, **kwargs):
self.call("pressed", *args, *self._pressed_args, **kwargs, **self._pressed_kwargs)

View File

@ -0,0 +1,23 @@
from . import Abstract, Layout, abstract_property
class Container(Abstract):
def __init__(self, layout: Layout = Layout.HORIZONTAL, children: list = None, platform: str = None):
super().__init__(platform=platform)
if children is None:
children = []
self._children = children
self._layout = layout
@abstract_property
def layout(self, value):
self._layout = value
def get_children(self):
return self._children
def add_child(self, child):
self._children.append(child)
def pop_child(self, index):
return self._children.pop(index)

View File

@ -0,0 +1,11 @@
from . import Abstract, abstract_property
class Window(Abstract):
def __init__(self, child: Abstract = None, platform: str = None):
super().__init__(platform=platform)
self._child = child
@abstract_property
def child(self, value):
self._child = value

View File

@ -0,0 +1,18 @@
import gi
gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
from gi.repository import Gtk, Adw
application_id = None
def requirements(app_id: str):
global application_id
application_id = app_id
from .button import Button
from .container import Container
from .window import Window
from .application import Application

View File

@ -0,0 +1,42 @@
from native_ui import abstract
from . import Adw
from pathlib import Path
from typing import Any
from .. import gnome
platform = Path(__file__).parent.name
class Application(abstract.Application):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs, platform=platform)
def changed_gnome(self, property_name: str, index: int = None):
pass
def called_gnome(self, property_name: str, res: Any, *args, **kwargs):
pass
def build_gnome(self):
class GnomeApp(Adw.Application):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.connect('activate', self.on_activate)
self.abstract_window = None
self.window = None
def on_activate(self, app):
if self.window is None:
if self.abstract_window is not None:
self.window = self.abstract_window.build(app)
else:
return
self.window.present()
self.native = GnomeApp(application_id=gnome.application_id)
if self.window is not None:
self.native.abstract_window = self.window
return self.native
def run_gnome(self, *args, **kwargs):
self.native.run(*args, **kwargs)

View File

@ -0,0 +1,25 @@
from native_ui import abstract
from . import Gtk
from pathlib import Path
from typing import Any
platform = Path(__file__).parent.name
class Button(abstract.Button):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs, platform=platform)
def changed_gnome(self, property_name: str, index: int = None):
pass
def called_gnome(self, property_name: str, res: Any, *args, **kwargs):
pass
def clicked(self, _native_button):
self.call("press", self)
def build_gnome(self):
self.native = Gtk.Button(label=self.label)
self.native.connect('clicked', self.clicked)
return self.native

View File

@ -0,0 +1,27 @@
from native_ui import abstract
from . import Gtk
from pathlib import Path
from typing import Any
platform = Path(__file__).parent.name
class Container(abstract.Container):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs, platform=platform)
def changed_gnome(self, property_name: str, index: int = None):
pass
def called_gnome(self, property_name: str, res: Any, *args, **kwargs):
if property_name == "add_child":
self.native.append(args[0].build())
def build_gnome(self):
orientation = Gtk.Orientation.VERTICAL
if self.layout == abstract.Layout.HORIZONTAL:
orientation = Gtk.Orientation.HORIZONTAL
self.native = Gtk.Box(orientation=orientation)
for child in self.call("get_children"):
self.native.append(child.build())
return self.native

View File

@ -0,0 +1,24 @@
from native_ui import abstract
from . import Gtk
from pathlib import Path
from typing import Any
platform = Path(__file__).parent.name
class Window(abstract.Window):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs, platform=platform)
def changed_gnome(self, property_name: str, index: int = None):
if "property_name" == "child":
self.native.set_child(self.child.build())
def called_gnome(self, property_name: str, res: Any, *args, **kwargs):
pass
def build_gnome(self, app: abstract.Application = None):
self.native = Gtk.ApplicationWindow(application=app)
if self.child is not None:
self.native.set_child(self.child.build())
return self.native