Initial commit
This commit is contained in:
parent
9e9b31d49c
commit
cf3d949369
2
.gitignore
vendored
2
.gitignore
vendored
@ -127,3 +127,5 @@ dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
.idea/
|
||||
|
43
example.py
Normal file
43
example.py
Normal 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
19
native_ui/__init__.py
Normal 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")
|
||||
|
82
native_ui/abstract/__init__.py
Normal file
82
native_ui/abstract/__init__.py
Normal 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
|
14
native_ui/abstract/application.py
Normal file
14
native_ui/abstract/application.py
Normal 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)
|
45
native_ui/abstract/button.py
Normal file
45
native_ui/abstract/button.py
Normal 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)
|
23
native_ui/abstract/container.py
Normal file
23
native_ui/abstract/container.py
Normal 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)
|
11
native_ui/abstract/window.py
Normal file
11
native_ui/abstract/window.py
Normal 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
|
18
native_ui/impl/gnome/__init__.py
Normal file
18
native_ui/impl/gnome/__init__.py
Normal 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
|
42
native_ui/impl/gnome/application.py
Normal file
42
native_ui/impl/gnome/application.py
Normal 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)
|
25
native_ui/impl/gnome/button.py
Normal file
25
native_ui/impl/gnome/button.py
Normal 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
|
27
native_ui/impl/gnome/container.py
Normal file
27
native_ui/impl/gnome/container.py
Normal 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
|
24
native_ui/impl/gnome/window.py
Normal file
24
native_ui/impl/gnome/window.py
Normal 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
|
Loading…
Reference in New Issue
Block a user