mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 12:11:33 -06:00
Kivy: Add a button prompt box (#3470)
* Kivy: Add a button prompt box * auto format the buttons to display 2 per row to look nicer * update to kivymd * have the uri popup use the new API * have messenger use the new API * make the buttonprompt import even more lazy * messenger needs to be lazy too * make the buttons take up the full dialog width --------- Co-authored-by: NewSoupVi <57900059+NewSoupVi@users.noreply.github.com>
This commit is contained in:
40
Launcher.py
40
Launcher.py
@@ -121,46 +121,28 @@ def handle_uri(path: str, launch_args: tuple[str, ...]) -> None:
|
||||
launch_args = (path, *launch_args)
|
||||
client_component = []
|
||||
text_client_component = None
|
||||
if "game" in queries:
|
||||
game = queries["game"][0]
|
||||
else: # TODO around 0.6.0 - this is for pre this change webhost uri's
|
||||
game = "Archipelago"
|
||||
game = queries["game"][0]
|
||||
for component in components:
|
||||
if component.supports_uri and component.game_name == game:
|
||||
client_component.append(component)
|
||||
elif component.display_name == "Text Client":
|
||||
text_client_component = component
|
||||
|
||||
from kvui import MDButton, MDButtonText
|
||||
from kivymd.uix.dialog import MDDialog, MDDialogHeadlineText, MDDialogContentContainer, MDDialogSupportingText
|
||||
from kivymd.uix.divider import MDDivider
|
||||
|
||||
if not client_component:
|
||||
run_component(text_client_component, *launch_args)
|
||||
return
|
||||
else:
|
||||
popup_text = MDDialogSupportingText(text="Select client to open and connect with.")
|
||||
component_buttons = [MDDivider()]
|
||||
for component in [text_client_component, *client_component]:
|
||||
component_buttons.append(MDButton(
|
||||
MDButtonText(text=component.display_name),
|
||||
on_release=lambda *args, comp=component: run_component(comp, *launch_args),
|
||||
style="text"
|
||||
))
|
||||
component_buttons.append(MDDivider())
|
||||
|
||||
MDDialog(
|
||||
# Headline
|
||||
MDDialogHeadlineText(text="Connect to Multiworld"),
|
||||
# Text
|
||||
popup_text,
|
||||
# Content
|
||||
MDDialogContentContainer(
|
||||
*component_buttons,
|
||||
orientation="vertical"
|
||||
),
|
||||
|
||||
).open()
|
||||
from kvui import ButtonsPrompt
|
||||
component_options = {
|
||||
text_client_component.display_name: text_client_component,
|
||||
**{component.display_name: component for component in client_component}
|
||||
}
|
||||
popup = ButtonsPrompt("Connect to Multiworld",
|
||||
"Select client to open and connect with.",
|
||||
lambda component_name: run_component(component_options[component_name], *launch_args),
|
||||
*component_options.keys())
|
||||
popup.open()
|
||||
|
||||
|
||||
def identify(path: None | str) -> tuple[None | str, None | Component]:
|
||||
|
@@ -222,3 +222,8 @@
|
||||
spacing: 10
|
||||
size_hint_y: None
|
||||
height: self.minimum_height
|
||||
<MessageBoxLabel>:
|
||||
valign: "middle"
|
||||
halign: "center"
|
||||
text_size: self.width, None
|
||||
height: self.texture_size[1]
|
||||
|
54
kvui.py
54
kvui.py
@@ -6,7 +6,6 @@ import re
|
||||
import io
|
||||
import pkgutil
|
||||
from collections import deque
|
||||
|
||||
assert "kivy" not in sys.modules, "kvui should be imported before kivy for frozen compatibility"
|
||||
|
||||
if sys.platform == "win32":
|
||||
@@ -57,6 +56,7 @@ from kivy.animation import Animation
|
||||
from kivy.uix.popup import Popup
|
||||
from kivy.uix.image import AsyncImage
|
||||
from kivymd.app import MDApp
|
||||
from kivymd.uix.dialog import MDDialog, MDDialogHeadlineText, MDDialogSupportingText, MDDialogButtonContainer
|
||||
from kivymd.uix.gridlayout import MDGridLayout
|
||||
from kivymd.uix.floatlayout import MDFloatLayout
|
||||
from kivymd.uix.boxlayout import MDBoxLayout
|
||||
@@ -710,20 +710,62 @@ class CommandPromptTextInput(ResizableTextField):
|
||||
self.text = self._command_history[self._command_history_index]
|
||||
|
||||
|
||||
class MessageBoxLabel(MDLabel):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self._label.refresh()
|
||||
|
||||
|
||||
class MessageBox(Popup):
|
||||
class MessageBoxLabel(MDLabel):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self._label.refresh()
|
||||
|
||||
def __init__(self, title, text, error=False, **kwargs):
|
||||
label = MessageBox.MessageBoxLabel(text=text)
|
||||
label = MessageBoxLabel(text=text)
|
||||
separator_color = [217 / 255, 129 / 255, 122 / 255, 1.] if error else [47 / 255., 167 / 255., 212 / 255, 1.]
|
||||
super().__init__(title=title, content=label, size_hint=(0.5, None), width=max(100, int(label.width) + 40),
|
||||
separator_color=separator_color, **kwargs)
|
||||
self.height += max(0, label.height - 18)
|
||||
|
||||
|
||||
class ButtonsPrompt(MDDialog):
|
||||
def __init__(self, title: str, text: str, response: typing.Callable[[str], None],
|
||||
*prompts: str, **kwargs) -> None:
|
||||
"""
|
||||
Customizable popup box that lets you create any number of buttons. The text of the pressed button is returned to
|
||||
the callback.
|
||||
|
||||
:param title: The title of the popup.
|
||||
:param text: The message prompt in the popup.
|
||||
:param response: A callable that will get called when the user presses a button. The prompt will not close
|
||||
itself so should be done here if you want to close it when certain buttons are pressed.
|
||||
:param prompts: Any number of strings to be used for the buttons.
|
||||
"""
|
||||
layout = MDBoxLayout(orientation="vertical")
|
||||
label = MessageBoxLabel(text=text)
|
||||
layout.add_widget(label)
|
||||
|
||||
def on_release(button: MDButton, *args) -> None:
|
||||
response(button.text)
|
||||
|
||||
buttons = [MDDivider()]
|
||||
for prompt in prompts:
|
||||
button = MDButton(
|
||||
MDButtonText(text=prompt, pos_hint={"center_x": 0.5, "center_y": 0.5}),
|
||||
on_release=on_release,
|
||||
style="text",
|
||||
theme_width="Custom",
|
||||
size_hint_x=1,
|
||||
)
|
||||
button.text = prompt
|
||||
buttons.extend([button, MDDivider()])
|
||||
|
||||
super().__init__(
|
||||
MDDialogHeadlineText(text=title),
|
||||
MDDialogSupportingText(text=text),
|
||||
MDDialogButtonContainer(*buttons, orientation="vertical"),
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
class ClientTabs(MDTabsSecondary):
|
||||
carousel: MDTabsCarousel
|
||||
lock_swiping = True
|
||||
|
@@ -2,35 +2,28 @@ import argparse
|
||||
import io
|
||||
import logging
|
||||
import os.path
|
||||
import requests
|
||||
import subprocess
|
||||
import urllib.request
|
||||
from shutil import which
|
||||
from typing import Any
|
||||
from typing import Any, Callable, TYPE_CHECKING
|
||||
from zipfile import ZipFile
|
||||
from Utils import open_file
|
||||
from Utils import is_windows, messagebox, open_file, tuplize_version
|
||||
|
||||
import requests
|
||||
|
||||
from Utils import is_windows, messagebox, tuplize_version
|
||||
if TYPE_CHECKING:
|
||||
from kvui import ButtonsPrompt
|
||||
|
||||
|
||||
MOD_URL = "https://api.github.com/repos/alwaysintreble/TheMessengerRandomizerModAP/releases/latest"
|
||||
|
||||
|
||||
def ask_yes_no_cancel(title: str, text: str) -> bool | None:
|
||||
"""
|
||||
Wrapper for tkinter.messagebox.askyesnocancel, that creates a popup dialog box with yes, no, and cancel buttons.
|
||||
def create_yes_no_popup(title: str, text: str, callback: Callable[[str], None]) -> "ButtonsPrompt":
|
||||
from kvui import ButtonsPrompt
|
||||
buttons = ["Yes", "No", "Cancel"]
|
||||
|
||||
:param title: Title to be displayed at the top of the message box.
|
||||
:param text: Text to be displayed inside the message box.
|
||||
:return: Returns True if yes, False if no, None if cancel.
|
||||
"""
|
||||
from tkinter import Tk, messagebox
|
||||
root = Tk()
|
||||
root.withdraw()
|
||||
ret = messagebox.askyesnocancel(title, text)
|
||||
root.update()
|
||||
return ret
|
||||
prompt = ButtonsPrompt(title, text, callback, *buttons)
|
||||
prompt.open()
|
||||
return prompt
|
||||
|
||||
|
||||
def launch_game(*args) -> None:
|
||||
@@ -151,6 +144,76 @@ def launch_game(*args) -> None:
|
||||
# one of the alpha builds
|
||||
return "alpha" in latest_version or tuplize_version(latest_version) > tuplize_version(installed_version)
|
||||
|
||||
def after_courier_install_popup(answer: str) -> None:
|
||||
"""Gets called if the user doesn't have courier installed. Handle the button they pressed."""
|
||||
nonlocal prompt
|
||||
|
||||
prompt.dismiss()
|
||||
if answer in ("No", "Cancel"):
|
||||
return
|
||||
logging.info("Installing Courier")
|
||||
install_courier()
|
||||
prompt = create_yes_no_popup("Install Mod",
|
||||
"No randomizer mod detected. Would you like to install now?",
|
||||
after_mod_install_popup)
|
||||
|
||||
def after_mod_install_popup(answer: str) -> None:
|
||||
"""Gets called if the user has courier but mod isn't installed, or there's an available update."""
|
||||
nonlocal prompt
|
||||
|
||||
prompt.dismiss()
|
||||
if answer in ("No", "Cancel"):
|
||||
return
|
||||
logging.info("Installing Mod")
|
||||
install_mod()
|
||||
prompt = create_yes_no_popup("Launch Game",
|
||||
"Courier and Game mod installed successfully. Launch game now?",
|
||||
launch)
|
||||
|
||||
def after_mod_update_popup(answer: str) -> None:
|
||||
"""Gets called if there's an available update."""
|
||||
nonlocal prompt
|
||||
|
||||
prompt.dismiss()
|
||||
if answer == "Cancel":
|
||||
return
|
||||
if answer == "Yes":
|
||||
logging.info("Updating Mod")
|
||||
install_mod()
|
||||
prompt = create_yes_no_popup("Launch Game",
|
||||
"Courier and Game mod installed successfully. Launch game now?",
|
||||
launch)
|
||||
else:
|
||||
prompt = create_yes_no_popup("Launch Game",
|
||||
"Game Mod not updated. Launch game now?",
|
||||
launch)
|
||||
|
||||
def launch(answer: str | None = None) -> None:
|
||||
"""Launch the game."""
|
||||
nonlocal args
|
||||
|
||||
if prompt:
|
||||
prompt.dismiss()
|
||||
if answer and answer in ("No", "Cancel"):
|
||||
return
|
||||
|
||||
parser = argparse.ArgumentParser(description="Messenger Client Launcher")
|
||||
parser.add_argument("url", type=str, nargs="?", help="Archipelago Webhost uri to auto connect to.")
|
||||
args = parser.parse_args(args)
|
||||
|
||||
if not is_windows:
|
||||
if args.url:
|
||||
open_file(f"steam://rungameid/764790//{args.url}/")
|
||||
else:
|
||||
open_file("steam://rungameid/764790")
|
||||
else:
|
||||
os.chdir(game_folder)
|
||||
if args.url:
|
||||
subprocess.Popen([MessengerWorld.settings.game_path, str(args.url)])
|
||||
else:
|
||||
subprocess.Popen(MessengerWorld.settings.game_path)
|
||||
os.chdir(working_directory)
|
||||
|
||||
from . import MessengerWorld
|
||||
try:
|
||||
game_folder = os.path.dirname(MessengerWorld.settings.game_path)
|
||||
@@ -172,49 +235,24 @@ def launch_game(*args) -> None:
|
||||
except ImportError:
|
||||
pass
|
||||
if not courier_installed():
|
||||
should_install = ask_yes_no_cancel("Install Courier",
|
||||
"No Courier installation detected. Would you like to install now?")
|
||||
if not should_install:
|
||||
return
|
||||
logging.info("Installing Courier")
|
||||
install_courier()
|
||||
prompt = create_yes_no_popup("Install Courier",
|
||||
"No Courier installation detected. Would you like to install now?",
|
||||
after_courier_install_popup)
|
||||
return
|
||||
if not mod_installed():
|
||||
should_install = ask_yes_no_cancel("Install Mod",
|
||||
"No randomizer mod detected. Would you like to install now?")
|
||||
if not should_install:
|
||||
return
|
||||
logging.info("Installing Mod")
|
||||
install_mod()
|
||||
prompt = create_yes_no_popup("Install Mod",
|
||||
"No randomizer mod detected. Would you like to install now?",
|
||||
after_mod_install_popup)
|
||||
return
|
||||
else:
|
||||
latest = request_data(MOD_URL)["tag_name"]
|
||||
if available_mod_update(latest):
|
||||
should_update = ask_yes_no_cancel("Update Mod",
|
||||
f"New mod version detected. Would you like to update to {latest} now?")
|
||||
if should_update:
|
||||
logging.info("Updating mod")
|
||||
install_mod()
|
||||
elif should_update is None:
|
||||
return
|
||||
|
||||
if not args:
|
||||
should_launch = ask_yes_no_cancel("Launch Game",
|
||||
"Mod installed and up to date. Would you like to launch the game now?")
|
||||
if not should_launch:
|
||||
prompt = create_yes_no_popup("Update Mod",
|
||||
f"New mod version detected. Would you like to update to {latest} now?",
|
||||
after_mod_update_popup)
|
||||
return
|
||||
|
||||
parser = argparse.ArgumentParser(description="Messenger Client Launcher")
|
||||
parser.add_argument("url", type=str, nargs="?", help="Archipelago Webhost uri to auto connect to.")
|
||||
args = parser.parse_args(args)
|
||||
|
||||
if not is_windows:
|
||||
if args.url:
|
||||
open_file(f"steam://rungameid/764790//{args.url}/")
|
||||
else:
|
||||
open_file("steam://rungameid/764790")
|
||||
else:
|
||||
os.chdir(game_folder)
|
||||
if args.url:
|
||||
subprocess.Popen([MessengerWorld.settings.game_path, str(args.url)])
|
||||
else:
|
||||
subprocess.Popen(MessengerWorld.settings.game_path)
|
||||
os.chdir(working_directory)
|
||||
if not args:
|
||||
prompt = create_yes_no_popup("Launch Game",
|
||||
"Mod installed and up to date. Would you like to launch the game now?",
|
||||
launch)
|
||||
|
Reference in New Issue
Block a user