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 type checker
|
||||||
.pyre/
|
.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