mirror of
https://github.com/Faraphel/Atlas-Install.git
synced 2025-07-05 12:18:21 +02:00
started implementing ModSettings (only gui testing for now)
This commit is contained in:
parent
51a6e46930
commit
b4e52cfe58
13 changed files with 200 additions and 55 deletions
|
@ -6,15 +6,15 @@
|
||||||
|
|
||||||
"settings": {
|
"settings": {
|
||||||
"mode": {
|
"mode": {
|
||||||
"type": "str",
|
"type": "choices",
|
||||||
"choices": [
|
"choices": [
|
||||||
"normal",
|
"normal",
|
||||||
"debug"
|
"debug"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"highlight_if": {
|
"highlight_if": {
|
||||||
"type": "str",
|
"type": "string",
|
||||||
"safe_eval_preview": "track"
|
"preview": "track_formatting"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"track_new_if": "'Retro' not in getattr(track, 'tags', []) and getattr(track, 'warning', 0) == 0",
|
"track_new_if": "'Retro' not in getattr(track, 'tags', []) and getattr(track, 'warning', 0) == 0",
|
||||||
|
|
|
@ -9,7 +9,7 @@ from tkinter import messagebox
|
||||||
import webbrowser
|
import webbrowser
|
||||||
from typing import Generator
|
from typing import Generator
|
||||||
|
|
||||||
from source.gui import better_gui_error, mystuff, mod_configuration
|
from source.gui import better_gui_error, mystuff, mod_settings
|
||||||
from source.mkw.Game import Game
|
from source.mkw.Game import Game
|
||||||
from source.mkw.ModConfig import ModConfig
|
from source.mkw.ModConfig import ModConfig
|
||||||
from source.option import Option
|
from source.option import Option
|
||||||
|
@ -515,7 +515,7 @@ class SelectPack(ttk.Frame):
|
||||||
super().__init__(master)
|
super().__init__(master)
|
||||||
|
|
||||||
self.combobox = ttk.Combobox(self)
|
self.combobox = ttk.Combobox(self)
|
||||||
self.combobox.grid(row=1, column=1)
|
self.combobox.grid(row=1, column=1, sticky="NEWS")
|
||||||
|
|
||||||
self.button_settings = ttk.Button(self, text="...", width=2, command=self.open_mod_configuration)
|
self.button_settings = ttk.Button(self, text="...", width=2, command=self.open_mod_configuration)
|
||||||
self.button_settings.grid(row=1, column=2, sticky="NEWS")
|
self.button_settings.grid(row=1, column=2, sticky="NEWS")
|
||||||
|
@ -529,7 +529,7 @@ class SelectPack(ttk.Frame):
|
||||||
self.combobox.bind("<<ComboboxSelected>>", lambda _: self.select())
|
self.combobox.bind("<<ComboboxSelected>>", lambda _: self.select())
|
||||||
|
|
||||||
def open_mod_configuration(self) -> None:
|
def open_mod_configuration(self) -> None:
|
||||||
mod_configuration.Window(self.mod_config)
|
mod_settings.Window(self.mod_config)
|
||||||
|
|
||||||
def refresh_packs(self) -> None:
|
def refresh_packs(self) -> None:
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
import tkinter
|
|
||||||
from tkinter import ttk
|
|
||||||
from source.translation import translate as _
|
|
||||||
from source.gui.preview import track_formatting
|
|
||||||
|
|
||||||
|
|
||||||
ModConfig: any
|
|
||||||
|
|
||||||
|
|
||||||
class Window(tkinter.Toplevel):
|
|
||||||
def __init__(self, mod_config: "ModConfig"):
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
self.rowconfigure(1, weight=1)
|
|
||||||
self.columnconfigure(1, weight=1)
|
|
||||||
|
|
||||||
self.mod_config = mod_config
|
|
||||||
|
|
||||||
self.panel_window = ttk.Notebook(self)
|
|
||||||
self.panel_window.grid(row=1, column=1, sticky="NEWS")
|
|
||||||
|
|
||||||
self.frame_global_settings = FrameGlobalSettings(self.panel_window)
|
|
||||||
self.frame_specific_settings = FrameSpecificSettings(self.panel_window)
|
|
||||||
|
|
||||||
|
|
||||||
class FrameGlobalSettings(ttk.Frame):
|
|
||||||
def __init__(self, master: ttk.Notebook):
|
|
||||||
super().__init__(master)
|
|
||||||
master.add(self, text=_("GLOBAL_MOD_SETTINGS"))
|
|
||||||
|
|
||||||
# TODO: overwrite new tracks entry
|
|
||||||
button = ttk.Button(self, text="test search", command=self.open_test_button)
|
|
||||||
button.grid(row=1, column=1)
|
|
||||||
|
|
||||||
def open_test_button(self):
|
|
||||||
track_formatting.Window(self.master.master.mod_config)
|
|
||||||
|
|
||||||
|
|
||||||
class FrameSpecificSettings(ttk.Frame):
|
|
||||||
def __init__(self, master: ttk.Notebook):
|
|
||||||
super().__init__(master)
|
|
||||||
master.add(self, text=_("SPECIFIC_MOD_SETTINGS"))
|
|
60
source/gui/mod_settings.py
Normal file
60
source/gui/mod_settings.py
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
import tkinter
|
||||||
|
from tkinter import ttk
|
||||||
|
from source.translation import translate as _
|
||||||
|
from source.gui.preview import track_formatting
|
||||||
|
|
||||||
|
|
||||||
|
ModConfig: any
|
||||||
|
|
||||||
|
|
||||||
|
class Window(tkinter.Toplevel):
|
||||||
|
def __init__(self, mod_config: "ModConfig"):
|
||||||
|
super().__init__()
|
||||||
|
self.resizable(False, False)
|
||||||
|
self.grab_set()
|
||||||
|
|
||||||
|
self.rowconfigure(1, weight=1)
|
||||||
|
self.columnconfigure(1, weight=1)
|
||||||
|
|
||||||
|
self.mod_config = mod_config
|
||||||
|
|
||||||
|
self.panel_window = ttk.Notebook(self)
|
||||||
|
self.panel_window.grid(row=1, column=1, sticky="NEWS")
|
||||||
|
|
||||||
|
self.frame_global_settings = FrameGlobalSettings(self.panel_window)
|
||||||
|
self.frame_specific_settings = FrameSpecificSettings(self.panel_window)
|
||||||
|
|
||||||
|
|
||||||
|
class FrameGlobalSettings(ttk.Frame):
|
||||||
|
def __init__(self, master: ttk.Notebook):
|
||||||
|
super().__init__(master)
|
||||||
|
master.add(self, text=_("GLOBAL_MOD_SETTINGS"))
|
||||||
|
|
||||||
|
self.checkbutton_override_random_new = ttk.Checkbutton(self, text=_("OVERRIDE_RANDOM_NEW"))
|
||||||
|
self.frame_override_random_new = ttk.LabelFrame(self, labelwidget=self.checkbutton_override_random_new)
|
||||||
|
self.frame_override_random_new.grid(row=1, column=1, sticky="NEWS")
|
||||||
|
|
||||||
|
variable = tkinter.StringVar(self)
|
||||||
|
variable.trace_add("write", lambda *_: setattr(self, "value", variable.get()))
|
||||||
|
self.entry_override_random_new_cup = ttk.Entry(self.frame_override_random_new, width=70, textvariable=variable)
|
||||||
|
self.entry_override_random_new_cup.grid(row=1, column=1, sticky="NEWS")
|
||||||
|
|
||||||
|
self.button_preview_override_random_new = ttk.Button(self.frame_override_random_new, text="...", width=3)
|
||||||
|
self.button_preview_override_random_new.configure(command=lambda: track_formatting.Window.ask_for_template(
|
||||||
|
mod_config=self.master.master.mod_config,
|
||||||
|
variable=variable,
|
||||||
|
template=self.entry_override_random_new_cup.get(),
|
||||||
|
))
|
||||||
|
self.button_preview_override_random_new.grid(row=1, column=2, sticky="NEWS")
|
||||||
|
|
||||||
|
|
||||||
|
class FrameSpecificSettings(ttk.Frame):
|
||||||
|
def __init__(self, master: ttk.Notebook):
|
||||||
|
super().__init__(master)
|
||||||
|
master.add(self, text=_("SPECIFIC_MOD_SETTINGS"))
|
||||||
|
|
||||||
|
for index, (settings_name, settings_data) in enumerate(self.master.master.mod_config.settings.items()):
|
||||||
|
frame = ttk.LabelFrame(self, text=settings_name)
|
||||||
|
frame.grid(row=index, column=1, sticky="NEWS")
|
||||||
|
|
||||||
|
settings_data.tkinter_show(frame)
|
0
source/gui/preview/__init__.py
Normal file
0
source/gui/preview/__init__.py
Normal file
|
@ -8,16 +8,19 @@ ModConfig: any
|
||||||
|
|
||||||
|
|
||||||
class Window(tkinter.Toplevel):
|
class Window(tkinter.Toplevel):
|
||||||
def __init__(self, mod_config: "ModConfig"):
|
def __init__(self, mod_config: "ModConfig", template: str = ""):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.grid_rowconfigure(2, weight=1)
|
self.grid_rowconfigure(2, weight=1)
|
||||||
self.grid_columnconfigure(1, weight=1)
|
self.grid_columnconfigure(1, weight=1)
|
||||||
|
self.grab_set()
|
||||||
|
|
||||||
self.mod_config = mod_config
|
self.mod_config = mod_config
|
||||||
|
|
||||||
self.entry_format_input = ttk.Entry(self, width=100)
|
self.stringvar_template_input = tkinter.StringVar(self.master)
|
||||||
self.entry_format_input.grid(row=1, column=1, columnspan=2, sticky="NEWS")
|
self.entry_template_input = ttk.Entry(self, width=100, textvariable=self.stringvar_template_input)
|
||||||
self.entry_format_input.bind("<Return>", self.preview)
|
self.entry_template_input.grid(row=1, column=1, columnspan=2, sticky="NEWS")
|
||||||
|
self.entry_template_input.insert(tkinter.END, template)
|
||||||
|
self.entry_template_input.bind("<Return>", self.preview)
|
||||||
|
|
||||||
self.text_track_preview = tkinter.Text(self, background="black", foreground=MKWColor("off").color_code)
|
self.text_track_preview = tkinter.Text(self, background="black", foreground=MKWColor("off").color_code)
|
||||||
self.text_track_preview.grid(row=2, column=1, sticky="NEWS")
|
self.text_track_preview.grid(row=2, column=1, sticky="NEWS")
|
||||||
|
@ -31,6 +34,20 @@ class Window(tkinter.Toplevel):
|
||||||
self.text_track_preview.tag_configure(color.bmg, foreground=color.color_code)
|
self.text_track_preview.tag_configure(color.bmg, foreground=color.color_code)
|
||||||
self.text_track_preview.tag_configure("error", background="red", foreground="white")
|
self.text_track_preview.tag_configure("error", background="red", foreground="white")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def ask_for_template(cls, variable=None, *args, **kwargs) -> str:
|
||||||
|
"""
|
||||||
|
prompt the user for a template. Return the final template typed by the user
|
||||||
|
:entry: entry widget wwhere the final template can be inserted
|
||||||
|
:return: final template entered by the user
|
||||||
|
"""
|
||||||
|
window = cls(*args, **kwargs)
|
||||||
|
window.wait_window()
|
||||||
|
|
||||||
|
result = window.stringvar_template_input.get()
|
||||||
|
if variable is not None: variable.set(result)
|
||||||
|
return result
|
||||||
|
|
||||||
def preview(self, event: tkinter.Event = None):
|
def preview(self, event: tkinter.Event = None):
|
||||||
"""
|
"""
|
||||||
Preview all the tracks name with the track format
|
Preview all the tracks name with the track format
|
||||||
|
@ -42,7 +59,7 @@ class Window(tkinter.Toplevel):
|
||||||
for track in self.mod_config.get_tracks():
|
for track in self.mod_config.get_tracks():
|
||||||
try:
|
try:
|
||||||
track_repr = track.repr_format(
|
track_repr = track.repr_format(
|
||||||
self.mod_config, self.entry_format_input.get()
|
self.mod_config, self.entry_template_input.get()
|
||||||
)
|
)
|
||||||
|
|
||||||
offset: int = 0 # the color tag is removed at every sub, so keep track of the offset
|
offset: int = 0 # the color tag is removed at every sub, so keep track of the offset
|
||||||
|
@ -77,3 +94,4 @@ class Window(tkinter.Toplevel):
|
||||||
formatted_exc = str(exc).replace('\n', ' ')
|
formatted_exc = str(exc).replace('\n', ' ')
|
||||||
self.text_track_preview.insert(tkinter.END, f"< Error: {formatted_exc} >\n")
|
self.text_track_preview.insert(tkinter.END, f"< Error: {formatted_exc} >\n")
|
||||||
self.text_track_preview.tag_add("error", "end-1c-1l", "end-1c")
|
self.text_track_preview.tag_add("error", "end-1c-1l", "end-1c")
|
||||||
|
|
||||||
|
|
0
source/gui/preview/track_selecting.py
Normal file
0
source/gui/preview/track_selecting.py
Normal file
|
@ -8,6 +8,7 @@ from source import threaded
|
||||||
from source.mkw import Tag
|
from source.mkw import Tag
|
||||||
from source.mkw.Cup import Cup
|
from source.mkw.Cup import Cup
|
||||||
from source.mkw.MKWColor import bmg_color_text
|
from source.mkw.MKWColor import bmg_color_text
|
||||||
|
from source.mkw.ModSettings import ModSettings
|
||||||
from source.mkw.Track import Track
|
from source.mkw.Track import Track
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
@ -42,7 +43,7 @@ class ModConfig:
|
||||||
self.path = Path(path)
|
self.path = Path(path)
|
||||||
self.macros: dict = macros if macros is not None else {}
|
self.macros: dict = macros if macros is not None else {}
|
||||||
self.messages: dict = messages if messages is not None else {}
|
self.messages: dict = messages if messages is not None else {}
|
||||||
self.settings: dict = settings if settings is not None else {}
|
self.settings: dict = ModSettings(settings if settings is not None else {})
|
||||||
|
|
||||||
self.name: str = name
|
self.name: str = name
|
||||||
self.nickname: str = nickname if nickname is not None else name
|
self.nickname: str = nickname if nickname is not None else name
|
||||||
|
|
23
source/mkw/ModSettings/TypeSettings/Choices.py
Normal file
23
source/mkw/ModSettings/TypeSettings/Choices.py
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import tkinter
|
||||||
|
from tkinter import ttk
|
||||||
|
from source.mkw.ModSettings.TypeSettings import AbstractTypeSettings
|
||||||
|
|
||||||
|
|
||||||
|
class Choices(AbstractTypeSettings):
|
||||||
|
"""
|
||||||
|
This setting type allow you to input a string text.
|
||||||
|
You can optionally add a "preview" to allow the user to use a window to select the value.
|
||||||
|
"""
|
||||||
|
|
||||||
|
type = "choices"
|
||||||
|
|
||||||
|
def __init__(self, choices: list[str], value: str = None):
|
||||||
|
self.value = value if value is not None else choices[0]
|
||||||
|
self.choices = choices
|
||||||
|
|
||||||
|
def tkinter_show(self, master) -> None:
|
||||||
|
variable = tkinter.StringVar(master, value=self.value)
|
||||||
|
variable.trace_add("write", lambda *_: setattr(self, "value", variable.get()))
|
||||||
|
|
||||||
|
combobox = ttk.Combobox(master, width=100, values=self.choices, textvariable=variable)
|
||||||
|
combobox.grid(row=1, column=1)
|
27
source/mkw/ModSettings/TypeSettings/String.py
Normal file
27
source/mkw/ModSettings/TypeSettings/String.py
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import tkinter
|
||||||
|
from tkinter import ttk
|
||||||
|
|
||||||
|
from source.mkw.ModSettings.TypeSettings import AbstractTypeSettings
|
||||||
|
|
||||||
|
|
||||||
|
class String(AbstractTypeSettings):
|
||||||
|
"""
|
||||||
|
This setting type allow you to input a string text.
|
||||||
|
You can optionally add a "preview" to allow the user to use a window to select the value.
|
||||||
|
"""
|
||||||
|
|
||||||
|
type = "string"
|
||||||
|
|
||||||
|
def __init__(self, value=None, preview: str = None):
|
||||||
|
self.value: str = value if value is not None else ""
|
||||||
|
self.preview: str | None = preview
|
||||||
|
|
||||||
|
def tkinter_show(self, master) -> None:
|
||||||
|
variable = tkinter.StringVar(master, value=self.value)
|
||||||
|
variable.trace_add("write", lambda *_: setattr(self, "value", variable.get()))
|
||||||
|
|
||||||
|
entry = ttk.Entry(master, width=100, textvariable=variable)
|
||||||
|
entry.grid(row=1, column=1, sticky="NEWS")
|
||||||
|
|
||||||
|
button = ttk.Button(master, text="...", width=3)
|
||||||
|
button.grid(row=1, column=2, sticky="NEWS")
|
16
source/mkw/ModSettings/TypeSettings/__init__.py
Normal file
16
source/mkw/ModSettings/TypeSettings/__init__.py
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
|
|
||||||
|
class AbstractTypeSettings(ABC):
|
||||||
|
type: str # type name of the settings
|
||||||
|
value: str # value for the settings
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def tkinter_show(self, master) -> None:
|
||||||
|
"""
|
||||||
|
Show the option inside a tkinter widget
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
from source.mkw.ModSettings.TypeSettings import String, Choices
|
42
source/mkw/ModSettings/__init__.py
Normal file
42
source/mkw/ModSettings/__init__.py
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
from source.mkw.ModSettings.TypeSettings import AbstractTypeSettings
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidSettingsType(Exception):
|
||||||
|
def __init__(self, settings_type: str):
|
||||||
|
super().__init__(f"Error : Type of mod settings '{settings_type}' not found.")
|
||||||
|
|
||||||
|
|
||||||
|
class ModSettings:
|
||||||
|
def __new__(cls, settings_dict: dict) -> dict[str, AbstractTypeSettings]:
|
||||||
|
"""
|
||||||
|
Load all the settings in mod_settings_dict
|
||||||
|
:param settings_dict: dictionnary containing all the settings defined for the mod
|
||||||
|
"""
|
||||||
|
settings: dict[str, AbstractTypeSettings] = {}
|
||||||
|
|
||||||
|
for settings_name, settings_data in settings_dict.items():
|
||||||
|
for subclass in filter(
|
||||||
|
lambda subclass: subclass.type == settings_data["type"], AbstractTypeSettings.__subclasses__()
|
||||||
|
):
|
||||||
|
settings_data.pop("type")
|
||||||
|
settings[settings_name] = subclass(**settings_data)
|
||||||
|
break
|
||||||
|
else: raise InvalidSettingsType(settings_name)
|
||||||
|
|
||||||
|
return settings
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
example_settings = {
|
||||||
|
"mode": {
|
||||||
|
"type": "choices",
|
||||||
|
"choices": [
|
||||||
|
"normal",
|
||||||
|
"debug"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"highlight_if": {
|
||||||
|
"type": "str",
|
||||||
|
"preview": "track_formatting"
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,7 +8,7 @@ Patch: any
|
||||||
|
|
||||||
class Special(AbstractOperation):
|
class Special(AbstractOperation):
|
||||||
"""
|
"""
|
||||||
use a file defined as special in the patch to replate the current file content
|
use a file defined as special in the patch to replace the current file content
|
||||||
"""
|
"""
|
||||||
|
|
||||||
type = "special"
|
type = "special"
|
||||||
|
|
Loading…
Reference in a new issue