Skip to content

Core: hot reload components from installed apworld #3480

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 6, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 52 additions & 21 deletions Launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import webbrowser
from os.path import isfile
from shutil import which
from typing import Sequence, Union, Optional
from typing import Callable, Sequence, Union, Optional

import Utils
import settings
Expand Down Expand Up @@ -160,6 +160,9 @@ def launch(exe, in_terminal=False):
subprocess.Popen(exe)


refresh_components: Optional[Callable[[], None]] = None


def run_gui():
from kvui import App, ContainerLayout, GridLayout, Button, Label, ScrollBox, Widget
from kivy.core.window import Window
Expand All @@ -170,30 +173,18 @@ class Launcher(App):
base_title: str = "Archipelago Launcher"
container: ContainerLayout
grid: GridLayout

_tools = {c.display_name: c for c in components if c.type == Type.TOOL}
_clients = {c.display_name: c for c in components if c.type == Type.CLIENT}
_adjusters = {c.display_name: c for c in components if c.type == Type.ADJUSTER}
_miscs = {c.display_name: c for c in components if c.type == Type.MISC}
_tool_layout: ScrollBox
_client_layout: ScrollBox

def __init__(self, ctx=None):
self.title = self.base_title
self.ctx = ctx
self.icon = r"data/icon.png"
self._tool_layout = ScrollBox()
self._client_layout = ScrollBox()
super().__init__()

def build(self):
self.container = ContainerLayout()
self.grid = GridLayout(cols=2)
self.container.add_widget(self.grid)
self.grid.add_widget(Label(text="General", size_hint_y=None, height=40))
self.grid.add_widget(Label(text="Clients", size_hint_y=None, height=40))
tool_layout = ScrollBox()
tool_layout.layout.orientation = "vertical"
self.grid.add_widget(tool_layout)
client_layout = ScrollBox()
client_layout.layout.orientation = "vertical"
self.grid.add_widget(client_layout)
def _refresh_components(self) -> None:

def build_button(component: Component) -> Widget:
"""
Expand All @@ -218,14 +209,47 @@ def build_button(component: Component) -> Widget:
return box_layout
return button

# clear before repopulating
tool_children = reversed(self._tool_layout.layout.children)
for child in tool_children:
self._tool_layout.layout.remove_widget(child)
client_children = reversed(self._client_layout.layout.children)
for child in client_children:
self._client_layout.layout.remove_widget(child)

_tools = {c.display_name: c for c in components if c.type == Type.TOOL}
_clients = {c.display_name: c for c in components if c.type == Type.CLIENT}
_adjusters = {c.display_name: c for c in components if c.type == Type.ADJUSTER}
_miscs = {c.display_name: c for c in components if c.type == Type.MISC}

for (tool, client) in itertools.zip_longest(itertools.chain(
self._tools.items(), self._miscs.items(), self._adjusters.items()), self._clients.items()):
_tools.items(), _miscs.items(), _adjusters.items()
), _clients.items()):
# column 1
if tool:
tool_layout.layout.add_widget(build_button(tool[1]))
self._tool_layout.layout.add_widget(build_button(tool[1]))
# column 2
if client:
client_layout.layout.add_widget(build_button(client[1]))
self._client_layout.layout.add_widget(build_button(client[1]))

def build(self):
self.container = ContainerLayout()
self.grid = GridLayout(cols=2)
self.container.add_widget(self.grid)
self.grid.add_widget(Label(text="General", size_hint_y=None, height=40))
self.grid.add_widget(Label(text="Clients", size_hint_y=None, height=40))
self._tool_layout.layout.orientation = "vertical"
self.grid.add_widget(self._tool_layout)
self._client_layout.layout.orientation = "vertical"
self.grid.add_widget(self._client_layout)

self._refresh_components()

def _ref():
self._refresh_components()

global refresh_components
refresh_components = _ref

Window.bind(on_drop_file=self._on_drop_file)

Expand Down Expand Up @@ -254,10 +278,17 @@ def _stop(self, *largs):

Launcher().run()

# avoiding Launcher reference leak
# and don't try to do something with widgets after window closed
global refresh_components
refresh_components = None


def run_component(component: Component, *args):
if component.func:
component.func(*args)
if refresh_components:
refresh_components()
elif component.script_name:
subprocess.run([*get_exe(component.script_name), *args])
else:
Expand Down
6 changes: 6 additions & 0 deletions typings/kivy/uix/boxlayout.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from typing import Literal
from .layout import Layout


class BoxLayout(Layout):
orientation: Literal['horizontal', 'vertical']
8 changes: 7 additions & 1 deletion typings/kivy/uix/layout.pyi
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
from typing import Any
from typing import Any, Sequence

from .widget import Widget


class Layout(Widget):
@property
def children(self) -> Sequence[Widget]: ...

def add_widget(self, widget: Widget) -> None: ...

def remove_widget(self, widget: Widget) -> None: ...

def do_layout(self, *largs: Any, **kwargs: Any) -> None: ...
17 changes: 17 additions & 0 deletions typings/schema/__init__.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from typing import Any, Callable


class And:
def __init__(self, __type: type, __func: Callable[[Any], bool]) -> None: ...


class Or:
def __init__(self, *args: object) -> None: ...


class Schema:
def __init__(self, __x: object) -> None: ...


class Optional(Schema):
...
2 changes: 2 additions & 0 deletions worlds/LauncherComponents.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ def _install_apworld(apworld_src: str = "") -> Optional[Tuple[pathlib.Path, path
import shutil
shutil.copyfile(apworld_path, target)

worlds.WorldSource(str(target), is_zip=True).load()

return apworld_path, target


Expand Down
Loading