Skip to content
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

Launcher: support Component icons inside apworlds #3629

Merged
merged 7 commits into from
Nov 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
7 changes: 3 additions & 4 deletions Launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,8 @@ def launch(exe, in_terminal=False):


def run_gui():
from kvui import App, ContainerLayout, GridLayout, Button, Label, ScrollBox, Widget
from kvui import App, ContainerLayout, GridLayout, Button, Label, ScrollBox, Widget, ApAsyncImage
from kivy.core.window import Window
from kivy.uix.image import AsyncImage
from kivy.uix.relativelayout import RelativeLayout

class Launcher(App):
Expand Down Expand Up @@ -199,8 +198,8 @@ def build_button(component: Component) -> Widget:
button.component = component
button.bind(on_release=self.component_action)
if component.icon != "icon":
image = AsyncImage(source=icon_paths[component.icon],
size=(38, 38), size_hint=(None, 1), pos=(5, 0))
image = ApAsyncImage(source=icon_paths[component.icon],
size=(38, 38), size_hint=(None, 1), pos=(5, 0))
box_layout = RelativeLayout(size_hint_y=None, height=40)
box_layout.add_widget(button)
box_layout.add_widget(image)
Expand Down
38 changes: 38 additions & 0 deletions kvui.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import sys
import typing
import re
import io
import pkgutil
from collections import deque

if sys.platform == "win32":
Expand Down Expand Up @@ -35,6 +37,7 @@
from kivy.core.window import Window
from kivy.core.clipboard import Clipboard
from kivy.core.text.markup import MarkupLabel
from kivy.core.image import ImageLoader, ImageLoaderBase, ImageData
from kivy.base import ExceptionHandler, ExceptionManager
from kivy.clock import Clock
from kivy.factory import Factory
Expand All @@ -61,6 +64,7 @@
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
from kivy.animation import Animation
from kivy.uix.popup import Popup
from kivy.uix.image import AsyncImage

fade_in_animation = Animation(opacity=0, duration=0) + Animation(opacity=1, duration=0.25)

Expand Down Expand Up @@ -770,6 +774,40 @@ def fix_heights(self):
element.height = max_height


class ApAsyncImage(AsyncImage):
def is_uri(self, filename: str) -> bool:
if filename.startswith("ap:"):
return True
else:
return super().is_uri(filename)


class ImageLoaderPkgutil(ImageLoaderBase):
def load(self, filename: str) -> typing.List[ImageData]:
# take off the "ap:" prefix
module, path = filename[3:].split("/", 1)
data = pkgutil.get_data(module, path)
return self._bytes_to_data(data)

def _bytes_to_data(self, data: typing.Union[bytes, bytearray]) -> typing.List[ImageData]:
loader = next(loader for loader in ImageLoader.loaders if loader.can_load_memory())
return loader.load(loader, io.BytesIO(data))


# grab the default loader method so we can override it but use it as a fallback
_original_image_loader_load = ImageLoader.load


def load_override(filename: str, default_load=_original_image_loader_load, **kwargs):
if filename.startswith("ap:"):
return ImageLoaderPkgutil(filename)
else:
return default_load(filename, **kwargs)


ImageLoader.load = load_override


class E(ExceptionHandler):
logger = logging.getLogger("Client")

Expand Down
1 change: 1 addition & 0 deletions worlds/LauncherComponents.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ def install_apworld(apworld_path: str = "") -> None:
]


# if registering an icon from within an apworld, the format "ap:module.name/path/to/file.png" can be used
icon_paths = {
'icon': local_path('data', 'icon.png'),
'mcicon': local_path('data', 'mcicon.png'),
Expand Down
Loading