From 2f8b4e22f897d22542344b7289217c1262d51ecc Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Sun, 9 May 2021 16:34:52 +0100 Subject: [PATCH 001/139] Cleaned up the version select UI --- amulet_map_editor/api/wx/ui/version_select.py | 52 ++++++++++++++++--- 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/version_select.py b/amulet_map_editor/api/wx/ui/version_select.py index b975e934..bbe6cdd5 100644 --- a/amulet_map_editor/api/wx/ui/version_select.py +++ b/amulet_map_editor/api/wx/ui/version_select.py @@ -29,6 +29,9 @@ class PlatformSelect(wx.Panel): + """ + A UI element that allows you to pick between the platforms in the translator. + """ def __init__( self, parent: wx.Window, @@ -39,9 +42,22 @@ def __init__( allowed_platforms: Tuple[PlatformType, ...] = None, **kwargs ): + """ + Construct a :class:`PlatformSelect` UI. + + :param parent: The parent window. + :param translation_manager: The translation manager to populate from. + :param platform: The default platform (optional) + :param allow_universal: If True the universal format will be included. + :param allow_vanilla: If True the vanilla formats will be included. + :param allowed_platforms: A whitelist of platforms. + :param kwargs: Keyword args to be given to the Panel. + """ super().__init__(parent, style=wx.BORDER_SIMPLE) - self._sizer = wx.BoxSizer(wx.VERTICAL) - self.SetSizer(self._sizer) + sizer = wx.BoxSizer() + self.SetSizer(sizer) + self._sizer = wx.FlexGridSizer(2, 5, 5) + sizer.Add(self._sizer, 0, wx.ALL, 5) self._translation_manager = translation_manager self._allow_universal = allow_universal @@ -62,12 +78,10 @@ def __init__( def _add_ui_element( self, label: str, obj: Type[wx.Control], shown=True, **kwargs ) -> Any: - sizer = wx.BoxSizer(wx.HORIZONTAL) - self._sizer.Add(sizer, 0, wx.EXPAND | wx.ALL, 5) text = wx.StaticText(self, label=label, style=wx.ALIGN_CENTER) - sizer.Add(text, 1) + self._sizer.Add(text, 0, wx.ALIGN_CENTER) wx_obj = obj(self, **kwargs) - sizer.Add(wx_obj, 2) + self._sizer.Add(wx_obj, 0, wx.EXPAND) if not shown: text.Hide() wx_obj.Hide() @@ -102,6 +116,9 @@ def _populate_platform(self): class VersionSelect(PlatformSelect): + """ + A UI element that allows you to pick between the platforms and versions in the translator. + """ def __init__( self, parent: wx.Window, @@ -114,6 +131,19 @@ def __init__( allow_blockstate: bool = True, **kwargs ): + """ + Construct a :class:`VersionSelect` UI. + + :param parent: The parent window. + :param translation_manager: The translation manager to populate from. + :param platform: The default platform (optional) + :param version_number: The default version number (optional) + :param force_blockstate: If True and the native format is numerical will use the custom blockstate format. Else will use the native format. + :param show_force_blockstate: Should the format selection be shown to the user. + :param allow_numerical: Should the numerical versions be shown to the user. + :param allow_blockstate: Should the blockstate versions be shown to the user. + :param kwargs: Keyword args to be given to the :class:`PlatformSelect` and Panel. + """ super().__init__(parent, translation_manager, platform, **kwargs) self._allow_numerical = allow_numerical self._allow_blockstate = allow_blockstate @@ -240,15 +270,21 @@ def main(): app = wx.App() for cls in ( PlatformSelect, - VersionSelect, lambda *args: VersionSelect(*args, show_force_blockstate=False), + VersionSelect, ): dialog = wx.Dialog(None) sizer = wx.BoxSizer() dialog.SetSizer(sizer) - sizer.Add(cls(dialog, translation_manager)) + sizer.Add(cls(dialog, translation_manager), 0, wx.ALL, 5) dialog.Show() dialog.Fit() + + def get_on_close(dialog_): + def on_close(evt): + dialog_.Destroy() + return on_close + dialog.Bind(wx.EVT_CLOSE, get_on_close(dialog)) app.MainLoop() main() From 187006ecd7fba7320454feacb3f5d498e6dbfddd Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Sun, 9 May 2021 16:43:34 +0100 Subject: [PATCH 002/139] Fixed some scaling issues in the version select --- amulet_map_editor/api/wx/ui/version_select.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/version_select.py b/amulet_map_editor/api/wx/ui/version_select.py index bbe6cdd5..6bd3a4ea 100644 --- a/amulet_map_editor/api/wx/ui/version_select.py +++ b/amulet_map_editor/api/wx/ui/version_select.py @@ -54,10 +54,13 @@ def __init__( :param kwargs: Keyword args to be given to the Panel. """ super().__init__(parent, style=wx.BORDER_SIMPLE) - sizer = wx.BoxSizer() + sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(sizer) self._sizer = wx.FlexGridSizer(2, 5, 5) - sizer.Add(self._sizer, 0, wx.ALL, 5) + sizer.Add(self._sizer, 1, wx.ALL | wx.EXPAND, 5) + + self._sizer.AddGrowableCol(0) + self._sizer.AddGrowableCol(1) self._translation_manager = translation_manager self._allow_universal = allow_universal From 2bcbecc8684a5d8a37f60709061ee00bbe317de8 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Sun, 9 May 2021 16:55:31 +0100 Subject: [PATCH 003/139] Split the version select module into a package --- .../api/wx/ui/version_select/__init__.py | 3 + .../api/wx/ui/version_select/__main__.py | 29 ++++ .../api/wx/ui/version_select/events.py | 23 +++ .../wx/ui/version_select/platform_select.py | 98 ++++++++++++ .../ui/{ => version_select}/version_select.py | 143 +----------------- 5 files changed, 156 insertions(+), 140 deletions(-) create mode 100644 amulet_map_editor/api/wx/ui/version_select/__init__.py create mode 100644 amulet_map_editor/api/wx/ui/version_select/__main__.py create mode 100644 amulet_map_editor/api/wx/ui/version_select/events.py create mode 100644 amulet_map_editor/api/wx/ui/version_select/platform_select.py rename amulet_map_editor/api/wx/ui/{ => version_select}/version_select.py (55%) diff --git a/amulet_map_editor/api/wx/ui/version_select/__init__.py b/amulet_map_editor/api/wx/ui/version_select/__init__.py new file mode 100644 index 00000000..3fbb59ed --- /dev/null +++ b/amulet_map_editor/api/wx/ui/version_select/__init__.py @@ -0,0 +1,3 @@ +from .platform_select import PlatformSelect +from .version_select import VersionSelect +from .events import PlatformChangeEvent, EVT_PLATFORM_CHANGE, VersionNumberChangeEvent, EVT_VERSION_NUMBER_CHANGE, FormatChangeEvent, EVT_VERSION_CHANGE, VersionChangeEvent, EVT_FORMAT_CHANGE diff --git a/amulet_map_editor/api/wx/ui/version_select/__main__.py b/amulet_map_editor/api/wx/ui/version_select/__main__.py new file mode 100644 index 00000000..b60d74f7 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/version_select/__main__.py @@ -0,0 +1,29 @@ +from amulet_map_editor.api.wx.ui.version_select import PlatformSelect, VersionSelect + +if __name__ == "__main__": + import wx + import PyMCTranslate + + def main(): + translation_manager = PyMCTranslate.new_translation_manager() + app = wx.App() + for cls in ( + PlatformSelect, + lambda *args: VersionSelect(*args, show_force_blockstate=False), + VersionSelect, + ): + dialog = wx.Dialog(None) + sizer = wx.BoxSizer() + dialog.SetSizer(sizer) + sizer.Add(cls(dialog, translation_manager), 0, wx.ALL, 5) + dialog.Show() + dialog.Fit() + + def get_on_close(dialog_): + def on_close(evt): + dialog_.Destroy() + return on_close + dialog.Bind(wx.EVT_CLOSE, get_on_close(dialog)) + app.MainLoop() + + main() diff --git a/amulet_map_editor/api/wx/ui/version_select/events.py b/amulet_map_editor/api/wx/ui/version_select/events.py new file mode 100644 index 00000000..d3703ed4 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/version_select/events.py @@ -0,0 +1,23 @@ +import wx +from wx.lib import newevent + +( + PlatformChangeEvent, + EVT_PLATFORM_CHANGE, +) = newevent.NewCommandEvent() # the platform entry changed +( + VersionNumberChangeEvent, + EVT_VERSION_NUMBER_CHANGE, +) = newevent.NewCommandEvent() # the version number entry changed +( + FormatChangeEvent, + EVT_FORMAT_CHANGE, +) = ( + newevent.NewCommandEvent() +) # the format entry changed (is fired even if the entry isn't visible) +( + VersionChangeEvent, + EVT_VERSION_CHANGE, +) = ( + newevent.NewCommandEvent() +) # one of the above changed. Fired after EVT_FORMAT_CHANGE diff --git a/amulet_map_editor/api/wx/ui/version_select/platform_select.py b/amulet_map_editor/api/wx/ui/version_select/platform_select.py new file mode 100644 index 00000000..38ab5c90 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/version_select/platform_select.py @@ -0,0 +1,98 @@ +from amulet_map_editor.api.wx.ui.simple import SimpleChoice +import wx +import PyMCTranslate +from typing import Tuple, Type, Any + +from amulet.api.data_types import PlatformType +from .events import PlatformChangeEvent + + +class PlatformSelect(wx.Panel): + """ + A UI element that allows you to pick between the platforms in the translator. + """ + def __init__( + self, + parent: wx.Window, + translation_manager: PyMCTranslate.TranslationManager, + platform: PlatformType = None, + allow_universal: bool = True, + allow_vanilla: bool = True, + allowed_platforms: Tuple[PlatformType, ...] = None, + **kwargs + ): + """ + Construct a :class:`PlatformSelect` UI. + + :param parent: The parent window. + :param translation_manager: The translation manager to populate from. + :param platform: The default platform (optional) + :param allow_universal: If True the universal format will be included. + :param allow_vanilla: If True the vanilla formats will be included. + :param allowed_platforms: A whitelist of platforms. + :param kwargs: Keyword args to be given to the Panel. + """ + kwargs.setdefault("style", wx.BORDER_SIMPLE) + super().__init__(parent, **kwargs) + sizer = wx.BoxSizer(wx.VERTICAL) + self.SetSizer(sizer) + self._sizer = wx.FlexGridSizer(2, 5, 5) + sizer.Add(self._sizer, 1, wx.ALL | wx.EXPAND, 5) + + self._sizer.AddGrowableCol(0) + self._sizer.AddGrowableCol(1) + + self._translation_manager = translation_manager + self._allow_universal = allow_universal + self._allow_vanilla = allow_vanilla + self._allowed_platforms = allowed_platforms + self._platform_choice: SimpleChoice = self._add_ui_element( + "Platform:", SimpleChoice + ) + self._populate_platform() + self._set_platform(platform) + self._platform_choice.Bind( + wx.EVT_CHOICE, + lambda evt: wx.PostEvent( + self, PlatformChangeEvent(self.GetId(), platform=self.platform) + ), + ) + + def _add_ui_element( + self, label: str, obj: Type[wx.Control], shown=True, **kwargs + ) -> Any: + text = wx.StaticText(self, label=label, style=wx.ALIGN_CENTER) + self._sizer.Add(text, 0, wx.ALIGN_CENTER) + wx_obj = obj(self, **kwargs) + self._sizer.Add(wx_obj, 0, wx.EXPAND) + if not shown: + text.Hide() + wx_obj.Hide() + return wx_obj + + @property + def platform(self) -> PlatformType: + return self._platform_choice.GetCurrentString() + + @platform.setter + def platform(self, platform: PlatformType): + self._set_platform(platform) + wx.PostEvent(self, PlatformChangeEvent(self.GetId(), platform=self.platform)) + + def _set_platform(self, platform: PlatformType): + if platform and platform in self._platform_choice.GetItems(): + self._platform_choice.SetSelection( + self._platform_choice.GetItems().index(platform) + ) + else: + self._platform_choice.SetSelection(0) + + def _populate_platform(self): + platforms = self._translation_manager.platforms() + if self._allowed_platforms is not None: + platforms = [p for p in platforms if p in self._allowed_platforms] + if not self._allow_universal: + platforms = [p for p in platforms if p != "universal"] + if not self._allow_vanilla: + platforms = [p for p in platforms if p == "universal"] + self._platform_choice.SetItems(platforms) diff --git a/amulet_map_editor/api/wx/ui/version_select.py b/amulet_map_editor/api/wx/ui/version_select/version_select.py similarity index 55% rename from amulet_map_editor/api/wx/ui/version_select.py rename to amulet_map_editor/api/wx/ui/version_select/version_select.py index 6bd3a4ea..1392b093 100644 --- a/amulet_map_editor/api/wx/ui/version_select.py +++ b/amulet_map_editor/api/wx/ui/version_select/version_select.py @@ -1,121 +1,11 @@ from amulet_map_editor.api.wx.ui.simple import SimpleChoice, SimpleChoiceAny import wx -from wx.lib import newevent import PyMCTranslate -from typing import Tuple, Optional, Type, Any +from typing import Tuple, Optional from amulet.api.data_types import VersionNumberTuple, PlatformType - -( - PlatformChangeEvent, - EVT_PLATFORM_CHANGE, -) = newevent.NewCommandEvent() # the platform entry changed -( - VersionNumberChangeEvent, - EVT_VERSION_NUMBER_CHANGE, -) = newevent.NewCommandEvent() # the version number entry changed -( - FormatChangeEvent, - EVT_FORMAT_CHANGE, -) = ( - newevent.NewCommandEvent() -) # the format entry changed (is fired even if the entry isn't visible) -( - VersionChangeEvent, - EVT_VERSION_CHANGE, -) = ( - newevent.NewCommandEvent() -) # one of the above changed. Fired after EVT_FORMAT_CHANGE - - -class PlatformSelect(wx.Panel): - """ - A UI element that allows you to pick between the platforms in the translator. - """ - def __init__( - self, - parent: wx.Window, - translation_manager: PyMCTranslate.TranslationManager, - platform: PlatformType = None, - allow_universal: bool = True, - allow_vanilla: bool = True, - allowed_platforms: Tuple[PlatformType, ...] = None, - **kwargs - ): - """ - Construct a :class:`PlatformSelect` UI. - - :param parent: The parent window. - :param translation_manager: The translation manager to populate from. - :param platform: The default platform (optional) - :param allow_universal: If True the universal format will be included. - :param allow_vanilla: If True the vanilla formats will be included. - :param allowed_platforms: A whitelist of platforms. - :param kwargs: Keyword args to be given to the Panel. - """ - super().__init__(parent, style=wx.BORDER_SIMPLE) - sizer = wx.BoxSizer(wx.VERTICAL) - self.SetSizer(sizer) - self._sizer = wx.FlexGridSizer(2, 5, 5) - sizer.Add(self._sizer, 1, wx.ALL | wx.EXPAND, 5) - - self._sizer.AddGrowableCol(0) - self._sizer.AddGrowableCol(1) - - self._translation_manager = translation_manager - self._allow_universal = allow_universal - self._allow_vanilla = allow_vanilla - self._allowed_platforms = allowed_platforms - self._platform_choice: SimpleChoice = self._add_ui_element( - "Platform:", SimpleChoice - ) - self._populate_platform() - self._set_platform(platform) - self._platform_choice.Bind( - wx.EVT_CHOICE, - lambda evt: wx.PostEvent( - self, PlatformChangeEvent(self.GetId(), platform=self.platform) - ), - ) - - def _add_ui_element( - self, label: str, obj: Type[wx.Control], shown=True, **kwargs - ) -> Any: - text = wx.StaticText(self, label=label, style=wx.ALIGN_CENTER) - self._sizer.Add(text, 0, wx.ALIGN_CENTER) - wx_obj = obj(self, **kwargs) - self._sizer.Add(wx_obj, 0, wx.EXPAND) - if not shown: - text.Hide() - wx_obj.Hide() - return wx_obj - - @property - def platform(self) -> PlatformType: - return self._platform_choice.GetCurrentString() - - @platform.setter - def platform(self, platform: PlatformType): - self._set_platform(platform) - wx.PostEvent(self, PlatformChangeEvent(self.GetId(), platform=self.platform)) - - def _set_platform(self, platform: PlatformType): - if platform and platform in self._platform_choice.GetItems(): - self._platform_choice.SetSelection( - self._platform_choice.GetItems().index(platform) - ) - else: - self._platform_choice.SetSelection(0) - - def _populate_platform(self): - platforms = self._translation_manager.platforms() - if self._allowed_platforms is not None: - platforms = [p for p in platforms if p in self._allowed_platforms] - if not self._allow_universal: - platforms = [p for p in platforms if p != "universal"] - if not self._allow_vanilla: - platforms = [p for p in platforms if p == "universal"] - self._platform_choice.SetItems(platforms) +from .platform_select import PlatformSelect +from .events import PlatformChangeEvent, EVT_PLATFORM_CHANGE, VersionNumberChangeEvent, EVT_VERSION_NUMBER_CHANGE, FormatChangeEvent, EVT_VERSION_CHANGE, VersionChangeEvent, EVT_FORMAT_CHANGE class VersionSelect(PlatformSelect): @@ -264,30 +154,3 @@ def _on_version_number_change(self, evt): self._populate_blockstate() self.force_blockstate = None evt.Skip() - - -if __name__ == "__main__": - - def main(): - translation_manager = PyMCTranslate.new_translation_manager() - app = wx.App() - for cls in ( - PlatformSelect, - lambda *args: VersionSelect(*args, show_force_blockstate=False), - VersionSelect, - ): - dialog = wx.Dialog(None) - sizer = wx.BoxSizer() - dialog.SetSizer(sizer) - sizer.Add(cls(dialog, translation_manager), 0, wx.ALL, 5) - dialog.Show() - dialog.Fit() - - def get_on_close(dialog_): - def on_close(evt): - dialog_.Destroy() - return on_close - dialog.Bind(wx.EVT_CLOSE, get_on_close(dialog)) - app.MainLoop() - - main() From a7311f6d9cbf0aed4cff59634ff07db2d6d1809a Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Sun, 9 May 2021 17:25:25 +0100 Subject: [PATCH 004/139] Switched the version select events to class events --- .../api/wx/ui/version_select/__init__.py | 11 +- .../api/wx/ui/version_select/__main__.py | 2 + .../api/wx/ui/version_select/events.py | 119 ++++++++++++++---- .../wx/ui/version_select/platform_select.py | 7 +- .../wx/ui/version_select/version_select.py | 29 +++-- 5 files changed, 129 insertions(+), 39 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/version_select/__init__.py b/amulet_map_editor/api/wx/ui/version_select/__init__.py index 3fbb59ed..e26a72f9 100644 --- a/amulet_map_editor/api/wx/ui/version_select/__init__.py +++ b/amulet_map_editor/api/wx/ui/version_select/__init__.py @@ -1,3 +1,12 @@ from .platform_select import PlatformSelect from .version_select import VersionSelect -from .events import PlatformChangeEvent, EVT_PLATFORM_CHANGE, VersionNumberChangeEvent, EVT_VERSION_NUMBER_CHANGE, FormatChangeEvent, EVT_VERSION_CHANGE, VersionChangeEvent, EVT_FORMAT_CHANGE +from .events import ( + PlatformChangeEvent, + EVT_PLATFORM_CHANGE, + VersionNumberChangeEvent, + EVT_VERSION_NUMBER_CHANGE, + FormatChangeEvent, + EVT_VERSION_CHANGE, + VersionChangeEvent, + EVT_FORMAT_CHANGE, +) diff --git a/amulet_map_editor/api/wx/ui/version_select/__main__.py b/amulet_map_editor/api/wx/ui/version_select/__main__.py index b60d74f7..d46c1465 100644 --- a/amulet_map_editor/api/wx/ui/version_select/__main__.py +++ b/amulet_map_editor/api/wx/ui/version_select/__main__.py @@ -22,7 +22,9 @@ def main(): def get_on_close(dialog_): def on_close(evt): dialog_.Destroy() + return on_close + dialog.Bind(wx.EVT_CLOSE, get_on_close(dialog)) app.MainLoop() diff --git a/amulet_map_editor/api/wx/ui/version_select/events.py b/amulet_map_editor/api/wx/ui/version_select/events.py index d3703ed4..e486da8e 100644 --- a/amulet_map_editor/api/wx/ui/version_select/events.py +++ b/amulet_map_editor/api/wx/ui/version_select/events.py @@ -1,23 +1,98 @@ import wx -from wx.lib import newevent - -( - PlatformChangeEvent, - EVT_PLATFORM_CHANGE, -) = newevent.NewCommandEvent() # the platform entry changed -( - VersionNumberChangeEvent, - EVT_VERSION_NUMBER_CHANGE, -) = newevent.NewCommandEvent() # the version number entry changed -( - FormatChangeEvent, - EVT_FORMAT_CHANGE, -) = ( - newevent.NewCommandEvent() -) # the format entry changed (is fired even if the entry isn't visible) -( - VersionChangeEvent, - EVT_VERSION_CHANGE, -) = ( - newevent.NewCommandEvent() -) # one of the above changed. Fired after EVT_FORMAT_CHANGE +from typing import Tuple + +_PlatformEventType = wx.NewEventType() +EVT_PLATFORM_CHANGE = wx.PyEventBinder(_PlatformEventType) + + +class PlatformChangeEvent(wx.PyEvent): + """ + Event run when the platform input is changed. + Is run when the user or code changes the platform. + """ + + def __init__(self, platform: str): + wx.PyEvent.__init__(self, eventType=_PlatformEventType) + self._platform = platform + + @property + def platform(self) -> str: + """The platform that the selection was changed to.""" + return self._platform + + +_VersionNumberChangeEventType = wx.NewEventType() +EVT_VERSION_NUMBER_CHANGE = wx.PyEventBinder(_VersionNumberChangeEventType) + + +class VersionNumberChangeEvent(wx.PyEvent): + """ + Event run when the version number input is changed. + Is run when the user or code changes the version number. + """ + + def __init__(self, version_number: Tuple[int, ...]): + wx.PyEvent.__init__(self, eventType=_VersionNumberChangeEventType) + self._version_number = version_number + + @property + def version_number(self) -> Tuple[int, ...]: + """The version_number that the selection was changed to.""" + return self._version_number + + +_FormatChangeEventType = wx.NewEventType() +EVT_FORMAT_CHANGE = wx.PyEventBinder(_FormatChangeEventType) + + +class FormatChangeEvent(wx.PyEvent): + """ + Event run when the format input is changed. + Is run when the user or code changes the format input. + """ + + def __init__(self, force_blockstate: bool): + wx.PyEvent.__init__(self, eventType=_FormatChangeEventType) + self._force_blockstate = force_blockstate + + @property + def force_blockstate(self) -> bool: + """ + True if the format is force blockstate, False otherwise. + """ + return self._force_blockstate + + +_VersionChangeEventType = wx.NewEventType() +EVT_VERSION_CHANGE = wx.PyEventBinder(_VersionChangeEventType) + + +class VersionChangeEvent(wx.PyEvent): + """ + Event is run at the same time as :class:`FormatChangeEvent` but holds all the information about the version. + """ + + def __init__( + self, platform: str, version_number: Tuple[int, ...], force_blockstate: bool + ): + wx.PyEvent.__init__(self, eventType=_VersionChangeEventType) + self._platform = platform + self._version_number = version_number + self._force_blockstate = force_blockstate + + @property + def platform(self) -> str: + """The platform that the selection was changed to.""" + return self._platform + + @property + def version_number(self) -> Tuple[int, ...]: + """The version_number that the selection was changed to.""" + return self._version_number + + @property + def force_blockstate(self) -> bool: + """ + True if the format is force blockstate, False otherwise. + """ + return self._force_blockstate diff --git a/amulet_map_editor/api/wx/ui/version_select/platform_select.py b/amulet_map_editor/api/wx/ui/version_select/platform_select.py index 38ab5c90..fe831560 100644 --- a/amulet_map_editor/api/wx/ui/version_select/platform_select.py +++ b/amulet_map_editor/api/wx/ui/version_select/platform_select.py @@ -11,6 +11,7 @@ class PlatformSelect(wx.Panel): """ A UI element that allows you to pick between the platforms in the translator. """ + def __init__( self, parent: wx.Window, @@ -53,9 +54,7 @@ def __init__( self._set_platform(platform) self._platform_choice.Bind( wx.EVT_CHOICE, - lambda evt: wx.PostEvent( - self, PlatformChangeEvent(self.GetId(), platform=self.platform) - ), + lambda evt: wx.PostEvent(self, PlatformChangeEvent(self.platform)), ) def _add_ui_element( @@ -77,7 +76,7 @@ def platform(self) -> PlatformType: @platform.setter def platform(self, platform: PlatformType): self._set_platform(platform) - wx.PostEvent(self, PlatformChangeEvent(self.GetId(), platform=self.platform)) + wx.PostEvent(self, PlatformChangeEvent(self.platform)) def _set_platform(self, platform: PlatformType): if platform and platform in self._platform_choice.GetItems(): diff --git a/amulet_map_editor/api/wx/ui/version_select/version_select.py b/amulet_map_editor/api/wx/ui/version_select/version_select.py index 1392b093..9cc2775c 100644 --- a/amulet_map_editor/api/wx/ui/version_select/version_select.py +++ b/amulet_map_editor/api/wx/ui/version_select/version_select.py @@ -5,13 +5,21 @@ from amulet.api.data_types import VersionNumberTuple, PlatformType from .platform_select import PlatformSelect -from .events import PlatformChangeEvent, EVT_PLATFORM_CHANGE, VersionNumberChangeEvent, EVT_VERSION_NUMBER_CHANGE, FormatChangeEvent, EVT_VERSION_CHANGE, VersionChangeEvent, EVT_FORMAT_CHANGE +from .events import ( + PlatformChangeEvent, + EVT_PLATFORM_CHANGE, + VersionNumberChangeEvent, + EVT_VERSION_NUMBER_CHANGE, + FormatChangeEvent, + VersionChangeEvent, +) class VersionSelect(PlatformSelect): """ A UI element that allows you to pick between the platforms and versions in the translator. """ + def __init__( self, parent: wx.Window, @@ -52,9 +60,7 @@ def __init__( wx.EVT_CHOICE, lambda evt: wx.PostEvent( self, - VersionNumberChangeEvent( - self.GetId(), version_number=self.version_number - ), + VersionNumberChangeEvent(self.version_number), ), ) @@ -73,15 +79,14 @@ def __init__( def _post_version_change(self): wx.PostEvent( self, - FormatChangeEvent(self.GetId(), force_blockstate=self.force_blockstate), + FormatChangeEvent(self.force_blockstate), ), wx.PostEvent( self, VersionChangeEvent( - self.GetId(), - platform=self.platform, - version_number=self.version_number, - force_blockstate=self.force_blockstate, + self.platform, + self.version_number, + self.force_blockstate, ), ) @@ -94,7 +99,7 @@ def version_number(self, version_number: VersionNumberTuple): self._set_version_number(version_number) wx.PostEvent( self, - VersionNumberChangeEvent(self.GetId(), version_number=self.version_number), + VersionNumberChangeEvent(self.version_number), ) def _set_version_number(self, version_number: VersionNumberTuple): @@ -145,12 +150,12 @@ def _populate_blockstate(self): else: self._blockstate_choice.Disable() - def _on_platform_change(self, evt): + def _on_platform_change(self, evt: PlatformChangeEvent): self._populate_version() self.version_number = None evt.Skip() - def _on_version_number_change(self, evt): + def _on_version_number_change(self, evt: VersionChangeEvent): self._populate_blockstate() self.force_blockstate = None evt.Skip() From 6248c8dd60b44fc814a5ec04cc90fe22a602496d Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Tue, 11 May 2021 09:46:55 +0100 Subject: [PATCH 005/139] Split base_select into a package Made type name a property --- .../api/wx/ui/base_select/__init__.py | 11 +++++++ .../wx/ui/{ => base_select}/base_select.py | 29 ++++++------------- .../api/wx/ui/base_select/events.py | 20 +++++++++++++ .../api/wx/ui/biome_select/biome_select.py | 4 ++- .../api/wx/ui/block_select/block_select.py | 5 +++- 5 files changed, 47 insertions(+), 22 deletions(-) create mode 100644 amulet_map_editor/api/wx/ui/base_select/__init__.py rename amulet_map_editor/api/wx/ui/{ => base_select}/base_select.py (92%) create mode 100644 amulet_map_editor/api/wx/ui/base_select/events.py diff --git a/amulet_map_editor/api/wx/ui/base_select/__init__.py b/amulet_map_editor/api/wx/ui/base_select/__init__.py new file mode 100644 index 00000000..1836d800 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/base_select/__init__.py @@ -0,0 +1,11 @@ +from .events import ( + ItemNamespaceChangeEvent, + EVT_ITEM_NAMESPACE_CHANGE, + ItemNameChangeEvent, + EVT_ITEM_NAME_CHANGE, + ItemChangeEvent, + EVT_ITEM_CHANGE, + PickEvent, + EVT_PICK, +) +from .base_select import BaseSelect diff --git a/amulet_map_editor/api/wx/ui/base_select.py b/amulet_map_editor/api/wx/ui/base_select/base_select.py similarity index 92% rename from amulet_map_editor/api/wx/ui/base_select.py rename to amulet_map_editor/api/wx/ui/base_select/base_select.py index 43214c78..320c0859 100644 --- a/amulet_map_editor/api/wx/ui/base_select.py +++ b/amulet_map_editor/api/wx/ui/base_select/base_select.py @@ -1,34 +1,19 @@ import wx -from wx.lib import newevent from typing import Tuple, List, Optional import PyMCTranslate from amulet_map_editor.api.image import COLOUR_PICKER - -( +from .events import ( ItemNamespaceChangeEvent, - EVT_ITEM_NAMESPACE_CHANGE, -) = newevent.NewCommandEvent() # the namespace entry changed -( ItemNameChangeEvent, - EVT_ITEM_NAME_CHANGE, -) = newevent.NewCommandEvent() # the name entry changed -( ItemChangeEvent, - EVT_ITEM_CHANGE, -) = ( - newevent.NewCommandEvent() -) # the name or namespace changed. Generated after EVT_ITEM_NAME_CHANGE -( + EVT_ITEM_NAMESPACE_CHANGE, PickEvent, - EVT_PICK, -) = newevent.NewCommandEvent() # The pick button was pressed +) class BaseSelect(wx.Panel): - TypeName = "?" - def __init__( self, parent: wx.Window, @@ -74,7 +59,7 @@ def __init__( sizer.Add(header_sizer, 0, wx.EXPAND | wx.BOTTOM, 5) header_sizer.Add( wx.StaticText( - self, label=f"{self.TypeName.capitalize()} name:", style=wx.ALIGN_CENTER + self, label=f"{self.type_name.capitalize()} name:", style=wx.ALIGN_CENTER ), 1, wx.ALIGN_CENTER_VERTICAL, @@ -99,6 +84,10 @@ def __init__( self.set_name(default_name) self._list_box.Bind(wx.EVT_LISTBOX, lambda evt: self._post_item_change()) + @property + def type_name(self) -> str: + raise NotImplementedError + def _post_namespace_change(self): if self._do_text_event: wx.PostEvent( @@ -175,7 +164,7 @@ def _populate_namespace(self): def _populate_item_name(self): raise NotImplementedError("This method should be overridden in child classes.") - def _on_namespace_change(self, evt): + def _on_namespace_change(self, evt: ItemNamespaceChangeEvent): self._populate_item_name() self.name = None evt.Skip() diff --git a/amulet_map_editor/api/wx/ui/base_select/events.py b/amulet_map_editor/api/wx/ui/base_select/events.py new file mode 100644 index 00000000..6e209aa5 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/base_select/events.py @@ -0,0 +1,20 @@ +from wx.lib import newevent + +( + ItemNamespaceChangeEvent, + EVT_ITEM_NAMESPACE_CHANGE, +) = newevent.NewCommandEvent() # the namespace entry changed +( + ItemNameChangeEvent, + EVT_ITEM_NAME_CHANGE, +) = newevent.NewCommandEvent() # the name entry changed +( + ItemChangeEvent, + EVT_ITEM_CHANGE, +) = ( + newevent.NewCommandEvent() +) # the name or namespace changed. Generated after EVT_ITEM_NAME_CHANGE +( + PickEvent, + EVT_PICK, +) = newevent.NewCommandEvent() # The pick button was pressed diff --git a/amulet_map_editor/api/wx/ui/biome_select/biome_select.py b/amulet_map_editor/api/wx/ui/biome_select/biome_select.py index def90b2f..9bf1cb0c 100644 --- a/amulet_map_editor/api/wx/ui/biome_select/biome_select.py +++ b/amulet_map_editor/api/wx/ui/biome_select/biome_select.py @@ -2,7 +2,9 @@ class BiomeSelect(BaseSelect): - TypeName = "Biome" + @property + def type_name(self) -> str: + return "Biome" def _populate_namespace(self): version = self._translation_manager.get_version( diff --git a/amulet_map_editor/api/wx/ui/block_select/block_select.py b/amulet_map_editor/api/wx/ui/block_select/block_select.py index 8abf6f9d..9f15a69d 100644 --- a/amulet_map_editor/api/wx/ui/block_select/block_select.py +++ b/amulet_map_editor/api/wx/ui/block_select/block_select.py @@ -7,7 +7,9 @@ class BlockSelect(BaseSelect): - TypeName = "Block" + @property + def type_name(self) -> str: + return "Block" def __init__( self, @@ -56,6 +58,7 @@ def main(): sizer = wx.BoxSizer() dialog.SetSizer(sizer) sizer.Add(BlockSelect(dialog, translation_manager, "java", (1, 16, 0), False)) + dialog.Bind(wx.EVT_CLOSE, lambda evt: dialog.Destroy()) dialog.Show() dialog.Fit() app.MainLoop() From 0060e412528cf1a42ebc78e86fd39299bc99b187 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Tue, 11 May 2021 09:47:25 +0100 Subject: [PATCH 006/139] Fixed the demo code not exiting --- amulet_map_editor/api/wx/ui/block_select/block_define.py | 1 + amulet_map_editor/api/wx/ui/block_select/multi_block_define.py | 1 + amulet_map_editor/api/wx/ui/block_select/properties.py | 1 + 3 files changed, 3 insertions(+) diff --git a/amulet_map_editor/api/wx/ui/block_select/block_define.py b/amulet_map_editor/api/wx/ui/block_select/block_define.py index 837ae78e..9c72b076 100644 --- a/amulet_map_editor/api/wx/ui/block_select/block_define.py +++ b/amulet_map_editor/api/wx/ui/block_select/block_define.py @@ -171,6 +171,7 @@ def main(): ) dialog.Show() dialog.Fit() + dialog.Bind(wx.EVT_CLOSE, lambda evt: dialog.Destroy()) app.MainLoop() main() diff --git a/amulet_map_editor/api/wx/ui/block_select/multi_block_define.py b/amulet_map_editor/api/wx/ui/block_select/multi_block_define.py index 542daee5..9c86088d 100644 --- a/amulet_map_editor/api/wx/ui/block_select/multi_block_define.py +++ b/amulet_map_editor/api/wx/ui/block_select/multi_block_define.py @@ -197,6 +197,7 @@ def main(): sizer = wx.BoxSizer() dialog.SetSizer(sizer) sizer.Add(MultiBlockDefine(dialog, translation_manager), 1, wx.EXPAND) + dialog.Bind(wx.EVT_CLOSE, lambda evt: dialog.Destroy()) dialog.Show() dialog.Fit() app.MainLoop() diff --git a/amulet_map_editor/api/wx/ui/block_select/properties.py b/amulet_map_editor/api/wx/ui/block_select/properties.py index 89d92026..9dd7f740 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties.py @@ -378,6 +378,7 @@ def main(): wx.ALL, 5, ) + dialog.Bind(wx.EVT_CLOSE, lambda evt: dialog.Destroy()) dialog.Show() dialog.Fit() app.MainLoop() From ca7fb6d286605b1460a1d98bb22f307e30c368f6 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Tue, 11 May 2021 09:50:38 +0100 Subject: [PATCH 007/139] Removed the format change event This can be managed by the version change --- .../api/wx/ui/version_select/__init__.py | 4 +--- .../api/wx/ui/version_select/events.py | 22 ------------------- .../wx/ui/version_select/version_select.py | 5 ----- 3 files changed, 1 insertion(+), 30 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/version_select/__init__.py b/amulet_map_editor/api/wx/ui/version_select/__init__.py index e26a72f9..88bd7329 100644 --- a/amulet_map_editor/api/wx/ui/version_select/__init__.py +++ b/amulet_map_editor/api/wx/ui/version_select/__init__.py @@ -5,8 +5,6 @@ EVT_PLATFORM_CHANGE, VersionNumberChangeEvent, EVT_VERSION_NUMBER_CHANGE, - FormatChangeEvent, - EVT_VERSION_CHANGE, VersionChangeEvent, - EVT_FORMAT_CHANGE, + EVT_VERSION_CHANGE, ) diff --git a/amulet_map_editor/api/wx/ui/version_select/events.py b/amulet_map_editor/api/wx/ui/version_select/events.py index e486da8e..367149f8 100644 --- a/amulet_map_editor/api/wx/ui/version_select/events.py +++ b/amulet_map_editor/api/wx/ui/version_select/events.py @@ -41,28 +41,6 @@ def version_number(self) -> Tuple[int, ...]: return self._version_number -_FormatChangeEventType = wx.NewEventType() -EVT_FORMAT_CHANGE = wx.PyEventBinder(_FormatChangeEventType) - - -class FormatChangeEvent(wx.PyEvent): - """ - Event run when the format input is changed. - Is run when the user or code changes the format input. - """ - - def __init__(self, force_blockstate: bool): - wx.PyEvent.__init__(self, eventType=_FormatChangeEventType) - self._force_blockstate = force_blockstate - - @property - def force_blockstate(self) -> bool: - """ - True if the format is force blockstate, False otherwise. - """ - return self._force_blockstate - - _VersionChangeEventType = wx.NewEventType() EVT_VERSION_CHANGE = wx.PyEventBinder(_VersionChangeEventType) diff --git a/amulet_map_editor/api/wx/ui/version_select/version_select.py b/amulet_map_editor/api/wx/ui/version_select/version_select.py index 9cc2775c..fa46b079 100644 --- a/amulet_map_editor/api/wx/ui/version_select/version_select.py +++ b/amulet_map_editor/api/wx/ui/version_select/version_select.py @@ -10,7 +10,6 @@ EVT_PLATFORM_CHANGE, VersionNumberChangeEvent, EVT_VERSION_NUMBER_CHANGE, - FormatChangeEvent, VersionChangeEvent, ) @@ -77,10 +76,6 @@ def __init__( ) def _post_version_change(self): - wx.PostEvent( - self, - FormatChangeEvent(self.force_blockstate), - ), wx.PostEvent( self, VersionChangeEvent( From c9623d5ee77c787353066a797be174c4d683ebfb Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Tue, 11 May 2021 09:51:09 +0100 Subject: [PATCH 008/139] Reformatted --- amulet_map_editor/api/wx/ui/base_select/base_select.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/amulet_map_editor/api/wx/ui/base_select/base_select.py b/amulet_map_editor/api/wx/ui/base_select/base_select.py index 320c0859..7c8b50bd 100644 --- a/amulet_map_editor/api/wx/ui/base_select/base_select.py +++ b/amulet_map_editor/api/wx/ui/base_select/base_select.py @@ -59,7 +59,9 @@ def __init__( sizer.Add(header_sizer, 0, wx.EXPAND | wx.BOTTOM, 5) header_sizer.Add( wx.StaticText( - self, label=f"{self.type_name.capitalize()} name:", style=wx.ALIGN_CENTER + self, + label=f"{self.type_name.capitalize()} name:", + style=wx.ALIGN_CENTER, ), 1, wx.ALIGN_CENTER_VERTICAL, From 761bc5cd936b9f3d91ba784ca53b41d0644dd271 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Tue, 11 May 2021 10:15:27 +0100 Subject: [PATCH 009/139] Added a little documentation and demo code --- .../api/wx/ui/base_select/base_select.py | 7 +++++ .../api/wx/ui/biome_select/biome_define.py | 26 +++++++++++++++++ .../api/wx/ui/biome_select/biome_select.py | 29 +++++++++++++++++++ .../api/wx/ui/block_select/block_define.py | 10 ++++--- .../api/wx/ui/block_select/block_select.py | 11 ++++++- 5 files changed, 78 insertions(+), 5 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/base_select/base_select.py b/amulet_map_editor/api/wx/ui/base_select/base_select.py index 7c8b50bd..5b43fc3a 100644 --- a/amulet_map_editor/api/wx/ui/base_select/base_select.py +++ b/amulet_map_editor/api/wx/ui/base_select/base_select.py @@ -14,6 +14,13 @@ class BaseSelect(wx.Panel): + """ + BaseSelect is a base class for a UI containing + a namespace choice + a base name search + a list of base names + """ + def __init__( self, parent: wx.Window, diff --git a/amulet_map_editor/api/wx/ui/biome_select/biome_define.py b/amulet_map_editor/api/wx/ui/biome_select/biome_define.py index 42ee554e..a10b687e 100644 --- a/amulet_map_editor/api/wx/ui/biome_select/biome_define.py +++ b/amulet_map_editor/api/wx/ui/biome_select/biome_define.py @@ -8,6 +8,10 @@ class BiomeDefine(BaseDefine): + """ + A UI that merges a version select widget with a biome select widget. + """ + def __init__( self, parent, @@ -66,3 +70,25 @@ def universal_biome(self, universal_biome: str): self.biome = self._translation_manager.get_version( self.platform, self.version_number ).biome.from_universal(universal_biome) + + +if __name__ == "__main__": + + def main(): + app = wx.App() + translation_manager = PyMCTranslate.new_translation_manager() + dialog = wx.Dialog(None, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER) + sizer = wx.BoxSizer() + dialog.SetSizer(sizer) + sizer.Add( + BiomeDefine(dialog, translation_manager, wx.HORIZONTAL), + 1, + wx.ALL | wx.EXPAND, + 5, + ) + dialog.Show() + dialog.Fit() + dialog.Bind(wx.EVT_CLOSE, lambda evt: dialog.Destroy()) + app.MainLoop() + + main() diff --git a/amulet_map_editor/api/wx/ui/biome_select/biome_select.py b/amulet_map_editor/api/wx/ui/biome_select/biome_select.py index 9bf1cb0c..7f9e00c1 100644 --- a/amulet_map_editor/api/wx/ui/biome_select/biome_select.py +++ b/amulet_map_editor/api/wx/ui/biome_select/biome_select.py @@ -2,6 +2,10 @@ class BiomeSelect(BaseSelect): + """ + A UI consisting of a namespace choice, biome name search box and list of biome names. + """ + @property def type_name(self) -> str: return "Biome" @@ -28,3 +32,28 @@ def _populate_item_name(self): if biome_id.startswith(self.namespace) ] self._list_box.SetItems(self._names) + + +if __name__ == "__main__": + + def main(): + import wx + import PyMCTranslate + + app = wx.App() + translation_manager = PyMCTranslate.new_translation_manager() + dialog = wx.Dialog(None, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER) + sizer = wx.BoxSizer() + dialog.SetSizer(sizer) + sizer.Add( + BiomeSelect(dialog, translation_manager, "java", (1, 16, 0), False), + 1, + wx.ALL | wx.EXPAND, + 5, + ) + dialog.Show() + dialog.Fit() + dialog.Bind(wx.EVT_CLOSE, lambda evt: dialog.Destroy()) + app.MainLoop() + + main() diff --git a/amulet_map_editor/api/wx/ui/block_select/block_define.py b/amulet_map_editor/api/wx/ui/block_select/block_define.py index 9c72b076..1281e513 100644 --- a/amulet_map_editor/api/wx/ui/block_select/block_define.py +++ b/amulet_map_editor/api/wx/ui/block_select/block_define.py @@ -17,6 +17,10 @@ class BlockDefine(BaseDefine): + """ + A UI that merges a version select widget with a block select widget and a property select. + """ + def __init__( self, parent, @@ -47,10 +51,8 @@ def __init__( ) right_sizer = wx.BoxSizer(wx.VERTICAL) - if orientation == wx.HORIZONTAL: - self._sizer.Add(right_sizer, 1, wx.EXPAND | wx.LEFT, 5) - else: - self._sizer.Add(right_sizer, 1, wx.EXPAND | wx.TOP, 5) + border = wx.LEFT if orientation == wx.HORIZONTAL else wx.TOP + self._sizer.Add(right_sizer, 1, wx.EXPAND | border, 5) self._property_picker = PropertySelect( self, diff --git a/amulet_map_editor/api/wx/ui/block_select/block_select.py b/amulet_map_editor/api/wx/ui/block_select/block_select.py index 9f15a69d..c464c2c9 100644 --- a/amulet_map_editor/api/wx/ui/block_select/block_select.py +++ b/amulet_map_editor/api/wx/ui/block_select/block_select.py @@ -7,6 +7,10 @@ class BlockSelect(BaseSelect): + """ + A UI consisting of a namespace choice, block name search box and list of block names. + """ + @property def type_name(self) -> str: return "Block" @@ -57,7 +61,12 @@ def main(): dialog = wx.Dialog(None) sizer = wx.BoxSizer() dialog.SetSizer(sizer) - sizer.Add(BlockSelect(dialog, translation_manager, "java", (1, 16, 0), False)) + sizer.Add( + BlockSelect(dialog, translation_manager, "java", (1, 16, 0), False), + 1, + wx.ALL | wx.EXPAND, + 5, + ) dialog.Bind(wx.EVT_CLOSE, lambda evt: dialog.Destroy()) dialog.Show() dialog.Fit() From 05a97ba993ca7d5fb17c178466ead7ad233490b6 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Tue, 11 May 2021 17:53:54 +0100 Subject: [PATCH 010/139] Removed depreciated GetAny method --- amulet_map_editor/api/wx/ui/simple.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/simple.py b/amulet_map_editor/api/wx/ui/simple.py index 9dec9a28..6d40ff27 100644 --- a/amulet_map_editor/api/wx/ui/simple.py +++ b/amulet_map_editor/api/wx/ui/simple.py @@ -95,7 +95,7 @@ def SetItems( default: StringableType = None, ): """Set items. Does not have to be strings. - If items is a dictionary the string of the values are show to the user and the key is returned from GetAny + If items is a dictionary the string of the values are show to the user and the key is returned from GetCurrentObject If it is just an iterable the string of the values are shown and the raw equivalent input is returned.""" if not items: return @@ -125,14 +125,6 @@ def SetValue(self, value: Any): if value in self._keys: self.SetSelection(self._keys.index(value)) - def GetAny(self) -> Optional[Any]: - """Return the value currently selected in the form before it was converted to a string""" - log.warning( - "SimpleChoiceAny.GetAny is being depreciated and will be removed in the future. Please use SimpleChoiceAny.GetCurrentObject instead", - exc_info=True, - ) - return self.GetCurrentObject() - def GetCurrentObject(self) -> Optional[Any]: """Return the value currently selected in the form before it was converted to a string""" if self._values: From 65c7d19a41597c128c0302de5d67f3b04d07f871 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Tue, 11 May 2021 17:58:07 +0100 Subject: [PATCH 011/139] Added the start of the updated fill and replace UI --- amulet_map_editor/lang/en.lang | 5 + .../plugins/tools/fill_replace/__init__.py | 0 .../plugins/tools/fill_replace/__main__.py | 22 ++ .../tools/fill_replace/fill_replace_widget.py | 67 +++++ .../tools/fill_replace/replace_widget.py | 231 ++++++++++++++++++ 5 files changed, 325 insertions(+) create mode 100644 amulet_map_editor/programs/edit/plugins/tools/fill_replace/__init__.py create mode 100644 amulet_map_editor/programs/edit/plugins/tools/fill_replace/__main__.py create mode 100644 amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_widget.py create mode 100644 amulet_map_editor/programs/edit/plugins/tools/fill_replace/replace_widget.py diff --git a/amulet_map_editor/lang/en.lang b/amulet_map_editor/lang/en.lang index 6b859b7a..5f753819 100644 --- a/amulet_map_editor/lang/en.lang +++ b/amulet_map_editor/lang/en.lang @@ -77,6 +77,11 @@ program_3d_edit.select_tool.button_point2_tooltip=Press and hold this button and program_3d_edit.select_tool.button_selection_box=Move Box program_3d_edit.select_tool.button_selection_box_tooltip=Press and hold this button and use the movement controls to move the active box. +program_3d_edit.fill_tool.replace=Replace +program_3d_edit.fill_tool.replace_mode.single=Single +program_3d_edit.fill_tool.replace_mode.sequence=Sequence +program_3d_edit.fill_tool.replace_mode.map=Map + program_3d_edit.paste_tool.location_label=Location program_3d_edit.paste_tool.location_x_label=x program_3d_edit.paste_tool.location_x_tooltip=The x location where the centre of the selection will be placed. Type in a number, scroll wheel over or use the arrows to change. diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/__init__.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/__main__.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/__main__.py new file mode 100644 index 00000000..87a8e4fe --- /dev/null +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/__main__.py @@ -0,0 +1,22 @@ +import wx +from amulet_map_editor.programs.edit.plugins.tools.fill_replace.fill_replace_widget import FillReplaceWidget + +if __name__ == "__main__": + + def main(): + app = wx.App() + dialog = wx.Dialog(None, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER) + sizer = wx.BoxSizer() + dialog.SetSizer(sizer) + sizer.Add( + FillReplaceWidget(dialog), + 1, + wx.ALL | wx.EXPAND, + 5, + ) + dialog.Show() + dialog.Fit() + dialog.Bind(wx.EVT_CLOSE, lambda evt: dialog.Destroy()) + app.MainLoop() + + main() diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_widget.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_widget.py new file mode 100644 index 00000000..49393375 --- /dev/null +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_widget.py @@ -0,0 +1,67 @@ +import wx +from enum import Enum +from typing import List +from .replace_widget import ReplaceOperationWidget +from amulet_map_editor.api.wx.ui.simple import SimpleChoiceAny +from amulet_map_editor import lang + + +class ReplaceMode(Enum): + Single = 0 + Sequence = 1 + Map = 2 + + +LeftRightBorder = wx.LEFT | wx.RIGHT +BottomBorder = LeftRightBorder | wx.BOTTOM + + +class FillReplaceWidget(wx.Panel): + def __init__(self, parent: wx.Window, **kwargs): + super().__init__(parent, **kwargs) + + sizer = wx.BoxSizer(wx.VERTICAL) + self.SetSizer(sizer) + + top_sizer = wx.BoxSizer(wx.HORIZONTAL) + sizer.Add(top_sizer, 0, wx.ALL | wx.EXPAND, 5) + + self._replace = wx.CheckBox(self, wx.ID_ANY, lang.get("program_3d_edit.fill_tool.replace")) + self._replace.Bind(wx.EVT_CHECKBOX, self._on_replace_change) + top_sizer.Add(self._replace, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) + + self._replace_mode = SimpleChoiceAny(self, sort=False) + self._replace_mode.SetItems({ + ReplaceMode.Single: lang.get("program_3d_edit.fill_tool.replace_mode.single"), + ReplaceMode.Sequence: lang.get("program_3d_edit.fill_tool.replace_mode.sequence"), + ReplaceMode.Map: lang.get("program_3d_edit.fill_tool.replace_mode.map"), + }) + # self.choice_1.SetSelection(0) + top_sizer.Add(self._replace_mode, 1, wx.LEFT, 5) + + self._operation_sizer = wx.BoxSizer(wx.VERTICAL) + sizer.Add(self._operation_sizer, 0, wx.EXPAND, 0) + + self._operations: List[ReplaceOperationWidget] = [] + self._add_operation() + + self.Layout() + + def _add_operation(self): + replace_operation = ReplaceOperationWidget(self) + self._operations.append(replace_operation) + self._operation_sizer.Add(replace_operation, 0, wx.EXPAND | BottomBorder, 5) + + def _on_replace_change(self, evt): + for operation in self._operations: + operation.replace(self.replace) + self.GetTopLevelParent().Layout() + + @property + def replace(self) -> bool: + """Is the replace check box ticked.""" + return self._replace.GetValue() + + @property + def replace_mode(self) -> ReplaceMode: + return self._replace_mode.GetCurrentObject() diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/replace_widget.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/replace_widget.py new file mode 100644 index 00000000..e3940f8b --- /dev/null +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/replace_widget.py @@ -0,0 +1,231 @@ +from typing import List, Iterable, Tuple +import wx +from wx.lib import newevent + +from amulet.api.block import Block, UniversalAirBlock + +LeftRightBorder = wx.LEFT | wx.RIGHT +BottomBorder = LeftRightBorder | wx.BOTTOM + +BlockCloseEvent, EVT_BLOCK_CLOSE = newevent.NewEvent() + + +class BlockPickButton(wx.Button): + def __init__(self, parent: wx.Window, block: Block, **kwargs): + super().__init__(parent, label=block.full_blockstate, **kwargs) + self._block = block + + @property + def block(self) -> Block: + return self._block + + @block.setter + def block(self, block: Block): + if not isinstance(block, Block): + raise ValueError(f"block must be a Block instance.") + self._block = block + self.SetLabel(block.full_blockstate) + + +class BlockEntry(wx.Panel): + def __init__(self, parent: wx.Window, block: Block, show_weight=False, **kwargs): + super().__init__(parent, **kwargs) + sizer = wx.BoxSizer(wx.HORIZONTAL) + self.SetSizer(sizer) + self._block_button = BlockPickButton(self, block) + sizer.Add(self._block_button, 1) + self._weight = wx.SpinCtrlDouble(self, initial=100.0, min=0.0, max=100.0) + self._weight.SetDigits(2) + sizer.Add(self._weight) + self._close_button = wx.Button(self, label="❌") + self._close_button.Bind(wx.EVT_BUTTON, self._on_close) + self._close_button.SetMinSize((28, 28)) + sizer.Add(self._close_button) + self.show_weight(show_weight) + + def _on_close(self, evt): + evt2 = BlockCloseEvent() + evt2.SetEventObject(self) + wx.PostEvent(self, evt2) + + @property + def block(self) -> Block: + """The universal block contained within this widget.""" + return self._block_button.block + + @block.setter + def block(self, block: Block): + self._block_button.block = block + + @property + def weight(self) -> float: + """The weighting value for this entry. May be unused.""" + return self._weight.GetValue() + + @weight.setter + def weight(self, weight: float): + self._weight.SetValue(weight) + + def show_weight(self, show: bool = True): + """Show or hide the weight entry.""" + self._weight.Show(show) + self.Layout() + + def show_close(self, show: bool = True): + """Show or hide the close button.""" + self._close_button.Show(show) + self.Layout() + + def enable_close(self, enable: bool = True): + self._close_button.Enable(enable) + + +class BaseBlockContainer(wx.Panel): + def __init__(self, parent: wx.Window, **kwargs): + super().__init__(parent, **kwargs) + + sizer = wx.BoxSizer(wx.VERTICAL) + self.SetSizer(sizer) + + top_sizer = wx.BoxSizer(wx.HORIZONTAL) + sizer.Add(top_sizer, 0, wx.EXPAND | wx.ALL, 5) + find_label = wx.StaticText(self, label=self.name, style=wx.ALIGN_CENTER_HORIZONTAL) + top_sizer.Add(find_label, 1, wx.ALIGN_CENTER_VERTICAL) + + self._add_button = wx.Button(self, label="➕") + self._add_button.Bind(wx.EVT_BUTTON, lambda evt: self._add_block()) + self._add_button.SetMinSize((28, 28)) + top_sizer.Add(self._add_button) + + self._block_sizer = wx.BoxSizer(wx.VERTICAL) + sizer.Add(self._block_sizer, 0, wx.EXPAND, 0) + + self._blocks: List[BlockEntry] = [] + self._add_block() + + def _add_block(self): + block = BlockEntry(self, UniversalAirBlock) + block.Bind(EVT_BLOCK_CLOSE, self._remove_block) + self._block_sizer.Add(block, 0, wx.EXPAND | BottomBorder, 5) + self._blocks.append(block) + if len(self._blocks) == 1: + self._blocks[-1].enable_close(False) + if len(self._blocks) == 2: + for block in self._blocks: + block.enable_close() + elif len(self._blocks) >= 2: + self._blocks[-1].enable_close() + self.GetTopLevelParent().Layout() + + def _remove_block(self, evt): + window = evt.GetEventObject() + if isinstance(window, BlockEntry): + if window in self._blocks: + self._blocks.remove(window) + window.Destroy() + if len(self._blocks) == 1: + block = self._blocks[-1] + block.enable_close(False) + block.show_weight(False) + self.GetTopLevelParent().Layout() + + @property + def name(self) -> str: + raise NotImplementedError + + @property + def blocks(self) -> Tuple[Block]: + """The universal blocks contained within this widget.""" + return tuple(entry.block for entry in self._blocks) + + @blocks.setter + def blocks(self, blocks: Iterable[Block]): + blocks = tuple(blocks) + while len(self._blocks) > len(blocks): + window = self._blocks.pop() + window.Destroy() + while len(self._blocks) < len(blocks): + self._add_block() + for block, window in zip(blocks, self._blocks): + window.block = block + + +class FindWidget(BaseBlockContainer): + @property + def name(self) -> str: + return "Find" + + +class FillWidget(BaseBlockContainer): + def _add_block(self): + super()._add_block() + if len(self._blocks) == 2: + for block in self._blocks: + block.show_weight() + elif len(self._blocks) >= 2: + self._blocks[-1].show_weight() + self.GetTopLevelParent().Layout() + + @property + def name(self) -> str: + return "Fill" + + @property + def weights(self) -> Tuple[float]: + """The weighting values for the blocks contained in this widget. May be unused.""" + return tuple(entry.weight for entry in self._blocks) + + +class ReplaceOperationWidget(wx.Panel): + def __init__(self, parent: wx.Window, **kwargs): + kwargs["style"] = kwargs.get("style", 0) | wx.BORDER_SIMPLE + super().__init__(parent, **kwargs) + self._replace = False + + sizer = wx.BoxSizer(wx.VERTICAL) + self.SetSizer(sizer) + + self._find = FindWidget(self) + self._find.Hide() + sizer.Add(self._find, 0, wx.EXPAND, 0) + + self._swap_button = wx.Button(self, wx.ID_ANY, u"▲ Swap ▼") + self._swap_button.Bind(wx.EVT_BUTTON, self._swap_blocks) + self._swap_button.Hide() + sizer.Add(self._swap_button, 0, wx.EXPAND | BottomBorder, 5) + + self._fill = FillWidget(self) + sizer.Add(self._fill, 0, wx.EXPAND, 0) + + self.Layout() + + def _swap_blocks(self, evt): + self.Freeze() + self._find.blocks, self._fill.blocks = self._fill.blocks, self._find.blocks + self.GetTopLevelParent().Layout() + self.Thaw() + + def replace(self, replace: bool): + self._find.Show(replace) + self._swap_button.Show(replace) + + +if __name__ == "__main__": + + def main(): + app = wx.App() + dialog = wx.Dialog(None, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER) + sizer = wx.BoxSizer() + dialog.SetSizer(sizer) + sizer.Add( + ReplaceOperationWidget(dialog), + 1, + wx.ALL | wx.EXPAND, + 5, + ) + dialog.Show() + dialog.Fit() + dialog.Bind(wx.EVT_CLOSE, lambda evt: dialog.Destroy()) + app.MainLoop() + + main() From 88951c803642d0156fce4cfafafe4ec09941a834 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Wed, 12 May 2021 10:54:20 +0100 Subject: [PATCH 012/139] Added a version select to the fill replace ui Also localised a bit of text --- amulet_map_editor/lang/en.lang | 3 + .../plugins/tools/fill_replace/__main__.py | 7 +- .../tools/fill_replace/fill_replace_widget.py | 57 ++++++-- .../tools/fill_replace/replace_widget.py | 131 ++++++++++++++++-- 4 files changed, 171 insertions(+), 27 deletions(-) diff --git a/amulet_map_editor/lang/en.lang b/amulet_map_editor/lang/en.lang index 5f753819..64266d1e 100644 --- a/amulet_map_editor/lang/en.lang +++ b/amulet_map_editor/lang/en.lang @@ -81,6 +81,9 @@ program_3d_edit.fill_tool.replace=Replace program_3d_edit.fill_tool.replace_mode.single=Single program_3d_edit.fill_tool.replace_mode.sequence=Sequence program_3d_edit.fill_tool.replace_mode.map=Map +program_3d_edit.fill_tool.swap=▲ Swap ▼ +program_3d_edit.fill_tool.find=Find +program_3d_edit.fill_tool.fill=Fill program_3d_edit.paste_tool.location_label=Location program_3d_edit.paste_tool.location_x_label=x diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/__main__.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/__main__.py index 87a8e4fe..f7c0cf56 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/__main__.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/__main__.py @@ -1,5 +1,8 @@ import wx -from amulet_map_editor.programs.edit.plugins.tools.fill_replace.fill_replace_widget import FillReplaceWidget +import PyMCTranslate +from amulet_map_editor.programs.edit.plugins.tools.fill_replace.fill_replace_widget import ( + FillReplaceWidget, +) if __name__ == "__main__": @@ -9,7 +12,7 @@ def main(): sizer = wx.BoxSizer() dialog.SetSizer(sizer) sizer.Add( - FillReplaceWidget(dialog), + FillReplaceWidget(dialog, PyMCTranslate.new_translation_manager()), 1, wx.ALL | wx.EXPAND, 5, diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_widget.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_widget.py index 49393375..cb612dee 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_widget.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_widget.py @@ -1,9 +1,19 @@ -import wx from enum import Enum from typing import List -from .replace_widget import ReplaceOperationWidget -from amulet_map_editor.api.wx.ui.simple import SimpleChoiceAny + +import wx + +import PyMCTranslate + from amulet_map_editor import lang +from amulet_map_editor.api.wx.ui.simple import SimpleChoiceAny +from amulet_map_editor.api.wx.ui.version_select import ( + VersionSelect, + VersionChangeEvent, + EVT_VERSION_CHANGE, +) + +from .replace_widget import ReplaceOperationWidget class ReplaceMode(Enum): @@ -17,25 +27,43 @@ class ReplaceMode(Enum): class FillReplaceWidget(wx.Panel): - def __init__(self, parent: wx.Window, **kwargs): + def __init__( + self, + parent: wx.Window, + translation_manager: PyMCTranslate.TranslationManager, + **kwargs + ): super().__init__(parent, **kwargs) + self._translation_manager = translation_manager sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(sizer) + self._version_select = VersionSelect(self, translation_manager) + self._version_select.Bind(EVT_VERSION_CHANGE, self._on_version_change) + sizer.Add(self._version_select, 0, wx.EXPAND) + top_sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.Add(top_sizer, 0, wx.ALL | wx.EXPAND, 5) - self._replace = wx.CheckBox(self, wx.ID_ANY, lang.get("program_3d_edit.fill_tool.replace")) + self._replace = wx.CheckBox( + self, wx.ID_ANY, lang.get("program_3d_edit.fill_tool.replace") + ) self._replace.Bind(wx.EVT_CHECKBOX, self._on_replace_change) top_sizer.Add(self._replace, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) self._replace_mode = SimpleChoiceAny(self, sort=False) - self._replace_mode.SetItems({ - ReplaceMode.Single: lang.get("program_3d_edit.fill_tool.replace_mode.single"), - ReplaceMode.Sequence: lang.get("program_3d_edit.fill_tool.replace_mode.sequence"), - ReplaceMode.Map: lang.get("program_3d_edit.fill_tool.replace_mode.map"), - }) + self._replace_mode.SetItems( + { + ReplaceMode.Single: lang.get( + "program_3d_edit.fill_tool.replace_mode.single" + ), + ReplaceMode.Sequence: lang.get( + "program_3d_edit.fill_tool.replace_mode.sequence" + ), + ReplaceMode.Map: lang.get("program_3d_edit.fill_tool.replace_mode.map"), + } + ) # self.choice_1.SetSelection(0) top_sizer.Add(self._replace_mode, 1, wx.LEFT, 5) @@ -48,10 +76,17 @@ def __init__(self, parent: wx.Window, **kwargs): self.Layout() def _add_operation(self): - replace_operation = ReplaceOperationWidget(self) + replace_operation = ReplaceOperationWidget( + self, self._translation_manager, self._version_select.version + ) self._operations.append(replace_operation) self._operation_sizer.Add(replace_operation, 0, wx.EXPAND | BottomBorder, 5) + def _on_version_change(self, evt: VersionChangeEvent): + version = evt.platform, evt.version_number, evt.force_blockstate + for op in self._operations: + op.version = version + def _on_replace_change(self, evt): for operation in self._operations: operation.replace(self.replace) diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/replace_widget.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/replace_widget.py index e3940f8b..58d74007 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/replace_widget.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/replace_widget.py @@ -2,21 +2,64 @@ import wx from wx.lib import newevent +import PyMCTranslate from amulet.api.block import Block, UniversalAirBlock +from amulet_map_editor import lang LeftRightBorder = wx.LEFT | wx.RIGHT BottomBorder = LeftRightBorder | wx.BOTTOM BlockCloseEvent, EVT_BLOCK_CLOSE = newevent.NewEvent() +VersionType = Tuple[str, Tuple[int, ...], bool] + + +def _check_version(version: VersionType): + assert isinstance(version, tuple) and len(version) == 3 + assert isinstance(version[0], str) + assert isinstance(version[1], tuple) and all(isinstance(v, int) for v in version[1]) + assert isinstance(version[2], bool) + class BlockPickButton(wx.Button): - def __init__(self, parent: wx.Window, block: Block, **kwargs): - super().__init__(parent, label=block.full_blockstate, **kwargs) + def __init__( + self, + parent: wx.Window, + translation_manager: PyMCTranslate.TranslationManager, + version: VersionType, + block: Block, + **kwargs, + ): + super().__init__(parent, **kwargs) + self._translation_manager = translation_manager + self._version = version self._block = block + self._update_button() + + @property + def version(self) -> VersionType: + return self._version + + @version.setter + def version(self, version: VersionType): + _check_version(version) + self._version = version + self._update_button() + + def _update_button(self): + platform, version, force_blockstate = self._version + block, _, _ = self._translation_manager.get_version( + platform, version + ).block.from_universal(self._block, force_blockstate=force_blockstate) + if not isinstance(block, Block): + block, _, _ = self._translation_manager.get_version( + platform, version + ).block.from_universal(UniversalAirBlock, force_blockstate=force_blockstate) + self.SetLabel(block.full_blockstate) @property def block(self) -> Block: + """The universal block object stored in this button.""" return self._block @block.setter @@ -28,11 +71,19 @@ def block(self, block: Block): class BlockEntry(wx.Panel): - def __init__(self, parent: wx.Window, block: Block, show_weight=False, **kwargs): + def __init__( + self, + parent: wx.Window, + translation_manager: PyMCTranslate.TranslationManager, + version: VersionType, + block: Block, + show_weight=False, + **kwargs, + ): super().__init__(parent, **kwargs) sizer = wx.BoxSizer(wx.HORIZONTAL) self.SetSizer(sizer) - self._block_button = BlockPickButton(self, block) + self._block_button = BlockPickButton(self, translation_manager, version, block) sizer.Add(self._block_button, 1) self._weight = wx.SpinCtrlDouble(self, initial=100.0, min=0.0, max=100.0) self._weight.SetDigits(2) @@ -43,6 +94,14 @@ def __init__(self, parent: wx.Window, block: Block, show_weight=False, **kwargs) sizer.Add(self._close_button) self.show_weight(show_weight) + @property + def version(self) -> VersionType: + return self._block_button.version + + @version.setter + def version(self, version: VersionType): + self._block_button.version = version + def _on_close(self, evt): evt2 = BlockCloseEvent() evt2.SetEventObject(self) @@ -81,15 +140,25 @@ def enable_close(self, enable: bool = True): class BaseBlockContainer(wx.Panel): - def __init__(self, parent: wx.Window, **kwargs): + def __init__( + self, + parent: wx.Window, + translation_manager: PyMCTranslate.TranslationManager, + version: VersionType, + **kwargs, + ): super().__init__(parent, **kwargs) + self._translation_manager = translation_manager + self._version = version sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(sizer) top_sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.Add(top_sizer, 0, wx.EXPAND | wx.ALL, 5) - find_label = wx.StaticText(self, label=self.name, style=wx.ALIGN_CENTER_HORIZONTAL) + find_label = wx.StaticText( + self, label=self.name, style=wx.ALIGN_CENTER_HORIZONTAL + ) top_sizer.Add(find_label, 1, wx.ALIGN_CENTER_VERTICAL) self._add_button = wx.Button(self, label="➕") @@ -103,8 +172,21 @@ def __init__(self, parent: wx.Window, **kwargs): self._blocks: List[BlockEntry] = [] self._add_block() + @property + def version(self) -> VersionType: + return self._version + + @version.setter + def version(self, version: VersionType): + _check_version(version) + self._version = version + for block in self._blocks: + block.version = version + def _add_block(self): - block = BlockEntry(self, UniversalAirBlock) + block = BlockEntry( + self, self._translation_manager, self._version, UniversalAirBlock + ) block.Bind(EVT_BLOCK_CLOSE, self._remove_block) self._block_sizer.Add(block, 0, wx.EXPAND | BottomBorder, 5) self._blocks.append(block) @@ -153,7 +235,7 @@ def blocks(self, blocks: Iterable[Block]): class FindWidget(BaseBlockContainer): @property def name(self) -> str: - return "Find" + return lang.get("program_3d_edit.fill_tool.find") class FillWidget(BaseBlockContainer): @@ -168,7 +250,7 @@ def _add_block(self): @property def name(self) -> str: - return "Fill" + return lang.get("program_3d_edit.fill_tool.fill") @property def weights(self) -> Tuple[float]: @@ -177,7 +259,13 @@ def weights(self) -> Tuple[float]: class ReplaceOperationWidget(wx.Panel): - def __init__(self, parent: wx.Window, **kwargs): + def __init__( + self, + parent: wx.Window, + translation_manager: PyMCTranslate.TranslationManager, + version: VersionType, + **kwargs, + ): kwargs["style"] = kwargs.get("style", 0) | wx.BORDER_SIMPLE super().__init__(parent, **kwargs) self._replace = False @@ -185,20 +273,31 @@ def __init__(self, parent: wx.Window, **kwargs): sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(sizer) - self._find = FindWidget(self) + self._find = FindWidget(self, translation_manager, version) self._find.Hide() sizer.Add(self._find, 0, wx.EXPAND, 0) - self._swap_button = wx.Button(self, wx.ID_ANY, u"▲ Swap ▼") + self._swap_button = wx.Button( + self, wx.ID_ANY, lang.get("program_3d_edit.fill_tool.swap") + ) self._swap_button.Bind(wx.EVT_BUTTON, self._swap_blocks) self._swap_button.Hide() sizer.Add(self._swap_button, 0, wx.EXPAND | BottomBorder, 5) - self._fill = FillWidget(self) + self._fill = FillWidget(self, translation_manager, version) sizer.Add(self._fill, 0, wx.EXPAND, 0) self.Layout() + @property + def version(self) -> VersionType: + return self._fill.version + + @version.setter + def version(self, version: VersionType): + self._find.version = version + self._fill.version = version + def _swap_blocks(self, evt): self.Freeze() self._find.blocks, self._fill.blocks = self._fill.blocks, self._find.blocks @@ -218,7 +317,11 @@ def main(): sizer = wx.BoxSizer() dialog.SetSizer(sizer) sizer.Add( - ReplaceOperationWidget(dialog), + ReplaceOperationWidget( + dialog, + PyMCTranslate.new_translation_manager(), + ("java", (1, 16, 0), False), + ), 1, wx.ALL | wx.EXPAND, 5, From 6c8068bec3a8930e62de176f8a722a3d7fa769ca Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Wed, 12 May 2021 13:40:54 +0100 Subject: [PATCH 013/139] Split properties module into a package --- .../wx/ui/block_select/properties/__init__.py | 2 + .../wx/ui/block_select/properties/events.py | 6 ++ .../{ => properties}/properties.py | 61 ++++++++++--------- 3 files changed, 41 insertions(+), 28 deletions(-) create mode 100644 amulet_map_editor/api/wx/ui/block_select/properties/__init__.py create mode 100644 amulet_map_editor/api/wx/ui/block_select/properties/events.py rename amulet_map_editor/api/wx/ui/block_select/{ => properties}/properties.py (93%) diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/__init__.py b/amulet_map_editor/api/wx/ui/block_select/properties/__init__.py new file mode 100644 index 00000000..2f536ee2 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/block_select/properties/__init__.py @@ -0,0 +1,2 @@ +from .events import PropertiesChangeEvent, EVT_PROPERTIES_CHANGE +from .properties import PropertySelect, WildcardSNBTType diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/events.py b/amulet_map_editor/api/wx/ui/block_select/properties/events.py new file mode 100644 index 00000000..0e80435e --- /dev/null +++ b/amulet_map_editor/api/wx/ui/block_select/properties/events.py @@ -0,0 +1,6 @@ +from wx.lib import newevent + +( + PropertiesChangeEvent, + EVT_PROPERTIES_CHANGE, +) = newevent.NewCommandEvent() # the properties changed diff --git a/amulet_map_editor/api/wx/ui/block_select/properties.py b/amulet_map_editor/api/wx/ui/block_select/properties/properties.py similarity index 93% rename from amulet_map_editor/api/wx/ui/block_select/properties.py rename to amulet_map_editor/api/wx/ui/block_select/properties/properties.py index 9dd7f740..90d4815b 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/properties.py @@ -1,5 +1,4 @@ import wx -from wx.lib import newevent from typing import Tuple, Dict, Optional, List, Union import weakref @@ -7,15 +6,10 @@ import amulet_nbt from amulet_nbt import SNBTType from amulet.api.block import PropertyDataTypes, PropertyType - -WildcardSNBTType = Union[SNBTType, str] - from amulet_map_editor.api.image import ADD_ICON, SUBTRACT_ICON +from .events import PropertiesChangeEvent -( - PropertiesChangeEvent, - EVT_PROPERTIES_CHANGE, -) = newevent.NewCommandEvent() # the properties changed +WildcardSNBTType = Union[SNBTType, str] class PropertySelect(wx.Panel): @@ -361,26 +355,37 @@ def properties(self, properties: Dict[str, SNBTType]): def main(): translation_manager = PyMCTranslate.new_translation_manager() app = wx.App() - dialog = wx.Dialog(None) - sizer = wx.BoxSizer() - dialog.SetSizer(sizer) - sizer.Add( - PropertySelect( - dialog, - translation_manager, - "java", - (1, 16, 0), - False, - "minecraft", - "oak_fence", - ), - 1, - wx.ALL, - 5, - ) - dialog.Bind(wx.EVT_CLOSE, lambda evt: dialog.Destroy()) - dialog.Show() - dialog.Fit() + for cls in ( + PropertySelect, + lambda *args: PropertySelect(*args, wildcard_mode=True), + ): + dialog = wx.Dialog(None) + sizer = wx.BoxSizer() + dialog.SetSizer(sizer) + sizer.Add( + cls( + dialog, + translation_manager, + "java", + (1, 16, 0), + False, + "minecraft", + "oak_fence", + ), + 1, + wx.ALL, + 5, + ) + + def get_on_close(dialog_): + def on_close(evt): + dialog_.Destroy() + + return on_close + + dialog.Bind(wx.EVT_CLOSE, get_on_close(dialog)) + dialog.Show() + dialog.Fit() app.MainLoop() main() From 1ff9db1199bee13862ce73a8c442e488d3783107 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Wed, 12 May 2021 14:00:26 +0100 Subject: [PATCH 014/139] Moved the properties demo code into a main file --- .../wx/ui/block_select/properties/__main__.py | 43 +++++++++++++++++++ .../ui/block_select/properties/properties.py | 41 ------------------ 2 files changed, 43 insertions(+), 41 deletions(-) create mode 100644 amulet_map_editor/api/wx/ui/block_select/properties/__main__.py diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/__main__.py b/amulet_map_editor/api/wx/ui/block_select/properties/__main__.py new file mode 100644 index 00000000..843f8b30 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/block_select/properties/__main__.py @@ -0,0 +1,43 @@ +import wx +import PyMCTranslate +from amulet_map_editor.api.wx.ui.block_select.properties import PropertySelect + +if __name__ == "__main__": + + def main(): + translation_manager = PyMCTranslate.new_translation_manager() + app = wx.App() + for block in (("minecraft", "oak_fence"), ("modded", "block")): + for cls in ( + PropertySelect, + lambda *args: PropertySelect(*args, wildcard_mode=True), + ): + dialog = wx.Dialog(None, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER) + sizer = wx.BoxSizer() + dialog.SetSizer(sizer) + sizer.Add( + cls( + dialog, + translation_manager, + "java", + (1, 16, 0), + False, + *block + ), + 1, + wx.ALL, + 5, + ) + + def get_on_close(dialog_): + def on_close(evt): + dialog_.Destroy() + + return on_close + + dialog.Bind(wx.EVT_CLOSE, get_on_close(dialog)) + dialog.Show() + dialog.Fit() + app.MainLoop() + + main() diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/properties.py b/amulet_map_editor/api/wx/ui/block_select/properties/properties.py index 90d4815b..b2e53734 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/properties.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/properties.py @@ -348,44 +348,3 @@ def properties(self, properties: Dict[str, SNBTType]): self._property_index = 0 for name, value in properties.items(): self._add_property(name, value) - - -if __name__ == "__main__": - - def main(): - translation_manager = PyMCTranslate.new_translation_manager() - app = wx.App() - for cls in ( - PropertySelect, - lambda *args: PropertySelect(*args, wildcard_mode=True), - ): - dialog = wx.Dialog(None) - sizer = wx.BoxSizer() - dialog.SetSizer(sizer) - sizer.Add( - cls( - dialog, - translation_manager, - "java", - (1, 16, 0), - False, - "minecraft", - "oak_fence", - ), - 1, - wx.ALL, - 5, - ) - - def get_on_close(dialog_): - def on_close(evt): - dialog_.Destroy() - - return on_close - - dialog.Bind(wx.EVT_CLOSE, get_on_close(dialog)) - dialog.Show() - dialog.Fit() - app.MainLoop() - - main() From a9f8a8b193d0a2aa68342d62a780f189ddbc3626 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Wed, 12 May 2021 14:01:26 +0100 Subject: [PATCH 015/139] Reformatted --- .../api/wx/ui/block_select/properties/__main__.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/__main__.py b/amulet_map_editor/api/wx/ui/block_select/properties/__main__.py index 843f8b30..4947eb78 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/__main__.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/__main__.py @@ -12,18 +12,13 @@ def main(): PropertySelect, lambda *args: PropertySelect(*args, wildcard_mode=True), ): - dialog = wx.Dialog(None, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER) + dialog = wx.Dialog( + None, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER + ) sizer = wx.BoxSizer() dialog.SetSizer(sizer) sizer.Add( - cls( - dialog, - translation_manager, - "java", - (1, 16, 0), - False, - *block - ), + cls(dialog, translation_manager, "java", (1, 16, 0), False, *block), 1, wx.ALL, 5, From b1d3f98bf24432f2364ac40d285d1bee4b24f5b1 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Wed, 12 May 2021 14:06:04 +0100 Subject: [PATCH 016/139] Added a dialog to pick the block from This adds the block define panel to a dialog and hooks it up to the block button. Formatting changes. Bug fixes. Added the operations to a scrollable panel --- .../tools/fill_replace/block_dialog.py | 67 ++++++++++ .../tools/fill_replace/fill_replace_widget.py | 17 ++- .../tools/fill_replace/replace_widget.py | 116 +++++++++--------- 3 files changed, 139 insertions(+), 61 deletions(-) create mode 100644 amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_dialog.py diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_dialog.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_dialog.py new file mode 100644 index 00000000..e1af8083 --- /dev/null +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_dialog.py @@ -0,0 +1,67 @@ +from typing import Tuple, Optional +import wx +import PyMCTranslate +from amulet.api.block import Block +from amulet.api.block_entity import BlockEntity +from amulet_map_editor.api.wx.ui.block_select import BlockDefine + + +class BlockSelectDialog(wx.Dialog): + def __init__( + self, + parent: wx.Window, + translation_manager: PyMCTranslate.TranslationManager, + block: Tuple[Block, Optional[BlockEntity]], + platform: str, + version_number: Tuple[int, ...], + force_blockstate: bool, + *args, + **kwargs + ): + # begin wxGlade: BlockSelectDialog.__init__ + kwargs["style"] = ( + kwargs.get("style", 0) | wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER + ) + super().__init__(parent, *args, **kwargs) + self.SetTitle("Pick Block") + + sizer = wx.BoxSizer(wx.VERTICAL) + self.SetSizer(sizer) + + self._block_define = BlockDefine( + self, + translation_manager, + wx.HORIZONTAL, + platform, + version_number, + force_blockstate, + ) + self._block_define.universal_block = block + sizer.Add(self._block_define, 1, wx.EXPAND | wx.LEFT | wx.TOP | wx.RIGHT, 5) + + button_sizer = wx.StdDialogButtonSizer() + sizer.Add(button_sizer, 0, wx.ALIGN_RIGHT | wx.ALL, 4) + + self.button_ok = wx.Button(self, wx.ID_OK, "") + self.button_ok.SetDefault() + button_sizer.AddButton(self.button_ok) + + self.button_cancel = wx.Button(self, wx.ID_CANCEL, "") + button_sizer.AddButton(self.button_cancel) + + button_sizer.Realize() + + sizer.Fit(self) + + self.SetAffirmativeId(self.button_ok.GetId()) + self.SetEscapeId(self.button_cancel.GetId()) + + self.Layout() + + @property + def universal_block(self) -> Tuple[Block, Optional[BlockEntity]]: + return self._block_define.universal_block + + @universal_block.setter + def universal_block(self, universal_block: Tuple[Block, Optional[BlockEntity]]): + self._block_define.universal_block = universal_block diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_widget.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_widget.py index cb612dee..c7e7aeba 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_widget.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_widget.py @@ -6,7 +6,7 @@ import PyMCTranslate from amulet_map_editor import lang -from amulet_map_editor.api.wx.ui.simple import SimpleChoiceAny +from amulet_map_editor.api.wx.ui.simple import SimpleChoiceAny, SimpleScrollablePanel from amulet_map_editor.api.wx.ui.version_select import ( VersionSelect, VersionChangeEvent, @@ -53,6 +53,7 @@ def __init__( top_sizer.Add(self._replace, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) self._replace_mode = SimpleChoiceAny(self, sort=False) + self._replace_mode.Hide() self._replace_mode.SetItems( { ReplaceMode.Single: lang.get( @@ -64,11 +65,14 @@ def __init__( ReplaceMode.Map: lang.get("program_3d_edit.fill_tool.replace_mode.map"), } ) - # self.choice_1.SetSelection(0) - top_sizer.Add(self._replace_mode, 1, wx.LEFT, 5) + top_sizer.Add( + self._replace_mode, 1, wx.LEFT | wx.RESERVE_SPACE_EVEN_IF_HIDDEN, 5 + ) + self._operation_panel = SimpleScrollablePanel(self) + sizer.Add(self._operation_panel, 1, wx.EXPAND) self._operation_sizer = wx.BoxSizer(wx.VERTICAL) - sizer.Add(self._operation_sizer, 0, wx.EXPAND, 0) + self._operation_panel.SetSizer(self._operation_sizer) self._operations: List[ReplaceOperationWidget] = [] self._add_operation() @@ -77,7 +81,9 @@ def __init__( def _add_operation(self): replace_operation = ReplaceOperationWidget( - self, self._translation_manager, self._version_select.version + self._operation_panel, + self._translation_manager, + self._version_select.version, ) self._operations.append(replace_operation) self._operation_sizer.Add(replace_operation, 0, wx.EXPAND | BottomBorder, 5) @@ -88,6 +94,7 @@ def _on_version_change(self, evt: VersionChangeEvent): op.version = version def _on_replace_change(self, evt): + self._replace_mode.Show(self.replace) for operation in self._operations: operation.replace(self.replace) self.GetTopLevelParent().Layout() diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/replace_widget.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/replace_widget.py index 58d74007..101433ad 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/replace_widget.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/replace_widget.py @@ -1,10 +1,12 @@ -from typing import List, Iterable, Tuple +from typing import List, Iterable, Tuple, Optional import wx from wx.lib import newevent import PyMCTranslate from amulet.api.block import Block, UniversalAirBlock +from amulet.api.block_entity import BlockEntity from amulet_map_editor import lang +from .block_dialog import BlockSelectDialog LeftRightBorder = wx.LEFT | wx.RIGHT BottomBorder = LeftRightBorder | wx.BOTTOM @@ -13,6 +15,8 @@ VersionType = Tuple[str, Tuple[int, ...], bool] +BlockStorage = Tuple[Block, Optional[BlockEntity]] + def _check_version(version: VersionType): assert isinstance(version, tuple) and len(version) == 3 @@ -27,7 +31,7 @@ def __init__( parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager, version: VersionType, - block: Block, + block: BlockStorage, **kwargs, ): super().__init__(parent, **kwargs) @@ -35,6 +39,7 @@ def __init__( self._version = version self._block = block self._update_button() + self.Bind(wx.EVT_BUTTON, self._on_press) @property def version(self) -> VersionType: @@ -46,28 +51,50 @@ def version(self, version: VersionType): self._version = version self._update_button() + def _on_press(self, evt): + platform, verison_number, force_blockstate = self._version + dialog = BlockSelectDialog( + self, + self._translation_manager, + self._block, + platform, + verison_number, + force_blockstate, + ) + response = dialog.ShowModal() + if response == wx.ID_OK: + self.block = dialog.universal_block + dialog.Destroy() + def _update_button(self): platform, version, force_blockstate = self._version - block, _, _ = self._translation_manager.get_version( + block, block_entity = self._block + block, block_entity, _ = self._translation_manager.get_version( platform, version - ).block.from_universal(self._block, force_blockstate=force_blockstate) + ).block.from_universal(block, block_entity, force_blockstate=force_blockstate) if not isinstance(block, Block): - block, _, _ = self._translation_manager.get_version( + block, block_entity, _ = self._translation_manager.get_version( platform, version ).block.from_universal(UniversalAirBlock, force_blockstate=force_blockstate) self.SetLabel(block.full_blockstate) @property - def block(self) -> Block: + def block(self) -> BlockStorage: """The universal block object stored in this button.""" return self._block @block.setter - def block(self, block: Block): - if not isinstance(block, Block): - raise ValueError(f"block must be a Block instance.") + def block(self, block: BlockStorage): + if not ( + isinstance(block, tuple) + and len(block) == 2 + and isinstance(block[0], Block) + and block[1] is None + or (isinstance(block[1], BlockEntity)) + ): + raise ValueError(f"block must be a tuple of Block and BlockEntity.") self._block = block - self.SetLabel(block.full_blockstate) + self._update_button() class BlockEntry(wx.Panel): @@ -76,7 +103,7 @@ def __init__( parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager, version: VersionType, - block: Block, + block: BlockStorage, show_weight=False, **kwargs, ): @@ -108,12 +135,12 @@ def _on_close(self, evt): wx.PostEvent(self, evt2) @property - def block(self) -> Block: + def block(self) -> BlockStorage: """The universal block contained within this widget.""" return self._block_button.block @block.setter - def block(self, block: Block): + def block(self, block: BlockStorage): self._block_button.block = block @property @@ -155,7 +182,7 @@ def __init__( self.SetSizer(sizer) top_sizer = wx.BoxSizer(wx.HORIZONTAL) - sizer.Add(top_sizer, 0, wx.EXPAND | wx.ALL, 5) + sizer.Add(top_sizer, 0, wx.EXPAND, 5) find_label = wx.StaticText( self, label=self.name, style=wx.ALIGN_CENTER_HORIZONTAL ) @@ -185,10 +212,10 @@ def version(self, version: VersionType): def _add_block(self): block = BlockEntry( - self, self._translation_manager, self._version, UniversalAirBlock + self, self._translation_manager, self._version, (UniversalAirBlock, None) ) block.Bind(EVT_BLOCK_CLOSE, self._remove_block) - self._block_sizer.Add(block, 0, wx.EXPAND | BottomBorder, 5) + self._block_sizer.Add(block, 0, wx.EXPAND | wx.TOP, 5) self._blocks.append(block) if len(self._blocks) == 1: self._blocks[-1].enable_close(False) @@ -202,30 +229,32 @@ def _add_block(self): def _remove_block(self, evt): window = evt.GetEventObject() if isinstance(window, BlockEntry): - if window in self._blocks: - self._blocks.remove(window) - window.Destroy() - if len(self._blocks) == 1: - block = self._blocks[-1] - block.enable_close(False) - block.show_weight(False) - self.GetTopLevelParent().Layout() + self._destroy_window(window) + + def _destroy_window(self, window: BlockEntry): + if window in self._blocks: + self._blocks.remove(window) + window.Destroy() + if len(self._blocks) == 1: + block = self._blocks[-1] + block.enable_close(False) + block.show_weight(False) + self.GetTopLevelParent().Layout() @property def name(self) -> str: raise NotImplementedError @property - def blocks(self) -> Tuple[Block]: + def blocks(self) -> Tuple[BlockStorage, ...]: """The universal blocks contained within this widget.""" return tuple(entry.block for entry in self._blocks) @blocks.setter - def blocks(self, blocks: Iterable[Block]): + def blocks(self, blocks: Iterable[BlockStorage]): blocks = tuple(blocks) while len(self._blocks) > len(blocks): - window = self._blocks.pop() - window.Destroy() + self._destroy_window(self._blocks[-1]) while len(self._blocks) < len(blocks): self._add_block() for block, window in zip(blocks, self._blocks): @@ -275,17 +304,17 @@ def __init__( self._find = FindWidget(self, translation_manager, version) self._find.Hide() - sizer.Add(self._find, 0, wx.EXPAND, 0) + sizer.Add(self._find, 0, wx.EXPAND | wx.ALL, 5) self._swap_button = wx.Button( self, wx.ID_ANY, lang.get("program_3d_edit.fill_tool.swap") ) self._swap_button.Bind(wx.EVT_BUTTON, self._swap_blocks) self._swap_button.Hide() - sizer.Add(self._swap_button, 0, wx.EXPAND | BottomBorder, 5) + sizer.Add(self._swap_button, 0, wx.EXPAND | wx.ALL, 5) self._fill = FillWidget(self, translation_manager, version) - sizer.Add(self._fill, 0, wx.EXPAND, 0) + sizer.Add(self._fill, 0, wx.EXPAND | wx.ALL, 5) self.Layout() @@ -307,28 +336,3 @@ def _swap_blocks(self, evt): def replace(self, replace: bool): self._find.Show(replace) self._swap_button.Show(replace) - - -if __name__ == "__main__": - - def main(): - app = wx.App() - dialog = wx.Dialog(None, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER) - sizer = wx.BoxSizer() - dialog.SetSizer(sizer) - sizer.Add( - ReplaceOperationWidget( - dialog, - PyMCTranslate.new_translation_manager(), - ("java", (1, 16, 0), False), - ), - 1, - wx.ALL | wx.EXPAND, - 5, - ) - dialog.Show() - dialog.Fit() - dialog.Bind(wx.EVT_CLOSE, lambda evt: dialog.Destroy()) - app.MainLoop() - - main() From c367aa9eede3fe6ec8479d152603dd8632ef5c31 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Sat, 29 May 2021 18:31:25 +0100 Subject: [PATCH 017/139] Added demo code for the UI elements When run this will show all of the UI elements making testing easier --- .gitignore | 4 +- .../api/wx/ui/biome_select/biome_define.py | 34 ++++++++------ .../api/wx/ui/biome_select/biome_select.py | 40 ++++++++++------- .../api/wx/ui/biome_select/demo.py | 26 +++++++++++ .../api/wx/ui/block_select/block_define.py | 34 ++++++++------ .../api/wx/ui/block_select/block_select.py | 34 ++++++++------ .../api/wx/ui/block_select/demo.py | 30 +++++++++++++ .../wx/ui/block_select/multi_block_define.py | 24 ++++++---- .../wx/ui/block_select/properties/__main__.py | 38 ---------------- .../api/wx/ui/block_select/properties/demo.py | 22 +++++++++ .../ui/block_select/properties/properties.py | 32 +++++++++++++ amulet_map_editor/api/wx/ui/demo.py | 26 +++++++++++ amulet_map_editor/api/wx/ui/nbt_editor.py | 25 +++++++---- amulet_map_editor/api/wx/ui/select_world.py | 45 +++++++++++++++---- .../api/wx/ui/version_select/__main__.py | 31 ------------- .../api/wx/ui/version_select/demo.py | 22 +++++++++ .../wx/ui/version_select/platform_select.py | 20 +++++++++ .../wx/ui/version_select/version_select.py | 26 +++++++++++ 18 files changed, 363 insertions(+), 150 deletions(-) create mode 100644 amulet_map_editor/api/wx/ui/biome_select/demo.py create mode 100644 amulet_map_editor/api/wx/ui/block_select/demo.py delete mode 100644 amulet_map_editor/api/wx/ui/block_select/properties/__main__.py create mode 100644 amulet_map_editor/api/wx/ui/block_select/properties/demo.py create mode 100644 amulet_map_editor/api/wx/ui/demo.py delete mode 100644 amulet_map_editor/api/wx/ui/version_select/__main__.py create mode 100644 amulet_map_editor/api/wx/ui/version_select/demo.py diff --git a/.gitignore b/.gitignore index 4637a410..fbe19a51 100644 --- a/.gitignore +++ b/.gitignore @@ -107,5 +107,5 @@ venv.bak/ /world_temp /resource_packs /plugins -/config -/cache +*.config +**/cache diff --git a/amulet_map_editor/api/wx/ui/biome_select/biome_define.py b/amulet_map_editor/api/wx/ui/biome_select/biome_define.py index a10b687e..271eeb78 100644 --- a/amulet_map_editor/api/wx/ui/biome_select/biome_define.py +++ b/amulet_map_editor/api/wx/ui/biome_select/biome_define.py @@ -72,23 +72,31 @@ def universal_biome(self, universal_biome: str): ).biome.from_universal(universal_biome) +def demo(): + """ + Show a demo version of the UI. + An app instance must be created first. + """ + translation_manager = PyMCTranslate.new_translation_manager() + dialog = wx.Dialog(None, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT) + sizer = wx.BoxSizer() + dialog.SetSizer(sizer) + sizer.Add( + BiomeDefine(dialog, translation_manager, wx.HORIZONTAL), + 1, + wx.ALL | wx.EXPAND, + 5, + ) + dialog.Show() + dialog.Fit() + dialog.Bind(wx.EVT_CLOSE, lambda evt: dialog.Destroy()) + + if __name__ == "__main__": def main(): app = wx.App() - translation_manager = PyMCTranslate.new_translation_manager() - dialog = wx.Dialog(None, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER) - sizer = wx.BoxSizer() - dialog.SetSizer(sizer) - sizer.Add( - BiomeDefine(dialog, translation_manager, wx.HORIZONTAL), - 1, - wx.ALL | wx.EXPAND, - 5, - ) - dialog.Show() - dialog.Fit() - dialog.Bind(wx.EVT_CLOSE, lambda evt: dialog.Destroy()) + demo() app.MainLoop() main() diff --git a/amulet_map_editor/api/wx/ui/biome_select/biome_select.py b/amulet_map_editor/api/wx/ui/biome_select/biome_select.py index 7f9e00c1..125a8e97 100644 --- a/amulet_map_editor/api/wx/ui/biome_select/biome_select.py +++ b/amulet_map_editor/api/wx/ui/biome_select/biome_select.py @@ -1,3 +1,4 @@ +import wx from amulet_map_editor.api.wx.ui.base_select import BaseSelect @@ -34,26 +35,33 @@ def _populate_item_name(self): self._list_box.SetItems(self._names) +def demo(): + """ + Show a demo version of the UI. + An app instance must be created first. + """ + import PyMCTranslate + + translation_manager = PyMCTranslate.new_translation_manager() + dialog = wx.Dialog(None, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT) + sizer = wx.BoxSizer() + dialog.SetSizer(sizer) + sizer.Add( + BiomeSelect(dialog, translation_manager, "java", (1, 16, 0), False), + 1, + wx.ALL | wx.EXPAND, + 5, + ) + dialog.Show() + dialog.Fit() + dialog.Bind(wx.EVT_CLOSE, lambda evt: dialog.Destroy()) + + if __name__ == "__main__": def main(): - import wx - import PyMCTranslate - app = wx.App() - translation_manager = PyMCTranslate.new_translation_manager() - dialog = wx.Dialog(None, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER) - sizer = wx.BoxSizer() - dialog.SetSizer(sizer) - sizer.Add( - BiomeSelect(dialog, translation_manager, "java", (1, 16, 0), False), - 1, - wx.ALL | wx.EXPAND, - 5, - ) - dialog.Show() - dialog.Fit() - dialog.Bind(wx.EVT_CLOSE, lambda evt: dialog.Destroy()) + demo() app.MainLoop() main() diff --git a/amulet_map_editor/api/wx/ui/biome_select/demo.py b/amulet_map_editor/api/wx/ui/biome_select/demo.py new file mode 100644 index 00000000..5d6c4c57 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/biome_select/demo.py @@ -0,0 +1,26 @@ +import wx +from amulet_map_editor.api.wx.ui.biome_select.biome_select import ( + demo as biome_select_demo, +) +from amulet_map_editor.api.wx.ui.biome_select.biome_define import ( + demo as biome_define_demo, +) + + +def demo(): + """ + Show a demo version of the UI. + An app instance must be created first. + """ + biome_select_demo() + biome_define_demo() + + +if __name__ == "__main__": + + def main(): + app = wx.App() + demo() + app.MainLoop() + + main() diff --git a/amulet_map_editor/api/wx/ui/block_select/block_define.py b/amulet_map_editor/api/wx/ui/block_select/block_define.py index 1281e513..3af1c05c 100644 --- a/amulet_map_editor/api/wx/ui/block_select/block_define.py +++ b/amulet_map_editor/api/wx/ui/block_select/block_define.py @@ -157,23 +157,31 @@ def universal_block(self, universal_block: Tuple[Block, Optional[BlockEntity]]): self.block_entity = v_block_entity +def demo(): + """ + Show a demo version of the UI. + An app instance must be created first. + """ + translation_manager = PyMCTranslate.new_translation_manager() + dialog = wx.Dialog(None, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT) + sizer = wx.BoxSizer() + dialog.SetSizer(sizer) + sizer.Add( + BlockDefine(dialog, translation_manager, wx.HORIZONTAL), + 1, + wx.ALL | wx.EXPAND, + 5, + ) + dialog.Show() + dialog.Fit() + dialog.Bind(wx.EVT_CLOSE, lambda evt: dialog.Destroy()) + + if __name__ == "__main__": def main(): app = wx.App() - translation_manager = PyMCTranslate.new_translation_manager() - dialog = wx.Dialog(None, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER) - sizer = wx.BoxSizer() - dialog.SetSizer(sizer) - sizer.Add( - BlockDefine(dialog, translation_manager, wx.HORIZONTAL), - 1, - wx.ALL | wx.EXPAND, - 5, - ) - dialog.Show() - dialog.Fit() - dialog.Bind(wx.EVT_CLOSE, lambda evt: dialog.Destroy()) + demo() app.MainLoop() main() diff --git a/amulet_map_editor/api/wx/ui/block_select/block_select.py b/amulet_map_editor/api/wx/ui/block_select/block_select.py index c464c2c9..f55bea75 100644 --- a/amulet_map_editor/api/wx/ui/block_select/block_select.py +++ b/amulet_map_editor/api/wx/ui/block_select/block_select.py @@ -53,23 +53,31 @@ def _populate_item_name(self): self._list_box.SetItems(self._names) +def demo(): + """ + Show a demo version of the UI. + An app instance must be created first. + """ + translation_manager = PyMCTranslate.new_translation_manager() + dialog = wx.Dialog(None, style=wx.DEFAULT_DIALOG_STYLE | wx.DIALOG_NO_PARENT) + sizer = wx.BoxSizer() + dialog.SetSizer(sizer) + sizer.Add( + BlockSelect(dialog, translation_manager, "java", (1, 16, 0), False), + 1, + wx.ALL | wx.EXPAND, + 5, + ) + dialog.Bind(wx.EVT_CLOSE, lambda evt: dialog.Destroy()) + dialog.Show() + dialog.Fit() + + if __name__ == "__main__": def main(): app = wx.App() - translation_manager = PyMCTranslate.new_translation_manager() - dialog = wx.Dialog(None) - sizer = wx.BoxSizer() - dialog.SetSizer(sizer) - sizer.Add( - BlockSelect(dialog, translation_manager, "java", (1, 16, 0), False), - 1, - wx.ALL | wx.EXPAND, - 5, - ) - dialog.Bind(wx.EVT_CLOSE, lambda evt: dialog.Destroy()) - dialog.Show() - dialog.Fit() + demo() app.MainLoop() main() diff --git a/amulet_map_editor/api/wx/ui/block_select/demo.py b/amulet_map_editor/api/wx/ui/block_select/demo.py new file mode 100644 index 00000000..84b5ec94 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/block_select/demo.py @@ -0,0 +1,30 @@ +import wx +from amulet_map_editor.api.wx.ui.block_select.block_select import ( + demo as block_select_demo, +) +from amulet_map_editor.api.wx.ui.block_select.block_define import ( + demo as block_define_demo, +) +from amulet_map_editor.api.wx.ui.block_select.properties.demo import ( + demo as properties_demo, +) + + +def demo(): + """ + Show a demo version of the UI. + An app instance must be created first. + """ + block_select_demo() + block_define_demo() + properties_demo() + + +if __name__ == "__main__": + + def main(): + app = wx.App() + demo() + app.MainLoop() + + main() diff --git a/amulet_map_editor/api/wx/ui/block_select/multi_block_define.py b/amulet_map_editor/api/wx/ui/block_select/multi_block_define.py index 9c86088d..a3f7259f 100644 --- a/amulet_map_editor/api/wx/ui/block_select/multi_block_define.py +++ b/amulet_map_editor/api/wx/ui/block_select/multi_block_define.py @@ -188,18 +188,26 @@ def _gen_block_string(self): return f"{base}[{properties}]" if properties else base +def demo(): + """ + Show a demo version of the UI. + An app instance must be created first. + """ + translation_manager = PyMCTranslate.new_translation_manager() + dialog = wx.Dialog(None, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT) + sizer = wx.BoxSizer() + dialog.SetSizer(sizer) + sizer.Add(MultiBlockDefine(dialog, translation_manager), 1, wx.EXPAND) + dialog.Bind(wx.EVT_CLOSE, lambda evt: dialog.Destroy()) + dialog.Show() + dialog.Fit() + + if __name__ == "__main__": def main(): app = wx.App() - translation_manager = PyMCTranslate.new_translation_manager() - dialog = wx.Dialog(None, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER) - sizer = wx.BoxSizer() - dialog.SetSizer(sizer) - sizer.Add(MultiBlockDefine(dialog, translation_manager), 1, wx.EXPAND) - dialog.Bind(wx.EVT_CLOSE, lambda evt: dialog.Destroy()) - dialog.Show() - dialog.Fit() + demo() app.MainLoop() main() diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/__main__.py b/amulet_map_editor/api/wx/ui/block_select/properties/__main__.py deleted file mode 100644 index 4947eb78..00000000 --- a/amulet_map_editor/api/wx/ui/block_select/properties/__main__.py +++ /dev/null @@ -1,38 +0,0 @@ -import wx -import PyMCTranslate -from amulet_map_editor.api.wx.ui.block_select.properties import PropertySelect - -if __name__ == "__main__": - - def main(): - translation_manager = PyMCTranslate.new_translation_manager() - app = wx.App() - for block in (("minecraft", "oak_fence"), ("modded", "block")): - for cls in ( - PropertySelect, - lambda *args: PropertySelect(*args, wildcard_mode=True), - ): - dialog = wx.Dialog( - None, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER - ) - sizer = wx.BoxSizer() - dialog.SetSizer(sizer) - sizer.Add( - cls(dialog, translation_manager, "java", (1, 16, 0), False, *block), - 1, - wx.ALL, - 5, - ) - - def get_on_close(dialog_): - def on_close(evt): - dialog_.Destroy() - - return on_close - - dialog.Bind(wx.EVT_CLOSE, get_on_close(dialog)) - dialog.Show() - dialog.Fit() - app.MainLoop() - - main() diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/demo.py b/amulet_map_editor/api/wx/ui/block_select/properties/demo.py new file mode 100644 index 00000000..beb91594 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/block_select/properties/demo.py @@ -0,0 +1,22 @@ +import wx +from amulet_map_editor.api.wx.ui.block_select.properties.properties import ( + demo as properties_demo, +) + + +def demo(): + """ + Show a demo version of the UI. + An app instance must be created first. + """ + properties_demo() + + +if __name__ == "__main__": + + def main(): + app = wx.App() + demo() + app.MainLoop() + + main() diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/properties.py b/amulet_map_editor/api/wx/ui/block_select/properties/properties.py index b2e53734..8f0f5081 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/properties.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/properties.py @@ -348,3 +348,35 @@ def properties(self, properties: Dict[str, SNBTType]): self._property_index = 0 for name, value in properties.items(): self._add_property(name, value) + + +def demo(): + """ + Show a demo version of the UI. + An app instance must be created first. + """ + translation_manager = PyMCTranslate.new_translation_manager() + for block in (("minecraft", "oak_fence"), ("modded", "block")): + for cls in ( + PropertySelect, + lambda *args: PropertySelect(*args, wildcard_mode=True), + ): + dialog = wx.Dialog(None, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT) + sizer = wx.BoxSizer() + dialog.SetSizer(sizer) + sizer.Add( + cls(dialog, translation_manager, "java", (1, 16, 0), False, *block), + 1, + wx.ALL, + 5, + ) + + def get_on_close(dialog_): + def on_close(evt): + dialog_.Destroy() + + return on_close + + dialog.Bind(wx.EVT_CLOSE, get_on_close(dialog)) + dialog.Show() + dialog.Fit() diff --git a/amulet_map_editor/api/wx/ui/demo.py b/amulet_map_editor/api/wx/ui/demo.py new file mode 100644 index 00000000..2553d56a --- /dev/null +++ b/amulet_map_editor/api/wx/ui/demo.py @@ -0,0 +1,26 @@ +from amulet_map_editor.api.wx.ui.biome_select.demo import demo as biome_demo +from amulet_map_editor.api.wx.ui.block_select.demo import demo as block_demo +from amulet_map_editor.api.wx.ui.nbt_editor import demo as nbt_editor_demo +from amulet_map_editor.api.wx.ui.select_world import demo as select_world_demo +from amulet_map_editor.api.wx.ui.version_select.demo import demo as version_select_demo + + +def demo(): + """ + Show a demo version of the UI. + An app instance must be created first. + """ + biome_demo() + block_demo() + nbt_editor_demo() + select_world_demo() + version_select_demo() + + +if __name__ == "__main__": + + import wx + + app = wx.App() + demo() + app.MainLoop() diff --git a/amulet_map_editor/api/wx/ui/nbt_editor.py b/amulet_map_editor/api/wx/ui/nbt_editor.py index 814b346d..8ce9b9ad 100644 --- a/amulet_map_editor/api/wx/ui/nbt_editor.py +++ b/amulet_map_editor/api/wx/ui/nbt_editor.py @@ -13,8 +13,6 @@ nbt_resources = image.nbt -NBT_FILE = b"\x0A\x00\x0B\x68\x65\x6C\x6C\x6F\x20\x77\x6F\x72\x6C\x64\x08\x00\x04\x6E\x61\x6D\x65\x00\x09\x42\x61\x6E\x61\x6E\x72\x61\x6D\x61\x00" - class NBTRadioButton(simple.SimplePanel): def __init__(self, parent, nbt_tag_class, icon): @@ -381,12 +379,23 @@ def save(self, evt): self.Close() -if __name__ == "__main__": - import wx.lib.inspection - - app = wx.App() - wx.lib.inspection.InspectionTool().Show() +def demo(): + """ + Show a demo version of the UI. + An app instance must be created first. + """ frame = wx.Frame(None) - NBTEditor(frame, nbt.load(NBT_FILE), callback=lambda nbt_data: print(nbt_data)) + NBTEditor( + frame, + nbt.load( + b"\x0A\x00\x0B\x68\x65\x6C\x6C\x6F\x20\x77\x6F\x72\x6C\x64\x08\x00\x04\x6E\x61\x6D\x65\x00\x09\x42\x61\x6E\x61\x6E\x72\x61\x6D\x61\x00" + ), + callback=lambda nbt_data: print(nbt_data), + ) frame.Show() + + +if __name__ == "__main__": + app = wx.App() + demo() app.MainLoop() diff --git a/amulet_map_editor/api/wx/ui/select_world.py b/amulet_map_editor/api/wx/ui/select_world.py index 1b5564d3..267f8d00 100644 --- a/amulet_map_editor/api/wx/ui/select_world.py +++ b/amulet_map_editor/api/wx/ui/select_world.py @@ -2,7 +2,7 @@ import wx import glob from sys import platform -from typing import List, Dict, Tuple, Callable, TYPE_CHECKING +from typing import List, Dict, Tuple, Callable, TYPE_CHECKING, Optional import traceback from amulet import load_format @@ -57,7 +57,7 @@ def get_world_image(image_path: str) -> Tuple[wx.Bitmap, int]: or world_images[image_path][0] != os.stat(image_path)[8] ): img = wx.Image(image_path, wx.BITMAP_TYPE_ANY) - width = min((img.GetWidth() / img.GetHeight()) * 128, 300) + width = int(min((img.GetWidth() / img.GetHeight()) * 128, 300)) world_images[image_path] = ( os.stat(image_path)[8], @@ -327,17 +327,23 @@ def _update_recent(self, path): self._open_world_callback(path) +WORLD_SELECT_DIALOG_STYLE = wx.DEFAULT_DIALOG_STYLE | wx.MAXIMIZE_BOX | wx.TAB_TRAVERSAL | wx.CLIP_CHILDREN | wx.RESIZE_BORDER + + @preserve_ui_preferences class WorldSelectDialog(wx.Dialog): - def __init__(self, parent: wx.Window, open_world_callback: Callable[[str], None]): + def __init__(self, parent: Optional[wx.Window], open_world_callback: Callable[[str], None], **kwargs): + if isinstance(parent, wx.Window): + size = wx.Size(*[int(s * 0.95) for s in parent.GetSize()]) + else: + size = wx.DefaultSize super().__init__( parent, title=lang.get("select_world.title"), pos=wx.Point(50, 50), - size=wx.Size(*[int(s * 0.95) for s in parent.GetSize()]), - style=wx.CAPTION | wx.CLOSE_BOX | wx.MAXIMIZE_BOX - # | wx.MAXIMIZE - | wx.SYSTEM_MENU | wx.TAB_TRAVERSAL | wx.CLIP_CHILDREN | wx.RESIZE_BORDER, + size=size, + style=kwargs.pop("style", WORLD_SELECT_DIALOG_STYLE), + **kwargs ) self.Bind(wx.EVT_CLOSE, self._hide_event) @@ -349,7 +355,8 @@ def _run_callback(self, path): self._open_world_callback(path) def _hide_event(self, evt): - self._close() + if self.IsModal(): + self.EndModal(0) evt.Skip() def _close(self): @@ -357,3 +364,25 @@ def _close(self): self.EndModal(0) else: self.Close() + + +def demo(): + """ + Show a demo version of the UI. + An app instance must be created first. + """ + def open_world_callback(path): + print(f"Open world {path}") + dialog = WorldSelectDialog( + None, + open_world_callback, + style=WORLD_SELECT_DIALOG_STYLE | wx.DIALOG_NO_PARENT, + ) + dialog.Bind(wx.EVT_CLOSE, lambda evt: dialog.Destroy()) + dialog.Show() + + +if __name__ == "__main__": + app = wx.App() + demo() + app.MainLoop() diff --git a/amulet_map_editor/api/wx/ui/version_select/__main__.py b/amulet_map_editor/api/wx/ui/version_select/__main__.py deleted file mode 100644 index d46c1465..00000000 --- a/amulet_map_editor/api/wx/ui/version_select/__main__.py +++ /dev/null @@ -1,31 +0,0 @@ -from amulet_map_editor.api.wx.ui.version_select import PlatformSelect, VersionSelect - -if __name__ == "__main__": - import wx - import PyMCTranslate - - def main(): - translation_manager = PyMCTranslate.new_translation_manager() - app = wx.App() - for cls in ( - PlatformSelect, - lambda *args: VersionSelect(*args, show_force_blockstate=False), - VersionSelect, - ): - dialog = wx.Dialog(None) - sizer = wx.BoxSizer() - dialog.SetSizer(sizer) - sizer.Add(cls(dialog, translation_manager), 0, wx.ALL, 5) - dialog.Show() - dialog.Fit() - - def get_on_close(dialog_): - def on_close(evt): - dialog_.Destroy() - - return on_close - - dialog.Bind(wx.EVT_CLOSE, get_on_close(dialog)) - app.MainLoop() - - main() diff --git a/amulet_map_editor/api/wx/ui/version_select/demo.py b/amulet_map_editor/api/wx/ui/version_select/demo.py new file mode 100644 index 00000000..aa2eec94 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/version_select/demo.py @@ -0,0 +1,22 @@ +import wx +from amulet_map_editor.api.wx.ui.version_select.platform_select import demo as platform_demo +from amulet_map_editor.api.wx.ui.version_select.version_select import demo as version_demo + + +def demo(): + """ + Show a demo version of the UI. + An app instance must be created first. + """ + platform_demo() + version_demo() + + +if __name__ == "__main__": + + def main(): + app = wx.App() + demo() + app.MainLoop() + + main() diff --git a/amulet_map_editor/api/wx/ui/version_select/platform_select.py b/amulet_map_editor/api/wx/ui/version_select/platform_select.py index fe831560..dd1c6c57 100644 --- a/amulet_map_editor/api/wx/ui/version_select/platform_select.py +++ b/amulet_map_editor/api/wx/ui/version_select/platform_select.py @@ -95,3 +95,23 @@ def _populate_platform(self): if not self._allow_vanilla: platforms = [p for p in platforms if p == "universal"] self._platform_choice.SetItems(platforms) + + +def demo(): + """ + Show a demo version of the UI. + An app instance must be created first. + """ + translation_manager = PyMCTranslate.new_translation_manager() + dialog = wx.Dialog(None, style=wx.DEFAULT_DIALOG_STYLE | wx.DIALOG_NO_PARENT) + sizer = wx.BoxSizer() + dialog.SetSizer(sizer) + sizer.Add( + PlatformSelect(dialog, translation_manager, "java"), + 1, + wx.ALL | wx.EXPAND, + 5, + ) + dialog.Bind(wx.EVT_CLOSE, lambda evt: dialog.Destroy()) + dialog.Show() + dialog.Fit() diff --git a/amulet_map_editor/api/wx/ui/version_select/version_select.py b/amulet_map_editor/api/wx/ui/version_select/version_select.py index fa46b079..1bc19f35 100644 --- a/amulet_map_editor/api/wx/ui/version_select/version_select.py +++ b/amulet_map_editor/api/wx/ui/version_select/version_select.py @@ -154,3 +154,29 @@ def _on_version_number_change(self, evt: VersionChangeEvent): self._populate_blockstate() self.force_blockstate = None evt.Skip() + + +def demo(): + """ + Show a demo version of the UI. + An app instance must be created first. + """ + translation_manager = PyMCTranslate.new_translation_manager() + for cls in ( + lambda *args: VersionSelect(*args, show_force_blockstate=False), + VersionSelect, + ): + dialog = wx.Dialog(None, style=wx.DEFAULT_DIALOG_STYLE | wx.DIALOG_NO_PARENT) + sizer = wx.BoxSizer() + dialog.SetSizer(sizer) + sizer.Add(cls(dialog, translation_manager), 0, wx.ALL, 5) + dialog.Show() + dialog.Fit() + + def get_on_close(dialog_): + def on_close(evt): + dialog_.Destroy() + + return on_close + + dialog.Bind(wx.EVT_CLOSE, get_on_close(dialog)) From d8f35e18085f644ad7a34f1e19f82fa30bb46691 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Sun, 30 May 2021 10:45:50 +0100 Subject: [PATCH 018/139] Added titles to each of the demos --- amulet_map_editor/api/wx/ui/biome_select/biome_define.py | 2 +- amulet_map_editor/api/wx/ui/biome_select/biome_select.py | 2 +- amulet_map_editor/api/wx/ui/block_select/block_define.py | 2 +- amulet_map_editor/api/wx/ui/block_select/block_select.py | 2 +- amulet_map_editor/api/wx/ui/block_select/demo.py | 4 ++++ .../api/wx/ui/block_select/multi_block_define.py | 2 +- .../api/wx/ui/block_select/properties/properties.py | 8 ++++---- .../api/wx/ui/version_select/platform_select.py | 2 +- .../api/wx/ui/version_select/version_select.py | 8 ++++---- 9 files changed, 18 insertions(+), 14 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/biome_select/biome_define.py b/amulet_map_editor/api/wx/ui/biome_select/biome_define.py index 271eeb78..71ad100a 100644 --- a/amulet_map_editor/api/wx/ui/biome_select/biome_define.py +++ b/amulet_map_editor/api/wx/ui/biome_select/biome_define.py @@ -78,7 +78,7 @@ def demo(): An app instance must be created first. """ translation_manager = PyMCTranslate.new_translation_manager() - dialog = wx.Dialog(None, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT) + dialog = wx.Dialog(None, title="BiomeDefine", style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT) sizer = wx.BoxSizer() dialog.SetSizer(sizer) sizer.Add( diff --git a/amulet_map_editor/api/wx/ui/biome_select/biome_select.py b/amulet_map_editor/api/wx/ui/biome_select/biome_select.py index 125a8e97..f8f4109a 100644 --- a/amulet_map_editor/api/wx/ui/biome_select/biome_select.py +++ b/amulet_map_editor/api/wx/ui/biome_select/biome_select.py @@ -43,7 +43,7 @@ def demo(): import PyMCTranslate translation_manager = PyMCTranslate.new_translation_manager() - dialog = wx.Dialog(None, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT) + dialog = wx.Dialog(None, title="BiomeSelect", style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT) sizer = wx.BoxSizer() dialog.SetSizer(sizer) sizer.Add( diff --git a/amulet_map_editor/api/wx/ui/block_select/block_define.py b/amulet_map_editor/api/wx/ui/block_select/block_define.py index 3af1c05c..ae29547a 100644 --- a/amulet_map_editor/api/wx/ui/block_select/block_define.py +++ b/amulet_map_editor/api/wx/ui/block_select/block_define.py @@ -163,7 +163,7 @@ def demo(): An app instance must be created first. """ translation_manager = PyMCTranslate.new_translation_manager() - dialog = wx.Dialog(None, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT) + dialog = wx.Dialog(None, title="BlockDefine", style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT) sizer = wx.BoxSizer() dialog.SetSizer(sizer) sizer.Add( diff --git a/amulet_map_editor/api/wx/ui/block_select/block_select.py b/amulet_map_editor/api/wx/ui/block_select/block_select.py index f55bea75..ea051f33 100644 --- a/amulet_map_editor/api/wx/ui/block_select/block_select.py +++ b/amulet_map_editor/api/wx/ui/block_select/block_select.py @@ -59,7 +59,7 @@ def demo(): An app instance must be created first. """ translation_manager = PyMCTranslate.new_translation_manager() - dialog = wx.Dialog(None, style=wx.DEFAULT_DIALOG_STYLE | wx.DIALOG_NO_PARENT) + dialog = wx.Dialog(None, title="BlockSelect", style=wx.DEFAULT_DIALOG_STYLE | wx.DIALOG_NO_PARENT) sizer = wx.BoxSizer() dialog.SetSizer(sizer) sizer.Add( diff --git a/amulet_map_editor/api/wx/ui/block_select/demo.py b/amulet_map_editor/api/wx/ui/block_select/demo.py index 84b5ec94..bdada46a 100644 --- a/amulet_map_editor/api/wx/ui/block_select/demo.py +++ b/amulet_map_editor/api/wx/ui/block_select/demo.py @@ -5,6 +5,9 @@ from amulet_map_editor.api.wx.ui.block_select.block_define import ( demo as block_define_demo, ) +from amulet_map_editor.api.wx.ui.block_select.multi_block_define import ( + demo as multi_block_define_demo, +) from amulet_map_editor.api.wx.ui.block_select.properties.demo import ( demo as properties_demo, ) @@ -17,6 +20,7 @@ def demo(): """ block_select_demo() block_define_demo() + multi_block_define_demo() properties_demo() diff --git a/amulet_map_editor/api/wx/ui/block_select/multi_block_define.py b/amulet_map_editor/api/wx/ui/block_select/multi_block_define.py index a3f7259f..1abbb99c 100644 --- a/amulet_map_editor/api/wx/ui/block_select/multi_block_define.py +++ b/amulet_map_editor/api/wx/ui/block_select/multi_block_define.py @@ -194,7 +194,7 @@ def demo(): An app instance must be created first. """ translation_manager = PyMCTranslate.new_translation_manager() - dialog = wx.Dialog(None, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT) + dialog = wx.Dialog(None, title="MultiBlockDefine", style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT) sizer = wx.BoxSizer() dialog.SetSizer(sizer) sizer.Add(MultiBlockDefine(dialog, translation_manager), 1, wx.EXPAND) diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/properties.py b/amulet_map_editor/api/wx/ui/block_select/properties/properties.py index 8f0f5081..3eba77e9 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/properties.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/properties.py @@ -357,11 +357,11 @@ def demo(): """ translation_manager = PyMCTranslate.new_translation_manager() for block in (("minecraft", "oak_fence"), ("modded", "block")): - for cls in ( - PropertySelect, - lambda *args: PropertySelect(*args, wildcard_mode=True), + for cls, title in ( + (PropertySelect, f"PropertySelect with block {block[0]}:{block[1]}"), + (lambda *args: PropertySelect(*args, wildcard_mode=True), f"PropertySelect in wildcard mode with block {block[0]}:{block[1]}"), ): - dialog = wx.Dialog(None, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT) + dialog = wx.Dialog(None, title=title, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT) sizer = wx.BoxSizer() dialog.SetSizer(sizer) sizer.Add( diff --git a/amulet_map_editor/api/wx/ui/version_select/platform_select.py b/amulet_map_editor/api/wx/ui/version_select/platform_select.py index dd1c6c57..a73a5d41 100644 --- a/amulet_map_editor/api/wx/ui/version_select/platform_select.py +++ b/amulet_map_editor/api/wx/ui/version_select/platform_select.py @@ -103,7 +103,7 @@ def demo(): An app instance must be created first. """ translation_manager = PyMCTranslate.new_translation_manager() - dialog = wx.Dialog(None, style=wx.DEFAULT_DIALOG_STYLE | wx.DIALOG_NO_PARENT) + dialog = wx.Dialog(None, title="PlatformSelect", style=wx.DEFAULT_DIALOG_STYLE | wx.DIALOG_NO_PARENT) sizer = wx.BoxSizer() dialog.SetSizer(sizer) sizer.Add( diff --git a/amulet_map_editor/api/wx/ui/version_select/version_select.py b/amulet_map_editor/api/wx/ui/version_select/version_select.py index 1bc19f35..b71467ae 100644 --- a/amulet_map_editor/api/wx/ui/version_select/version_select.py +++ b/amulet_map_editor/api/wx/ui/version_select/version_select.py @@ -162,11 +162,11 @@ def demo(): An app instance must be created first. """ translation_manager = PyMCTranslate.new_translation_manager() - for cls in ( - lambda *args: VersionSelect(*args, show_force_blockstate=False), - VersionSelect, + for cls, title in ( + (lambda *args: VersionSelect(*args, show_force_blockstate=False), "VersionSelect without format choice"), + (VersionSelect, "VersionSelect with format choice"), ): - dialog = wx.Dialog(None, style=wx.DEFAULT_DIALOG_STYLE | wx.DIALOG_NO_PARENT) + dialog = wx.Dialog(None, title=title, style=wx.DEFAULT_DIALOG_STYLE | wx.DIALOG_NO_PARENT) sizer = wx.BoxSizer() dialog.SetSizer(sizer) sizer.Add(cls(dialog, translation_manager), 0, wx.ALL, 5) From b306289cdee4db06b179777a9af85d72897f8d5a Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Sun, 30 May 2021 15:55:59 +0100 Subject: [PATCH 019/139] Rewritten the property select UI Removed wildcard mode. This is going to get its own class that can do more. --- .../api/wx/ui/base_select/base_select.py | 4 +- .../api/wx/ui/block_select/block_define.py | 33 +-- .../wx/ui/block_select/properties/__init__.py | 2 +- .../block_select/properties/base_property.py | 106 +++++++ .../ui/block_select/properties/properties.py | 276 +++++++----------- 5 files changed, 229 insertions(+), 192 deletions(-) create mode 100644 amulet_map_editor/api/wx/ui/block_select/properties/base_property.py diff --git a/amulet_map_editor/api/wx/ui/base_select/base_select.py b/amulet_map_editor/api/wx/ui/base_select/base_select.py index 5b43fc3a..213287e4 100644 --- a/amulet_map_editor/api/wx/ui/base_select/base_select.py +++ b/amulet_map_editor/api/wx/ui/base_select/base_select.py @@ -26,7 +26,7 @@ def __init__( parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager, platform: str, - version_number: Tuple[int, int, int], + version_number: Tuple[int, ...], force_blockstate: bool = None, namespace: str = None, default_name: str = None, @@ -121,7 +121,7 @@ def version(self, version: Tuple[str, Tuple[int, int, int], bool]): self._populate_namespace() self.namespace = None - def _set_version(self, version: Tuple[str, Tuple[int, int, int], bool]): + def _set_version(self, version: Tuple[str, Tuple[int, ...], bool]): assert ( version[0] in self._translation_manager.platforms() and version[1] in self._translation_manager.version_numbers(version[0]) diff --git a/amulet_map_editor/api/wx/ui/block_select/block_define.py b/amulet_map_editor/api/wx/ui/block_select/block_define.py index ae29547a..9d3f1b91 100644 --- a/amulet_map_editor/api/wx/ui/block_select/block_define.py +++ b/amulet_map_editor/api/wx/ui/block_select/block_define.py @@ -11,7 +11,6 @@ from amulet_map_editor.api.wx.ui.block_select.properties import ( PropertySelect, - WildcardSNBTType, EVT_PROPERTIES_CHANGE, ) @@ -54,17 +53,19 @@ def __init__( border = wx.LEFT if orientation == wx.HORIZONTAL else wx.TOP self._sizer.Add(right_sizer, 1, wx.EXPAND | border, 5) - self._property_picker = PropertySelect( - self, - translation_manager, - self._version_picker.platform, - self._version_picker.version_number, - self._version_picker.force_blockstate, - self._picker.namespace, - self._picker.name, - properties, - wildcard_properties, - ) + if wildcard_properties: + raise NotImplementedError + else: + self._property_picker = PropertySelect( + self, + translation_manager, + self._version_picker.platform, + self._version_picker.version_number, + self._version_picker.force_blockstate, + self._picker.namespace, + self._picker.name, + properties, + ) right_sizer.Add(self._property_picker, 1, wx.EXPAND) self._property_picker.Bind(EVT_PROPERTIES_CHANGE, self._on_property_change) @@ -104,14 +105,6 @@ def block_name(self) -> str: def block_name(self, block_name: str): self._picker.name = block_name - @property - def str_properties(self) -> Dict[str, "WildcardSNBTType"]: - return self._property_picker.str_properties - - @str_properties.setter - def str_properties(self, str_properties: Dict[str, "WildcardSNBTType"]): - self._property_picker.str_properties = str_properties - @property def properties(self) -> PropertyType: return self._property_picker.properties diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/__init__.py b/amulet_map_editor/api/wx/ui/block_select/properties/__init__.py index 2f536ee2..06cd7633 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/__init__.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/__init__.py @@ -1,2 +1,2 @@ from .events import PropertiesChangeEvent, EVT_PROPERTIES_CHANGE -from .properties import PropertySelect, WildcardSNBTType +from .properties import PropertySelect diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/base_property.py b/amulet_map_editor/api/wx/ui/block_select/properties/base_property.py new file mode 100644 index 00000000..77d64015 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/block_select/properties/base_property.py @@ -0,0 +1,106 @@ +import wx +from typing import Tuple + +import PyMCTranslate +from amulet.api.block import PropertyType +from .events import PropertiesChangeEvent + + +class BasePropertySelect(wx.Panel): + def __init__( + self, + parent: wx.Window, + translation_manager: PyMCTranslate.TranslationManager, + platform: str, + version_number: Tuple[int, int, int], + force_blockstate: bool, + namespace: str, + block_name: str, + **kwargs, + ): + super().__init__(parent, **kwargs) + self._sizer = wx.BoxSizer(wx.VERTICAL) + self.SetSizer(self._sizer) + + self._translation_manager = translation_manager + + self._platform: str = platform + self._version_number: Tuple[int, int, int] = version_number + self._force_blockstate: bool = force_blockstate + self._namespace: str = namespace + self._block_name: str = block_name + + self._set_version_block( + (platform, version_number, force_blockstate, namespace, block_name) + ) + + @property + def version_block(self) -> Tuple[str, Tuple[int, int, int], bool, str, str]: + """The version and block these properties relate to.""" + return ( + self._platform, + self._version_number, + self._force_blockstate, + self._namespace, + self._block_name, + ) + + @version_block.setter + def version_block( + self, version_block: Tuple[str, Tuple[int, int, int], bool, str, str] + ): + """Set the version and block and update the UI.""" + self._set_version_block(version_block) + self._rebuild_ui() + + def _set_version_block( + self, version_block: Tuple[str, Tuple[int, int, int], bool, str, str] + ): + """ + Set the version and block these properties relate to. + + :param version_block: The platform, version number, format, namespace and block name + """ + version = version_block[:3] + assert ( + version[0] in self._translation_manager.platforms() + and version[1] in self._translation_manager.version_numbers(version[0]) + and isinstance(version[2], bool) + ), f"{version} is not a valid version" + self._platform, self._version_number, self._force_blockstate = version + block = version_block[3:5] + assert isinstance(block[0], str) and isinstance( + block[1], str + ), "The block namespace and block name must be strings" + self._namespace, self._block_name = block + + def _rebuild_ui(self): + """ + Rebuild the UI. + Run when the version or block is changed. + """ + raise NotImplementedError + + @property + def properties(self) -> PropertyType: + """ + The selected values for each property. + + :return: A dictionary mapping property name to the nbt value. + """ + return self._get_properties() + + @properties.setter + def properties(self, properties: PropertyType): + self._set_properties(properties) + wx.PostEvent( + self, PropertiesChangeEvent() + ) + + def _get_properties(self) -> PropertyType: + """Get the selected values for each property.""" + raise NotImplementedError + + def _set_properties(self, properties: PropertyType): + """Set the selected values for each property.""" + raise NotImplementedError diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/properties.py b/amulet_map_editor/api/wx/ui/block_select/properties/properties.py index 3eba77e9..3662ca26 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/properties.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/properties.py @@ -1,6 +1,5 @@ import wx -from typing import Tuple, Dict, Optional, List, Union -import weakref +from typing import Tuple, Dict, List, Union import PyMCTranslate import amulet_nbt @@ -8,11 +7,10 @@ from amulet.api.block import PropertyDataTypes, PropertyType from amulet_map_editor.api.image import ADD_ICON, SUBTRACT_ICON from .events import PropertiesChangeEvent +from .base_property import BasePropertySelect -WildcardSNBTType = Union[SNBTType, str] - -class PropertySelect(wx.Panel): +class PropertySelect(BasePropertySelect): def __init__( self, parent: wx.Window, @@ -22,109 +20,36 @@ def __init__( force_blockstate: bool, namespace: str, block_name: str, - properties: Dict[str, SNBTType] = None, - wildcard_mode=False, + properties: PropertyType = None, ): - super().__init__(parent, style=wx.BORDER_SIMPLE) - self._parent = weakref.ref(parent) - sizer = wx.BoxSizer(wx.VERTICAL) - self.SetSizer(sizer) - - self._translation_manager = translation_manager - - self._platform: Optional[str] = None - self._version_number: Optional[Tuple[int, int, int]] = None - self._force_blockstate: Optional[bool] = None - self._namespace: Optional[str] = None - self._block_name: Optional[str] = None + super().__init__( + parent, + translation_manager, + platform, + version_number, + force_blockstate, + namespace, + block_name, + style=wx.BORDER_SIMPLE) self._manual_enabled = False - self._simple = SimplePropertySelect(self, translation_manager, wildcard_mode) - sizer.Add(self._simple, 1, wx.EXPAND) + self._simple = SimplePropertySelect(self, translation_manager) + self._sizer.Add(self._simple, 1, wx.EXPAND) self._manual = ManualPropertySelect(self, translation_manager) - sizer.Add(self._manual, 1, wx.EXPAND) - - self._wildcard_mode = wildcard_mode - - self._set_version_block( - (platform, version_number, force_blockstate, namespace, block_name) - ) - self.set_properties(properties) - - @property - def parent(self) -> wx.Window: - return self._parent() - - @property - def wildcard_mode(self) -> bool: - return self._wildcard_mode - - @property - def version_block(self) -> Tuple[str, Tuple[int, int, int], bool, str, str]: - return ( - self._platform, - self._version_number, - self._force_blockstate, - self._namespace, - self._block_name, - ) - - @version_block.setter - def version_block( - self, version_block: Tuple[str, Tuple[int, int, int], bool, str, str] - ): - self._set_version_block(version_block) - self.str_properties = None + self._sizer.Add(self._manual, 1, wx.EXPAND) - def _set_version_block( - self, version_block: Tuple[str, Tuple[int, int, int], bool, str, str] - ): - version = version_block[:3] - assert ( - version[0] in self._translation_manager.platforms() - and version[1] in self._translation_manager.version_numbers(version[0]) - and isinstance(version[2], bool) - ), f"{version} is not a valid version" - self._platform, self._version_number, self._force_blockstate = version - block = version_block[3:5] - assert isinstance(block[0], str) and isinstance( - block[1], str - ), "The block namespace and block name must be strings" - self._namespace, self._block_name = block - self._set_ui() + if properties is None: + properties = {} + self._set_properties(properties) + self._rebuild_ui() - @property - def str_properties(self) -> Dict[str, WildcardSNBTType]: + def _get_properties(self) -> PropertyType: if self._manual_enabled: return self._manual.properties else: return self._simple.properties - @str_properties.setter - def str_properties(self, properties: Dict[str, WildcardSNBTType]): - self.set_properties(properties) - wx.PostEvent( - self, PropertiesChangeEvent(self.GetId(), properties=self.str_properties) - ) - - @property - def properties(self) -> PropertyType: - if self.wildcard_mode: - raise Exception( - "Accessing the properties attribute is invalid in wildcard mode" - ) - return { - key: amulet_nbt.from_snbt(val) for key, val in self.str_properties.items() - } - - @properties.setter - def properties(self, properties: PropertyType): - self.str_properties = { - key: val.to_snbt() for key, val in (properties or {}).items() - } - - def set_properties(self, properties: Dict[str, SNBTType]): - properties = properties or {} + def _set_properties(self, properties: PropertyType): self.Freeze() if self._manual_enabled: self._manual.properties = properties @@ -133,7 +58,7 @@ def set_properties(self, properties: Dict[str, SNBTType]): self.TopLevelParent.Layout() self.Thaw() - def _set_ui(self): + def _rebuild_ui(self): self.Freeze() translator = self._translation_manager.get_version( self._platform, self._version_number @@ -146,30 +71,46 @@ def _set_ui(self): self._simple.Hide() self._manual.Show() else: + self._manual.Hide() self._simple.Show() - self._simple.set_specification( + self._simple.rebuild_ui( translator.get_specification( self._namespace, self._block_name, self._force_blockstate ) ) - self._manual.Hide() self.Thaw() -class SimplePropertySelect(wx.Panel): +class BaseSubPropertySelect(wx.Panel): def __init__( self, parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager, - wildcard_mode, ): super().__init__(parent) - sizer = wx.BoxSizer(wx.VERTICAL) - self.SetSizer(sizer) + self._sizer = wx.BoxSizer(wx.VERTICAL) + self.SetSizer(self._sizer) self._translation_manager = translation_manager + @property + def properties(self) -> PropertyType: + raise NotImplementedError + + @properties.setter + def properties(self, properties: PropertyType): + raise NotImplementedError + + +class SimplePropertySelect(BaseSubPropertySelect): + def __init__( + self, + parent: wx.Window, + translation_manager: PyMCTranslate.TranslationManager, + ): + super().__init__(parent, translation_manager) + header_sizer = wx.BoxSizer(wx.HORIZONTAL) - sizer.Add(header_sizer, 0, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, 5) + self._sizer.Add(header_sizer, 0, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, 5) label = wx.StaticText(self, label="Property Name", style=wx.ALIGN_CENTER) header_sizer.Add(label, 1) label = wx.StaticText( @@ -177,37 +118,27 @@ def __init__( ) header_sizer.Add(label, 1, wx.LEFT, 5) self._property_sizer = wx.GridSizer(2, 5, 5) - sizer.Add(self._property_sizer, 0, wx.ALL | wx.EXPAND, 5) + self._sizer.Add(self._property_sizer, 0, wx.ALL | wx.EXPAND, 5) self._properties: Dict[str, wx.Choice] = {} self._specification: dict = {} - self._wildcard_mode = wildcard_mode - def set_specification(self, specification: dict): + def rebuild_ui(self, specification: dict): + """ + Rebuild the UI. + Run when the version or block is changed. + """ self._specification = specification - - @property - def properties(self) -> Dict[str, SNBTType]: - return { - name: choice.GetString(choice.GetSelection()) - for name, choice in self._properties.items() - } - - @properties.setter - def properties(self, properties: Dict[str, SNBTType]): self.Freeze() self._properties.clear() self._property_sizer.Clear(True) spec_properties: Dict[str, List[str]] = self._specification.get( "properties", {} ) - spec_defaults = self._specification.get("defaults", {}) for name, choices in spec_properties.items(): label = wx.StaticText(self, label=name) self._property_sizer.Add(label, 0, wx.ALIGN_CENTER) - if self._wildcard_mode: - choices = ["*"] + choices choice = wx.Choice(self, choices=choices) self._property_sizer.Add(choice, 0, wx.EXPAND) choice.Bind( @@ -217,38 +148,48 @@ def properties(self, properties: Dict[str, SNBTType]): PropertiesChangeEvent(self.GetId(), properties=self.properties), ), ) - val = spec_defaults[name] - if name in properties and val != "*": - try: - snbt = amulet_nbt.from_snbt(properties[name]).to_snbt() - except: - pass - else: - if snbt in choices: - val = snbt - if val in choices: - choice.SetSelection(choices.index(val)) self._properties[name] = choice - self.Thaw() + self.properties = self._specification.get("defaults", {}) self.Fit() - self.Layout() + self.GetTopLevelParent().Layout() + self.Thaw() + + @property + def properties(self) -> PropertyType: + return { + name: amulet_nbt.from_snbt(choice.GetString(choice.GetSelection())) + for name, choice in self._properties.items() + } + + @properties.setter + def properties(self, properties: PropertyType): + self.Freeze() + for name, nbt in properties.items(): + if name in self._properties: + if isinstance(nbt, amulet_nbt.BaseValueType): + snbt = nbt.to_snbt() + elif isinstance(nbt, str): + snbt = nbt + else: + continue + choice = self._properties[name] + index = choice.FindString(snbt) + if index != wx.NOT_FOUND: + choice.SetSelection(index) + self.Thaw() -class ManualPropertySelect(wx.Panel): +class ManualPropertySelect(BaseSubPropertySelect): def __init__( self, parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager ): - super().__init__(parent) - sizer = wx.BoxSizer(wx.VERTICAL) - self.SetSizer(sizer) - self._translation_manager = translation_manager - + super().__init__(parent, translation_manager) header_sizer = wx.BoxSizer(wx.HORIZONTAL) add_button = wx.BitmapButton( self, bitmap=ADD_ICON.bitmap(30, 30), size=(30, 30) ) header_sizer.Add(add_button) - sizer.Add(header_sizer, 0, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, 5) + self._sizer.Add(header_sizer, 0, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, 5) label = wx.StaticText(self, label="Property Name", style=wx.ALIGN_CENTER) header_sizer.Add(label, 1, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5) label = wx.StaticText( @@ -258,7 +199,7 @@ def __init__( header_sizer.AddStretchSpacer(1) self._property_sizer = wx.BoxSizer(wx.VERTICAL) - sizer.Add( + self._sizer.Add( self._property_sizer, 0, wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, 5 ) @@ -330,24 +271,25 @@ def _on_remove_property(self, sizer: wx.Sizer, key: int): self._post_property_change() @property - def properties(self) -> Dict[str, SNBTType]: + def properties(self) -> PropertyType: properties = {} - for name, value in self._properties.values(): + for name_ui, value_ui in self._properties.values(): try: - nbt = amulet_nbt.from_snbt(value.GetValue()) + nbt = amulet_nbt.from_snbt(value_ui.GetValue()) except: continue - if name.GetValue() and isinstance(nbt, PropertyDataTypes): - properties[name.GetValue()] = nbt.to_snbt() + name: str = name_ui.GetValue() + if name and isinstance(nbt, PropertyDataTypes): + properties[name] = nbt return properties @properties.setter - def properties(self, properties: Dict[str, SNBTType]): + def properties(self, properties: PropertyType): self._property_sizer.Clear(True) self._properties.clear() self._property_index = 0 for name, value in properties.items(): - self._add_property(name, value) + self._add_property(name, value.to_snbt()) def demo(): @@ -357,26 +299,22 @@ def demo(): """ translation_manager = PyMCTranslate.new_translation_manager() for block in (("minecraft", "oak_fence"), ("modded", "block")): - for cls, title in ( - (PropertySelect, f"PropertySelect with block {block[0]}:{block[1]}"), - (lambda *args: PropertySelect(*args, wildcard_mode=True), f"PropertySelect in wildcard mode with block {block[0]}:{block[1]}"), - ): - dialog = wx.Dialog(None, title=title, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT) - sizer = wx.BoxSizer() - dialog.SetSizer(sizer) - sizer.Add( - cls(dialog, translation_manager, "java", (1, 16, 0), False, *block), - 1, - wx.ALL, - 5, - ) + dialog = wx.Dialog(None, title=f"PropertySelect with block {block[0]}:{block[1]}", style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT) + sizer = wx.BoxSizer() + dialog.SetSizer(sizer) + sizer.Add( + PropertySelect(dialog, translation_manager, "java", (1, 16, 0), False, *block), + 1, + wx.ALL, + 5, + ) - def get_on_close(dialog_): - def on_close(evt): - dialog_.Destroy() + def get_on_close(dialog_): + def on_close(evt): + dialog_.Destroy() - return on_close + return on_close - dialog.Bind(wx.EVT_CLOSE, get_on_close(dialog)) - dialog.Show() - dialog.Fit() + dialog.Bind(wx.EVT_CLOSE, get_on_close(dialog)) + dialog.Show() + dialog.Fit() From f2b14f7fb6f6134f4bdbad62d2e63c9b97384437 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Sun, 30 May 2021 17:47:29 +0100 Subject: [PATCH 020/139] Partially added a wildcard property UI --- .../api/wx/ui/block_select/block_define.py | 27 +- .../wx/ui/block_select/properties/__init__.py | 1 + .../api/wx/ui/block_select/properties/demo.py | 4 + .../ui/block_select/properties/properties.py | 10 +- .../properties/wildcard_properties.py | 406 ++++++++++++++++++ 5 files changed, 443 insertions(+), 5 deletions(-) create mode 100644 amulet_map_editor/api/wx/ui/block_select/properties/wildcard_properties.py diff --git a/amulet_map_editor/api/wx/ui/block_select/block_define.py b/amulet_map_editor/api/wx/ui/block_select/block_define.py index 9d3f1b91..6f5019ed 100644 --- a/amulet_map_editor/api/wx/ui/block_select/block_define.py +++ b/amulet_map_editor/api/wx/ui/block_select/block_define.py @@ -1,6 +1,6 @@ import wx import wx.lib.scrolledpanel -from typing import Tuple, Optional, Dict +from typing import Tuple, Optional import PyMCTranslate from amulet.api.block import PropertyType, Block @@ -11,6 +11,7 @@ from amulet_map_editor.api.wx.ui.block_select.properties import ( PropertySelect, + WildcardPropertySelect, EVT_PROPERTIES_CHANGE, ) @@ -52,9 +53,18 @@ def __init__( right_sizer = wx.BoxSizer(wx.VERTICAL) border = wx.LEFT if orientation == wx.HORIZONTAL else wx.TOP self._sizer.Add(right_sizer, 1, wx.EXPAND | border, 5) - + self._wildcard_mode = wildcard_properties if wildcard_properties: - raise NotImplementedError + self._property_picker = PropertySelect( + self, + translation_manager, + self._version_picker.platform, + self._version_picker.version_number, + self._version_picker.force_blockstate, + self._picker.namespace, + self._picker.name, + properties, + ) else: self._property_picker = PropertySelect( self, @@ -72,6 +82,15 @@ def __init__( self.SetSizerAndFit(self._sizer) self.Layout() + @property + def wildcard_mode(self) -> bool: + """ + Is the UI in wildcard mode. + If True multiple property values can be selected. + These can be accessed through :attr:`extra_properties` + """ + return self._wildcard_mode + def _on_picker_change(self, evt): self._update_properties() evt.Skip() @@ -113,6 +132,8 @@ def properties(self) -> PropertyType: def properties(self, properties: PropertyType): self._property_picker.properties = properties + + @property def block(self) -> Block: return Block(self.namespace, self.block_name, self.properties) diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/__init__.py b/amulet_map_editor/api/wx/ui/block_select/properties/__init__.py index 06cd7633..8eb168f5 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/__init__.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/__init__.py @@ -1,2 +1,3 @@ from .events import PropertiesChangeEvent, EVT_PROPERTIES_CHANGE from .properties import PropertySelect +from .wildcard_properties import WildcardPropertySelect diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/demo.py b/amulet_map_editor/api/wx/ui/block_select/properties/demo.py index beb91594..5a3b6229 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/demo.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/demo.py @@ -2,6 +2,9 @@ from amulet_map_editor.api.wx.ui.block_select.properties.properties import ( demo as properties_demo, ) +from amulet_map_editor.api.wx.ui.block_select.properties.wildcard_properties import ( + demo as wildcard_properties_demo, +) def demo(): @@ -10,6 +13,7 @@ def demo(): An app instance must be created first. """ properties_demo() + wildcard_properties_demo() if __name__ == "__main__": diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/properties.py b/amulet_map_editor/api/wx/ui/block_select/properties/properties.py index 3662ca26..779e5932 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/properties.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/properties.py @@ -1,5 +1,5 @@ import wx -from typing import Tuple, Dict, List, Union +from typing import Tuple, Dict, List import PyMCTranslate import amulet_nbt @@ -11,6 +11,12 @@ class PropertySelect(BasePropertySelect): + """ + This is a UI which lets the user pick one value for each property value for a given block. + If the block is known it will be populated from the specification. + If it is not known the user can populate it themselves. + """ + def __init__( self, parent: wx.Window, @@ -166,7 +172,7 @@ def properties(self, properties: PropertyType): self.Freeze() for name, nbt in properties.items(): if name in self._properties: - if isinstance(nbt, amulet_nbt.BaseValueType): + if isinstance(nbt, PropertyDataTypes): snbt = nbt.to_snbt() elif isinstance(nbt, str): snbt = nbt diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/wildcard_properties.py b/amulet_map_editor/api/wx/ui/block_select/properties/wildcard_properties.py new file mode 100644 index 00000000..0bce4a6a --- /dev/null +++ b/amulet_map_editor/api/wx/ui/block_select/properties/wildcard_properties.py @@ -0,0 +1,406 @@ +import wx +from typing import Tuple, Dict, List, Iterable + +import PyMCTranslate +import amulet_nbt +from amulet_nbt import SNBTType +from amulet.api.block import PropertyDataTypes, PropertyType, PropertyValueType +from amulet_map_editor.api.image import ADD_ICON, SUBTRACT_ICON +from .events import PropertiesChangeEvent +from .base_property import BasePropertySelect + + +class WildcardPropertySelect(BasePropertySelect): + """ + This is a UI which lets the user pick zero or more values for each property. + If the block is known it will be populated from the specification. + If it is not known the user can populate it themselves. + """ + + def __init__( + self, + parent: wx.Window, + translation_manager: PyMCTranslate.TranslationManager, + platform: str, + version_number: Tuple[int, int, int], + force_blockstate: bool, + namespace: str, + block_name: str, + properties: PropertyType = None, + ): + super().__init__( + parent, + translation_manager, + platform, + version_number, + force_blockstate, + namespace, + block_name, + style=wx.BORDER_SIMPLE) + + self._manual_enabled = False + self._simple = SimpleWildcardPropertySelect(self, translation_manager) + self._sizer.Add(self._simple, 1, wx.EXPAND) + self._manual = ManualWildcardPropertySelect(self, translation_manager) + self._sizer.Add(self._manual, 1, wx.EXPAND) + + if properties is None: + properties = {} + self._set_properties(properties) + self._rebuild_ui() + + def _get_properties(self) -> PropertyType: + if self._manual_enabled: + return self._manual.properties + else: + return self._simple.properties + + def _set_properties(self, properties: PropertyType): + self.Freeze() + if self._manual_enabled: + self._manual.properties = properties + else: + self._simple.properties = properties + self.TopLevelParent.Layout() + self.Thaw() + + @property + def extra_properties(self) -> Dict[str, Tuple[PropertyValueType, ...]]: + """ + The values that are checked for each property. + This UI can have more than one property value checked (ticked) but only one selected (highlighted blue). + :attr:`properties` will return the entry that is highlighted blue. + """ + raise NotImplementedError + + @extra_properties.setter + def extra_properties(self, properties: Dict[str, Tuple[PropertyValueType, ...]]): + raise NotImplementedError + + def _rebuild_ui(self): + self.Freeze() + translator = self._translation_manager.get_version( + self._platform, self._version_number + ).block + + self._manual_enabled = self._block_name not in translator.base_names( + self._namespace, self._force_blockstate + ) + if self._manual_enabled: + self._simple.Hide() + self._manual.Show() + else: + self._manual.Hide() + self._simple.Show() + self._simple.rebuild_ui( + translator.get_specification( + self._namespace, self._block_name, self._force_blockstate + ) + ) + self.Thaw() + + +class PropertyValueCheckList(wx.Panel): + def __init__(self, parent: wx.Window, values: Iterable[str]): + super().__init__(parent) + sizer = wx.BoxSizer(wx.VERTICAL) + self.SetSizer(sizer) + self._toggle_checkbox = wx.CheckBox(self, style=wx.CHK_3STATE) + sizer.Add(self._toggle_checkbox, 0, wx.ALL, 3) + self._check_list_box = wx.CheckListBox(self, choices=values) + self._check_list_box.SetCheckedStrings(values) + sizer.Add(self._check_list_box, 0) + + self._toggle_checkbox.Bind(wx.EVT_CHECKBOX, self._on_toggle) + self._check_list_box.Bind(wx.EVT_CHECKLISTBOX, self._on_check) + + def _on_toggle(self, evt): + if self._toggle_checkbox.GetValue(): + self._check_list_box.SetCheckedItems(list(range(self._check_list_box.GetCount()))) + else: + self._check_list_box.SetCheckedItems([]) + + def _on_check(self, evt): + items = self._check_list_box.GetCheckedItems() + if len(items) == self._check_list_box.GetCount(): + self._toggle_checkbox.Set3StateValue(wx.CHK_CHECKED) + elif len(items) == 0: + self._toggle_checkbox.Set3StateValue(wx.CHK_UNCHECKED) + else: + self._toggle_checkbox.Set3StateValue(wx.CHK_UNDETERMINED) + + @property + def value(self) -> str: + return self._check_list_box.GetStringSelection() + + @value.setter + def value(self, value: str): + self._check_list_box.SetStringSelection(value) + + @property + def extra_values(self) -> Tuple[str]: + return self._check_list_box.GetCheckedStrings() + + @extra_values.setter + def extra_values(self, extra_values: Iterable[str]): + self._check_list_box.SetCheckedStrings(extra_values) + + +class PropertyValueComboPopup(wx.ComboPopup): + def __init__(self, values: Iterable[str]): + super().__init__() + self.pvcl: PropertyValueCheckList = None + self._values = values + + def Create(self, parent): + self.pvcl = PropertyValueCheckList(parent, self._values) + return True + + def GetControl(self): + return self.pvcl + + def GetStringValue(self): + return "|".join(self.pvcl.extra_values) + + +class BaseSubWildcardPropertySelect(wx.Panel): + def __init__( + self, + parent: wx.Window, + translation_manager: PyMCTranslate.TranslationManager, + ): + super().__init__(parent) + self._sizer = wx.BoxSizer(wx.VERTICAL) + self.SetSizer(self._sizer) + self._translation_manager = translation_manager + + @property + def properties(self) -> PropertyType: + raise NotImplementedError + + @properties.setter + def properties(self, properties: PropertyType): + raise NotImplementedError + + +class SimpleWildcardPropertySelect(BaseSubWildcardPropertySelect): + def __init__( + self, + parent: wx.Window, + translation_manager: PyMCTranslate.TranslationManager, + ): + super().__init__(parent, translation_manager) + + header_sizer = wx.BoxSizer(wx.HORIZONTAL) + self._sizer.Add(header_sizer, 0, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, 5) + label = wx.StaticText(self, label="Property Name", style=wx.ALIGN_CENTER) + header_sizer.Add(label, 1) + label = wx.StaticText( + self, label="Property Value (SNBT)", style=wx.ALIGN_CENTER + ) + header_sizer.Add(label, 1, wx.LEFT, 5) + self._property_sizer = wx.GridSizer(2, 5, 5) + self._sizer.Add(self._property_sizer, 0, wx.ALL | wx.EXPAND, 5) + + self._properties: Dict[str, PropertyValueComboPopup] = {} + self._specification: dict = {} + + def rebuild_ui(self, specification: dict): + """ + Rebuild the UI. + Run when the version or block is changed. + """ + self._specification = specification + self.Freeze() + self._properties.clear() + self._property_sizer.Clear(True) + spec_properties: Dict[str, List[str]] = self._specification.get( + "properties", {} + ) + + for name, choices in spec_properties.items(): + label = wx.StaticText(self, label=name) + self._property_sizer.Add(label, 0, wx.ALIGN_CENTER) + + choice = wx.ComboCtrl(self, style=wx.CB_READONLY) + l = PropertyValueComboPopup(choices) + choice.SetPopupControl(l) + + self._property_sizer.Add(choice, 0, wx.EXPAND) + choice.Bind( + wx.EVT_CHOICE, + lambda evt: wx.PostEvent( + self, + PropertiesChangeEvent(self.GetId(), properties=self.properties), + ), + ) + self._properties[name] = choice + self.properties = self._specification.get("defaults", {}) + self.Fit() + self.GetTopLevelParent().Layout() + self.Thaw() + + @property + def properties(self) -> PropertyType: + return { + # name: amulet_nbt.from_snbt(choice.GetString(choice.GetSelection())) + # for name, choice in self._properties.items() + } + + @properties.setter + def properties(self, properties: PropertyType): + self.Freeze() + for name, nbt in properties.items(): + if name in self._properties: + if isinstance(nbt, PropertyDataTypes): + snbt = nbt.to_snbt() + elif isinstance(nbt, str): + snbt = nbt + else: + continue + choice = self._properties[name] + # index = choice.FindString(snbt) + # if index != wx.NOT_FOUND: + # choice.SetSelection(index) + self.Thaw() + + +class ManualWildcardPropertySelect(BaseSubWildcardPropertySelect): + def __init__( + self, parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager + ): + super().__init__(parent, translation_manager) + header_sizer = wx.BoxSizer(wx.HORIZONTAL) + add_button = wx.BitmapButton( + self, bitmap=ADD_ICON.bitmap(30, 30), size=(30, 30) + ) + header_sizer.Add(add_button) + self._sizer.Add(header_sizer, 0, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, 5) + label = wx.StaticText(self, label="Property Name", style=wx.ALIGN_CENTER) + header_sizer.Add(label, 1, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5) + label = wx.StaticText( + self, label="Property Value (SNBT)", style=wx.ALIGN_CENTER + ) + header_sizer.Add(label, 1, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5) + header_sizer.AddStretchSpacer(1) + + self._property_sizer = wx.BoxSizer(wx.VERTICAL) + self._sizer.Add( + self._property_sizer, 0, wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, 5 + ) + + add_button.Bind(wx.EVT_BUTTON, lambda evt: self._add_property()) + + self._property_index = 0 + self._properties: Dict[int, Tuple[wx.TextCtrl, wx.TextCtrl]] = {} + + def _post_property_change(self): + wx.PostEvent( + self, PropertiesChangeEvent(self.GetId(), properties=self.properties) + ) + + def _add_property(self, name: str = "", value: SNBTType = ""): + self.Freeze() + sizer = wx.BoxSizer(wx.HORIZONTAL) + self._property_index += 1 + subtract_button = wx.BitmapButton( + self, bitmap=SUBTRACT_ICON.bitmap(30, 30), size=(30, 30) + ) + sizer.Add(subtract_button, 0, wx.ALIGN_CENTER_VERTICAL) + index = self._property_index + subtract_button.Bind( + wx.EVT_BUTTON, lambda evt: self._on_remove_property(sizer, index) + ) + name_entry = wx.TextCtrl(self, value=name, style=wx.TE_CENTER) + sizer.Add(name_entry, 1, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) + name_entry.Bind(wx.EVT_TEXT, lambda evt: self._post_property_change()) + value_entry = wx.TextCtrl(self, value=value, style=wx.TE_CENTER) + sizer.Add(value_entry, 1, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) + snbt_text = wx.StaticText(self, style=wx.ALIGN_CENTER) + sizer.Add(snbt_text, 1, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) + self._change_value("", snbt_text) + value_entry.Bind(wx.EVT_TEXT, lambda evt: self._on_value_change(evt, snbt_text)) + + self._property_sizer.Add(sizer, 1, wx.TOP | wx.EXPAND, 5) + self._properties[self._property_index] = (name_entry, value_entry) + self.Fit() + self.TopLevelParent.Layout() + self.Thaw() + + def _on_value_change(self, evt, snbt_text: wx.StaticText): + self._change_value(evt.GetString(), snbt_text) + self._post_property_change() + evt.Skip() + + def _change_value(self, snbt: SNBTType, snbt_text: wx.StaticText): + try: + nbt = amulet_nbt.from_snbt(snbt) + except: + snbt_text.SetLabel("Invalid SNBT") + snbt_text.SetBackgroundColour((255, 200, 200)) + else: + if isinstance(nbt, PropertyDataTypes): + snbt_text.SetLabel(nbt.to_snbt()) + snbt_text.SetBackgroundColour(wx.NullColour) + else: + snbt_text.SetLabel(f"{nbt.__class__.__name__} not valid") + snbt_text.SetBackgroundColour((255, 200, 200)) + self.Layout() + + def _on_remove_property(self, sizer: wx.Sizer, key: int): + self.Freeze() + self._property_sizer.Detach(sizer) + sizer.Clear(True) + del self._properties[key] + self.TopLevelParent.Layout() + self.Thaw() + self._post_property_change() + + @property + def properties(self) -> PropertyType: + properties = {} + for name_ui, value_ui in self._properties.values(): + try: + nbt = amulet_nbt.from_snbt(value_ui.GetValue()) + except: + continue + name: str = name_ui.GetValue() + if name and isinstance(nbt, PropertyDataTypes): + properties[name] = nbt + return properties + + @properties.setter + def properties(self, properties: PropertyType): + self._property_sizer.Clear(True) + self._properties.clear() + self._property_index = 0 + for name, value in properties.items(): + self._add_property(name, value.to_snbt()) + + +def demo(): + """ + Show a demo version of the UI. + An app instance must be created first. + """ + translation_manager = PyMCTranslate.new_translation_manager() + for block in (("minecraft", "oak_fence"), ("modded", "block")): + dialog = wx.Dialog(None, title=f"WildcardPropertySelect with block {block[0]}:{block[1]}", style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT) + sizer = wx.BoxSizer() + dialog.SetSizer(sizer) + sizer.Add( + WildcardPropertySelect(dialog, translation_manager, "java", (1, 16, 0), False, *block), + 1, + wx.ALL, + 5, + ) + + def get_on_close(dialog_): + def on_close(evt): + dialog_.Destroy() + + return on_close + + dialog.Bind(wx.EVT_CLOSE, get_on_close(dialog)) + dialog.Show() + dialog.Fit() From 5b9f54b0974945de4c30a0160bafcd8c4551443e Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Sat, 26 Jun 2021 13:50:53 +0100 Subject: [PATCH 021/139] Reformatted --- .../api/wx/ui/biome_select/biome_define.py | 6 +++++- .../api/wx/ui/biome_select/biome_select.py | 6 +++++- .../api/wx/ui/block_select/block_define.py | 8 +++++--- .../api/wx/ui/block_select/block_select.py | 4 +++- .../wx/ui/block_select/multi_block_define.py | 6 +++++- .../block_select/properties/base_property.py | 4 +--- .../ui/block_select/properties/properties.py | 13 ++++++++++--- .../properties/wildcard_properties.py | 17 +++++++++++++---- amulet_map_editor/api/wx/ui/select_world.py | 19 ++++++++++++++++--- .../api/wx/ui/version_select/demo.py | 8 ++++++-- .../wx/ui/version_select/platform_select.py | 6 +++++- .../wx/ui/version_select/version_select.py | 9 +++++++-- 12 files changed, 81 insertions(+), 25 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/biome_select/biome_define.py b/amulet_map_editor/api/wx/ui/biome_select/biome_define.py index 71ad100a..704f6492 100644 --- a/amulet_map_editor/api/wx/ui/biome_select/biome_define.py +++ b/amulet_map_editor/api/wx/ui/biome_select/biome_define.py @@ -78,7 +78,11 @@ def demo(): An app instance must be created first. """ translation_manager = PyMCTranslate.new_translation_manager() - dialog = wx.Dialog(None, title="BiomeDefine", style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT) + dialog = wx.Dialog( + None, + title="BiomeDefine", + style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT, + ) sizer = wx.BoxSizer() dialog.SetSizer(sizer) sizer.Add( diff --git a/amulet_map_editor/api/wx/ui/biome_select/biome_select.py b/amulet_map_editor/api/wx/ui/biome_select/biome_select.py index f8f4109a..ee1e832e 100644 --- a/amulet_map_editor/api/wx/ui/biome_select/biome_select.py +++ b/amulet_map_editor/api/wx/ui/biome_select/biome_select.py @@ -43,7 +43,11 @@ def demo(): import PyMCTranslate translation_manager = PyMCTranslate.new_translation_manager() - dialog = wx.Dialog(None, title="BiomeSelect", style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT) + dialog = wx.Dialog( + None, + title="BiomeSelect", + style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT, + ) sizer = wx.BoxSizer() dialog.SetSizer(sizer) sizer.Add( diff --git a/amulet_map_editor/api/wx/ui/block_select/block_define.py b/amulet_map_editor/api/wx/ui/block_select/block_define.py index 6f5019ed..39e52dc8 100644 --- a/amulet_map_editor/api/wx/ui/block_select/block_define.py +++ b/amulet_map_editor/api/wx/ui/block_select/block_define.py @@ -132,8 +132,6 @@ def properties(self) -> PropertyType: def properties(self, properties: PropertyType): self._property_picker.properties = properties - - @property def block(self) -> Block: return Block(self.namespace, self.block_name, self.properties) @@ -177,7 +175,11 @@ def demo(): An app instance must be created first. """ translation_manager = PyMCTranslate.new_translation_manager() - dialog = wx.Dialog(None, title="BlockDefine", style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT) + dialog = wx.Dialog( + None, + title="BlockDefine", + style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT, + ) sizer = wx.BoxSizer() dialog.SetSizer(sizer) sizer.Add( diff --git a/amulet_map_editor/api/wx/ui/block_select/block_select.py b/amulet_map_editor/api/wx/ui/block_select/block_select.py index ea051f33..88c97723 100644 --- a/amulet_map_editor/api/wx/ui/block_select/block_select.py +++ b/amulet_map_editor/api/wx/ui/block_select/block_select.py @@ -59,7 +59,9 @@ def demo(): An app instance must be created first. """ translation_manager = PyMCTranslate.new_translation_manager() - dialog = wx.Dialog(None, title="BlockSelect", style=wx.DEFAULT_DIALOG_STYLE | wx.DIALOG_NO_PARENT) + dialog = wx.Dialog( + None, title="BlockSelect", style=wx.DEFAULT_DIALOG_STYLE | wx.DIALOG_NO_PARENT + ) sizer = wx.BoxSizer() dialog.SetSizer(sizer) sizer.Add( diff --git a/amulet_map_editor/api/wx/ui/block_select/multi_block_define.py b/amulet_map_editor/api/wx/ui/block_select/multi_block_define.py index 1abbb99c..26c82245 100644 --- a/amulet_map_editor/api/wx/ui/block_select/multi_block_define.py +++ b/amulet_map_editor/api/wx/ui/block_select/multi_block_define.py @@ -194,7 +194,11 @@ def demo(): An app instance must be created first. """ translation_manager = PyMCTranslate.new_translation_manager() - dialog = wx.Dialog(None, title="MultiBlockDefine", style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT) + dialog = wx.Dialog( + None, + title="MultiBlockDefine", + style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT, + ) sizer = wx.BoxSizer() dialog.SetSizer(sizer) sizer.Add(MultiBlockDefine(dialog, translation_manager), 1, wx.EXPAND) diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/base_property.py b/amulet_map_editor/api/wx/ui/block_select/properties/base_property.py index 77d64015..9629ac0c 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/base_property.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/base_property.py @@ -93,9 +93,7 @@ def properties(self) -> PropertyType: @properties.setter def properties(self, properties: PropertyType): self._set_properties(properties) - wx.PostEvent( - self, PropertiesChangeEvent() - ) + wx.PostEvent(self, PropertiesChangeEvent()) def _get_properties(self) -> PropertyType: """Get the selected values for each property.""" diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/properties.py b/amulet_map_editor/api/wx/ui/block_select/properties/properties.py index 779e5932..d80f2ac0 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/properties.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/properties.py @@ -36,7 +36,8 @@ def __init__( force_blockstate, namespace, block_name, - style=wx.BORDER_SIMPLE) + style=wx.BORDER_SIMPLE, + ) self._manual_enabled = False self._simple = SimplePropertySelect(self, translation_manager) @@ -305,11 +306,17 @@ def demo(): """ translation_manager = PyMCTranslate.new_translation_manager() for block in (("minecraft", "oak_fence"), ("modded", "block")): - dialog = wx.Dialog(None, title=f"PropertySelect with block {block[0]}:{block[1]}", style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT) + dialog = wx.Dialog( + None, + title=f"PropertySelect with block {block[0]}:{block[1]}", + style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT, + ) sizer = wx.BoxSizer() dialog.SetSizer(sizer) sizer.Add( - PropertySelect(dialog, translation_manager, "java", (1, 16, 0), False, *block), + PropertySelect( + dialog, translation_manager, "java", (1, 16, 0), False, *block + ), 1, wx.ALL, 5, diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/wildcard_properties.py b/amulet_map_editor/api/wx/ui/block_select/properties/wildcard_properties.py index 0bce4a6a..0c174268 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/wildcard_properties.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/wildcard_properties.py @@ -36,7 +36,8 @@ def __init__( force_blockstate, namespace, block_name, - style=wx.BORDER_SIMPLE) + style=wx.BORDER_SIMPLE, + ) self._manual_enabled = False self._simple = SimpleWildcardPropertySelect(self, translation_manager) @@ -116,7 +117,9 @@ def __init__(self, parent: wx.Window, values: Iterable[str]): def _on_toggle(self, evt): if self._toggle_checkbox.GetValue(): - self._check_list_box.SetCheckedItems(list(range(self._check_list_box.GetCount()))) + self._check_list_box.SetCheckedItems( + list(range(self._check_list_box.GetCount())) + ) else: self._check_list_box.SetCheckedItems([]) @@ -385,11 +388,17 @@ def demo(): """ translation_manager = PyMCTranslate.new_translation_manager() for block in (("minecraft", "oak_fence"), ("modded", "block")): - dialog = wx.Dialog(None, title=f"WildcardPropertySelect with block {block[0]}:{block[1]}", style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT) + dialog = wx.Dialog( + None, + title=f"WildcardPropertySelect with block {block[0]}:{block[1]}", + style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT, + ) sizer = wx.BoxSizer() dialog.SetSizer(sizer) sizer.Add( - WildcardPropertySelect(dialog, translation_manager, "java", (1, 16, 0), False, *block), + WildcardPropertySelect( + dialog, translation_manager, "java", (1, 16, 0), False, *block + ), 1, wx.ALL, 5, diff --git a/amulet_map_editor/api/wx/ui/select_world.py b/amulet_map_editor/api/wx/ui/select_world.py index 267f8d00..e85d1e72 100644 --- a/amulet_map_editor/api/wx/ui/select_world.py +++ b/amulet_map_editor/api/wx/ui/select_world.py @@ -327,12 +327,23 @@ def _update_recent(self, path): self._open_world_callback(path) -WORLD_SELECT_DIALOG_STYLE = wx.DEFAULT_DIALOG_STYLE | wx.MAXIMIZE_BOX | wx.TAB_TRAVERSAL | wx.CLIP_CHILDREN | wx.RESIZE_BORDER +WORLD_SELECT_DIALOG_STYLE = ( + wx.DEFAULT_DIALOG_STYLE + | wx.MAXIMIZE_BOX + | wx.TAB_TRAVERSAL + | wx.CLIP_CHILDREN + | wx.RESIZE_BORDER +) @preserve_ui_preferences class WorldSelectDialog(wx.Dialog): - def __init__(self, parent: Optional[wx.Window], open_world_callback: Callable[[str], None], **kwargs): + def __init__( + self, + parent: Optional[wx.Window], + open_world_callback: Callable[[str], None], + **kwargs, + ): if isinstance(parent, wx.Window): size = wx.Size(*[int(s * 0.95) for s in parent.GetSize()]) else: @@ -343,7 +354,7 @@ def __init__(self, parent: Optional[wx.Window], open_world_callback: Callable[[s pos=wx.Point(50, 50), size=size, style=kwargs.pop("style", WORLD_SELECT_DIALOG_STYLE), - **kwargs + **kwargs, ) self.Bind(wx.EVT_CLOSE, self._hide_event) @@ -371,8 +382,10 @@ def demo(): Show a demo version of the UI. An app instance must be created first. """ + def open_world_callback(path): print(f"Open world {path}") + dialog = WorldSelectDialog( None, open_world_callback, diff --git a/amulet_map_editor/api/wx/ui/version_select/demo.py b/amulet_map_editor/api/wx/ui/version_select/demo.py index aa2eec94..ce06282d 100644 --- a/amulet_map_editor/api/wx/ui/version_select/demo.py +++ b/amulet_map_editor/api/wx/ui/version_select/demo.py @@ -1,6 +1,10 @@ import wx -from amulet_map_editor.api.wx.ui.version_select.platform_select import demo as platform_demo -from amulet_map_editor.api.wx.ui.version_select.version_select import demo as version_demo +from amulet_map_editor.api.wx.ui.version_select.platform_select import ( + demo as platform_demo, +) +from amulet_map_editor.api.wx.ui.version_select.version_select import ( + demo as version_demo, +) def demo(): diff --git a/amulet_map_editor/api/wx/ui/version_select/platform_select.py b/amulet_map_editor/api/wx/ui/version_select/platform_select.py index a73a5d41..bc9ce547 100644 --- a/amulet_map_editor/api/wx/ui/version_select/platform_select.py +++ b/amulet_map_editor/api/wx/ui/version_select/platform_select.py @@ -103,7 +103,11 @@ def demo(): An app instance must be created first. """ translation_manager = PyMCTranslate.new_translation_manager() - dialog = wx.Dialog(None, title="PlatformSelect", style=wx.DEFAULT_DIALOG_STYLE | wx.DIALOG_NO_PARENT) + dialog = wx.Dialog( + None, + title="PlatformSelect", + style=wx.DEFAULT_DIALOG_STYLE | wx.DIALOG_NO_PARENT, + ) sizer = wx.BoxSizer() dialog.SetSizer(sizer) sizer.Add( diff --git a/amulet_map_editor/api/wx/ui/version_select/version_select.py b/amulet_map_editor/api/wx/ui/version_select/version_select.py index b71467ae..a45d8b2e 100644 --- a/amulet_map_editor/api/wx/ui/version_select/version_select.py +++ b/amulet_map_editor/api/wx/ui/version_select/version_select.py @@ -163,10 +163,15 @@ def demo(): """ translation_manager = PyMCTranslate.new_translation_manager() for cls, title in ( - (lambda *args: VersionSelect(*args, show_force_blockstate=False), "VersionSelect without format choice"), + ( + lambda *args: VersionSelect(*args, show_force_blockstate=False), + "VersionSelect without format choice", + ), (VersionSelect, "VersionSelect with format choice"), ): - dialog = wx.Dialog(None, title=title, style=wx.DEFAULT_DIALOG_STYLE | wx.DIALOG_NO_PARENT) + dialog = wx.Dialog( + None, title=title, style=wx.DEFAULT_DIALOG_STYLE | wx.DIALOG_NO_PARENT + ) sizer = wx.BoxSizer() dialog.SetSizer(sizer) sizer.Add(cls(dialog, translation_manager), 0, wx.ALL, 5) From 73c12da1c1167bf65ccda059c66cddf5314b4af6 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Wed, 30 Jun 2021 10:00:55 +0100 Subject: [PATCH 022/139] Split up the single properties classes This still needs some work --- .../block_select/properties/base_property.py | 24 -- .../ui/block_select/properties/properties.py | 333 ------------------ .../properties/single_properties/__init__.py | 1 + .../automatic_single_properties.py | 91 +++++ .../base_single_properties.py | 33 ++ .../manual_single_properties.py | 135 +++++++ .../single_properties/single_properties.py | 138 ++++++++ 7 files changed, 398 insertions(+), 357 deletions(-) delete mode 100644 amulet_map_editor/api/wx/ui/block_select/properties/properties.py create mode 100644 amulet_map_editor/api/wx/ui/block_select/properties/single_properties/__init__.py create mode 100644 amulet_map_editor/api/wx/ui/block_select/properties/single_properties/automatic_single_properties.py create mode 100644 amulet_map_editor/api/wx/ui/block_select/properties/single_properties/base_single_properties.py create mode 100644 amulet_map_editor/api/wx/ui/block_select/properties/single_properties/manual_single_properties.py create mode 100644 amulet_map_editor/api/wx/ui/block_select/properties/single_properties/single_properties.py diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/base_property.py b/amulet_map_editor/api/wx/ui/block_select/properties/base_property.py index 9629ac0c..66fc8d3f 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/base_property.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/base_property.py @@ -2,8 +2,6 @@ from typing import Tuple import PyMCTranslate -from amulet.api.block import PropertyType -from .events import PropertiesChangeEvent class BasePropertySelect(wx.Panel): @@ -80,25 +78,3 @@ def _rebuild_ui(self): Run when the version or block is changed. """ raise NotImplementedError - - @property - def properties(self) -> PropertyType: - """ - The selected values for each property. - - :return: A dictionary mapping property name to the nbt value. - """ - return self._get_properties() - - @properties.setter - def properties(self, properties: PropertyType): - self._set_properties(properties) - wx.PostEvent(self, PropertiesChangeEvent()) - - def _get_properties(self) -> PropertyType: - """Get the selected values for each property.""" - raise NotImplementedError - - def _set_properties(self, properties: PropertyType): - """Set the selected values for each property.""" - raise NotImplementedError diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/properties.py b/amulet_map_editor/api/wx/ui/block_select/properties/properties.py deleted file mode 100644 index d80f2ac0..00000000 --- a/amulet_map_editor/api/wx/ui/block_select/properties/properties.py +++ /dev/null @@ -1,333 +0,0 @@ -import wx -from typing import Tuple, Dict, List - -import PyMCTranslate -import amulet_nbt -from amulet_nbt import SNBTType -from amulet.api.block import PropertyDataTypes, PropertyType -from amulet_map_editor.api.image import ADD_ICON, SUBTRACT_ICON -from .events import PropertiesChangeEvent -from .base_property import BasePropertySelect - - -class PropertySelect(BasePropertySelect): - """ - This is a UI which lets the user pick one value for each property value for a given block. - If the block is known it will be populated from the specification. - If it is not known the user can populate it themselves. - """ - - def __init__( - self, - parent: wx.Window, - translation_manager: PyMCTranslate.TranslationManager, - platform: str, - version_number: Tuple[int, int, int], - force_blockstate: bool, - namespace: str, - block_name: str, - properties: PropertyType = None, - ): - super().__init__( - parent, - translation_manager, - platform, - version_number, - force_blockstate, - namespace, - block_name, - style=wx.BORDER_SIMPLE, - ) - - self._manual_enabled = False - self._simple = SimplePropertySelect(self, translation_manager) - self._sizer.Add(self._simple, 1, wx.EXPAND) - self._manual = ManualPropertySelect(self, translation_manager) - self._sizer.Add(self._manual, 1, wx.EXPAND) - - if properties is None: - properties = {} - self._set_properties(properties) - self._rebuild_ui() - - def _get_properties(self) -> PropertyType: - if self._manual_enabled: - return self._manual.properties - else: - return self._simple.properties - - def _set_properties(self, properties: PropertyType): - self.Freeze() - if self._manual_enabled: - self._manual.properties = properties - else: - self._simple.properties = properties - self.TopLevelParent.Layout() - self.Thaw() - - def _rebuild_ui(self): - self.Freeze() - translator = self._translation_manager.get_version( - self._platform, self._version_number - ).block - - self._manual_enabled = self._block_name not in translator.base_names( - self._namespace, self._force_blockstate - ) - if self._manual_enabled: - self._simple.Hide() - self._manual.Show() - else: - self._manual.Hide() - self._simple.Show() - self._simple.rebuild_ui( - translator.get_specification( - self._namespace, self._block_name, self._force_blockstate - ) - ) - self.Thaw() - - -class BaseSubPropertySelect(wx.Panel): - def __init__( - self, - parent: wx.Window, - translation_manager: PyMCTranslate.TranslationManager, - ): - super().__init__(parent) - self._sizer = wx.BoxSizer(wx.VERTICAL) - self.SetSizer(self._sizer) - self._translation_manager = translation_manager - - @property - def properties(self) -> PropertyType: - raise NotImplementedError - - @properties.setter - def properties(self, properties: PropertyType): - raise NotImplementedError - - -class SimplePropertySelect(BaseSubPropertySelect): - def __init__( - self, - parent: wx.Window, - translation_manager: PyMCTranslate.TranslationManager, - ): - super().__init__(parent, translation_manager) - - header_sizer = wx.BoxSizer(wx.HORIZONTAL) - self._sizer.Add(header_sizer, 0, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, 5) - label = wx.StaticText(self, label="Property Name", style=wx.ALIGN_CENTER) - header_sizer.Add(label, 1) - label = wx.StaticText( - self, label="Property Value (SNBT)", style=wx.ALIGN_CENTER - ) - header_sizer.Add(label, 1, wx.LEFT, 5) - self._property_sizer = wx.GridSizer(2, 5, 5) - self._sizer.Add(self._property_sizer, 0, wx.ALL | wx.EXPAND, 5) - - self._properties: Dict[str, wx.Choice] = {} - self._specification: dict = {} - - def rebuild_ui(self, specification: dict): - """ - Rebuild the UI. - Run when the version or block is changed. - """ - self._specification = specification - self.Freeze() - self._properties.clear() - self._property_sizer.Clear(True) - spec_properties: Dict[str, List[str]] = self._specification.get( - "properties", {} - ) - - for name, choices in spec_properties.items(): - label = wx.StaticText(self, label=name) - self._property_sizer.Add(label, 0, wx.ALIGN_CENTER) - choice = wx.Choice(self, choices=choices) - self._property_sizer.Add(choice, 0, wx.EXPAND) - choice.Bind( - wx.EVT_CHOICE, - lambda evt: wx.PostEvent( - self, - PropertiesChangeEvent(self.GetId(), properties=self.properties), - ), - ) - self._properties[name] = choice - self.properties = self._specification.get("defaults", {}) - self.Fit() - self.GetTopLevelParent().Layout() - self.Thaw() - - @property - def properties(self) -> PropertyType: - return { - name: amulet_nbt.from_snbt(choice.GetString(choice.GetSelection())) - for name, choice in self._properties.items() - } - - @properties.setter - def properties(self, properties: PropertyType): - self.Freeze() - for name, nbt in properties.items(): - if name in self._properties: - if isinstance(nbt, PropertyDataTypes): - snbt = nbt.to_snbt() - elif isinstance(nbt, str): - snbt = nbt - else: - continue - choice = self._properties[name] - index = choice.FindString(snbt) - if index != wx.NOT_FOUND: - choice.SetSelection(index) - self.Thaw() - - -class ManualPropertySelect(BaseSubPropertySelect): - def __init__( - self, parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager - ): - super().__init__(parent, translation_manager) - header_sizer = wx.BoxSizer(wx.HORIZONTAL) - add_button = wx.BitmapButton( - self, bitmap=ADD_ICON.bitmap(30, 30), size=(30, 30) - ) - header_sizer.Add(add_button) - self._sizer.Add(header_sizer, 0, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, 5) - label = wx.StaticText(self, label="Property Name", style=wx.ALIGN_CENTER) - header_sizer.Add(label, 1, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5) - label = wx.StaticText( - self, label="Property Value (SNBT)", style=wx.ALIGN_CENTER - ) - header_sizer.Add(label, 1, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5) - header_sizer.AddStretchSpacer(1) - - self._property_sizer = wx.BoxSizer(wx.VERTICAL) - self._sizer.Add( - self._property_sizer, 0, wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, 5 - ) - - add_button.Bind(wx.EVT_BUTTON, lambda evt: self._add_property()) - - self._property_index = 0 - self._properties: Dict[int, Tuple[wx.TextCtrl, wx.TextCtrl]] = {} - - def _post_property_change(self): - wx.PostEvent( - self, PropertiesChangeEvent(self.GetId(), properties=self.properties) - ) - - def _add_property(self, name: str = "", value: SNBTType = ""): - self.Freeze() - sizer = wx.BoxSizer(wx.HORIZONTAL) - self._property_index += 1 - subtract_button = wx.BitmapButton( - self, bitmap=SUBTRACT_ICON.bitmap(30, 30), size=(30, 30) - ) - sizer.Add(subtract_button, 0, wx.ALIGN_CENTER_VERTICAL) - index = self._property_index - subtract_button.Bind( - wx.EVT_BUTTON, lambda evt: self._on_remove_property(sizer, index) - ) - name_entry = wx.TextCtrl(self, value=name, style=wx.TE_CENTER) - sizer.Add(name_entry, 1, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) - name_entry.Bind(wx.EVT_TEXT, lambda evt: self._post_property_change()) - value_entry = wx.TextCtrl(self, value=value, style=wx.TE_CENTER) - sizer.Add(value_entry, 1, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) - snbt_text = wx.StaticText(self, style=wx.ALIGN_CENTER) - sizer.Add(snbt_text, 1, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) - self._change_value("", snbt_text) - value_entry.Bind(wx.EVT_TEXT, lambda evt: self._on_value_change(evt, snbt_text)) - - self._property_sizer.Add(sizer, 1, wx.TOP | wx.EXPAND, 5) - self._properties[self._property_index] = (name_entry, value_entry) - self.Fit() - self.TopLevelParent.Layout() - self.Thaw() - - def _on_value_change(self, evt, snbt_text: wx.StaticText): - self._change_value(evt.GetString(), snbt_text) - self._post_property_change() - evt.Skip() - - def _change_value(self, snbt: SNBTType, snbt_text: wx.StaticText): - try: - nbt = amulet_nbt.from_snbt(snbt) - except: - snbt_text.SetLabel("Invalid SNBT") - snbt_text.SetBackgroundColour((255, 200, 200)) - else: - if isinstance(nbt, PropertyDataTypes): - snbt_text.SetLabel(nbt.to_snbt()) - snbt_text.SetBackgroundColour(wx.NullColour) - else: - snbt_text.SetLabel(f"{nbt.__class__.__name__} not valid") - snbt_text.SetBackgroundColour((255, 200, 200)) - self.Layout() - - def _on_remove_property(self, sizer: wx.Sizer, key: int): - self.Freeze() - self._property_sizer.Detach(sizer) - sizer.Clear(True) - del self._properties[key] - self.TopLevelParent.Layout() - self.Thaw() - self._post_property_change() - - @property - def properties(self) -> PropertyType: - properties = {} - for name_ui, value_ui in self._properties.values(): - try: - nbt = amulet_nbt.from_snbt(value_ui.GetValue()) - except: - continue - name: str = name_ui.GetValue() - if name and isinstance(nbt, PropertyDataTypes): - properties[name] = nbt - return properties - - @properties.setter - def properties(self, properties: PropertyType): - self._property_sizer.Clear(True) - self._properties.clear() - self._property_index = 0 - for name, value in properties.items(): - self._add_property(name, value.to_snbt()) - - -def demo(): - """ - Show a demo version of the UI. - An app instance must be created first. - """ - translation_manager = PyMCTranslate.new_translation_manager() - for block in (("minecraft", "oak_fence"), ("modded", "block")): - dialog = wx.Dialog( - None, - title=f"PropertySelect with block {block[0]}:{block[1]}", - style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT, - ) - sizer = wx.BoxSizer() - dialog.SetSizer(sizer) - sizer.Add( - PropertySelect( - dialog, translation_manager, "java", (1, 16, 0), False, *block - ), - 1, - wx.ALL, - 5, - ) - - def get_on_close(dialog_): - def on_close(evt): - dialog_.Destroy() - - return on_close - - dialog.Bind(wx.EVT_CLOSE, get_on_close(dialog)) - dialog.Show() - dialog.Fit() diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/__init__.py b/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/__init__.py new file mode 100644 index 00000000..b764589c --- /dev/null +++ b/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/__init__.py @@ -0,0 +1 @@ +from .single_properties import SinglePropertySelect diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/automatic_single_properties.py b/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/automatic_single_properties.py new file mode 100644 index 00000000..82642749 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/automatic_single_properties.py @@ -0,0 +1,91 @@ +import wx +from typing import Dict, List + +import PyMCTranslate +import amulet_nbt +from amulet.api.block import PropertyDataTypes, PropertyType +from ..events import PropertiesChangeEvent +from .base_single_properties import BaseSingleProperty + + +class AutomaticSingleProperty(BaseSingleProperty): + """ + A UI from which a user can choose one value for each property. + + The UI is automatically populated from the given specification. + """ + def __init__( + self, + parent: wx.Window, + translation_manager: PyMCTranslate.TranslationManager, + ): + super().__init__(parent, translation_manager) + + header_sizer = wx.BoxSizer(wx.HORIZONTAL) + self._sizer.Add(header_sizer, 0, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, 5) + label = wx.StaticText(self, label="Property Name", style=wx.ALIGN_CENTER) + header_sizer.Add(label, 1) + label = wx.StaticText( + self, label="Property Value (SNBT)", style=wx.ALIGN_CENTER + ) + header_sizer.Add(label, 1, wx.LEFT, 5) + self._property_sizer = wx.GridSizer(2, 5, 5) + self._sizer.Add(self._property_sizer, 0, wx.ALL | wx.EXPAND, 5) + + self._properties: Dict[str, wx.Choice] = {} + self._specification: dict = {} + + def rebuild_ui(self, specification: dict): + """ + Rebuild the UI from the given specification. + Run when the version or block is changed. + """ + self._specification = specification + self.Freeze() + self._properties.clear() + self._property_sizer.Clear(True) + spec_properties: Dict[str, List[str]] = self._specification.get( + "properties", {} + ) + + for name, choices in spec_properties.items(): + label = wx.StaticText(self, label=name) + self._property_sizer.Add(label, 0, wx.ALIGN_CENTER) + choice = wx.Choice(self, choices=choices) + self._property_sizer.Add(choice, 0, wx.EXPAND) + choice.Bind( + wx.EVT_CHOICE, + lambda evt: wx.PostEvent( + self, + PropertiesChangeEvent(self.GetId(), properties=self.properties), + ), + ) + self._properties[name] = choice + self.properties = self._specification.get("defaults", {}) + self.Fit() + self.GetTopLevelParent().Layout() + self.Thaw() + + @property + def properties(self) -> PropertyType: + return { + name: amulet_nbt.from_snbt(choice.GetString(choice.GetSelection())) + for name, choice in self._properties.items() + } + + @properties.setter + def properties(self, properties: PropertyType): + self.Freeze() + for name, nbt in properties.items(): + if name in self._properties: + if isinstance(nbt, PropertyDataTypes): + snbt = nbt.to_snbt() + elif isinstance(nbt, str): + snbt = nbt + else: + continue + choice = self._properties[name] + index = choice.FindString(snbt) + if index != wx.NOT_FOUND: + choice.SetSelection(index) + self.Thaw() diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/base_single_properties.py b/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/base_single_properties.py new file mode 100644 index 00000000..0759b02d --- /dev/null +++ b/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/base_single_properties.py @@ -0,0 +1,33 @@ +import wx + +import PyMCTranslate +from amulet.api.block import PropertyType + + +class BaseSingleProperty(wx.Panel): + """ + A UI from which a user can choose one value for each property. + + This is base class for both flavours of single property selection UIs. + Subclasses must implement the logic. + """ + def __init__( + self, + parent: wx.Window, + translation_manager: PyMCTranslate.TranslationManager, + ): + super().__init__(parent) + self._sizer = wx.BoxSizer(wx.VERTICAL) + self.SetSizer(self._sizer) + self._translation_manager = translation_manager + + @property + def properties(self) -> PropertyType: + """ + A dictionary mapping string property names to the selected NBT values. + """ + raise NotImplementedError + + @properties.setter + def properties(self, properties: PropertyType): + raise NotImplementedError diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/manual_single_properties.py b/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/manual_single_properties.py new file mode 100644 index 00000000..b081e8fb --- /dev/null +++ b/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/manual_single_properties.py @@ -0,0 +1,135 @@ +import wx +from typing import Tuple, Dict + +import PyMCTranslate +import amulet_nbt +from amulet_nbt import SNBTType +from amulet.api.block import PropertyDataTypes, PropertyType +from amulet_map_editor.api.image import ADD_ICON, SUBTRACT_ICON +from ..events import PropertiesChangeEvent +from .base_single_properties import BaseSingleProperty + + +class ManualSingleProperty(BaseSingleProperty): + """ + A UI from which a user can choose one value for each property. + + This is used when the block is not know so the user can define the properties themselves. + """ + def __init__( + self, parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager + ): + super().__init__(parent, translation_manager) + header_sizer = wx.BoxSizer(wx.HORIZONTAL) + add_button = wx.BitmapButton( + self, bitmap=ADD_ICON.bitmap(30, 30), size=(30, 30) + ) + header_sizer.Add(add_button) + self._sizer.Add(header_sizer, 0, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, 5) + label = wx.StaticText(self, label="Property Name", style=wx.ALIGN_CENTER) + header_sizer.Add(label, 1, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5) + label = wx.StaticText( + self, label="Property Value (SNBT)", style=wx.ALIGN_CENTER + ) + header_sizer.Add(label, 1, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5) + header_sizer.AddStretchSpacer(1) + + self._property_sizer = wx.BoxSizer(wx.VERTICAL) + self._sizer.Add( + self._property_sizer, 0, wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, 5 + ) + + add_button.Bind(wx.EVT_BUTTON, lambda evt: self._add_property()) + + self._property_index = 0 + self._properties: Dict[int, Tuple[wx.TextCtrl, wx.TextCtrl]] = {} + + def _post_property_change(self): + wx.PostEvent( + self, PropertiesChangeEvent(self.GetId(), properties=self.properties) + ) + + def _add_property(self, name: str = "", value: SNBTType = ""): + """ + Add a property to the UI with the given data. + + :param name: The name of the property. + :param value: The SNBT text of the value for that property. + :return: + """ + self.Freeze() + sizer = wx.BoxSizer(wx.HORIZONTAL) + self._property_index += 1 + subtract_button = wx.BitmapButton( + self, bitmap=SUBTRACT_ICON.bitmap(30, 30), size=(30, 30) + ) + sizer.Add(subtract_button, 0, wx.ALIGN_CENTER_VERTICAL) + index = self._property_index + subtract_button.Bind( + wx.EVT_BUTTON, lambda evt: self._on_remove_property(sizer, index) + ) + name_entry = wx.TextCtrl(self, value=name, style=wx.TE_CENTER) + sizer.Add(name_entry, 1, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) + name_entry.Bind(wx.EVT_TEXT, lambda evt: self._post_property_change()) + value_entry = wx.TextCtrl(self, value=value, style=wx.TE_CENTER) + sizer.Add(value_entry, 1, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) + snbt_text = wx.StaticText(self, style=wx.ALIGN_CENTER) + sizer.Add(snbt_text, 1, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) + self._change_value("", snbt_text) + value_entry.Bind(wx.EVT_TEXT, lambda evt: self._on_value_change(evt, snbt_text)) + + self._property_sizer.Add(sizer, 1, wx.TOP | wx.EXPAND, 5) + self._properties[self._property_index] = (name_entry, value_entry) + self.Fit() + self.TopLevelParent.Layout() + self.Thaw() + + def _on_value_change(self, evt, snbt_text: wx.StaticText): + self._change_value(evt.GetString(), snbt_text) + self._post_property_change() + evt.Skip() + + def _change_value(self, snbt: SNBTType, snbt_text: wx.StaticText): + try: + nbt = amulet_nbt.from_snbt(snbt) + except: + snbt_text.SetLabel("Invalid SNBT") + snbt_text.SetBackgroundColour((255, 200, 200)) + else: + if isinstance(nbt, PropertyDataTypes): + snbt_text.SetLabel(nbt.to_snbt()) + snbt_text.SetBackgroundColour(wx.NullColour) + else: + snbt_text.SetLabel(f"{nbt.__class__.__name__} not valid") + snbt_text.SetBackgroundColour((255, 200, 200)) + self.Layout() + + def _on_remove_property(self, sizer: wx.Sizer, key: int): + self.Freeze() + self._property_sizer.Detach(sizer) + sizer.Clear(True) + del self._properties[key] + self.TopLevelParent.Layout() + self.Thaw() + self._post_property_change() + + @property + def properties(self) -> PropertyType: + properties = {} + for name_ui, value_ui in self._properties.values(): + try: + nbt = amulet_nbt.from_snbt(value_ui.GetValue()) + except: + continue + name: str = name_ui.GetValue() + if name and isinstance(nbt, PropertyDataTypes): + properties[name] = nbt + return properties + + @properties.setter + def properties(self, properties: PropertyType): + self._property_sizer.Clear(True) + self._properties.clear() + self._property_index = 0 + for name, value in properties.items(): + self._add_property(name, value.to_snbt()) diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/single_properties.py b/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/single_properties.py new file mode 100644 index 00000000..447742cd --- /dev/null +++ b/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/single_properties.py @@ -0,0 +1,138 @@ +import wx +from typing import Tuple + +import PyMCTranslate +from amulet.api.block import PropertyType +from ..base_property import BasePropertySelect +from ..events import PropertiesChangeEvent +from .automatic_single_properties import AutomaticSingleProperty +from .manual_single_properties import ManualSingleProperty + + +class SinglePropertySelect(BasePropertySelect): + """ + This is a UI which lets the user pick one value for each property for a given block. + If the block is known it will be populated from the specification. + If it is not known the user can populate it themselves. + """ + + def __init__( + self, + parent: wx.Window, + translation_manager: PyMCTranslate.TranslationManager, + platform: str, + version_number: Tuple[int, int, int], + force_blockstate: bool, + namespace: str, + block_name: str, + properties: PropertyType = None, + ): + super().__init__( + parent, + translation_manager, + platform, + version_number, + force_blockstate, + namespace, + block_name, + style=wx.BORDER_SIMPLE, + ) + + self._manual_enabled = False + self._simple = AutomaticSingleProperty(self, translation_manager) + self._sizer.Add(self._simple, 1, wx.EXPAND) + self._manual = ManualSingleProperty(self, translation_manager) + self._sizer.Add(self._manual, 1, wx.EXPAND) + + if properties is None: + properties = {} + self._set_properties(properties) + self._rebuild_ui() + + + @property + def properties(self) -> PropertyType: + """ + The selected values for each property. + + :return: A dictionary mapping property name to the nbt value. + """ + return self._get_properties() + + @properties.setter + def properties(self, properties: PropertyType): + self._set_properties(properties) + wx.PostEvent(self, PropertiesChangeEvent()) + + def _get_properties(self) -> PropertyType: + """Get the selected values for each property.""" + if self._manual_enabled: + return self._manual.properties + else: + return self._simple.properties + + def _set_properties(self, properties: PropertyType): + """Set the selected values for each property.""" + self.Freeze() + if self._manual_enabled: + self._manual.properties = properties + else: + self._simple.properties = properties + self.TopLevelParent.Layout() + self.Thaw() + + def _rebuild_ui(self): + self.Freeze() + translator = self._translation_manager.get_version( + self._platform, self._version_number + ).block + + self._manual_enabled = self._block_name not in translator.base_names( + self._namespace, self._force_blockstate + ) + if self._manual_enabled: + self._simple.Hide() + self._manual.Show() + else: + self._manual.Hide() + self._simple.Show() + self._simple.rebuild_ui( + translator.get_specification( + self._namespace, self._block_name, self._force_blockstate + ) + ) + self.Thaw() + + +def demo(): + """ + Show a demo version of the UI. + An app instance must be created first. + """ + translation_manager = PyMCTranslate.new_translation_manager() + for block in (("minecraft", "oak_fence"), ("modded", "block")): + dialog = wx.Dialog( + None, + title=f"PropertySelect with block {block[0]}:{block[1]}", + style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT, + ) + sizer = wx.BoxSizer() + dialog.SetSizer(sizer) + sizer.Add( + SinglePropertySelect( + dialog, translation_manager, "java", (1, 16, 0), False, *block + ), + 1, + wx.ALL, + 5, + ) + + def get_on_close(dialog_): + def on_close(evt): + dialog_.Destroy() + + return on_close + + dialog.Bind(wx.EVT_CLOSE, get_on_close(dialog)) + dialog.Show() + dialog.Fit() From 517040efad7b7ee0ac9dbd9e373115f0cd6b28eb Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Wed, 30 Jun 2021 11:09:48 +0100 Subject: [PATCH 023/139] Reformatted --- .../properties/single_properties/automatic_single_properties.py | 1 + .../properties/single_properties/base_single_properties.py | 1 + .../properties/single_properties/manual_single_properties.py | 1 + .../properties/single_properties/single_properties.py | 1 - 4 files changed, 3 insertions(+), 1 deletion(-) diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/automatic_single_properties.py b/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/automatic_single_properties.py index 82642749..a2111be6 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/automatic_single_properties.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/automatic_single_properties.py @@ -14,6 +14,7 @@ class AutomaticSingleProperty(BaseSingleProperty): The UI is automatically populated from the given specification. """ + def __init__( self, parent: wx.Window, diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/base_single_properties.py b/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/base_single_properties.py index 0759b02d..5be5d6f8 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/base_single_properties.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/base_single_properties.py @@ -11,6 +11,7 @@ class BaseSingleProperty(wx.Panel): This is base class for both flavours of single property selection UIs. Subclasses must implement the logic. """ + def __init__( self, parent: wx.Window, diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/manual_single_properties.py b/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/manual_single_properties.py index b081e8fb..c60e4514 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/manual_single_properties.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/manual_single_properties.py @@ -16,6 +16,7 @@ class ManualSingleProperty(BaseSingleProperty): This is used when the block is not know so the user can define the properties themselves. """ + def __init__( self, parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager ): diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/single_properties.py b/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/single_properties.py index 447742cd..0844ef5c 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/single_properties.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/single_properties.py @@ -49,7 +49,6 @@ def __init__( self._set_properties(properties) self._rebuild_ui() - @property def properties(self) -> PropertyType: """ From 30939ca0a09973f5894f499b47b841d0fb999eb1 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Wed, 30 Jun 2021 14:37:16 +0100 Subject: [PATCH 024/139] Cleaned up some name issues --- amulet_map_editor/api/wx/ui/block_select/__init__.py | 2 +- amulet_map_editor/api/wx/ui/block_select/block_define.py | 6 +++--- .../api/wx/ui/block_select/properties/__init__.py | 2 +- amulet_map_editor/api/wx/ui/block_select/properties/demo.py | 6 +++--- .../block_select/properties/single_properties/__init__.py | 4 +++- .../properties/single_properties/single_properties.py | 2 +- 6 files changed, 12 insertions(+), 10 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/block_select/__init__.py b/amulet_map_editor/api/wx/ui/block_select/__init__.py index d33b294f..2b602e1f 100644 --- a/amulet_map_editor/api/wx/ui/block_select/__init__.py +++ b/amulet_map_editor/api/wx/ui/block_select/__init__.py @@ -1,4 +1,4 @@ from .block_select import BlockSelect -from .properties import PropertySelect, EVT_PROPERTIES_CHANGE +from .properties import SinglePropertySelect, EVT_PROPERTIES_CHANGE from .block_define import BlockDefine from .multi_block_define import MultiBlockDefine diff --git a/amulet_map_editor/api/wx/ui/block_select/block_define.py b/amulet_map_editor/api/wx/ui/block_select/block_define.py index 39e52dc8..a171dd32 100644 --- a/amulet_map_editor/api/wx/ui/block_select/block_define.py +++ b/amulet_map_editor/api/wx/ui/block_select/block_define.py @@ -10,7 +10,7 @@ from amulet_map_editor.api.wx.ui.block_select import BlockSelect from amulet_map_editor.api.wx.ui.block_select.properties import ( - PropertySelect, + SinglePropertySelect, WildcardPropertySelect, EVT_PROPERTIES_CHANGE, ) @@ -55,7 +55,7 @@ def __init__( self._sizer.Add(right_sizer, 1, wx.EXPAND | border, 5) self._wildcard_mode = wildcard_properties if wildcard_properties: - self._property_picker = PropertySelect( + self._property_picker = SinglePropertySelect( self, translation_manager, self._version_picker.platform, @@ -66,7 +66,7 @@ def __init__( properties, ) else: - self._property_picker = PropertySelect( + self._property_picker = SinglePropertySelect( self, translation_manager, self._version_picker.platform, diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/__init__.py b/amulet_map_editor/api/wx/ui/block_select/properties/__init__.py index 8eb168f5..ef5f3004 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/__init__.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/__init__.py @@ -1,3 +1,3 @@ from .events import PropertiesChangeEvent, EVT_PROPERTIES_CHANGE -from .properties import PropertySelect +from .single_properties import SinglePropertySelect from .wildcard_properties import WildcardPropertySelect diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/demo.py b/amulet_map_editor/api/wx/ui/block_select/properties/demo.py index 5a3b6229..b24168f7 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/demo.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/demo.py @@ -1,6 +1,6 @@ import wx -from amulet_map_editor.api.wx.ui.block_select.properties.properties import ( - demo as properties_demo, +from amulet_map_editor.api.wx.ui.block_select.properties.single_properties import ( + demo as single_properties_demo, ) from amulet_map_editor.api.wx.ui.block_select.properties.wildcard_properties import ( demo as wildcard_properties_demo, @@ -12,7 +12,7 @@ def demo(): Show a demo version of the UI. An app instance must be created first. """ - properties_demo() + single_properties_demo() wildcard_properties_demo() diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/__init__.py b/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/__init__.py index b764589c..942cbe66 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/__init__.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/__init__.py @@ -1 +1,3 @@ -from .single_properties import SinglePropertySelect +"""A UI from which a user can chose one value for each property.""" + +from .single_properties import SinglePropertySelect, demo diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/single_properties.py b/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/single_properties.py index 0844ef5c..f77f842e 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/single_properties.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/single_properties.py @@ -112,7 +112,7 @@ def demo(): for block in (("minecraft", "oak_fence"), ("modded", "block")): dialog = wx.Dialog( None, - title=f"PropertySelect with block {block[0]}:{block[1]}", + title=f"SinglePropertySelect with block {block[0]}:{block[1]}", style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT, ) sizer = wx.BoxSizer() From 57b015c9b91fd0692e7b0faa140d0f5c1e357136 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Wed, 30 Jun 2021 15:22:31 +0100 Subject: [PATCH 025/139] Renamed packages and modules to be more readable --- .../properties/{single_properties => single}/__init__.py | 2 +- .../automatic_single_properties.py => single/automatic.py} | 2 +- .../base_single_properties.py => single/base.py} | 0 .../single_properties.py => single/main.py} | 4 ++-- .../manual_single_properties.py => single/manual.py} | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) rename amulet_map_editor/api/wx/ui/block_select/properties/{single_properties => single}/__init__.py (54%) rename amulet_map_editor/api/wx/ui/block_select/properties/{single_properties/automatic_single_properties.py => single/automatic.py} (98%) rename amulet_map_editor/api/wx/ui/block_select/properties/{single_properties/base_single_properties.py => single/base.py} (100%) rename amulet_map_editor/api/wx/ui/block_select/properties/{single_properties/single_properties.py => single/main.py} (97%) rename amulet_map_editor/api/wx/ui/block_select/properties/{single_properties/manual_single_properties.py => single/manual.py} (98%) diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/__init__.py b/amulet_map_editor/api/wx/ui/block_select/properties/single/__init__.py similarity index 54% rename from amulet_map_editor/api/wx/ui/block_select/properties/single_properties/__init__.py rename to amulet_map_editor/api/wx/ui/block_select/properties/single/__init__.py index 942cbe66..68b0d7e9 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/__init__.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/single/__init__.py @@ -1,3 +1,3 @@ """A UI from which a user can chose one value for each property.""" -from .single_properties import SinglePropertySelect, demo +from .main import SinglePropertySelect, demo diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/automatic_single_properties.py b/amulet_map_editor/api/wx/ui/block_select/properties/single/automatic.py similarity index 98% rename from amulet_map_editor/api/wx/ui/block_select/properties/single_properties/automatic_single_properties.py rename to amulet_map_editor/api/wx/ui/block_select/properties/single/automatic.py index a2111be6..dab5ae27 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/automatic_single_properties.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/single/automatic.py @@ -5,7 +5,7 @@ import amulet_nbt from amulet.api.block import PropertyDataTypes, PropertyType from ..events import PropertiesChangeEvent -from .base_single_properties import BaseSingleProperty +from .base import BaseSingleProperty class AutomaticSingleProperty(BaseSingleProperty): diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/base_single_properties.py b/amulet_map_editor/api/wx/ui/block_select/properties/single/base.py similarity index 100% rename from amulet_map_editor/api/wx/ui/block_select/properties/single_properties/base_single_properties.py rename to amulet_map_editor/api/wx/ui/block_select/properties/single/base.py diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/single_properties.py b/amulet_map_editor/api/wx/ui/block_select/properties/single/main.py similarity index 97% rename from amulet_map_editor/api/wx/ui/block_select/properties/single_properties/single_properties.py rename to amulet_map_editor/api/wx/ui/block_select/properties/single/main.py index f77f842e..4896d7d5 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/single_properties.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/single/main.py @@ -5,8 +5,8 @@ from amulet.api.block import PropertyType from ..base_property import BasePropertySelect from ..events import PropertiesChangeEvent -from .automatic_single_properties import AutomaticSingleProperty -from .manual_single_properties import ManualSingleProperty +from .automatic import AutomaticSingleProperty +from .manual import ManualSingleProperty class SinglePropertySelect(BasePropertySelect): diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/manual_single_properties.py b/amulet_map_editor/api/wx/ui/block_select/properties/single/manual.py similarity index 98% rename from amulet_map_editor/api/wx/ui/block_select/properties/single_properties/manual_single_properties.py rename to amulet_map_editor/api/wx/ui/block_select/properties/single/manual.py index c60e4514..3467dc34 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/single_properties/manual_single_properties.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/single/manual.py @@ -7,7 +7,7 @@ from amulet.api.block import PropertyDataTypes, PropertyType from amulet_map_editor.api.image import ADD_ICON, SUBTRACT_ICON from ..events import PropertiesChangeEvent -from .base_single_properties import BaseSingleProperty +from .base import BaseSingleProperty class ManualSingleProperty(BaseSingleProperty): From 9118e4c369c457a3248802f6aa9619f5d0128a43 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Wed, 30 Jun 2021 15:32:24 +0100 Subject: [PATCH 026/139] Moved wildcard properties into a package --- .../wx/ui/block_select/properties/__init__.py | 4 +- .../properties/{base_property.py => base.py} | 0 .../api/wx/ui/block_select/properties/demo.py | 4 +- .../properties/multiple/__init__.py | 1 + .../properties/multiple/automatic/__init__.py | 1 + .../multiple/automatic/automatic.py | 99 +++++ .../properties/multiple/automatic/popup.py | 89 ++++ .../block_select/properties/multiple/base.py | 24 + .../block_select/properties/multiple/main.py | 134 ++++++ .../properties/multiple/manual.py | 123 ++++++ .../ui/block_select/properties/single/main.py | 2 +- .../properties/wildcard_properties.py | 415 ------------------ 12 files changed, 476 insertions(+), 420 deletions(-) rename amulet_map_editor/api/wx/ui/block_select/properties/{base_property.py => base.py} (100%) create mode 100644 amulet_map_editor/api/wx/ui/block_select/properties/multiple/__init__.py create mode 100644 amulet_map_editor/api/wx/ui/block_select/properties/multiple/automatic/__init__.py create mode 100644 amulet_map_editor/api/wx/ui/block_select/properties/multiple/automatic/automatic.py create mode 100644 amulet_map_editor/api/wx/ui/block_select/properties/multiple/automatic/popup.py create mode 100644 amulet_map_editor/api/wx/ui/block_select/properties/multiple/base.py create mode 100644 amulet_map_editor/api/wx/ui/block_select/properties/multiple/main.py create mode 100644 amulet_map_editor/api/wx/ui/block_select/properties/multiple/manual.py delete mode 100644 amulet_map_editor/api/wx/ui/block_select/properties/wildcard_properties.py diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/__init__.py b/amulet_map_editor/api/wx/ui/block_select/properties/__init__.py index ef5f3004..338a2029 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/__init__.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/__init__.py @@ -1,3 +1,3 @@ from .events import PropertiesChangeEvent, EVT_PROPERTIES_CHANGE -from .single_properties import SinglePropertySelect -from .wildcard_properties import WildcardPropertySelect +from .single import SinglePropertySelect +from .multiple import WildcardPropertySelect diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/base_property.py b/amulet_map_editor/api/wx/ui/block_select/properties/base.py similarity index 100% rename from amulet_map_editor/api/wx/ui/block_select/properties/base_property.py rename to amulet_map_editor/api/wx/ui/block_select/properties/base.py diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/demo.py b/amulet_map_editor/api/wx/ui/block_select/properties/demo.py index b24168f7..ddf37858 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/demo.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/demo.py @@ -1,8 +1,8 @@ import wx -from amulet_map_editor.api.wx.ui.block_select.properties.single_properties import ( +from amulet_map_editor.api.wx.ui.block_select.properties.single import ( demo as single_properties_demo, ) -from amulet_map_editor.api.wx.ui.block_select.properties.wildcard_properties import ( +from amulet_map_editor.api.wx.ui.block_select.properties.multiple import ( demo as wildcard_properties_demo, ) diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/multiple/__init__.py b/amulet_map_editor/api/wx/ui/block_select/properties/multiple/__init__.py new file mode 100644 index 00000000..6258c532 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/block_select/properties/multiple/__init__.py @@ -0,0 +1 @@ +from .main import WildcardPropertySelect, demo diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/multiple/automatic/__init__.py b/amulet_map_editor/api/wx/ui/block_select/properties/multiple/automatic/__init__.py new file mode 100644 index 00000000..208151cb --- /dev/null +++ b/amulet_map_editor/api/wx/ui/block_select/properties/multiple/automatic/__init__.py @@ -0,0 +1 @@ +from .automatic import SimpleWildcardPropertySelect diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/multiple/automatic/automatic.py b/amulet_map_editor/api/wx/ui/block_select/properties/multiple/automatic/automatic.py new file mode 100644 index 00000000..1978241e --- /dev/null +++ b/amulet_map_editor/api/wx/ui/block_select/properties/multiple/automatic/automatic.py @@ -0,0 +1,99 @@ +import wx +from typing import Dict, List + +import PyMCTranslate +from amulet.api.block import PropertyDataTypes, PropertyType +from ...events import PropertiesChangeEvent +from ..base import BaseSubWildcardPropertySelect +from .popup import PropertyValueComboPopup + + +class SimpleWildcardPropertySelect(BaseSubWildcardPropertySelect): + def __init__( + self, + parent: wx.Window, + translation_manager: PyMCTranslate.TranslationManager, + ): + super().__init__(parent, translation_manager) + + header_sizer = wx.BoxSizer(wx.HORIZONTAL) + self._sizer.Add(header_sizer, 0, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, 5) + label = wx.StaticText(self, label="Property Name", style=wx.ALIGN_CENTER) + header_sizer.Add(label, 1) + label = wx.StaticText( + self, label="Property Value (SNBT)", style=wx.ALIGN_CENTER + ) + header_sizer.Add(label, 1, wx.LEFT, 5) + self._property_sizer = wx.GridSizer(2, 5, 5) + self._sizer.Add(self._property_sizer, 0, wx.ALL | wx.EXPAND, 5) + + self._properties: Dict[str, PropertyValueComboPopup] = {} + self._specification: dict = {} + + def rebuild_ui(self, specification: dict): + """ + Rebuild the UI. + Run when the version or block is changed. + """ + self._specification = specification + self.Freeze() + self._properties.clear() + self._property_sizer.Clear(True) + spec_properties: Dict[str, List[str]] = self._specification.get( + "properties", {} + ) + + for name, choices in spec_properties.items(): + label = wx.StaticText(self, label=name) + self._property_sizer.Add(label, 0, wx.ALIGN_CENTER) + + def create_choice(): + choice = wx.ComboCtrl(self, style=wx.CB_READONLY) + l = PropertyValueComboPopup(choices) + choice.SetPopupControl(l) + choice.SetValue(l.GetStringValue()) + + self._property_sizer.Add(choice, 0, wx.EXPAND) + + def on_close(evt): + choice.SetValue(l.GetStringValue()) + wx.PostEvent( + self, + PropertiesChangeEvent(self.GetId(), properties=self.properties), + ) + + choice.Bind( + wx.EVT_COMBOBOX_CLOSEUP, + on_close, + ) + self._properties[name] = l + + create_choice() + self.properties = self._specification.get("defaults", {}) + self.Fit() + self.GetTopLevelParent().Layout() + self.Thaw() + + @property + def properties(self) -> PropertyType: + return { + # name: amulet_nbt.from_snbt(choice.GetString(choice.GetSelection())) + # for name, choice in self._properties.items() + } + + @properties.setter + def properties(self, properties: PropertyType): + self.Freeze() + for name, nbt in properties.items(): + if name in self._properties: + if isinstance(nbt, PropertyDataTypes): + snbt = nbt.to_snbt() + elif isinstance(nbt, str): + snbt = nbt + else: + continue + choice = self._properties[name] + # index = choice.FindString(snbt) + # if index != wx.NOT_FOUND: + # choice.SetSelection(index) + self.Thaw() diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/multiple/automatic/popup.py b/amulet_map_editor/api/wx/ui/block_select/properties/multiple/automatic/popup.py new file mode 100644 index 00000000..25bedddb --- /dev/null +++ b/amulet_map_editor/api/wx/ui/block_select/properties/multiple/automatic/popup.py @@ -0,0 +1,89 @@ +import wx +from typing import Tuple, Iterable + + +class PropertyValueCheckList(wx.Panel): + def __init__(self, parent: wx.Window, values: Iterable[str]): + super().__init__(parent) + sizer = wx.BoxSizer(wx.VERTICAL) + self.SetSizer(sizer) + self._toggle_checkbox = wx.CheckBox(self, style=wx.CHK_3STATE) + self._toggle_checkbox.Set3StateValue(wx.CHK_CHECKED) + sizer.Add(self._toggle_checkbox, 0, wx.ALL, 3) + self._check_list_box = wx.CheckListBox(self, choices=values) + self._check_list_box.SetCheckedStrings(values) + sizer.Add(self._check_list_box, 0) + + self._toggle_checkbox.Bind(wx.EVT_CHECKBOX, self._on_toggle) + self._check_list_box.Bind(wx.EVT_LEFT_DOWN, self._on_left_down) + + @property + def value(self) -> str: + return self._check_list_box.GetStringSelection() + + @value.setter + def value(self, value: str): + self._check_list_box.SetStringSelection(value) + + @property + def extra_values(self) -> Tuple[str]: + return self._check_list_box.GetCheckedStrings() + + @extra_values.setter + def extra_values(self, extra_values: Iterable[str]): + self._check_list_box.SetCheckedStrings(extra_values) + + def _on_toggle(self, evt): + if self._toggle_checkbox.GetValue(): + self._check_list_box.SetCheckedItems( + list(range(self._check_list_box.GetCount())) + ) + else: + self._check_list_box.SetCheckedItems([]) + + def _update_check(self): + items = self._check_list_box.GetCheckedItems() + if len(items) == self._check_list_box.GetCount(): + self._toggle_checkbox.Set3StateValue(wx.CHK_CHECKED) + elif len(items) == 0: + self._toggle_checkbox.Set3StateValue(wx.CHK_UNCHECKED) + else: + self._toggle_checkbox.Set3StateValue(wx.CHK_UNDETERMINED) + + def _on_left_down(self, evt): + # not sure why I need to do this but it works + item = self._check_list_box.HitTest(evt.GetPosition()) + if item >= 0: + self._check_list_box.Check( + item, check=not self._check_list_box.IsChecked(item) + ) + self._update_check() + + +class PropertyValueComboPopup(wx.ComboPopup): + def __init__(self, values: Iterable[str]): + super().__init__() + self.pvcl: PropertyValueCheckList = None + self._values = values + + def Create(self, parent): + self.pvcl = PropertyValueCheckList(parent, self._values) + # self.pvcl.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) + return True + + def OnLeftDown(self, evt): + item = self.pvcl._check_list_box.HitTest(evt.GetPosition()) + if item >= 0: + self.pvcl._check_list_box.Check( + item, check=self.pvcl._check_list_box.IsChecked(item) + ) + + def GetControl(self): + return self.pvcl + + def GetStringValue(self): + return "|".join(self.pvcl.extra_values) + + def GetAdjustedSize(self, minWidth, prefHeight, maxHeight): + self.GetControl().Fit() + return self.GetControl().GetSize() diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/multiple/base.py b/amulet_map_editor/api/wx/ui/block_select/properties/multiple/base.py new file mode 100644 index 00000000..b889c770 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/block_select/properties/multiple/base.py @@ -0,0 +1,24 @@ +import wx + +import PyMCTranslate +from amulet.api.block import PropertyType + + +class BaseSubWildcardPropertySelect(wx.Panel): + def __init__( + self, + parent: wx.Window, + translation_manager: PyMCTranslate.TranslationManager, + ): + super().__init__(parent) + self._sizer = wx.BoxSizer(wx.VERTICAL) + self.SetSizer(self._sizer) + self._translation_manager = translation_manager + + @property + def properties(self) -> PropertyType: + raise NotImplementedError + + @properties.setter + def properties(self, properties: PropertyType): + raise NotImplementedError diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/multiple/main.py b/amulet_map_editor/api/wx/ui/block_select/properties/multiple/main.py new file mode 100644 index 00000000..2d7bb22e --- /dev/null +++ b/amulet_map_editor/api/wx/ui/block_select/properties/multiple/main.py @@ -0,0 +1,134 @@ +import wx +from typing import Tuple, Dict + +import PyMCTranslate +from amulet.api.block import PropertyType, PropertyValueType +from ..base import BasePropertySelect + +from .automatic import SimpleWildcardPropertySelect +from .manual import ManualWildcardPropertySelect + + +class WildcardPropertySelect(BasePropertySelect): + """ + This is a UI which lets the user pick zero or more values for each property. + If the block is known it will be populated from the specification. + If it is not known the user can populate it themselves. + """ + + def __init__( + self, + parent: wx.Window, + translation_manager: PyMCTranslate.TranslationManager, + platform: str, + version_number: Tuple[int, int, int], + force_blockstate: bool, + namespace: str, + block_name: str, + properties: PropertyType = None, + ): + super().__init__( + parent, + translation_manager, + platform, + version_number, + force_blockstate, + namespace, + block_name, + style=wx.BORDER_SIMPLE, + ) + + self._manual_enabled = False + self._simple = SimpleWildcardPropertySelect(self, translation_manager) + self._sizer.Add(self._simple, 1, wx.EXPAND) + self._manual = ManualWildcardPropertySelect(self, translation_manager) + self._sizer.Add(self._manual, 1, wx.EXPAND) + + if properties is None: + properties = {} + self._set_properties(properties) + self._rebuild_ui() + + def _get_properties(self) -> PropertyType: + if self._manual_enabled: + return self._manual.properties + else: + return self._simple.properties + + def _set_properties(self, properties: PropertyType): + self.Freeze() + if self._manual_enabled: + self._manual.properties = properties + else: + self._simple.properties = properties + self.TopLevelParent.Layout() + self.Thaw() + + @property + def extra_properties(self) -> Dict[str, Tuple[PropertyValueType, ...]]: + """ + The values that are checked for each property. + This UI can have more than one property value checked (ticked) but only one selected (highlighted blue). + :attr:`properties` will return the entry that is highlighted blue. + """ + raise NotImplementedError + + @extra_properties.setter + def extra_properties(self, properties: Dict[str, Tuple[PropertyValueType, ...]]): + raise NotImplementedError + + def _rebuild_ui(self): + self.Freeze() + translator = self._translation_manager.get_version( + self._platform, self._version_number + ).block + + self._manual_enabled = self._block_name not in translator.base_names( + self._namespace, self._force_blockstate + ) + if self._manual_enabled: + self._simple.Hide() + self._manual.Show() + else: + self._manual.Hide() + self._simple.Show() + self._simple.rebuild_ui( + translator.get_specification( + self._namespace, self._block_name, self._force_blockstate + ) + ) + self.Thaw() + + +def demo(): + """ + Show a demo version of the UI. + An app instance must be created first. + """ + translation_manager = PyMCTranslate.new_translation_manager() + for block in (("minecraft", "oak_fence"), ("modded", "block")): + dialog = wx.Dialog( + None, + title=f"WildcardPropertySelect with block {block[0]}:{block[1]}", + style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT, + ) + sizer = wx.BoxSizer() + dialog.SetSizer(sizer) + sizer.Add( + WildcardPropertySelect( + dialog, translation_manager, "java", (1, 16, 0), False, *block + ), + 1, + wx.ALL, + 5, + ) + + def get_on_close(dialog_): + def on_close(evt): + dialog_.Destroy() + + return on_close + + dialog.Bind(wx.EVT_CLOSE, get_on_close(dialog)) + dialog.Show() + dialog.Fit() diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/multiple/manual.py b/amulet_map_editor/api/wx/ui/block_select/properties/multiple/manual.py new file mode 100644 index 00000000..b857f327 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/block_select/properties/multiple/manual.py @@ -0,0 +1,123 @@ +import wx +from typing import Tuple, Dict + +import PyMCTranslate +import amulet_nbt +from amulet_nbt import SNBTType +from amulet.api.block import PropertyDataTypes, PropertyType +from amulet_map_editor.api.image import ADD_ICON, SUBTRACT_ICON +from ..events import PropertiesChangeEvent +from .base import BaseSubWildcardPropertySelect + + +class ManualWildcardPropertySelect(BaseSubWildcardPropertySelect): + def __init__( + self, parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager + ): + super().__init__(parent, translation_manager) + header_sizer = wx.BoxSizer(wx.HORIZONTAL) + add_button = wx.BitmapButton( + self, bitmap=ADD_ICON.bitmap(30, 30), size=(30, 30) + ) + header_sizer.Add(add_button) + self._sizer.Add(header_sizer, 0, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, 5) + label = wx.StaticText(self, label="Property Name", style=wx.ALIGN_CENTER) + header_sizer.Add(label, 1, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5) + label = wx.StaticText( + self, label="Property Value (SNBT)", style=wx.ALIGN_CENTER + ) + header_sizer.Add(label, 1, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5) + header_sizer.AddStretchSpacer(1) + + self._property_sizer = wx.BoxSizer(wx.VERTICAL) + self._sizer.Add( + self._property_sizer, 0, wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, 5 + ) + + add_button.Bind(wx.EVT_BUTTON, lambda evt: self._add_property()) + + self._property_index = 0 + self._properties: Dict[int, Tuple[wx.TextCtrl, wx.TextCtrl]] = {} + + def _post_property_change(self): + wx.PostEvent( + self, PropertiesChangeEvent(self.GetId(), properties=self.properties) + ) + + def _add_property(self, name: str = "", value: SNBTType = ""): + self.Freeze() + sizer = wx.BoxSizer(wx.HORIZONTAL) + self._property_index += 1 + subtract_button = wx.BitmapButton( + self, bitmap=SUBTRACT_ICON.bitmap(30, 30), size=(30, 30) + ) + sizer.Add(subtract_button, 0, wx.ALIGN_CENTER_VERTICAL) + index = self._property_index + subtract_button.Bind( + wx.EVT_BUTTON, lambda evt: self._on_remove_property(sizer, index) + ) + name_entry = wx.TextCtrl(self, value=name, style=wx.TE_CENTER) + sizer.Add(name_entry, 1, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) + name_entry.Bind(wx.EVT_TEXT, lambda evt: self._post_property_change()) + value_entry = wx.TextCtrl(self, value=value, style=wx.TE_CENTER) + sizer.Add(value_entry, 1, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) + snbt_text = wx.StaticText(self, style=wx.ALIGN_CENTER) + sizer.Add(snbt_text, 1, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) + self._change_value("", snbt_text) + value_entry.Bind(wx.EVT_TEXT, lambda evt: self._on_value_change(evt, snbt_text)) + + self._property_sizer.Add(sizer, 1, wx.TOP | wx.EXPAND, 5) + self._properties[self._property_index] = (name_entry, value_entry) + self.Fit() + self.TopLevelParent.Layout() + self.Thaw() + + def _on_value_change(self, evt, snbt_text: wx.StaticText): + self._change_value(evt.GetString(), snbt_text) + self._post_property_change() + evt.Skip() + + def _change_value(self, snbt: SNBTType, snbt_text: wx.StaticText): + try: + nbt = amulet_nbt.from_snbt(snbt) + except: + snbt_text.SetLabel("Invalid SNBT") + snbt_text.SetBackgroundColour((255, 200, 200)) + else: + if isinstance(nbt, PropertyDataTypes): + snbt_text.SetLabel(nbt.to_snbt()) + snbt_text.SetBackgroundColour(wx.NullColour) + else: + snbt_text.SetLabel(f"{nbt.__class__.__name__} not valid") + snbt_text.SetBackgroundColour((255, 200, 200)) + self.Layout() + + def _on_remove_property(self, sizer: wx.Sizer, key: int): + self.Freeze() + self._property_sizer.Detach(sizer) + sizer.Clear(True) + del self._properties[key] + self.TopLevelParent.Layout() + self.Thaw() + self._post_property_change() + + @property + def properties(self) -> PropertyType: + properties = {} + for name_ui, value_ui in self._properties.values(): + try: + nbt = amulet_nbt.from_snbt(value_ui.GetValue()) + except: + continue + name: str = name_ui.GetValue() + if name and isinstance(nbt, PropertyDataTypes): + properties[name] = nbt + return properties + + @properties.setter + def properties(self, properties: PropertyType): + self._property_sizer.Clear(True) + self._properties.clear() + self._property_index = 0 + for name, value in properties.items(): + self._add_property(name, value.to_snbt()) diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/single/main.py b/amulet_map_editor/api/wx/ui/block_select/properties/single/main.py index 4896d7d5..7df3e99f 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/single/main.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/single/main.py @@ -3,7 +3,7 @@ import PyMCTranslate from amulet.api.block import PropertyType -from ..base_property import BasePropertySelect +from ..base import BasePropertySelect from ..events import PropertiesChangeEvent from .automatic import AutomaticSingleProperty from .manual import ManualSingleProperty diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/wildcard_properties.py b/amulet_map_editor/api/wx/ui/block_select/properties/wildcard_properties.py deleted file mode 100644 index 0c174268..00000000 --- a/amulet_map_editor/api/wx/ui/block_select/properties/wildcard_properties.py +++ /dev/null @@ -1,415 +0,0 @@ -import wx -from typing import Tuple, Dict, List, Iterable - -import PyMCTranslate -import amulet_nbt -from amulet_nbt import SNBTType -from amulet.api.block import PropertyDataTypes, PropertyType, PropertyValueType -from amulet_map_editor.api.image import ADD_ICON, SUBTRACT_ICON -from .events import PropertiesChangeEvent -from .base_property import BasePropertySelect - - -class WildcardPropertySelect(BasePropertySelect): - """ - This is a UI which lets the user pick zero or more values for each property. - If the block is known it will be populated from the specification. - If it is not known the user can populate it themselves. - """ - - def __init__( - self, - parent: wx.Window, - translation_manager: PyMCTranslate.TranslationManager, - platform: str, - version_number: Tuple[int, int, int], - force_blockstate: bool, - namespace: str, - block_name: str, - properties: PropertyType = None, - ): - super().__init__( - parent, - translation_manager, - platform, - version_number, - force_blockstate, - namespace, - block_name, - style=wx.BORDER_SIMPLE, - ) - - self._manual_enabled = False - self._simple = SimpleWildcardPropertySelect(self, translation_manager) - self._sizer.Add(self._simple, 1, wx.EXPAND) - self._manual = ManualWildcardPropertySelect(self, translation_manager) - self._sizer.Add(self._manual, 1, wx.EXPAND) - - if properties is None: - properties = {} - self._set_properties(properties) - self._rebuild_ui() - - def _get_properties(self) -> PropertyType: - if self._manual_enabled: - return self._manual.properties - else: - return self._simple.properties - - def _set_properties(self, properties: PropertyType): - self.Freeze() - if self._manual_enabled: - self._manual.properties = properties - else: - self._simple.properties = properties - self.TopLevelParent.Layout() - self.Thaw() - - @property - def extra_properties(self) -> Dict[str, Tuple[PropertyValueType, ...]]: - """ - The values that are checked for each property. - This UI can have more than one property value checked (ticked) but only one selected (highlighted blue). - :attr:`properties` will return the entry that is highlighted blue. - """ - raise NotImplementedError - - @extra_properties.setter - def extra_properties(self, properties: Dict[str, Tuple[PropertyValueType, ...]]): - raise NotImplementedError - - def _rebuild_ui(self): - self.Freeze() - translator = self._translation_manager.get_version( - self._platform, self._version_number - ).block - - self._manual_enabled = self._block_name not in translator.base_names( - self._namespace, self._force_blockstate - ) - if self._manual_enabled: - self._simple.Hide() - self._manual.Show() - else: - self._manual.Hide() - self._simple.Show() - self._simple.rebuild_ui( - translator.get_specification( - self._namespace, self._block_name, self._force_blockstate - ) - ) - self.Thaw() - - -class PropertyValueCheckList(wx.Panel): - def __init__(self, parent: wx.Window, values: Iterable[str]): - super().__init__(parent) - sizer = wx.BoxSizer(wx.VERTICAL) - self.SetSizer(sizer) - self._toggle_checkbox = wx.CheckBox(self, style=wx.CHK_3STATE) - sizer.Add(self._toggle_checkbox, 0, wx.ALL, 3) - self._check_list_box = wx.CheckListBox(self, choices=values) - self._check_list_box.SetCheckedStrings(values) - sizer.Add(self._check_list_box, 0) - - self._toggle_checkbox.Bind(wx.EVT_CHECKBOX, self._on_toggle) - self._check_list_box.Bind(wx.EVT_CHECKLISTBOX, self._on_check) - - def _on_toggle(self, evt): - if self._toggle_checkbox.GetValue(): - self._check_list_box.SetCheckedItems( - list(range(self._check_list_box.GetCount())) - ) - else: - self._check_list_box.SetCheckedItems([]) - - def _on_check(self, evt): - items = self._check_list_box.GetCheckedItems() - if len(items) == self._check_list_box.GetCount(): - self._toggle_checkbox.Set3StateValue(wx.CHK_CHECKED) - elif len(items) == 0: - self._toggle_checkbox.Set3StateValue(wx.CHK_UNCHECKED) - else: - self._toggle_checkbox.Set3StateValue(wx.CHK_UNDETERMINED) - - @property - def value(self) -> str: - return self._check_list_box.GetStringSelection() - - @value.setter - def value(self, value: str): - self._check_list_box.SetStringSelection(value) - - @property - def extra_values(self) -> Tuple[str]: - return self._check_list_box.GetCheckedStrings() - - @extra_values.setter - def extra_values(self, extra_values: Iterable[str]): - self._check_list_box.SetCheckedStrings(extra_values) - - -class PropertyValueComboPopup(wx.ComboPopup): - def __init__(self, values: Iterable[str]): - super().__init__() - self.pvcl: PropertyValueCheckList = None - self._values = values - - def Create(self, parent): - self.pvcl = PropertyValueCheckList(parent, self._values) - return True - - def GetControl(self): - return self.pvcl - - def GetStringValue(self): - return "|".join(self.pvcl.extra_values) - - -class BaseSubWildcardPropertySelect(wx.Panel): - def __init__( - self, - parent: wx.Window, - translation_manager: PyMCTranslate.TranslationManager, - ): - super().__init__(parent) - self._sizer = wx.BoxSizer(wx.VERTICAL) - self.SetSizer(self._sizer) - self._translation_manager = translation_manager - - @property - def properties(self) -> PropertyType: - raise NotImplementedError - - @properties.setter - def properties(self, properties: PropertyType): - raise NotImplementedError - - -class SimpleWildcardPropertySelect(BaseSubWildcardPropertySelect): - def __init__( - self, - parent: wx.Window, - translation_manager: PyMCTranslate.TranslationManager, - ): - super().__init__(parent, translation_manager) - - header_sizer = wx.BoxSizer(wx.HORIZONTAL) - self._sizer.Add(header_sizer, 0, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, 5) - label = wx.StaticText(self, label="Property Name", style=wx.ALIGN_CENTER) - header_sizer.Add(label, 1) - label = wx.StaticText( - self, label="Property Value (SNBT)", style=wx.ALIGN_CENTER - ) - header_sizer.Add(label, 1, wx.LEFT, 5) - self._property_sizer = wx.GridSizer(2, 5, 5) - self._sizer.Add(self._property_sizer, 0, wx.ALL | wx.EXPAND, 5) - - self._properties: Dict[str, PropertyValueComboPopup] = {} - self._specification: dict = {} - - def rebuild_ui(self, specification: dict): - """ - Rebuild the UI. - Run when the version or block is changed. - """ - self._specification = specification - self.Freeze() - self._properties.clear() - self._property_sizer.Clear(True) - spec_properties: Dict[str, List[str]] = self._specification.get( - "properties", {} - ) - - for name, choices in spec_properties.items(): - label = wx.StaticText(self, label=name) - self._property_sizer.Add(label, 0, wx.ALIGN_CENTER) - - choice = wx.ComboCtrl(self, style=wx.CB_READONLY) - l = PropertyValueComboPopup(choices) - choice.SetPopupControl(l) - - self._property_sizer.Add(choice, 0, wx.EXPAND) - choice.Bind( - wx.EVT_CHOICE, - lambda evt: wx.PostEvent( - self, - PropertiesChangeEvent(self.GetId(), properties=self.properties), - ), - ) - self._properties[name] = choice - self.properties = self._specification.get("defaults", {}) - self.Fit() - self.GetTopLevelParent().Layout() - self.Thaw() - - @property - def properties(self) -> PropertyType: - return { - # name: amulet_nbt.from_snbt(choice.GetString(choice.GetSelection())) - # for name, choice in self._properties.items() - } - - @properties.setter - def properties(self, properties: PropertyType): - self.Freeze() - for name, nbt in properties.items(): - if name in self._properties: - if isinstance(nbt, PropertyDataTypes): - snbt = nbt.to_snbt() - elif isinstance(nbt, str): - snbt = nbt - else: - continue - choice = self._properties[name] - # index = choice.FindString(snbt) - # if index != wx.NOT_FOUND: - # choice.SetSelection(index) - self.Thaw() - - -class ManualWildcardPropertySelect(BaseSubWildcardPropertySelect): - def __init__( - self, parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager - ): - super().__init__(parent, translation_manager) - header_sizer = wx.BoxSizer(wx.HORIZONTAL) - add_button = wx.BitmapButton( - self, bitmap=ADD_ICON.bitmap(30, 30), size=(30, 30) - ) - header_sizer.Add(add_button) - self._sizer.Add(header_sizer, 0, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, 5) - label = wx.StaticText(self, label="Property Name", style=wx.ALIGN_CENTER) - header_sizer.Add(label, 1, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5) - label = wx.StaticText( - self, label="Property Value (SNBT)", style=wx.ALIGN_CENTER - ) - header_sizer.Add(label, 1, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5) - header_sizer.AddStretchSpacer(1) - - self._property_sizer = wx.BoxSizer(wx.VERTICAL) - self._sizer.Add( - self._property_sizer, 0, wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, 5 - ) - - add_button.Bind(wx.EVT_BUTTON, lambda evt: self._add_property()) - - self._property_index = 0 - self._properties: Dict[int, Tuple[wx.TextCtrl, wx.TextCtrl]] = {} - - def _post_property_change(self): - wx.PostEvent( - self, PropertiesChangeEvent(self.GetId(), properties=self.properties) - ) - - def _add_property(self, name: str = "", value: SNBTType = ""): - self.Freeze() - sizer = wx.BoxSizer(wx.HORIZONTAL) - self._property_index += 1 - subtract_button = wx.BitmapButton( - self, bitmap=SUBTRACT_ICON.bitmap(30, 30), size=(30, 30) - ) - sizer.Add(subtract_button, 0, wx.ALIGN_CENTER_VERTICAL) - index = self._property_index - subtract_button.Bind( - wx.EVT_BUTTON, lambda evt: self._on_remove_property(sizer, index) - ) - name_entry = wx.TextCtrl(self, value=name, style=wx.TE_CENTER) - sizer.Add(name_entry, 1, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) - name_entry.Bind(wx.EVT_TEXT, lambda evt: self._post_property_change()) - value_entry = wx.TextCtrl(self, value=value, style=wx.TE_CENTER) - sizer.Add(value_entry, 1, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) - snbt_text = wx.StaticText(self, style=wx.ALIGN_CENTER) - sizer.Add(snbt_text, 1, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) - self._change_value("", snbt_text) - value_entry.Bind(wx.EVT_TEXT, lambda evt: self._on_value_change(evt, snbt_text)) - - self._property_sizer.Add(sizer, 1, wx.TOP | wx.EXPAND, 5) - self._properties[self._property_index] = (name_entry, value_entry) - self.Fit() - self.TopLevelParent.Layout() - self.Thaw() - - def _on_value_change(self, evt, snbt_text: wx.StaticText): - self._change_value(evt.GetString(), snbt_text) - self._post_property_change() - evt.Skip() - - def _change_value(self, snbt: SNBTType, snbt_text: wx.StaticText): - try: - nbt = amulet_nbt.from_snbt(snbt) - except: - snbt_text.SetLabel("Invalid SNBT") - snbt_text.SetBackgroundColour((255, 200, 200)) - else: - if isinstance(nbt, PropertyDataTypes): - snbt_text.SetLabel(nbt.to_snbt()) - snbt_text.SetBackgroundColour(wx.NullColour) - else: - snbt_text.SetLabel(f"{nbt.__class__.__name__} not valid") - snbt_text.SetBackgroundColour((255, 200, 200)) - self.Layout() - - def _on_remove_property(self, sizer: wx.Sizer, key: int): - self.Freeze() - self._property_sizer.Detach(sizer) - sizer.Clear(True) - del self._properties[key] - self.TopLevelParent.Layout() - self.Thaw() - self._post_property_change() - - @property - def properties(self) -> PropertyType: - properties = {} - for name_ui, value_ui in self._properties.values(): - try: - nbt = amulet_nbt.from_snbt(value_ui.GetValue()) - except: - continue - name: str = name_ui.GetValue() - if name and isinstance(nbt, PropertyDataTypes): - properties[name] = nbt - return properties - - @properties.setter - def properties(self, properties: PropertyType): - self._property_sizer.Clear(True) - self._properties.clear() - self._property_index = 0 - for name, value in properties.items(): - self._add_property(name, value.to_snbt()) - - -def demo(): - """ - Show a demo version of the UI. - An app instance must be created first. - """ - translation_manager = PyMCTranslate.new_translation_manager() - for block in (("minecraft", "oak_fence"), ("modded", "block")): - dialog = wx.Dialog( - None, - title=f"WildcardPropertySelect with block {block[0]}:{block[1]}", - style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT, - ) - sizer = wx.BoxSizer() - dialog.SetSizer(sizer) - sizer.Add( - WildcardPropertySelect( - dialog, translation_manager, "java", (1, 16, 0), False, *block - ), - 1, - wx.ALL, - 5, - ) - - def get_on_close(dialog_): - def on_close(evt): - dialog_.Destroy() - - return on_close - - dialog.Bind(wx.EVT_CLOSE, get_on_close(dialog)) - dialog.Show() - dialog.Fit() From 669e772808b90942a44dddf91e3f39c910d57d77 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Wed, 30 Jun 2021 15:40:15 +0100 Subject: [PATCH 027/139] Renamed classes to be simpler --- .../api/wx/ui/block_select/block_define.py | 2 +- .../api/wx/ui/block_select/properties/__init__.py | 2 +- .../block_select/properties/multiple/__init__.py | 2 +- .../properties/multiple/automatic/__init__.py | 2 +- .../properties/multiple/automatic/automatic.py | 4 ++-- .../wx/ui/block_select/properties/multiple/base.py | 2 +- .../wx/ui/block_select/properties/multiple/main.py | 14 +++++++------- .../ui/block_select/properties/multiple/manual.py | 4 ++-- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/block_select/block_define.py b/amulet_map_editor/api/wx/ui/block_select/block_define.py index a171dd32..59e46cff 100644 --- a/amulet_map_editor/api/wx/ui/block_select/block_define.py +++ b/amulet_map_editor/api/wx/ui/block_select/block_define.py @@ -11,7 +11,7 @@ from amulet_map_editor.api.wx.ui.block_select.properties import ( SinglePropertySelect, - WildcardPropertySelect, + MultiplePropertySelect, EVT_PROPERTIES_CHANGE, ) diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/__init__.py b/amulet_map_editor/api/wx/ui/block_select/properties/__init__.py index 338a2029..fb8c3325 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/__init__.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/__init__.py @@ -1,3 +1,3 @@ from .events import PropertiesChangeEvent, EVT_PROPERTIES_CHANGE from .single import SinglePropertySelect -from .multiple import WildcardPropertySelect +from .multiple import MultiplePropertySelect diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/multiple/__init__.py b/amulet_map_editor/api/wx/ui/block_select/properties/multiple/__init__.py index 6258c532..a1d03d06 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/multiple/__init__.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/multiple/__init__.py @@ -1 +1 @@ -from .main import WildcardPropertySelect, demo +from .main import MultiplePropertySelect, demo diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/multiple/automatic/__init__.py b/amulet_map_editor/api/wx/ui/block_select/properties/multiple/automatic/__init__.py index 208151cb..f77e8f87 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/multiple/automatic/__init__.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/multiple/automatic/__init__.py @@ -1 +1 @@ -from .automatic import SimpleWildcardPropertySelect +from .automatic import AutomaticMultipleProperty diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/multiple/automatic/automatic.py b/amulet_map_editor/api/wx/ui/block_select/properties/multiple/automatic/automatic.py index 1978241e..d8dfca26 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/multiple/automatic/automatic.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/multiple/automatic/automatic.py @@ -4,11 +4,11 @@ import PyMCTranslate from amulet.api.block import PropertyDataTypes, PropertyType from ...events import PropertiesChangeEvent -from ..base import BaseSubWildcardPropertySelect +from ..base import BaseMultipleProperty from .popup import PropertyValueComboPopup -class SimpleWildcardPropertySelect(BaseSubWildcardPropertySelect): +class AutomaticMultipleProperty(BaseMultipleProperty): def __init__( self, parent: wx.Window, diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/multiple/base.py b/amulet_map_editor/api/wx/ui/block_select/properties/multiple/base.py index b889c770..36af7df2 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/multiple/base.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/multiple/base.py @@ -4,7 +4,7 @@ from amulet.api.block import PropertyType -class BaseSubWildcardPropertySelect(wx.Panel): +class BaseMultipleProperty(wx.Panel): def __init__( self, parent: wx.Window, diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/multiple/main.py b/amulet_map_editor/api/wx/ui/block_select/properties/multiple/main.py index 2d7bb22e..ad7d9ec4 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/multiple/main.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/multiple/main.py @@ -5,11 +5,11 @@ from amulet.api.block import PropertyType, PropertyValueType from ..base import BasePropertySelect -from .automatic import SimpleWildcardPropertySelect -from .manual import ManualWildcardPropertySelect +from .automatic import AutomaticMultipleProperty +from .manual import ManualMultipleProperty -class WildcardPropertySelect(BasePropertySelect): +class MultiplePropertySelect(BasePropertySelect): """ This is a UI which lets the user pick zero or more values for each property. If the block is known it will be populated from the specification. @@ -39,9 +39,9 @@ def __init__( ) self._manual_enabled = False - self._simple = SimpleWildcardPropertySelect(self, translation_manager) + self._simple = AutomaticMultipleProperty(self, translation_manager) self._sizer.Add(self._simple, 1, wx.EXPAND) - self._manual = ManualWildcardPropertySelect(self, translation_manager) + self._manual = ManualMultipleProperty(self, translation_manager) self._sizer.Add(self._manual, 1, wx.EXPAND) if properties is None: @@ -109,13 +109,13 @@ def demo(): for block in (("minecraft", "oak_fence"), ("modded", "block")): dialog = wx.Dialog( None, - title=f"WildcardPropertySelect with block {block[0]}:{block[1]}", + title=f"MultiplePropertySelect with block {block[0]}:{block[1]}", style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT, ) sizer = wx.BoxSizer() dialog.SetSizer(sizer) sizer.Add( - WildcardPropertySelect( + MultiplePropertySelect( dialog, translation_manager, "java", (1, 16, 0), False, *block ), 1, diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/multiple/manual.py b/amulet_map_editor/api/wx/ui/block_select/properties/multiple/manual.py index b857f327..6c7dfd5a 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/multiple/manual.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/multiple/manual.py @@ -7,10 +7,10 @@ from amulet.api.block import PropertyDataTypes, PropertyType from amulet_map_editor.api.image import ADD_ICON, SUBTRACT_ICON from ..events import PropertiesChangeEvent -from .base import BaseSubWildcardPropertySelect +from .base import BaseMultipleProperty -class ManualWildcardPropertySelect(BaseSubWildcardPropertySelect): +class ManualMultipleProperty(BaseMultipleProperty): def __init__( self, parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager ): From 96cbd391155d7a137ab5573473fae918f9ff5806 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Wed, 30 Jun 2021 17:11:40 +0100 Subject: [PATCH 028/139] Cleaned up events and multiple variant Cleaned up events Added extra_properties to multiple variants --- .../api/wx/ui/block_select/__init__.py | 9 ++- .../api/wx/ui/block_select/block_define.py | 6 +- .../wx/ui/block_select/properties/__init__.py | 13 ++++- .../wx/ui/block_select/properties/events.py | 6 -- .../properties/multiple/__init__.py | 3 + .../multiple/automatic/automatic.py | 48 +++++++--------- .../properties/multiple/automatic/popup.py | 57 +++++++++++-------- .../block_select/properties/multiple/base.py | 13 +++-- .../properties/multiple/events.py | 20 +++++++ .../block_select/properties/multiple/main.py | 16 ++++-- .../properties/multiple/manual.py | 25 ++++++-- .../properties/single/__init__.py | 1 + .../properties/single/automatic.py | 4 +- .../block_select/properties/single/events.py | 19 +++++++ .../ui/block_select/properties/single/main.py | 4 +- .../block_select/properties/single/manual.py | 6 +- 16 files changed, 167 insertions(+), 83 deletions(-) delete mode 100644 amulet_map_editor/api/wx/ui/block_select/properties/events.py create mode 100644 amulet_map_editor/api/wx/ui/block_select/properties/multiple/events.py create mode 100644 amulet_map_editor/api/wx/ui/block_select/properties/single/events.py diff --git a/amulet_map_editor/api/wx/ui/block_select/__init__.py b/amulet_map_editor/api/wx/ui/block_select/__init__.py index 2b602e1f..1b2321a4 100644 --- a/amulet_map_editor/api/wx/ui/block_select/__init__.py +++ b/amulet_map_editor/api/wx/ui/block_select/__init__.py @@ -1,4 +1,11 @@ from .block_select import BlockSelect -from .properties import SinglePropertySelect, EVT_PROPERTIES_CHANGE +from .properties import ( + SinglePropertySelect, + SinglePropertiesChangeEvent, + EVT_SINGLE_PROPERTIES_CHANGE, + MultiplePropertySelect, + MultiplePropertiesChangeEvent, + EVT_MULTIPLE_PROPERTIES_CHANGE, +) from .block_define import BlockDefine from .multi_block_define import MultiBlockDefine diff --git a/amulet_map_editor/api/wx/ui/block_select/block_define.py b/amulet_map_editor/api/wx/ui/block_select/block_define.py index 59e46cff..895b804f 100644 --- a/amulet_map_editor/api/wx/ui/block_select/block_define.py +++ b/amulet_map_editor/api/wx/ui/block_select/block_define.py @@ -12,7 +12,7 @@ from amulet_map_editor.api.wx.ui.block_select.properties import ( SinglePropertySelect, MultiplePropertySelect, - EVT_PROPERTIES_CHANGE, + EVT_SINGLE_PROPERTIES_CHANGE, ) @@ -77,7 +77,9 @@ def __init__( properties, ) right_sizer.Add(self._property_picker, 1, wx.EXPAND) - self._property_picker.Bind(EVT_PROPERTIES_CHANGE, self._on_property_change) + self._property_picker.Bind( + EVT_SINGLE_PROPERTIES_CHANGE, self._on_property_change + ) self.SetSizerAndFit(self._sizer) self.Layout() diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/__init__.py b/amulet_map_editor/api/wx/ui/block_select/properties/__init__.py index fb8c3325..035e915e 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/__init__.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/__init__.py @@ -1,3 +1,10 @@ -from .events import PropertiesChangeEvent, EVT_PROPERTIES_CHANGE -from .single import SinglePropertySelect -from .multiple import MultiplePropertySelect +from .single import ( + SinglePropertySelect, + SinglePropertiesChangeEvent, + EVT_SINGLE_PROPERTIES_CHANGE, +) +from .multiple import ( + MultiplePropertySelect, + MultiplePropertiesChangeEvent, + EVT_MULTIPLE_PROPERTIES_CHANGE, +) diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/events.py b/amulet_map_editor/api/wx/ui/block_select/properties/events.py deleted file mode 100644 index 0e80435e..00000000 --- a/amulet_map_editor/api/wx/ui/block_select/properties/events.py +++ /dev/null @@ -1,6 +0,0 @@ -from wx.lib import newevent - -( - PropertiesChangeEvent, - EVT_PROPERTIES_CHANGE, -) = newevent.NewCommandEvent() # the properties changed diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/multiple/__init__.py b/amulet_map_editor/api/wx/ui/block_select/properties/multiple/__init__.py index a1d03d06..063bf618 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/multiple/__init__.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/multiple/__init__.py @@ -1 +1,4 @@ +"""A UI from which a user can chose one or more values for each property.""" + +from .events import MultiplePropertiesChangeEvent, EVT_MULTIPLE_PROPERTIES_CHANGE from .main import MultiplePropertySelect, demo diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/multiple/automatic/automatic.py b/amulet_map_editor/api/wx/ui/block_select/properties/multiple/automatic/automatic.py index d8dfca26..909a1ca9 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/multiple/automatic/automatic.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/multiple/automatic/automatic.py @@ -1,9 +1,9 @@ import wx -from typing import Dict, List +from typing import Dict, List, Tuple import PyMCTranslate -from amulet.api.block import PropertyDataTypes, PropertyType -from ...events import PropertiesChangeEvent +from amulet.api.block import PropertyValueType +from ..events import MultiplePropertiesChangeEvent from ..base import BaseMultipleProperty from .popup import PropertyValueComboPopup @@ -27,7 +27,7 @@ def __init__( self._property_sizer = wx.GridSizer(2, 5, 5) self._sizer.Add(self._property_sizer, 0, wx.ALL | wx.EXPAND, 5) - self._properties: Dict[str, PropertyValueComboPopup] = {} + self._properties: Dict[str, Tuple[wx.ComboCtrl, PropertyValueComboPopup]] = {} self._specification: dict = {} def rebuild_ui(self, specification: dict): @@ -49,24 +49,24 @@ def rebuild_ui(self, specification: dict): def create_choice(): choice = wx.ComboCtrl(self, style=wx.CB_READONLY) - l = PropertyValueComboPopup(choices) - choice.SetPopupControl(l) - choice.SetValue(l.GetStringValue()) + popup = PropertyValueComboPopup(choices) + choice.SetPopupControl(popup) + choice.SetValue(popup.GetStringValue()) self._property_sizer.Add(choice, 0, wx.EXPAND) def on_close(evt): - choice.SetValue(l.GetStringValue()) + choice.SetValue(popup.GetStringValue()) wx.PostEvent( self, - PropertiesChangeEvent(self.GetId(), properties=self.properties), + MultiplePropertiesChangeEvent(self.extra_properties), ) choice.Bind( wx.EVT_COMBOBOX_CLOSEUP, on_close, ) - self._properties[name] = l + self._properties[name] = (choice, popup) create_choice() self.properties = self._specification.get("defaults", {}) @@ -75,25 +75,21 @@ def on_close(evt): self.Thaw() @property - def properties(self) -> PropertyType: + def extra_properties(self) -> Dict[str, Tuple[PropertyValueType, ...]]: + """ + The values that are checked for each property. + This UI can have more than one property value checked (ticked). + """ return { - # name: amulet_nbt.from_snbt(choice.GetString(choice.GetSelection())) - # for name, choice in self._properties.items() + prop: popup.checked_nbt for prop, (_, popup) in self._properties.items() } - @properties.setter - def properties(self, properties: PropertyType): + @extra_properties.setter + def extra_properties(self, properties: Dict[str, Tuple[PropertyValueType, ...]]): self.Freeze() - for name, nbt in properties.items(): + for name, nbt_tuple in properties.items(): if name in self._properties: - if isinstance(nbt, PropertyDataTypes): - snbt = nbt.to_snbt() - elif isinstance(nbt, str): - snbt = nbt - else: - continue - choice = self._properties[name] - # index = choice.FindString(snbt) - # if index != wx.NOT_FOUND: - # choice.SetSelection(index) + choice, popup = self._properties[name] + popup.checked_nbt = nbt_tuple + choice.SetValue(popup.GetStringValue()) self.Thaw() diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/multiple/automatic/popup.py b/amulet_map_editor/api/wx/ui/block_select/properties/multiple/automatic/popup.py index 25bedddb..3bf8407c 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/multiple/automatic/popup.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/multiple/automatic/popup.py @@ -1,5 +1,8 @@ import wx -from typing import Tuple, Iterable +from typing import Tuple, Iterable, Optional + +from amulet.api.block import PropertyValueType +import amulet_nbt class PropertyValueCheckList(wx.Panel): @@ -18,20 +21,26 @@ def __init__(self, parent: wx.Window, values: Iterable[str]): self._check_list_box.Bind(wx.EVT_LEFT_DOWN, self._on_left_down) @property - def value(self) -> str: - return self._check_list_box.GetStringSelection() + def checked_snbt(self) -> Tuple[str, ...]: + return self._check_list_box.GetCheckedStrings() - @value.setter - def value(self, value: str): - self._check_list_box.SetStringSelection(value) + @checked_snbt.setter + def checked_snbt(self, checked_snbt: Iterable[str]): + self._check_list_box.SetCheckedStrings(checked_snbt) @property - def extra_values(self) -> Tuple[str]: - return self._check_list_box.GetCheckedStrings() - - @extra_values.setter - def extra_values(self, extra_values: Iterable[str]): - self._check_list_box.SetCheckedStrings(extra_values) + def checked_nbt(self) -> Tuple[PropertyValueType, ...]: + nbt = [] + for entry in self._check_list_box.GetCheckedStrings(): + try: + nbt.append(amulet_nbt.from_snbt(entry)) + except: + pass + return tuple(nbt) + + @checked_nbt.setter + def checked_nbt(self, checked_nbt: Iterable[PropertyValueType]): + self._check_list_box.SetCheckedStrings([v.to_snbt() for v in checked_nbt]) def _on_toggle(self, evt): if self._toggle_checkbox.GetValue(): @@ -63,26 +72,26 @@ def _on_left_down(self, evt): class PropertyValueComboPopup(wx.ComboPopup): def __init__(self, values: Iterable[str]): super().__init__() - self.pvcl: PropertyValueCheckList = None + self._check_list: Optional[PropertyValueCheckList] = None self._values = values + @property + def checked_nbt(self) -> Tuple[PropertyValueType, ...]: + return self._check_list.checked_nbt + + @checked_nbt.setter + def checked_nbt(self, checked_nbt: Iterable[PropertyValueType]): + self._check_list.checked_nbt = checked_nbt + def Create(self, parent): - self.pvcl = PropertyValueCheckList(parent, self._values) - # self.pvcl.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) + self._check_list = PropertyValueCheckList(parent, self._values) return True - def OnLeftDown(self, evt): - item = self.pvcl._check_list_box.HitTest(evt.GetPosition()) - if item >= 0: - self.pvcl._check_list_box.Check( - item, check=self.pvcl._check_list_box.IsChecked(item) - ) - def GetControl(self): - return self.pvcl + return self._check_list def GetStringValue(self): - return "|".join(self.pvcl.extra_values) + return "|".join(self._check_list.checked_snbt) def GetAdjustedSize(self, minWidth, prefHeight, maxHeight): self.GetControl().Fit() diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/multiple/base.py b/amulet_map_editor/api/wx/ui/block_select/properties/multiple/base.py index 36af7df2..a7335c71 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/multiple/base.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/multiple/base.py @@ -1,7 +1,8 @@ import wx +from typing import Dict, Tuple import PyMCTranslate -from amulet.api.block import PropertyType +from amulet.api.block import PropertyValueType class BaseMultipleProperty(wx.Panel): @@ -16,9 +17,13 @@ def __init__( self._translation_manager = translation_manager @property - def properties(self) -> PropertyType: + def extra_properties(self) -> Dict[str, Tuple[PropertyValueType, ...]]: + """ + The values that are checked for each property. + This UI can have more than one property value checked (ticked). + """ raise NotImplementedError - @properties.setter - def properties(self, properties: PropertyType): + @extra_properties.setter + def extra_properties(self, properties: Dict[str, Tuple[PropertyValueType, ...]]): raise NotImplementedError diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/multiple/events.py b/amulet_map_editor/api/wx/ui/block_select/properties/multiple/events.py new file mode 100644 index 00000000..83503f2e --- /dev/null +++ b/amulet_map_editor/api/wx/ui/block_select/properties/multiple/events.py @@ -0,0 +1,20 @@ +import wx +from typing import Dict, Tuple +from amulet.api.block import PropertyValueType + +_MultiplePropertiesChangeEventType = wx.NewEventType() +EVT_MULTIPLE_PROPERTIES_CHANGE = wx.PyEventBinder(_MultiplePropertiesChangeEventType) + + +class MultiplePropertiesChangeEvent(wx.PyEvent): + """ + Run when the properties UI changes. + """ + + def __init__(self, properties: Dict[str, Tuple[PropertyValueType, ...]]): + wx.PyEvent.__init__(self, eventType=_MultiplePropertiesChangeEventType) + self._properties = properties + + @property + def properties(self) -> Dict[str, Tuple[PropertyValueType, ...]]: + return self._properties diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/multiple/main.py b/amulet_map_editor/api/wx/ui/block_select/properties/multiple/main.py index ad7d9ec4..3f0c28f8 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/multiple/main.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/multiple/main.py @@ -68,14 +68,22 @@ def _set_properties(self, properties: PropertyType): def extra_properties(self) -> Dict[str, Tuple[PropertyValueType, ...]]: """ The values that are checked for each property. - This UI can have more than one property value checked (ticked) but only one selected (highlighted blue). - :attr:`properties` will return the entry that is highlighted blue. + This UI can have more than one property value checked (ticked). """ - raise NotImplementedError + if self._manual_enabled: + return self._manual.extra_properties + else: + return self._simple.extra_properties @extra_properties.setter def extra_properties(self, properties: Dict[str, Tuple[PropertyValueType, ...]]): - raise NotImplementedError + self.Freeze() + if self._manual_enabled: + self._manual.extra_properties = properties + else: + self._simple.extra_properties = properties + self.TopLevelParent.Layout() + self.Thaw() def _rebuild_ui(self): self.Freeze() diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/multiple/manual.py b/amulet_map_editor/api/wx/ui/block_select/properties/multiple/manual.py index 6c7dfd5a..5f87ab27 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/multiple/manual.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/multiple/manual.py @@ -4,9 +4,9 @@ import PyMCTranslate import amulet_nbt from amulet_nbt import SNBTType -from amulet.api.block import PropertyDataTypes, PropertyType +from amulet.api.block import PropertyDataTypes, PropertyType, PropertyValueType from amulet_map_editor.api.image import ADD_ICON, SUBTRACT_ICON -from ..events import PropertiesChangeEvent +from .events import MultiplePropertiesChangeEvent from .base import BaseMultipleProperty @@ -40,9 +40,7 @@ def __init__( self._properties: Dict[int, Tuple[wx.TextCtrl, wx.TextCtrl]] = {} def _post_property_change(self): - wx.PostEvent( - self, PropertiesChangeEvent(self.GetId(), properties=self.properties) - ) + wx.PostEvent(self, MultiplePropertiesChangeEvent(self.extra_properties)) def _add_property(self, name: str = "", value: SNBTType = ""): self.Freeze() @@ -121,3 +119,20 @@ def properties(self, properties: PropertyType): self._property_index = 0 for name, value in properties.items(): self._add_property(name, value.to_snbt()) + + # TODO: implement this properly + @property + def extra_properties(self) -> Dict[str, Tuple[PropertyValueType, ...]]: + """ + The values that are checked for each property. + This UI can have more than one property value checked (ticked). + """ + return {prop: (val,) for prop, val in self.properties.items()} + + @extra_properties.setter + def extra_properties(self, properties: Dict[str, Tuple[PropertyValueType, ...]]): + props = {} + for prop, val in properties: + if val: + props[prop] = val[0] + self.properties = props diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/single/__init__.py b/amulet_map_editor/api/wx/ui/block_select/properties/single/__init__.py index 68b0d7e9..2928d4b9 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/single/__init__.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/single/__init__.py @@ -1,3 +1,4 @@ """A UI from which a user can chose one value for each property.""" +from .events import SinglePropertiesChangeEvent, EVT_SINGLE_PROPERTIES_CHANGE from .main import SinglePropertySelect, demo diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/single/automatic.py b/amulet_map_editor/api/wx/ui/block_select/properties/single/automatic.py index dab5ae27..66fb5300 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/single/automatic.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/single/automatic.py @@ -4,7 +4,7 @@ import PyMCTranslate import amulet_nbt from amulet.api.block import PropertyDataTypes, PropertyType -from ..events import PropertiesChangeEvent +from .events import SinglePropertiesChangeEvent from .base import BaseSingleProperty @@ -58,7 +58,7 @@ def rebuild_ui(self, specification: dict): wx.EVT_CHOICE, lambda evt: wx.PostEvent( self, - PropertiesChangeEvent(self.GetId(), properties=self.properties), + SinglePropertiesChangeEvent(self.properties), ), ) self._properties[name] = choice diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/single/events.py b/amulet_map_editor/api/wx/ui/block_select/properties/single/events.py new file mode 100644 index 00000000..07688b04 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/block_select/properties/single/events.py @@ -0,0 +1,19 @@ +import wx +from amulet.api.block import PropertyType + +_SinglePropertiesChangeEventType = wx.NewEventType() +EVT_SINGLE_PROPERTIES_CHANGE = wx.PyEventBinder(_SinglePropertiesChangeEventType) + + +class SinglePropertiesChangeEvent(wx.PyEvent): + """ + Run when the properties UI changes. + """ + + def __init__(self, properties: PropertyType): + wx.PyEvent.__init__(self, eventType=_SinglePropertiesChangeEventType) + self._properties = properties + + @property + def properties(self) -> PropertyType: + return self._properties diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/single/main.py b/amulet_map_editor/api/wx/ui/block_select/properties/single/main.py index 7df3e99f..69b5be8b 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/single/main.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/single/main.py @@ -4,7 +4,7 @@ import PyMCTranslate from amulet.api.block import PropertyType from ..base import BasePropertySelect -from ..events import PropertiesChangeEvent +from .events import SinglePropertiesChangeEvent from .automatic import AutomaticSingleProperty from .manual import ManualSingleProperty @@ -61,7 +61,7 @@ def properties(self) -> PropertyType: @properties.setter def properties(self, properties: PropertyType): self._set_properties(properties) - wx.PostEvent(self, PropertiesChangeEvent()) + wx.PostEvent(self, SinglePropertiesChangeEvent(self.properties)) def _get_properties(self) -> PropertyType: """Get the selected values for each property.""" diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/single/manual.py b/amulet_map_editor/api/wx/ui/block_select/properties/single/manual.py index 3467dc34..7fe4bae9 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/single/manual.py +++ b/amulet_map_editor/api/wx/ui/block_select/properties/single/manual.py @@ -6,7 +6,7 @@ from amulet_nbt import SNBTType from amulet.api.block import PropertyDataTypes, PropertyType from amulet_map_editor.api.image import ADD_ICON, SUBTRACT_ICON -from ..events import PropertiesChangeEvent +from .events import SinglePropertiesChangeEvent from .base import BaseSingleProperty @@ -46,9 +46,7 @@ def __init__( self._properties: Dict[int, Tuple[wx.TextCtrl, wx.TextCtrl]] = {} def _post_property_change(self): - wx.PostEvent( - self, PropertiesChangeEvent(self.GetId(), properties=self.properties) - ) + wx.PostEvent(self, SinglePropertiesChangeEvent(self.properties)) def _add_property(self, name: str = "", value: SNBTType = ""): """ From eaa638d074ee6d0c7cd84aa206152d34b75d7909 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Thu, 1 Jul 2021 09:40:37 +0100 Subject: [PATCH 029/139] Restructured the minecraft object UI elements --- .../api/wx/ui/block_select/__init__.py | 11 ------ .../api/wx/ui/block_select/demo.py | 34 ------------------- amulet_map_editor/api/wx/ui/demo.py | 6 ++-- amulet_map_editor/api/wx/ui/mc/__init__.py | 0 .../api/wx/ui/mc/base/__init__.py | 2 ++ .../api/wx/ui/{ => mc/base}/base_define.py | 4 +-- .../ui/{ => mc/base}/base_select/__init__.py | 0 .../{ => mc/base}/base_select/base_select.py | 0 .../wx/ui/{ => mc/base}/base_select/events.py | 0 .../ui/{biome_select => mc/biome}/__init__.py | 1 + .../biome}/biome_define.py | 4 +-- .../biome}/biome_select.py | 2 +- .../wx/ui/{biome_select => mc/biome}/demo.py | 4 +-- .../api/wx/ui/mc/block/__init__.py | 3 ++ .../block}/block_define.py | 7 ++-- .../block}/block_select.py | 2 +- amulet_map_editor/api/wx/ui/mc/block/demo.py | 30 ++++++++++++++++ .../block}/multi_block_define.py | 12 +++---- .../block}/properties/__init__.py | 0 .../block}/properties/base.py | 0 .../block}/properties/demo.py | 8 ++--- .../block}/properties/multiple/__init__.py | 0 .../properties/multiple/automatic/__init__.py | 0 .../multiple/automatic/automatic.py | 0 .../properties/multiple/automatic/popup.py | 0 .../block}/properties/multiple/base.py | 0 .../block}/properties/multiple/events.py | 0 .../block}/properties/multiple/main.py | 0 .../block}/properties/multiple/manual.py | 0 .../block}/properties/single/__init__.py | 0 .../block}/properties/single/automatic.py | 0 .../block}/properties/single/base.py | 0 .../block}/properties/single/events.py | 0 .../block}/properties/single/main.py | 0 .../block}/properties/single/manual.py | 0 amulet_map_editor/api/wx/ui/mc/demo.py | 22 ++++++++++++ .../wx/ui/{ => mc}/version_select/__init__.py | 0 .../api/wx/ui/{ => mc}/version_select/demo.py | 4 +-- .../wx/ui/{ => mc}/version_select/events.py | 0 .../version_select/platform_select.py | 0 .../{ => mc}/version_select/version_select.py | 0 amulet_map_editor/api/wx/ui/simple.py | 2 -- .../export_operations/construction.py | 2 +- .../export_operations/mcstructure.py | 2 +- .../export_operations/schematic.py | 2 +- .../export_operations/sponge_schematic.py | 2 +- .../stock_plugins/operations/fill.py | 4 +-- .../stock_plugins/operations/replace.py | 4 +-- .../stock_plugins/operations/set_biome.py | 4 +-- .../stock_plugins/operations/waterlog.py | 4 +-- .../tools/fill_replace/block_dialog.py | 2 +- 51 files changed, 97 insertions(+), 87 deletions(-) delete mode 100644 amulet_map_editor/api/wx/ui/block_select/__init__.py delete mode 100644 amulet_map_editor/api/wx/ui/block_select/demo.py create mode 100644 amulet_map_editor/api/wx/ui/mc/__init__.py create mode 100644 amulet_map_editor/api/wx/ui/mc/base/__init__.py rename amulet_map_editor/api/wx/ui/{ => mc/base}/base_define.py (95%) rename amulet_map_editor/api/wx/ui/{ => mc/base}/base_select/__init__.py (100%) rename amulet_map_editor/api/wx/ui/{ => mc/base}/base_select/base_select.py (100%) rename amulet_map_editor/api/wx/ui/{ => mc/base}/base_select/events.py (100%) rename amulet_map_editor/api/wx/ui/{biome_select => mc/biome}/__init__.py (50%) rename amulet_map_editor/api/wx/ui/{biome_select => mc/biome}/biome_define.py (94%) rename amulet_map_editor/api/wx/ui/{biome_select => mc/biome}/biome_select.py (96%) rename amulet_map_editor/api/wx/ui/{biome_select => mc/biome}/demo.py (72%) create mode 100644 amulet_map_editor/api/wx/ui/mc/block/__init__.py rename amulet_map_editor/api/wx/ui/{block_select => mc/block}/block_define.py (96%) rename amulet_map_editor/api/wx/ui/{block_select => mc/block}/block_select.py (96%) create mode 100644 amulet_map_editor/api/wx/ui/mc/block/demo.py rename amulet_map_editor/api/wx/ui/{block_select => mc/block}/multi_block_define.py (95%) rename amulet_map_editor/api/wx/ui/{block_select => mc/block}/properties/__init__.py (100%) rename amulet_map_editor/api/wx/ui/{block_select => mc/block}/properties/base.py (100%) rename amulet_map_editor/api/wx/ui/{block_select => mc/block}/properties/demo.py (58%) rename amulet_map_editor/api/wx/ui/{block_select => mc/block}/properties/multiple/__init__.py (100%) rename amulet_map_editor/api/wx/ui/{block_select => mc/block}/properties/multiple/automatic/__init__.py (100%) rename amulet_map_editor/api/wx/ui/{block_select => mc/block}/properties/multiple/automatic/automatic.py (100%) rename amulet_map_editor/api/wx/ui/{block_select => mc/block}/properties/multiple/automatic/popup.py (100%) rename amulet_map_editor/api/wx/ui/{block_select => mc/block}/properties/multiple/base.py (100%) rename amulet_map_editor/api/wx/ui/{block_select => mc/block}/properties/multiple/events.py (100%) rename amulet_map_editor/api/wx/ui/{block_select => mc/block}/properties/multiple/main.py (100%) rename amulet_map_editor/api/wx/ui/{block_select => mc/block}/properties/multiple/manual.py (100%) rename amulet_map_editor/api/wx/ui/{block_select => mc/block}/properties/single/__init__.py (100%) rename amulet_map_editor/api/wx/ui/{block_select => mc/block}/properties/single/automatic.py (100%) rename amulet_map_editor/api/wx/ui/{block_select => mc/block}/properties/single/base.py (100%) rename amulet_map_editor/api/wx/ui/{block_select => mc/block}/properties/single/events.py (100%) rename amulet_map_editor/api/wx/ui/{block_select => mc/block}/properties/single/main.py (100%) rename amulet_map_editor/api/wx/ui/{block_select => mc/block}/properties/single/manual.py (100%) create mode 100644 amulet_map_editor/api/wx/ui/mc/demo.py rename amulet_map_editor/api/wx/ui/{ => mc}/version_select/__init__.py (100%) rename amulet_map_editor/api/wx/ui/{ => mc}/version_select/demo.py (68%) rename amulet_map_editor/api/wx/ui/{ => mc}/version_select/events.py (100%) rename amulet_map_editor/api/wx/ui/{ => mc}/version_select/platform_select.py (100%) rename amulet_map_editor/api/wx/ui/{ => mc}/version_select/version_select.py (100%) diff --git a/amulet_map_editor/api/wx/ui/block_select/__init__.py b/amulet_map_editor/api/wx/ui/block_select/__init__.py deleted file mode 100644 index 1b2321a4..00000000 --- a/amulet_map_editor/api/wx/ui/block_select/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -from .block_select import BlockSelect -from .properties import ( - SinglePropertySelect, - SinglePropertiesChangeEvent, - EVT_SINGLE_PROPERTIES_CHANGE, - MultiplePropertySelect, - MultiplePropertiesChangeEvent, - EVT_MULTIPLE_PROPERTIES_CHANGE, -) -from .block_define import BlockDefine -from .multi_block_define import MultiBlockDefine diff --git a/amulet_map_editor/api/wx/ui/block_select/demo.py b/amulet_map_editor/api/wx/ui/block_select/demo.py deleted file mode 100644 index bdada46a..00000000 --- a/amulet_map_editor/api/wx/ui/block_select/demo.py +++ /dev/null @@ -1,34 +0,0 @@ -import wx -from amulet_map_editor.api.wx.ui.block_select.block_select import ( - demo as block_select_demo, -) -from amulet_map_editor.api.wx.ui.block_select.block_define import ( - demo as block_define_demo, -) -from amulet_map_editor.api.wx.ui.block_select.multi_block_define import ( - demo as multi_block_define_demo, -) -from amulet_map_editor.api.wx.ui.block_select.properties.demo import ( - demo as properties_demo, -) - - -def demo(): - """ - Show a demo version of the UI. - An app instance must be created first. - """ - block_select_demo() - block_define_demo() - multi_block_define_demo() - properties_demo() - - -if __name__ == "__main__": - - def main(): - app = wx.App() - demo() - app.MainLoop() - - main() diff --git a/amulet_map_editor/api/wx/ui/demo.py b/amulet_map_editor/api/wx/ui/demo.py index 2553d56a..665eb22e 100644 --- a/amulet_map_editor/api/wx/ui/demo.py +++ b/amulet_map_editor/api/wx/ui/demo.py @@ -1,8 +1,8 @@ -from amulet_map_editor.api.wx.ui.biome_select.demo import demo as biome_demo -from amulet_map_editor.api.wx.ui.block_select.demo import demo as block_demo +from amulet_map_editor.api.wx.ui.mc.biome.demo import demo as biome_demo +from amulet_map_editor.api.wx.ui.mc.block.demo import demo as block_demo from amulet_map_editor.api.wx.ui.nbt_editor import demo as nbt_editor_demo from amulet_map_editor.api.wx.ui.select_world import demo as select_world_demo -from amulet_map_editor.api.wx.ui.version_select.demo import demo as version_select_demo +from amulet_map_editor.api.wx.ui.mc.version_select.demo import demo as version_select_demo def demo(): diff --git a/amulet_map_editor/api/wx/ui/mc/__init__.py b/amulet_map_editor/api/wx/ui/mc/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/amulet_map_editor/api/wx/ui/mc/base/__init__.py b/amulet_map_editor/api/wx/ui/mc/base/__init__.py new file mode 100644 index 00000000..6ff1ba68 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/mc/base/__init__.py @@ -0,0 +1,2 @@ +from .base_select import BaseSelect +from .base_define import BaseDefine diff --git a/amulet_map_editor/api/wx/ui/base_define.py b/amulet_map_editor/api/wx/ui/mc/base/base_define.py similarity index 95% rename from amulet_map_editor/api/wx/ui/base_define.py rename to amulet_map_editor/api/wx/ui/mc/base/base_define.py index 19c62d8c..54e9614a 100644 --- a/amulet_map_editor/api/wx/ui/base_define.py +++ b/amulet_map_editor/api/wx/ui/mc/base/base_define.py @@ -5,8 +5,8 @@ import PyMCTranslate from amulet.api.data_types import VersionNumberTuple, PlatformType -from amulet_map_editor.api.wx.ui.base_select import EVT_ITEM_CHANGE, BaseSelect -from amulet_map_editor.api.wx.ui.version_select import ( +from amulet_map_editor.api.wx.ui.mc.base.base_select import EVT_ITEM_CHANGE, BaseSelect +from amulet_map_editor.api.wx.ui.mc.version_select import ( VersionSelect, EVT_VERSION_CHANGE, ) diff --git a/amulet_map_editor/api/wx/ui/base_select/__init__.py b/amulet_map_editor/api/wx/ui/mc/base/base_select/__init__.py similarity index 100% rename from amulet_map_editor/api/wx/ui/base_select/__init__.py rename to amulet_map_editor/api/wx/ui/mc/base/base_select/__init__.py diff --git a/amulet_map_editor/api/wx/ui/base_select/base_select.py b/amulet_map_editor/api/wx/ui/mc/base/base_select/base_select.py similarity index 100% rename from amulet_map_editor/api/wx/ui/base_select/base_select.py rename to amulet_map_editor/api/wx/ui/mc/base/base_select/base_select.py diff --git a/amulet_map_editor/api/wx/ui/base_select/events.py b/amulet_map_editor/api/wx/ui/mc/base/base_select/events.py similarity index 100% rename from amulet_map_editor/api/wx/ui/base_select/events.py rename to amulet_map_editor/api/wx/ui/mc/base/base_select/events.py diff --git a/amulet_map_editor/api/wx/ui/biome_select/__init__.py b/amulet_map_editor/api/wx/ui/mc/biome/__init__.py similarity index 50% rename from amulet_map_editor/api/wx/ui/biome_select/__init__.py rename to amulet_map_editor/api/wx/ui/mc/biome/__init__.py index 99571245..174646e8 100644 --- a/amulet_map_editor/api/wx/ui/biome_select/__init__.py +++ b/amulet_map_editor/api/wx/ui/mc/biome/__init__.py @@ -1 +1,2 @@ +from .biome_select import BiomeSelect from .biome_define import BiomeDefine diff --git a/amulet_map_editor/api/wx/ui/biome_select/biome_define.py b/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py similarity index 94% rename from amulet_map_editor/api/wx/ui/biome_select/biome_define.py rename to amulet_map_editor/api/wx/ui/mc/biome/biome_define.py index 704f6492..f812a4ba 100644 --- a/amulet_map_editor/api/wx/ui/biome_select/biome_define.py +++ b/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py @@ -3,8 +3,8 @@ import PyMCTranslate import wx -from amulet_map_editor.api.wx.ui.base_define import BaseDefine -from amulet_map_editor.api.wx.ui.biome_select.biome_select import BiomeSelect +from amulet_map_editor.api.wx.ui.mc.base.base_define import BaseDefine +from amulet_map_editor.api.wx.ui.mc.biome.biome_select import BiomeSelect class BiomeDefine(BaseDefine): diff --git a/amulet_map_editor/api/wx/ui/biome_select/biome_select.py b/amulet_map_editor/api/wx/ui/mc/biome/biome_select.py similarity index 96% rename from amulet_map_editor/api/wx/ui/biome_select/biome_select.py rename to amulet_map_editor/api/wx/ui/mc/biome/biome_select.py index ee1e832e..bf1b8be7 100644 --- a/amulet_map_editor/api/wx/ui/biome_select/biome_select.py +++ b/amulet_map_editor/api/wx/ui/mc/biome/biome_select.py @@ -1,5 +1,5 @@ import wx -from amulet_map_editor.api.wx.ui.base_select import BaseSelect +from amulet_map_editor.api.wx.ui.mc.base.base_select import BaseSelect class BiomeSelect(BaseSelect): diff --git a/amulet_map_editor/api/wx/ui/biome_select/demo.py b/amulet_map_editor/api/wx/ui/mc/biome/demo.py similarity index 72% rename from amulet_map_editor/api/wx/ui/biome_select/demo.py rename to amulet_map_editor/api/wx/ui/mc/biome/demo.py index 5d6c4c57..81e4d651 100644 --- a/amulet_map_editor/api/wx/ui/biome_select/demo.py +++ b/amulet_map_editor/api/wx/ui/mc/biome/demo.py @@ -1,8 +1,8 @@ import wx -from amulet_map_editor.api.wx.ui.biome_select.biome_select import ( +from amulet_map_editor.api.wx.ui.mc.biome.biome_select import ( demo as biome_select_demo, ) -from amulet_map_editor.api.wx.ui.biome_select.biome_define import ( +from amulet_map_editor.api.wx.ui.mc.biome.biome_define import ( demo as biome_define_demo, ) diff --git a/amulet_map_editor/api/wx/ui/mc/block/__init__.py b/amulet_map_editor/api/wx/ui/mc/block/__init__.py new file mode 100644 index 00000000..88b1654d --- /dev/null +++ b/amulet_map_editor/api/wx/ui/mc/block/__init__.py @@ -0,0 +1,3 @@ +from .block_select import BlockSelect +from .block_define import BlockDefine +from .multi_block_define import MultiBlockDefine diff --git a/amulet_map_editor/api/wx/ui/block_select/block_define.py b/amulet_map_editor/api/wx/ui/mc/block/block_define.py similarity index 96% rename from amulet_map_editor/api/wx/ui/block_select/block_define.py rename to amulet_map_editor/api/wx/ui/mc/block/block_define.py index 895b804f..8988f5e8 100644 --- a/amulet_map_editor/api/wx/ui/block_select/block_define.py +++ b/amulet_map_editor/api/wx/ui/mc/block/block_define.py @@ -6,12 +6,11 @@ from amulet.api.block import PropertyType, Block from amulet.api.block_entity import BlockEntity -from amulet_map_editor.api.wx.ui.base_define import BaseDefine -from amulet_map_editor.api.wx.ui.block_select import BlockSelect +from amulet_map_editor.api.wx.ui.mc.base.base_define import BaseDefine +from amulet_map_editor.api.wx.ui.mc.block import BlockSelect -from amulet_map_editor.api.wx.ui.block_select.properties import ( +from amulet_map_editor.api.wx.ui.mc.block.properties import ( SinglePropertySelect, - MultiplePropertySelect, EVT_SINGLE_PROPERTIES_CHANGE, ) diff --git a/amulet_map_editor/api/wx/ui/block_select/block_select.py b/amulet_map_editor/api/wx/ui/mc/block/block_select.py similarity index 96% rename from amulet_map_editor/api/wx/ui/block_select/block_select.py rename to amulet_map_editor/api/wx/ui/mc/block/block_select.py index 88c97723..53de46b9 100644 --- a/amulet_map_editor/api/wx/ui/block_select/block_select.py +++ b/amulet_map_editor/api/wx/ui/mc/block/block_select.py @@ -3,7 +3,7 @@ import PyMCTranslate -from amulet_map_editor.api.wx.ui.base_select import BaseSelect +from amulet_map_editor.api.wx.ui.mc.base.base_select import BaseSelect class BlockSelect(BaseSelect): diff --git a/amulet_map_editor/api/wx/ui/mc/block/demo.py b/amulet_map_editor/api/wx/ui/mc/block/demo.py new file mode 100644 index 00000000..da68731d --- /dev/null +++ b/amulet_map_editor/api/wx/ui/mc/block/demo.py @@ -0,0 +1,30 @@ +import wx +from amulet_map_editor.api.wx.ui.mc.block.block_select import ( + demo as block_select_demo, +) +from amulet_map_editor.api.wx.ui.mc.block.block_define import ( + demo as block_define_demo, +) +# from amulet_map_editor.api.wx.ui.mc.block.multi_block_define import ( +# demo as multi_block_define_demo, +# ) + + +def demo(): + """ + Show a demo version of the UI. + An app instance must be created first. + """ + block_select_demo() + block_define_demo() + # multi_block_define_demo() + + +if __name__ == "__main__": + + def main(): + app = wx.App() + demo() + app.MainLoop() + + main() diff --git a/amulet_map_editor/api/wx/ui/block_select/multi_block_define.py b/amulet_map_editor/api/wx/ui/mc/block/multi_block_define.py similarity index 95% rename from amulet_map_editor/api/wx/ui/block_select/multi_block_define.py rename to amulet_map_editor/api/wx/ui/mc/block/multi_block_define.py index 26c82245..e85ae750 100644 --- a/amulet_map_editor/api/wx/ui/block_select/multi_block_define.py +++ b/amulet_map_editor/api/wx/ui/mc/block/multi_block_define.py @@ -12,7 +12,10 @@ MAXIMIZE, MINIMIZE, ) -from amulet_map_editor.api.wx.ui.block_select import BlockDefine, EVT_PROPERTIES_CHANGE +from amulet_map_editor.api.wx.ui.mc.block import ( + BlockDefine +) +from amulet_map_editor.api.wx.ui.mc.block.properties import EVT_SINGLE_PROPERTIES_CHANGE class MultiBlockDefine(wx.lib.scrolledpanel.ScrolledPanel): @@ -150,7 +153,7 @@ def __init__(self, parent: MultiBlockDefine, translation_manager, collapsed=Fals self.expand_button.Bind( wx.EVT_BUTTON, lambda evt: self._toggle_block_expand(parent) ) - self.block_define.Bind(EVT_PROPERTIES_CHANGE, self._on_properties_change) + self.block_define.Bind(EVT_SINGLE_PROPERTIES_CHANGE, self._on_properties_change) @property def collapsed(self) -> bool: @@ -180,10 +183,7 @@ def _on_properties_change(self, evt): def _gen_block_string(self): base = f"{self.block_define.namespace}:{self.block_define.block_name}" properties = ",".join( - ( - f"{key}={value}" - for key, value in self.block_define.str_properties.items() - ) + (f"{key}={value}" for key, value in self.block_define.properties.items()) ) return f"{base}[{properties}]" if properties else base diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/__init__.py b/amulet_map_editor/api/wx/ui/mc/block/properties/__init__.py similarity index 100% rename from amulet_map_editor/api/wx/ui/block_select/properties/__init__.py rename to amulet_map_editor/api/wx/ui/mc/block/properties/__init__.py diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/base.py b/amulet_map_editor/api/wx/ui/mc/block/properties/base.py similarity index 100% rename from amulet_map_editor/api/wx/ui/block_select/properties/base.py rename to amulet_map_editor/api/wx/ui/mc/block/properties/base.py diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/demo.py b/amulet_map_editor/api/wx/ui/mc/block/properties/demo.py similarity index 58% rename from amulet_map_editor/api/wx/ui/block_select/properties/demo.py rename to amulet_map_editor/api/wx/ui/mc/block/properties/demo.py index ddf37858..330645a5 100644 --- a/amulet_map_editor/api/wx/ui/block_select/properties/demo.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/demo.py @@ -1,9 +1,9 @@ import wx -from amulet_map_editor.api.wx.ui.block_select.properties.single import ( +from amulet_map_editor.api.wx.ui.mc.block.properties.single import ( demo as single_properties_demo, ) -from amulet_map_editor.api.wx.ui.block_select.properties.multiple import ( - demo as wildcard_properties_demo, +from amulet_map_editor.api.wx.ui.mc.block.properties.multiple import ( + demo as multiple_properties_demo, ) @@ -13,7 +13,7 @@ def demo(): An app instance must be created first. """ single_properties_demo() - wildcard_properties_demo() + multiple_properties_demo() if __name__ == "__main__": diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/multiple/__init__.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/__init__.py similarity index 100% rename from amulet_map_editor/api/wx/ui/block_select/properties/multiple/__init__.py rename to amulet_map_editor/api/wx/ui/mc/block/properties/multiple/__init__.py diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/multiple/automatic/__init__.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/automatic/__init__.py similarity index 100% rename from amulet_map_editor/api/wx/ui/block_select/properties/multiple/automatic/__init__.py rename to amulet_map_editor/api/wx/ui/mc/block/properties/multiple/automatic/__init__.py diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/multiple/automatic/automatic.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/automatic/automatic.py similarity index 100% rename from amulet_map_editor/api/wx/ui/block_select/properties/multiple/automatic/automatic.py rename to amulet_map_editor/api/wx/ui/mc/block/properties/multiple/automatic/automatic.py diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/multiple/automatic/popup.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/automatic/popup.py similarity index 100% rename from amulet_map_editor/api/wx/ui/block_select/properties/multiple/automatic/popup.py rename to amulet_map_editor/api/wx/ui/mc/block/properties/multiple/automatic/popup.py diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/multiple/base.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/base.py similarity index 100% rename from amulet_map_editor/api/wx/ui/block_select/properties/multiple/base.py rename to amulet_map_editor/api/wx/ui/mc/block/properties/multiple/base.py diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/multiple/events.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/events.py similarity index 100% rename from amulet_map_editor/api/wx/ui/block_select/properties/multiple/events.py rename to amulet_map_editor/api/wx/ui/mc/block/properties/multiple/events.py diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/multiple/main.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py similarity index 100% rename from amulet_map_editor/api/wx/ui/block_select/properties/multiple/main.py rename to amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/multiple/manual.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/manual.py similarity index 100% rename from amulet_map_editor/api/wx/ui/block_select/properties/multiple/manual.py rename to amulet_map_editor/api/wx/ui/mc/block/properties/multiple/manual.py diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/single/__init__.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/__init__.py similarity index 100% rename from amulet_map_editor/api/wx/ui/block_select/properties/single/__init__.py rename to amulet_map_editor/api/wx/ui/mc/block/properties/single/__init__.py diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/single/automatic.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/automatic.py similarity index 100% rename from amulet_map_editor/api/wx/ui/block_select/properties/single/automatic.py rename to amulet_map_editor/api/wx/ui/mc/block/properties/single/automatic.py diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/single/base.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/base.py similarity index 100% rename from amulet_map_editor/api/wx/ui/block_select/properties/single/base.py rename to amulet_map_editor/api/wx/ui/mc/block/properties/single/base.py diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/single/events.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/events.py similarity index 100% rename from amulet_map_editor/api/wx/ui/block_select/properties/single/events.py rename to amulet_map_editor/api/wx/ui/mc/block/properties/single/events.py diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/single/main.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py similarity index 100% rename from amulet_map_editor/api/wx/ui/block_select/properties/single/main.py rename to amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py diff --git a/amulet_map_editor/api/wx/ui/block_select/properties/single/manual.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/manual.py similarity index 100% rename from amulet_map_editor/api/wx/ui/block_select/properties/single/manual.py rename to amulet_map_editor/api/wx/ui/mc/block/properties/single/manual.py diff --git a/amulet_map_editor/api/wx/ui/mc/demo.py b/amulet_map_editor/api/wx/ui/mc/demo.py new file mode 100644 index 00000000..8d7b2066 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/mc/demo.py @@ -0,0 +1,22 @@ +from amulet_map_editor.api.wx.ui.mc.version_select.demo import demo as version_select_demo +from amulet_map_editor.api.wx.ui.mc.biome.demo import demo as biome_demo +from amulet_map_editor.api.wx.ui.mc.block.demo import demo as block_demo + + +def demo(): + """ + Show a demo version of the UI. + An app instance must be created first. + """ + version_select_demo() + biome_demo() + block_demo() + + +if __name__ == "__main__": + + import wx + + app = wx.App() + demo() + app.MainLoop() diff --git a/amulet_map_editor/api/wx/ui/version_select/__init__.py b/amulet_map_editor/api/wx/ui/mc/version_select/__init__.py similarity index 100% rename from amulet_map_editor/api/wx/ui/version_select/__init__.py rename to amulet_map_editor/api/wx/ui/mc/version_select/__init__.py diff --git a/amulet_map_editor/api/wx/ui/version_select/demo.py b/amulet_map_editor/api/wx/ui/mc/version_select/demo.py similarity index 68% rename from amulet_map_editor/api/wx/ui/version_select/demo.py rename to amulet_map_editor/api/wx/ui/mc/version_select/demo.py index ce06282d..0b1afb32 100644 --- a/amulet_map_editor/api/wx/ui/version_select/demo.py +++ b/amulet_map_editor/api/wx/ui/mc/version_select/demo.py @@ -1,8 +1,8 @@ import wx -from amulet_map_editor.api.wx.ui.version_select.platform_select import ( +from amulet_map_editor.api.wx.ui.mc.version_select.platform_select import ( demo as platform_demo, ) -from amulet_map_editor.api.wx.ui.version_select.version_select import ( +from amulet_map_editor.api.wx.ui.mc.version_select.version_select import ( demo as version_demo, ) diff --git a/amulet_map_editor/api/wx/ui/version_select/events.py b/amulet_map_editor/api/wx/ui/mc/version_select/events.py similarity index 100% rename from amulet_map_editor/api/wx/ui/version_select/events.py rename to amulet_map_editor/api/wx/ui/mc/version_select/events.py diff --git a/amulet_map_editor/api/wx/ui/version_select/platform_select.py b/amulet_map_editor/api/wx/ui/mc/version_select/platform_select.py similarity index 100% rename from amulet_map_editor/api/wx/ui/version_select/platform_select.py rename to amulet_map_editor/api/wx/ui/mc/version_select/platform_select.py diff --git a/amulet_map_editor/api/wx/ui/version_select/version_select.py b/amulet_map_editor/api/wx/ui/mc/version_select/version_select.py similarity index 100% rename from amulet_map_editor/api/wx/ui/version_select/version_select.py rename to amulet_map_editor/api/wx/ui/mc/version_select/version_select.py diff --git a/amulet_map_editor/api/wx/ui/simple.py b/amulet_map_editor/api/wx/ui/simple.py index 6d40ff27..8fa98c60 100644 --- a/amulet_map_editor/api/wx/ui/simple.py +++ b/amulet_map_editor/api/wx/ui/simple.py @@ -5,8 +5,6 @@ from wx.lib.scrolledpanel import ScrolledPanel from typing import Iterable, Union, Any, List, Optional, Sequence, Dict, Tuple -from amulet_map_editor import log - class SimpleSizer: def __init__(self, sizer_dir=wx.VERTICAL): diff --git a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/construction.py b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/construction.py index da6415ff..6eea6749 100644 --- a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/construction.py +++ b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/construction.py @@ -7,7 +7,7 @@ from amulet.api.data_types import Dimension, OperationReturnType from amulet.level.formats.construction import ConstructionFormatWrapper -from amulet_map_editor.api.wx.ui.version_select import VersionSelect +from amulet_map_editor.api.wx.ui.mc.version_select import VersionSelect from amulet_map_editor.programs.edit.api.operations import ( SimpleOperationPanel, OperationError, diff --git a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/mcstructure.py b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/mcstructure.py index e0491f20..adb1644b 100644 --- a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/mcstructure.py +++ b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/mcstructure.py @@ -7,7 +7,7 @@ from amulet.api.data_types import Dimension, OperationReturnType from amulet.level.formats.mcstructure import MCStructureFormatWrapper -from amulet_map_editor.api.wx.ui.version_select import VersionSelect +from amulet_map_editor.api.wx.ui.mc.version_select import VersionSelect from amulet_map_editor.programs.edit.api.operations import ( SimpleOperationPanel, OperationError, diff --git a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/schematic.py b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/schematic.py index 323f73d9..aa8c14d8 100644 --- a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/schematic.py +++ b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/schematic.py @@ -7,7 +7,7 @@ from amulet.api.data_types import Dimension, OperationReturnType from amulet.level.formats.schematic import SchematicFormatWrapper -from amulet_map_editor.api.wx.ui.version_select import PlatformSelect +from amulet_map_editor.api.wx.ui.mc.version_select import PlatformSelect from amulet_map_editor.programs.edit.api.operations import ( SimpleOperationPanel, OperationError, diff --git a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/sponge_schematic.py b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/sponge_schematic.py index a4192b6f..6e188c89 100644 --- a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/sponge_schematic.py +++ b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/sponge_schematic.py @@ -7,7 +7,7 @@ from amulet.api.data_types import Dimension, OperationReturnType from amulet.level.formats.sponge_schem import SpongeSchemFormatWrapper -from amulet_map_editor.api.wx.ui.version_select import VersionSelect +from amulet_map_editor.api.wx.ui.mc.version_select import VersionSelect from amulet_map_editor.programs.edit.api.operations import ( SimpleOperationPanel, OperationError, diff --git a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/fill.py b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/fill.py index 433e4d18..635082fe 100644 --- a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/fill.py +++ b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/fill.py @@ -3,8 +3,8 @@ from amulet.operations.fill import fill -from amulet_map_editor.api.wx.ui.base_select import EVT_PICK -from amulet_map_editor.api.wx.ui.block_select import BlockDefine +from amulet_map_editor.api.wx.ui.mc.base.base_select import EVT_PICK +from amulet_map_editor.api.wx.ui.mc.block import BlockDefine from amulet_map_editor.programs.edit.api.operations import DefaultOperationUI if TYPE_CHECKING: diff --git a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/replace.py b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/replace.py index eac5b3c0..1bf5deae 100644 --- a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/replace.py +++ b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/replace.py @@ -4,8 +4,8 @@ from amulet.api.block import Block -from amulet_map_editor.api.wx.ui.base_select import EVT_PICK -from amulet_map_editor.api.wx.ui.block_select import BlockDefine +from amulet_map_editor.api.wx.ui.mc.base.base_select import EVT_PICK +from amulet_map_editor.api.wx.ui.mc.block import BlockDefine from amulet_map_editor.api.wx.ui.simple import SimpleScrollablePanel from amulet_map_editor.programs.edit.api.operations import DefaultOperationUI diff --git a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/set_biome.py b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/set_biome.py index 1b460924..0c6be7dc 100644 --- a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/set_biome.py +++ b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/set_biome.py @@ -5,8 +5,8 @@ from amulet.utils import block_coords_to_chunk_coords from amulet.api.chunk.biomes import BiomesShape -from amulet_map_editor.api.wx.ui.base_select import EVT_PICK -from amulet_map_editor.api.wx.ui.biome_select import BiomeDefine +from amulet_map_editor.api.wx.ui.mc.base.base_select import EVT_PICK +from amulet_map_editor.api.wx.ui.mc.biome import BiomeDefine from amulet_map_editor.programs.edit.api.operations import SimpleOperationPanel from amulet_map_editor.api.wx.ui.simple import SimpleChoiceAny diff --git a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/waterlog.py b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/waterlog.py index 7ea8f360..8924ad00 100644 --- a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/waterlog.py +++ b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/waterlog.py @@ -2,9 +2,9 @@ from typing import TYPE_CHECKING, Tuple import wx -from amulet_map_editor.api.wx.ui.base_select import EVT_PICK +from amulet_map_editor.api.wx.ui.mc.base.base_select import EVT_PICK from amulet_map_editor.api.wx.ui.simple import SimpleDialog -from amulet_map_editor.api.wx.ui.block_select import BlockDefine +from amulet_map_editor.api.wx.ui.mc.block import BlockDefine from amulet_map_editor.programs.edit.api.operations import DefaultOperationUI from amulet_map_editor.api import image diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_dialog.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_dialog.py index e1af8083..b3e21f7e 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_dialog.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_dialog.py @@ -3,7 +3,7 @@ import PyMCTranslate from amulet.api.block import Block from amulet.api.block_entity import BlockEntity -from amulet_map_editor.api.wx.ui.block_select import BlockDefine +from amulet_map_editor.api.wx.ui.mc.block import BlockDefine class BlockSelectDialog(wx.Dialog): From 8ec2d5b246a70d3952caf74b4f82322c63db6d67 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Thu, 1 Jul 2021 09:42:21 +0100 Subject: [PATCH 030/139] Renamed version_select package to version --- amulet_map_editor/api/wx/ui/demo.py | 2 +- amulet_map_editor/api/wx/ui/mc/base/base_define.py | 2 +- amulet_map_editor/api/wx/ui/mc/demo.py | 2 +- .../api/wx/ui/mc/{version_select => version}/__init__.py | 0 .../api/wx/ui/mc/{version_select => version}/demo.py | 4 ++-- .../api/wx/ui/mc/{version_select => version}/events.py | 0 .../wx/ui/mc/{version_select => version}/platform_select.py | 0 .../wx/ui/mc/{version_select => version}/version_select.py | 0 .../stock_plugins/export_operations/construction.py | 2 +- .../operations/stock_plugins/export_operations/mcstructure.py | 2 +- .../operations/stock_plugins/export_operations/schematic.py | 2 +- .../stock_plugins/export_operations/sponge_schematic.py | 2 +- 12 files changed, 9 insertions(+), 9 deletions(-) rename amulet_map_editor/api/wx/ui/mc/{version_select => version}/__init__.py (100%) rename amulet_map_editor/api/wx/ui/mc/{version_select => version}/demo.py (68%) rename amulet_map_editor/api/wx/ui/mc/{version_select => version}/events.py (100%) rename amulet_map_editor/api/wx/ui/mc/{version_select => version}/platform_select.py (100%) rename amulet_map_editor/api/wx/ui/mc/{version_select => version}/version_select.py (100%) diff --git a/amulet_map_editor/api/wx/ui/demo.py b/amulet_map_editor/api/wx/ui/demo.py index 665eb22e..6e009993 100644 --- a/amulet_map_editor/api/wx/ui/demo.py +++ b/amulet_map_editor/api/wx/ui/demo.py @@ -2,7 +2,7 @@ from amulet_map_editor.api.wx.ui.mc.block.demo import demo as block_demo from amulet_map_editor.api.wx.ui.nbt_editor import demo as nbt_editor_demo from amulet_map_editor.api.wx.ui.select_world import demo as select_world_demo -from amulet_map_editor.api.wx.ui.mc.version_select.demo import demo as version_select_demo +from amulet_map_editor.api.wx.ui.mc.version.demo import demo as version_select_demo def demo(): diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_define.py b/amulet_map_editor/api/wx/ui/mc/base/base_define.py index 54e9614a..487710f2 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/base_define.py +++ b/amulet_map_editor/api/wx/ui/mc/base/base_define.py @@ -6,7 +6,7 @@ from amulet.api.data_types import VersionNumberTuple, PlatformType from amulet_map_editor.api.wx.ui.mc.base.base_select import EVT_ITEM_CHANGE, BaseSelect -from amulet_map_editor.api.wx.ui.mc.version_select import ( +from amulet_map_editor.api.wx.ui.mc.version import ( VersionSelect, EVT_VERSION_CHANGE, ) diff --git a/amulet_map_editor/api/wx/ui/mc/demo.py b/amulet_map_editor/api/wx/ui/mc/demo.py index 8d7b2066..21552ff1 100644 --- a/amulet_map_editor/api/wx/ui/mc/demo.py +++ b/amulet_map_editor/api/wx/ui/mc/demo.py @@ -1,4 +1,4 @@ -from amulet_map_editor.api.wx.ui.mc.version_select.demo import demo as version_select_demo +from amulet_map_editor.api.wx.ui.mc.version.demo import demo as version_select_demo from amulet_map_editor.api.wx.ui.mc.biome.demo import demo as biome_demo from amulet_map_editor.api.wx.ui.mc.block.demo import demo as block_demo diff --git a/amulet_map_editor/api/wx/ui/mc/version_select/__init__.py b/amulet_map_editor/api/wx/ui/mc/version/__init__.py similarity index 100% rename from amulet_map_editor/api/wx/ui/mc/version_select/__init__.py rename to amulet_map_editor/api/wx/ui/mc/version/__init__.py diff --git a/amulet_map_editor/api/wx/ui/mc/version_select/demo.py b/amulet_map_editor/api/wx/ui/mc/version/demo.py similarity index 68% rename from amulet_map_editor/api/wx/ui/mc/version_select/demo.py rename to amulet_map_editor/api/wx/ui/mc/version/demo.py index 0b1afb32..375003b4 100644 --- a/amulet_map_editor/api/wx/ui/mc/version_select/demo.py +++ b/amulet_map_editor/api/wx/ui/mc/version/demo.py @@ -1,8 +1,8 @@ import wx -from amulet_map_editor.api.wx.ui.mc.version_select.platform_select import ( +from amulet_map_editor.api.wx.ui.mc.version.platform_select import ( demo as platform_demo, ) -from amulet_map_editor.api.wx.ui.mc.version_select.version_select import ( +from amulet_map_editor.api.wx.ui.mc.version.version_select import ( demo as version_demo, ) diff --git a/amulet_map_editor/api/wx/ui/mc/version_select/events.py b/amulet_map_editor/api/wx/ui/mc/version/events.py similarity index 100% rename from amulet_map_editor/api/wx/ui/mc/version_select/events.py rename to amulet_map_editor/api/wx/ui/mc/version/events.py diff --git a/amulet_map_editor/api/wx/ui/mc/version_select/platform_select.py b/amulet_map_editor/api/wx/ui/mc/version/platform_select.py similarity index 100% rename from amulet_map_editor/api/wx/ui/mc/version_select/platform_select.py rename to amulet_map_editor/api/wx/ui/mc/version/platform_select.py diff --git a/amulet_map_editor/api/wx/ui/mc/version_select/version_select.py b/amulet_map_editor/api/wx/ui/mc/version/version_select.py similarity index 100% rename from amulet_map_editor/api/wx/ui/mc/version_select/version_select.py rename to amulet_map_editor/api/wx/ui/mc/version/version_select.py diff --git a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/construction.py b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/construction.py index 6eea6749..04c57ff4 100644 --- a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/construction.py +++ b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/construction.py @@ -7,7 +7,7 @@ from amulet.api.data_types import Dimension, OperationReturnType from amulet.level.formats.construction import ConstructionFormatWrapper -from amulet_map_editor.api.wx.ui.mc.version_select import VersionSelect +from amulet_map_editor.api.wx.ui.mc.version import VersionSelect from amulet_map_editor.programs.edit.api.operations import ( SimpleOperationPanel, OperationError, diff --git a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/mcstructure.py b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/mcstructure.py index adb1644b..045cc91b 100644 --- a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/mcstructure.py +++ b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/mcstructure.py @@ -7,7 +7,7 @@ from amulet.api.data_types import Dimension, OperationReturnType from amulet.level.formats.mcstructure import MCStructureFormatWrapper -from amulet_map_editor.api.wx.ui.mc.version_select import VersionSelect +from amulet_map_editor.api.wx.ui.mc.version import VersionSelect from amulet_map_editor.programs.edit.api.operations import ( SimpleOperationPanel, OperationError, diff --git a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/schematic.py b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/schematic.py index aa8c14d8..b71dfe1e 100644 --- a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/schematic.py +++ b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/schematic.py @@ -7,7 +7,7 @@ from amulet.api.data_types import Dimension, OperationReturnType from amulet.level.formats.schematic import SchematicFormatWrapper -from amulet_map_editor.api.wx.ui.mc.version_select import PlatformSelect +from amulet_map_editor.api.wx.ui.mc.version import PlatformSelect from amulet_map_editor.programs.edit.api.operations import ( SimpleOperationPanel, OperationError, diff --git a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/sponge_schematic.py b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/sponge_schematic.py index 6e188c89..fb3db7d1 100644 --- a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/sponge_schematic.py +++ b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/sponge_schematic.py @@ -7,7 +7,7 @@ from amulet.api.data_types import Dimension, OperationReturnType from amulet.level.formats.sponge_schem import SpongeSchemFormatWrapper -from amulet_map_editor.api.wx.ui.mc.version_select import VersionSelect +from amulet_map_editor.api.wx.ui.mc.version import VersionSelect from amulet_map_editor.programs.edit.api.operations import ( SimpleOperationPanel, OperationError, From 269272811783b32233480811557779773efc8507 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Thu, 1 Jul 2021 09:42:54 +0100 Subject: [PATCH 031/139] Reformatted --- amulet_map_editor/api/wx/ui/mc/block/demo.py | 1 + amulet_map_editor/api/wx/ui/mc/block/multi_block_define.py | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/block/demo.py b/amulet_map_editor/api/wx/ui/mc/block/demo.py index da68731d..c6c05ca2 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/demo.py +++ b/amulet_map_editor/api/wx/ui/mc/block/demo.py @@ -5,6 +5,7 @@ from amulet_map_editor.api.wx.ui.mc.block.block_define import ( demo as block_define_demo, ) + # from amulet_map_editor.api.wx.ui.mc.block.multi_block_define import ( # demo as multi_block_define_demo, # ) diff --git a/amulet_map_editor/api/wx/ui/mc/block/multi_block_define.py b/amulet_map_editor/api/wx/ui/mc/block/multi_block_define.py index e85ae750..cb27bc41 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/multi_block_define.py +++ b/amulet_map_editor/api/wx/ui/mc/block/multi_block_define.py @@ -12,9 +12,7 @@ MAXIMIZE, MINIMIZE, ) -from amulet_map_editor.api.wx.ui.mc.block import ( - BlockDefine -) +from amulet_map_editor.api.wx.ui.mc.block import BlockDefine from amulet_map_editor.api.wx.ui.mc.block.properties import EVT_SINGLE_PROPERTIES_CHANGE From 65351eaa8950cd26dd5b9eec46adf97da89c62bd Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Thu, 1 Jul 2021 10:50:24 +0100 Subject: [PATCH 032/139] Split up BlockDefine BaseBlockDefine has the base logic and BlockDefine adds the property select to it. This will help add a wildcard variant --- .../api/wx/ui/mc/base/base_define.py | 2 +- .../api/wx/ui/mc/block/__init__.py | 10 ++- .../api/wx/ui/mc/block/define/__init__.py | 1 + .../api/wx/ui/mc/block/define/base.py | 78 ++++++++++++++++ .../ui/mc/block/{ => define}/block_define.py | 88 +++---------------- amulet_map_editor/api/wx/ui/mc/block/demo.py | 2 +- .../api/wx/ui/mc/block/multi_block_define.py | 2 +- .../api/wx/ui/mc/block/properties/__init__.py | 1 + 8 files changed, 106 insertions(+), 78 deletions(-) create mode 100644 amulet_map_editor/api/wx/ui/mc/block/define/__init__.py create mode 100644 amulet_map_editor/api/wx/ui/mc/block/define/base.py rename amulet_map_editor/api/wx/ui/mc/block/{ => define}/block_define.py (64%) diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_define.py b/amulet_map_editor/api/wx/ui/mc/base/base_define.py index 487710f2..d4d97fe2 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/base_define.py +++ b/amulet_map_editor/api/wx/ui/mc/base/base_define.py @@ -60,7 +60,7 @@ def __init__( left_sizer.Add(self._picker, 1, wx.EXPAND | wx.TOP, 5) self._picker.Bind(EVT_ITEM_CHANGE, self._on_picker_change) - self.SetSizerAndFit(self._sizer) + self.SetSizer(self._sizer) self.Layout() def _on_picker_change(self, evt): diff --git a/amulet_map_editor/api/wx/ui/mc/block/__init__.py b/amulet_map_editor/api/wx/ui/mc/block/__init__.py index 88b1654d..4dddc512 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/__init__.py +++ b/amulet_map_editor/api/wx/ui/mc/block/__init__.py @@ -1,3 +1,11 @@ from .block_select import BlockSelect -from .block_define import BlockDefine +from .define import BlockDefine from .multi_block_define import MultiBlockDefine +from .properties import ( + SinglePropertySelect, + SinglePropertiesChangeEvent, + EVT_SINGLE_PROPERTIES_CHANGE, + MultiplePropertySelect, + MultiplePropertiesChangeEvent, + EVT_MULTIPLE_PROPERTIES_CHANGE, +) diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/__init__.py b/amulet_map_editor/api/wx/ui/mc/block/define/__init__.py new file mode 100644 index 00000000..d98358f1 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/mc/block/define/__init__.py @@ -0,0 +1 @@ +from .block_define import BlockDefine, demo diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/base.py b/amulet_map_editor/api/wx/ui/mc/block/define/base.py new file mode 100644 index 00000000..5cf20d56 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/mc/block/define/base.py @@ -0,0 +1,78 @@ +import wx.lib.scrolledpanel +from typing import Tuple, Optional + +import PyMCTranslate + +from amulet_map_editor.api.wx.ui.mc.base.base_define import BaseDefine +from amulet_map_editor.api.wx.ui.mc.block import BlockSelect + +from amulet_map_editor.api.wx.ui.mc.block.properties import ( + BasePropertySelect, +) + + +class BaseBlockDefine(BaseDefine): + """ + A UI that merges a version select widget with a block select widget and a property select. + """ + + def __init__( + self, + parent, + translation_manager: PyMCTranslate.TranslationManager, + orientation=wx.VERTICAL, + platform: str = None, + version_number: Tuple[int, int, int] = None, + force_blockstate: bool = None, + namespace: str = None, + block_name: str = None, + show_pick_block: bool = False, + **kwargs, + ): + super().__init__( + parent, + translation_manager, + BlockSelect, + orientation, + platform, + version_number, + namespace, + default_name=block_name, + show_pick=show_pick_block, + force_blockstate=force_blockstate, + **kwargs, + ) + self._property_picker: Optional[BasePropertySelect] = None + + def _on_picker_change(self, evt): + self._update_properties() + evt.Skip() + + def _on_property_change(self, evt): + self.Layout() + evt.Skip() + + def _update_properties(self): + self._property_picker.version_block = ( + self._version_picker.platform, + self._version_picker.version_number, + self._version_picker.force_blockstate, + self._picker.namespace, + self._picker.name, + ) + + @property + def force_blockstate(self) -> bool: + return self._version_picker.force_blockstate + + @force_blockstate.setter + def force_blockstate(self, force_blockstate: bool): + self._version_picker.force_blockstate = force_blockstate + + @property + def block_name(self) -> str: + return self._picker.name + + @block_name.setter + def block_name(self, block_name: str): + self._picker.name = block_name diff --git a/amulet_map_editor/api/wx/ui/mc/block/block_define.py b/amulet_map_editor/api/wx/ui/mc/block/define/block_define.py similarity index 64% rename from amulet_map_editor/api/wx/ui/mc/block/block_define.py rename to amulet_map_editor/api/wx/ui/mc/block/define/block_define.py index 8988f5e8..a5d0fc57 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/block_define.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/block_define.py @@ -6,16 +6,14 @@ from amulet.api.block import PropertyType, Block from amulet.api.block_entity import BlockEntity -from amulet_map_editor.api.wx.ui.mc.base.base_define import BaseDefine -from amulet_map_editor.api.wx.ui.mc.block import BlockSelect - from amulet_map_editor.api.wx.ui.mc.block.properties import ( SinglePropertySelect, EVT_SINGLE_PROPERTIES_CHANGE, ) +from amulet_map_editor.api.wx.ui.mc.block.define.base import BaseBlockDefine -class BlockDefine(BaseDefine): +class BlockDefine(BaseBlockDefine): """ A UI that merges a version select widget with a block select widget and a property select. """ @@ -31,99 +29,41 @@ def __init__( namespace: str = None, block_name: str = None, properties: PropertyType = None, - wildcard_properties=False, show_pick_block: bool = False, **kwargs, ): super().__init__( parent, translation_manager, - BlockSelect, orientation, platform, version_number, + force_blockstate, namespace, - default_name=block_name, - show_pick=show_pick_block, - force_blockstate=force_blockstate, + block_name, + show_pick_block=show_pick_block, **kwargs, ) right_sizer = wx.BoxSizer(wx.VERTICAL) border = wx.LEFT if orientation == wx.HORIZONTAL else wx.TOP self._sizer.Add(right_sizer, 1, wx.EXPAND | border, 5) - self._wildcard_mode = wildcard_properties - if wildcard_properties: - self._property_picker = SinglePropertySelect( - self, - translation_manager, - self._version_picker.platform, - self._version_picker.version_number, - self._version_picker.force_blockstate, - self._picker.namespace, - self._picker.name, - properties, - ) - else: - self._property_picker = SinglePropertySelect( - self, - translation_manager, - self._version_picker.platform, - self._version_picker.version_number, - self._version_picker.force_blockstate, - self._picker.namespace, - self._picker.name, - properties, - ) - right_sizer.Add(self._property_picker, 1, wx.EXPAND) - self._property_picker.Bind( - EVT_SINGLE_PROPERTIES_CHANGE, self._on_property_change - ) - - self.SetSizerAndFit(self._sizer) - self.Layout() - - @property - def wildcard_mode(self) -> bool: - """ - Is the UI in wildcard mode. - If True multiple property values can be selected. - These can be accessed through :attr:`extra_properties` - """ - return self._wildcard_mode - - def _on_picker_change(self, evt): - self._update_properties() - evt.Skip() - - def _on_property_change(self, evt): - self.Layout() - evt.Skip() - - def _update_properties(self): - self._property_picker.version_block = ( + self._property_picker = SinglePropertySelect( + self, + translation_manager, self._version_picker.platform, self._version_picker.version_number, self._version_picker.force_blockstate, self._picker.namespace, self._picker.name, + properties, + ) + right_sizer.Add(self._property_picker, 1, wx.EXPAND) + self._property_picker.Bind( + EVT_SINGLE_PROPERTIES_CHANGE, self._on_property_change ) - @property - def force_blockstate(self) -> bool: - return self._version_picker.force_blockstate - - @force_blockstate.setter - def force_blockstate(self, force_blockstate: bool): - self._version_picker.force_blockstate = force_blockstate - - @property - def block_name(self) -> str: - return self._picker.name - - @block_name.setter - def block_name(self, block_name: str): - self._picker.name = block_name + self.Layout() @property def properties(self) -> PropertyType: diff --git a/amulet_map_editor/api/wx/ui/mc/block/demo.py b/amulet_map_editor/api/wx/ui/mc/block/demo.py index c6c05ca2..cc4694dd 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/demo.py +++ b/amulet_map_editor/api/wx/ui/mc/block/demo.py @@ -2,7 +2,7 @@ from amulet_map_editor.api.wx.ui.mc.block.block_select import ( demo as block_select_demo, ) -from amulet_map_editor.api.wx.ui.mc.block.block_define import ( +from amulet_map_editor.api.wx.ui.mc.block.define import ( demo as block_define_demo, ) diff --git a/amulet_map_editor/api/wx/ui/mc/block/multi_block_define.py b/amulet_map_editor/api/wx/ui/mc/block/multi_block_define.py index cb27bc41..76bd99d6 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/multi_block_define.py +++ b/amulet_map_editor/api/wx/ui/mc/block/multi_block_define.py @@ -12,7 +12,7 @@ MAXIMIZE, MINIMIZE, ) -from amulet_map_editor.api.wx.ui.mc.block import BlockDefine +from amulet_map_editor.api.wx.ui.mc.block.define import BlockDefine from amulet_map_editor.api.wx.ui.mc.block.properties import EVT_SINGLE_PROPERTIES_CHANGE diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/__init__.py b/amulet_map_editor/api/wx/ui/mc/block/properties/__init__.py index 035e915e..43514eb8 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/__init__.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/__init__.py @@ -1,3 +1,4 @@ +from .base import BasePropertySelect from .single import ( SinglePropertySelect, SinglePropertiesChangeEvent, From dc6d95a703855bba8225810c169a8d0f9da7f130 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Thu, 1 Jul 2021 11:30:39 +0100 Subject: [PATCH 033/139] Added a wildcard block defining UI --- .../api/wx/ui/mc/block/define/__init__.py | 4 +- .../api/wx/ui/mc/block/define/demo.py | 26 ++++ .../mc/block/define/wildcard_block_define.py | 111 ++++++++++++++++++ .../multiple/automatic/automatic.py | 7 +- .../ui/mc/block/properties/multiple/base.py | 7 +- .../ui/mc/block/properties/multiple/events.py | 7 +- .../ui/mc/block/properties/multiple/main.py | 22 ++-- .../ui/mc/block/properties/multiple/manual.py | 6 +- 8 files changed, 160 insertions(+), 30 deletions(-) create mode 100644 amulet_map_editor/api/wx/ui/mc/block/define/demo.py create mode 100644 amulet_map_editor/api/wx/ui/mc/block/define/wildcard_block_define.py diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/__init__.py b/amulet_map_editor/api/wx/ui/mc/block/define/__init__.py index d98358f1..c651689f 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/__init__.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/__init__.py @@ -1 +1,3 @@ -from .block_define import BlockDefine, demo +from .block_define import BlockDefine +from .wildcard_block_define import WildcardBlockDefine +from .demo import demo diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/demo.py b/amulet_map_editor/api/wx/ui/mc/block/define/demo.py new file mode 100644 index 00000000..e7681479 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/mc/block/define/demo.py @@ -0,0 +1,26 @@ +import wx +from amulet_map_editor.api.wx.ui.mc.block.define.block_define import ( + demo as block_define_demo, +) +from amulet_map_editor.api.wx.ui.mc.block.define.wildcard_block_define import ( + demo as wildcard_block_define_demo, +) + + +def demo(): + """ + Show a demo version of the UI. + An app instance must be created first. + """ + block_define_demo() + wildcard_block_define_demo() + + +if __name__ == "__main__": + + def main(): + app = wx.App() + demo() + app.MainLoop() + + main() diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/wildcard_block_define.py b/amulet_map_editor/api/wx/ui/mc/block/define/wildcard_block_define.py new file mode 100644 index 00000000..46f96fb4 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/mc/block/define/wildcard_block_define.py @@ -0,0 +1,111 @@ +import wx +import wx.lib.scrolledpanel +from typing import Tuple + +import PyMCTranslate +from amulet.api.block import PropertyTypeMultiple + +from amulet_map_editor.api.wx.ui.mc.block.properties import ( + MultiplePropertySelect, + EVT_MULTIPLE_PROPERTIES_CHANGE, +) +from amulet_map_editor.api.wx.ui.mc.block.define.base import BaseBlockDefine + + +class WildcardBlockDefine(BaseBlockDefine): + """ + A UI that merges a version select widget with a block select widget and a multi property select. + """ + + def __init__( + self, + parent, + translation_manager: PyMCTranslate.TranslationManager, + orientation=wx.VERTICAL, + platform: str = None, + version_number: Tuple[int, int, int] = None, + force_blockstate: bool = None, + namespace: str = None, + block_name: str = None, + properties: PropertyTypeMultiple = None, + show_pick_block: bool = False, + **kwargs, + ): + super().__init__( + parent, + translation_manager, + orientation, + platform, + version_number, + force_blockstate, + namespace, + block_name, + show_pick_block=show_pick_block, + **kwargs, + ) + + right_sizer = wx.BoxSizer(wx.VERTICAL) + border = wx.LEFT if orientation == wx.HORIZONTAL else wx.TOP + self._sizer.Add(right_sizer, 1, wx.EXPAND | border, 5) + self._property_picker = MultiplePropertySelect( + self, + translation_manager, + self._version_picker.platform, + self._version_picker.version_number, + self._version_picker.force_blockstate, + self._picker.namespace, + self._picker.name, + properties, + ) + right_sizer.Add(self._property_picker, 1, wx.EXPAND) + self._property_picker.Bind( + EVT_MULTIPLE_PROPERTIES_CHANGE, self._on_property_change + ) + + self.Layout() + + @property + def extra_properties(self) -> PropertyTypeMultiple: + """ + The values that are checked for each property. + This UI can have more than one property value checked (ticked). + """ + return self._property_picker.extra_properties + + @extra_properties.setter + def extra_properties(self, extra_properties: PropertyTypeMultiple): + self._property_picker.extra_properties = extra_properties + + +def demo(): + """ + Show a demo version of the UI. + An app instance must be created first. + """ + translation_manager = PyMCTranslate.new_translation_manager() + dialog = wx.Dialog( + None, + title="WildcardBlockDefine", + style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT, + ) + sizer = wx.BoxSizer() + dialog.SetSizer(sizer) + sizer.Add( + WildcardBlockDefine(dialog, translation_manager, wx.HORIZONTAL), + 1, + wx.ALL | wx.EXPAND, + 5, + ) + dialog.Show() + dialog.Fit() + dialog.Bind(wx.EVT_CLOSE, lambda evt: dialog.Destroy()) + + +if __name__ == "__main__": + + def main(): + app = wx.App() + demo() + app.MainLoop() + + main() diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/automatic/automatic.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/automatic/automatic.py index 909a1ca9..5068a68d 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/automatic/automatic.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/automatic/automatic.py @@ -2,7 +2,7 @@ from typing import Dict, List, Tuple import PyMCTranslate -from amulet.api.block import PropertyValueType +from amulet.api.block import PropertyTypeMultiple from ..events import MultiplePropertiesChangeEvent from ..base import BaseMultipleProperty from .popup import PropertyValueComboPopup @@ -69,13 +69,12 @@ def on_close(evt): self._properties[name] = (choice, popup) create_choice() - self.properties = self._specification.get("defaults", {}) self.Fit() self.GetTopLevelParent().Layout() self.Thaw() @property - def extra_properties(self) -> Dict[str, Tuple[PropertyValueType, ...]]: + def extra_properties(self) -> PropertyTypeMultiple: """ The values that are checked for each property. This UI can have more than one property value checked (ticked). @@ -85,7 +84,7 @@ def extra_properties(self) -> Dict[str, Tuple[PropertyValueType, ...]]: } @extra_properties.setter - def extra_properties(self, properties: Dict[str, Tuple[PropertyValueType, ...]]): + def extra_properties(self, properties: PropertyTypeMultiple): self.Freeze() for name, nbt_tuple in properties.items(): if name in self._properties: diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/base.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/base.py index a7335c71..ac28c089 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/base.py @@ -1,8 +1,7 @@ import wx -from typing import Dict, Tuple import PyMCTranslate -from amulet.api.block import PropertyValueType +from amulet.api.block import PropertyTypeMultiple class BaseMultipleProperty(wx.Panel): @@ -17,7 +16,7 @@ def __init__( self._translation_manager = translation_manager @property - def extra_properties(self) -> Dict[str, Tuple[PropertyValueType, ...]]: + def extra_properties(self) -> PropertyTypeMultiple: """ The values that are checked for each property. This UI can have more than one property value checked (ticked). @@ -25,5 +24,5 @@ def extra_properties(self) -> Dict[str, Tuple[PropertyValueType, ...]]: raise NotImplementedError @extra_properties.setter - def extra_properties(self, properties: Dict[str, Tuple[PropertyValueType, ...]]): + def extra_properties(self, properties: PropertyTypeMultiple): raise NotImplementedError diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/events.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/events.py index 83503f2e..21390572 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/events.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/events.py @@ -1,6 +1,5 @@ import wx -from typing import Dict, Tuple -from amulet.api.block import PropertyValueType +from amulet.api.block import PropertyTypeMultiple _MultiplePropertiesChangeEventType = wx.NewEventType() EVT_MULTIPLE_PROPERTIES_CHANGE = wx.PyEventBinder(_MultiplePropertiesChangeEventType) @@ -11,10 +10,10 @@ class MultiplePropertiesChangeEvent(wx.PyEvent): Run when the properties UI changes. """ - def __init__(self, properties: Dict[str, Tuple[PropertyValueType, ...]]): + def __init__(self, properties: PropertyTypeMultiple): wx.PyEvent.__init__(self, eventType=_MultiplePropertiesChangeEventType) self._properties = properties @property - def properties(self) -> Dict[str, Tuple[PropertyValueType, ...]]: + def properties(self) -> PropertyTypeMultiple: return self._properties diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py index 3f0c28f8..11f455d6 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py @@ -1,8 +1,8 @@ import wx -from typing import Tuple, Dict +from typing import Tuple import PyMCTranslate -from amulet.api.block import PropertyType, PropertyValueType +from amulet.api.block import PropertyTypeMultiple from ..base import BasePropertySelect from .automatic import AutomaticMultipleProperty @@ -25,7 +25,7 @@ def __init__( force_blockstate: bool, namespace: str, block_name: str, - properties: PropertyType = None, + properties: PropertyTypeMultiple = None, ): super().__init__( parent, @@ -49,13 +49,7 @@ def __init__( self._set_properties(properties) self._rebuild_ui() - def _get_properties(self) -> PropertyType: - if self._manual_enabled: - return self._manual.properties - else: - return self._simple.properties - - def _set_properties(self, properties: PropertyType): + def _set_properties(self, properties: PropertyTypeMultiple): self.Freeze() if self._manual_enabled: self._manual.properties = properties @@ -65,7 +59,7 @@ def _set_properties(self, properties: PropertyType): self.Thaw() @property - def extra_properties(self) -> Dict[str, Tuple[PropertyValueType, ...]]: + def extra_properties(self) -> PropertyTypeMultiple: """ The values that are checked for each property. This UI can have more than one property value checked (ticked). @@ -76,12 +70,12 @@ def extra_properties(self) -> Dict[str, Tuple[PropertyValueType, ...]]: return self._simple.extra_properties @extra_properties.setter - def extra_properties(self, properties: Dict[str, Tuple[PropertyValueType, ...]]): + def extra_properties(self, extra_properties: PropertyTypeMultiple): self.Freeze() if self._manual_enabled: - self._manual.extra_properties = properties + self._manual.extra_properties = extra_properties else: - self._simple.extra_properties = properties + self._simple.extra_properties = extra_properties self.TopLevelParent.Layout() self.Thaw() diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/manual.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/manual.py index 5f87ab27..11f7002c 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/manual.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/manual.py @@ -4,7 +4,7 @@ import PyMCTranslate import amulet_nbt from amulet_nbt import SNBTType -from amulet.api.block import PropertyDataTypes, PropertyType, PropertyValueType +from amulet.api.block import PropertyDataTypes, PropertyType, PropertyTypeMultiple from amulet_map_editor.api.image import ADD_ICON, SUBTRACT_ICON from .events import MultiplePropertiesChangeEvent from .base import BaseMultipleProperty @@ -122,7 +122,7 @@ def properties(self, properties: PropertyType): # TODO: implement this properly @property - def extra_properties(self) -> Dict[str, Tuple[PropertyValueType, ...]]: + def extra_properties(self) -> PropertyTypeMultiple: """ The values that are checked for each property. This UI can have more than one property value checked (ticked). @@ -130,7 +130,7 @@ def extra_properties(self) -> Dict[str, Tuple[PropertyValueType, ...]]: return {prop: (val,) for prop, val in self.properties.items()} @extra_properties.setter - def extra_properties(self, properties: Dict[str, Tuple[PropertyValueType, ...]]): + def extra_properties(self, properties: PropertyTypeMultiple): props = {} for prop, val in properties: if val: From ac384150f0bb4c4d5232ab34b074a9c4f6888baa Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Sat, 3 Jul 2021 14:48:08 +0100 Subject: [PATCH 034/139] Restructured base_define to be a package --- .../api/wx/ui/mc/base/base_define/__init__.py | 2 ++ .../wx/ui/mc/base/{ => base_define}/base_define.py | 11 ++--------- 2 files changed, 4 insertions(+), 9 deletions(-) create mode 100644 amulet_map_editor/api/wx/ui/mc/base/base_define/__init__.py rename amulet_map_editor/api/wx/ui/mc/base/{ => base_define}/base_define.py (90%) diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_define/__init__.py b/amulet_map_editor/api/wx/ui/mc/base/base_define/__init__.py new file mode 100644 index 00000000..6081b61d --- /dev/null +++ b/amulet_map_editor/api/wx/ui/mc/base/base_define/__init__.py @@ -0,0 +1,2 @@ +from .base_define import BaseDefine +from .api import BaseDefineAPI diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_define.py b/amulet_map_editor/api/wx/ui/mc/base/base_define/base_define.py similarity index 90% rename from amulet_map_editor/api/wx/ui/mc/base/base_define.py rename to amulet_map_editor/api/wx/ui/mc/base/base_define/base_define.py index d4d97fe2..d6b1ec9b 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/base_define.py +++ b/amulet_map_editor/api/wx/ui/mc/base/base_define/base_define.py @@ -10,9 +10,10 @@ VersionSelect, EVT_VERSION_CHANGE, ) +from .api import BaseDefineAPI -class BaseDefine(wx.Panel): +class BaseDefine(wx.Panel, BaseDefineAPI): def __init__( self, parent, @@ -90,14 +91,6 @@ def version_number(self) -> VersionNumberTuple: def version_number(self, version_number: VersionNumberTuple): self._version_picker.version_number = version_number - @property - def version(self) -> Tuple[PlatformType, VersionNumberTuple, bool]: - return self._version_picker.version - - @version.setter - def version(self, version: Tuple[PlatformType, VersionNumberTuple, bool]): - self._version_picker.version = version - @property def namespace(self) -> str: return self._picker.namespace From e901230a21ba99a9c2dbac3d937d8cecab3c54e3 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Sat, 3 Jul 2021 14:48:59 +0100 Subject: [PATCH 035/139] Added an abstract API class for all define objects --- .../api/wx/ui/mc/base/base_define/api.py | 27 ++++++++ .../api/wx/ui/mc/block/define/api.py | 66 +++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 amulet_map_editor/api/wx/ui/mc/base/base_define/api.py create mode 100644 amulet_map_editor/api/wx/ui/mc/block/define/api.py diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_define/api.py b/amulet_map_editor/api/wx/ui/mc/base/base_define/api.py new file mode 100644 index 00000000..81d64ecd --- /dev/null +++ b/amulet_map_editor/api/wx/ui/mc/base/base_define/api.py @@ -0,0 +1,27 @@ +from amulet.api.data_types import PlatformType, VersionNumberTuple + + +class BaseDefineAPI: + @property + def platform(self) -> PlatformType: + raise NotImplementedError + + @platform.setter + def platform(self, platform: PlatformType): + raise NotImplementedError + + @property + def version_number(self) -> VersionNumberTuple: + raise NotImplementedError + + @version_number.setter + def version_number(self, version_number: VersionNumberTuple): + raise NotImplementedError + + @property + def namespace(self) -> str: + raise NotImplementedError + + @namespace.setter + def namespace(self, namespace: str): + raise NotImplementedError diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/api.py b/amulet_map_editor/api/wx/ui/mc/block/define/api.py new file mode 100644 index 00000000..3b483274 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/mc/block/define/api.py @@ -0,0 +1,66 @@ +from typing import Optional, Tuple +from amulet.api.block import PropertyType, PropertyTypeMultiple, Block +from amulet.api.block_entity import BlockEntity +from amulet_map_editor.api.wx.ui.mc.base.base_define import BaseDefineAPI + + +class BaseBlockDefineAPI(BaseDefineAPI): + @property + def force_blockstate(self) -> bool: + raise NotImplementedError + + @force_blockstate.setter + def force_blockstate(self, force_blockstate: bool): + raise NotImplementedError + + @property + def block_name(self) -> str: + raise NotImplementedError + + @block_name.setter + def block_name(self, block_name: str): + raise NotImplementedError + + +class NormalBlockDefineAPI(BaseBlockDefineAPI): + @property + def properties(self) -> PropertyType: + raise NotImplementedError + + @properties.setter + def properties(self, properties: PropertyType): + raise NotImplementedError + + @property + def block(self) -> Block: + raise NotImplementedError + + @block.setter + def block(self, block: Block): + raise NotImplementedError + + @property + def block_entity(self) -> Optional[BlockEntity]: + raise NotImplementedError + + @block_entity.setter + def block_entity(self, block_entity: Optional[BlockEntity]): + raise NotImplementedError + + @property + def universal_block(self) -> Tuple[Block, Optional[BlockEntity]]: + raise NotImplementedError + + @universal_block.setter + def universal_block(self, universal_block: Tuple[Block, Optional[BlockEntity]]): + raise NotImplementedError + + +class WildcardBlockDefineAPI(BaseBlockDefineAPI): + @property + def extra_properties(self) -> PropertyTypeMultiple: + raise NotImplementedError + + @extra_properties.setter + def extra_properties(self, properties: PropertyTypeMultiple): + raise NotImplementedError From 6e2b248c18dd654ea682ae5198ff72e36cac0498 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Sat, 3 Jul 2021 14:50:53 +0100 Subject: [PATCH 036/139] Moved the define widget into a sub-package --- .../api/wx/ui/mc/block/define/__init__.py | 4 +-- .../api/wx/ui/mc/block/define/demo.py | 8 +++--- .../wx/ui/mc/block/define/widget/__init__.py | 3 +++ .../ui/mc/block/define/{ => widget}/base.py | 3 ++- .../block/define/{ => widget}/block_define.py | 5 ++-- .../api/wx/ui/mc/block/define/widget/demo.py | 26 +++++++++++++++++++ .../{ => widget}/wildcard_block_define.py | 5 ++-- 7 files changed, 42 insertions(+), 12 deletions(-) create mode 100644 amulet_map_editor/api/wx/ui/mc/block/define/widget/__init__.py rename amulet_map_editor/api/wx/ui/mc/block/define/{ => widget}/base.py (94%) rename amulet_map_editor/api/wx/ui/mc/block/define/{ => widget}/block_define.py (94%) create mode 100644 amulet_map_editor/api/wx/ui/mc/block/define/widget/demo.py rename amulet_map_editor/api/wx/ui/mc/block/define/{ => widget}/wildcard_block_define.py (92%) diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/__init__.py b/amulet_map_editor/api/wx/ui/mc/block/define/__init__.py index c651689f..57478642 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/__init__.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/__init__.py @@ -1,3 +1 @@ -from .block_define import BlockDefine -from .wildcard_block_define import WildcardBlockDefine -from .demo import demo +from .widget import BlockDefine, WildcardBlockDefine diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/demo.py b/amulet_map_editor/api/wx/ui/mc/block/define/demo.py index e7681479..679118c7 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/demo.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/demo.py @@ -1,9 +1,9 @@ import wx -from amulet_map_editor.api.wx.ui.mc.block.define.block_define import ( +from amulet_map_editor.api.wx.ui.mc.block.define.widget import ( demo as block_define_demo, ) -from amulet_map_editor.api.wx.ui.mc.block.define.wildcard_block_define import ( - demo as wildcard_block_define_demo, +from amulet_map_editor.api.wx.ui.mc.block.define.button import ( + demo as block_define_button_demo, ) @@ -13,7 +13,7 @@ def demo(): An app instance must be created first. """ block_define_demo() - wildcard_block_define_demo() + block_define_button_demo() if __name__ == "__main__": diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/__init__.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/__init__.py new file mode 100644 index 00000000..c651689f --- /dev/null +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/__init__.py @@ -0,0 +1,3 @@ +from .block_define import BlockDefine +from .wildcard_block_define import WildcardBlockDefine +from .demo import demo diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/base.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py similarity index 94% rename from amulet_map_editor/api/wx/ui/mc/block/define/base.py rename to amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py index 5cf20d56..f17eb611 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py @@ -9,9 +9,10 @@ from amulet_map_editor.api.wx.ui.mc.block.properties import ( BasePropertySelect, ) +from amulet_map_editor.api.wx.ui.mc.block.define.api import BaseBlockDefineAPI -class BaseBlockDefine(BaseDefine): +class BaseBlockDefine(BaseDefine, BaseBlockDefineAPI): """ A UI that merges a version select widget with a block select widget and a property select. """ diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/block_define.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/block_define.py similarity index 94% rename from amulet_map_editor/api/wx/ui/mc/block/define/block_define.py rename to amulet_map_editor/api/wx/ui/mc/block/define/widget/block_define.py index a5d0fc57..e0ace339 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/block_define.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/block_define.py @@ -10,10 +10,11 @@ SinglePropertySelect, EVT_SINGLE_PROPERTIES_CHANGE, ) -from amulet_map_editor.api.wx.ui.mc.block.define.base import BaseBlockDefine +from amulet_map_editor.api.wx.ui.mc.block.define.widget.base import BaseBlockDefine +from amulet_map_editor.api.wx.ui.mc.block.define.api import NormalBlockDefineAPI -class BlockDefine(BaseBlockDefine): +class BlockDefine(BaseBlockDefine, NormalBlockDefineAPI): """ A UI that merges a version select widget with a block select widget and a property select. """ diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/demo.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/demo.py new file mode 100644 index 00000000..e7681479 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/demo.py @@ -0,0 +1,26 @@ +import wx +from amulet_map_editor.api.wx.ui.mc.block.define.block_define import ( + demo as block_define_demo, +) +from amulet_map_editor.api.wx.ui.mc.block.define.wildcard_block_define import ( + demo as wildcard_block_define_demo, +) + + +def demo(): + """ + Show a demo version of the UI. + An app instance must be created first. + """ + block_define_demo() + wildcard_block_define_demo() + + +if __name__ == "__main__": + + def main(): + app = wx.App() + demo() + app.MainLoop() + + main() diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/wildcard_block_define.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard_block_define.py similarity index 92% rename from amulet_map_editor/api/wx/ui/mc/block/define/wildcard_block_define.py rename to amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard_block_define.py index 46f96fb4..2d6d49da 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/wildcard_block_define.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard_block_define.py @@ -9,10 +9,11 @@ MultiplePropertySelect, EVT_MULTIPLE_PROPERTIES_CHANGE, ) -from amulet_map_editor.api.wx.ui.mc.block.define.base import BaseBlockDefine +from amulet_map_editor.api.wx.ui.mc.block.define.widget.base import BaseBlockDefine +from amulet_map_editor.api.wx.ui.mc.block.define.api import WildcardBlockDefineAPI -class WildcardBlockDefine(BaseBlockDefine): +class WildcardBlockDefine(BaseBlockDefine, WildcardBlockDefineAPI): """ A UI that merges a version select widget with a block select widget and a multi property select. """ From 9657f49029943644144713e13009726baf68eb76 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Sat, 3 Jul 2021 14:51:32 +0100 Subject: [PATCH 037/139] Cleaned up some data types --- amulet_map_editor/api/wx/ui/mc/version/version_select.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/version/version_select.py b/amulet_map_editor/api/wx/ui/mc/version/version_select.py index a45d8b2e..acf0d101 100644 --- a/amulet_map_editor/api/wx/ui/mc/version/version_select.py +++ b/amulet_map_editor/api/wx/ui/mc/version/version_select.py @@ -1,9 +1,9 @@ from amulet_map_editor.api.wx.ui.simple import SimpleChoice, SimpleChoiceAny import wx import PyMCTranslate -from typing import Tuple, Optional +from typing import Optional -from amulet.api.data_types import VersionNumberTuple, PlatformType +from amulet.api.data_types import VersionNumberTuple, PlatformType, VersionGroupType from .platform_select import PlatformSelect from .events import ( PlatformChangeEvent, @@ -119,11 +119,11 @@ def _set_force_blockstate(self, force_blockstate: bool): self._blockstate_choice.SetSelection(int(force_blockstate)) @property - def version(self) -> Tuple[PlatformType, VersionNumberTuple, bool]: + def version(self) -> VersionGroupType: return self.platform, self.version_number, self.force_blockstate @version.setter - def version(self, version: Tuple[PlatformType, VersionNumberTuple, bool]): + def version(self, version: VersionGroupType): platform, version_number, force_blockstate = version self._set_platform(platform) self._set_version_number(version_number) From 119ead8ce458a4d08de01288db47fd1841f2e195 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Sun, 4 Jul 2021 14:15:55 +0100 Subject: [PATCH 038/139] Restructured the base block API --- .../api/wx/ui/mc/base/__init__.py | 5 +-- .../api/wx/ui/mc/base/api/__init__.py | 3 ++ .../api/wx/ui/mc/base/api/block/__init__.py | 3 ++ .../api/wx/ui/mc/base/api/block/base.py | 27 +++++++++++++++ .../api.py => base/api/block/normal.py} | 34 +++---------------- .../api/wx/ui/mc/base/api/block/wildcard.py | 13 +++++++ .../api/wx/ui/mc/base/api/platform.py | 16 +++++++++ .../api/wx/ui/mc/base/api/version.py | 26 ++++++++++++++ .../api/wx/ui/mc/base/base_define/__init__.py | 1 - .../api/wx/ui/mc/base/base_define/api.py | 27 --------------- .../wx/ui/mc/base/base_define/base_define.py | 4 +-- .../api/wx/ui/mc/block/define/widget/base.py | 4 +-- .../ui/mc/block/define/widget/block_define.py | 4 +-- .../define/widget/wildcard_block_define.py | 4 +-- 14 files changed, 103 insertions(+), 68 deletions(-) create mode 100644 amulet_map_editor/api/wx/ui/mc/base/api/__init__.py create mode 100644 amulet_map_editor/api/wx/ui/mc/base/api/block/__init__.py create mode 100644 amulet_map_editor/api/wx/ui/mc/base/api/block/base.py rename amulet_map_editor/api/wx/ui/mc/{block/define/api.py => base/api/block/normal.py} (50%) create mode 100644 amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py create mode 100644 amulet_map_editor/api/wx/ui/mc/base/api/platform.py create mode 100644 amulet_map_editor/api/wx/ui/mc/base/api/version.py delete mode 100644 amulet_map_editor/api/wx/ui/mc/base/base_define/api.py diff --git a/amulet_map_editor/api/wx/ui/mc/base/__init__.py b/amulet_map_editor/api/wx/ui/mc/base/__init__.py index 6ff1ba68..3f13d3eb 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/__init__.py +++ b/amulet_map_editor/api/wx/ui/mc/base/__init__.py @@ -1,2 +1,3 @@ -from .base_select import BaseSelect -from .base_define import BaseDefine +from .api import * +from .base_select import * +from .base_define import * diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/__init__.py b/amulet_map_editor/api/wx/ui/mc/base/api/__init__.py new file mode 100644 index 00000000..9f4eb0e0 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/mc/base/api/__init__.py @@ -0,0 +1,3 @@ +from .platform import BaseMCPlatformAPI +from .version import BaseMCVersionAPI +from .block import * diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/block/__init__.py b/amulet_map_editor/api/wx/ui/mc/base/api/block/__init__.py new file mode 100644 index 00000000..eafc6fd9 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/mc/base/api/block/__init__.py @@ -0,0 +1,3 @@ +from .base import BaseMCBlockAPI +from .normal import NormalMCBlockAPI +from .wildcard import WildcardMCBlockAPI diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/block/base.py b/amulet_map_editor/api/wx/ui/mc/base/api/block/base.py new file mode 100644 index 00000000..0a56fd9d --- /dev/null +++ b/amulet_map_editor/api/wx/ui/mc/base/api/block/base.py @@ -0,0 +1,27 @@ +from typing import Optional + +from ..version import BaseMCVersionAPI + + +class BaseMCBlockAPI(BaseMCVersionAPI): + @property + def namespace(self) -> str: + raise NotImplementedError + + @namespace.setter + def namespace(self, namespace: str): + raise NotImplementedError + + def set_namespace(self, namespace: str): + raise NotImplementedError + + @property + def block_name(self) -> str: + raise NotImplementedError + + @block_name.setter + def block_name(self, block_name: str): + raise NotImplementedError + + def set_block_name(self, block_name: str): + raise NotImplementedError diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/api.py b/amulet_map_editor/api/wx/ui/mc/base/api/block/normal.py similarity index 50% rename from amulet_map_editor/api/wx/ui/mc/block/define/api.py rename to amulet_map_editor/api/wx/ui/mc/base/api/block/normal.py index 3b483274..95cad4b6 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/api.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/block/normal.py @@ -1,28 +1,12 @@ from typing import Optional, Tuple -from amulet.api.block import PropertyType, PropertyTypeMultiple, Block -from amulet.api.block_entity import BlockEntity -from amulet_map_editor.api.wx.ui.mc.base.base_define import BaseDefineAPI - - -class BaseBlockDefineAPI(BaseDefineAPI): - @property - def force_blockstate(self) -> bool: - raise NotImplementedError - @force_blockstate.setter - def force_blockstate(self, force_blockstate: bool): - raise NotImplementedError - - @property - def block_name(self) -> str: - raise NotImplementedError +from amulet.api.block import PropertyType, Block +from amulet.api.block_entity import BlockEntity - @block_name.setter - def block_name(self, block_name: str): - raise NotImplementedError +from .base import BaseMCBlockAPI -class NormalBlockDefineAPI(BaseBlockDefineAPI): +class NormalMCBlockAPI(BaseMCBlockAPI): @property def properties(self) -> PropertyType: raise NotImplementedError @@ -54,13 +38,3 @@ def universal_block(self) -> Tuple[Block, Optional[BlockEntity]]: @universal_block.setter def universal_block(self, universal_block: Tuple[Block, Optional[BlockEntity]]): raise NotImplementedError - - -class WildcardBlockDefineAPI(BaseBlockDefineAPI): - @property - def extra_properties(self) -> PropertyTypeMultiple: - raise NotImplementedError - - @extra_properties.setter - def extra_properties(self, properties: PropertyTypeMultiple): - raise NotImplementedError diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py b/amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py new file mode 100644 index 00000000..e248e65a --- /dev/null +++ b/amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py @@ -0,0 +1,13 @@ +from amulet.api.block import PropertyTypeMultiple + +from .base import BaseMCBlockAPI + + +class WildcardMCBlockAPI(BaseMCBlockAPI): + @property + def extra_properties(self) -> PropertyTypeMultiple: + raise NotImplementedError + + @extra_properties.setter + def extra_properties(self, properties: PropertyTypeMultiple): + raise NotImplementedError diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/platform.py b/amulet_map_editor/api/wx/ui/mc/base/api/platform.py new file mode 100644 index 00000000..75e907e5 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/mc/base/api/platform.py @@ -0,0 +1,16 @@ +from typing import Optional +import PyMCTranslate +from amulet.api.data_types import PlatformType, VersionNumberTuple + + +class BaseMCPlatformAPI: + @property + def platform(self) -> PlatformType: + raise NotImplementedError + + @platform.setter + def platform(self, platform: PlatformType): + raise NotImplementedError + + def set_platform(self, platform: PlatformType): + raise NotImplementedError diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/version.py b/amulet_map_editor/api/wx/ui/mc/base/api/version.py new file mode 100644 index 00000000..d5372ec4 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/mc/base/api/version.py @@ -0,0 +1,26 @@ +from amulet.api.data_types import VersionNumberTuple +from .platform import BaseMCPlatformAPI + + +class BaseMCVersionAPI(BaseMCPlatformAPI): + @property + def version_number(self) -> VersionNumberTuple: + raise NotImplementedError + + @version_number.setter + def version_number(self, version_number: VersionNumberTuple): + raise NotImplementedError + + def set_version_number(self, version_number: VersionNumberTuple): + raise NotImplementedError + + @property + def force_blockstate(self) -> bool: + raise NotImplementedError + + @force_blockstate.setter + def force_blockstate(self, force_blockstate: bool): + raise NotImplementedError + + def set_force_blockstate(self, force_blockstate: bool): + raise NotImplementedError diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_define/__init__.py b/amulet_map_editor/api/wx/ui/mc/base/base_define/__init__.py index 6081b61d..42b06e1a 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/base_define/__init__.py +++ b/amulet_map_editor/api/wx/ui/mc/base/base_define/__init__.py @@ -1,2 +1 @@ from .base_define import BaseDefine -from .api import BaseDefineAPI diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_define/api.py b/amulet_map_editor/api/wx/ui/mc/base/base_define/api.py deleted file mode 100644 index 81d64ecd..00000000 --- a/amulet_map_editor/api/wx/ui/mc/base/base_define/api.py +++ /dev/null @@ -1,27 +0,0 @@ -from amulet.api.data_types import PlatformType, VersionNumberTuple - - -class BaseDefineAPI: - @property - def platform(self) -> PlatformType: - raise NotImplementedError - - @platform.setter - def platform(self, platform: PlatformType): - raise NotImplementedError - - @property - def version_number(self) -> VersionNumberTuple: - raise NotImplementedError - - @version_number.setter - def version_number(self, version_number: VersionNumberTuple): - raise NotImplementedError - - @property - def namespace(self) -> str: - raise NotImplementedError - - @namespace.setter - def namespace(self, namespace: str): - raise NotImplementedError diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_define/base_define.py b/amulet_map_editor/api/wx/ui/mc/base/base_define/base_define.py index d6b1ec9b..411abf55 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/base_define/base_define.py +++ b/amulet_map_editor/api/wx/ui/mc/base/base_define/base_define.py @@ -10,10 +10,10 @@ VersionSelect, EVT_VERSION_CHANGE, ) -from .api import BaseDefineAPI +from amulet_map_editor.api.wx.ui.mc.base.api import BaseMCVersionAPI -class BaseDefine(wx.Panel, BaseDefineAPI): +class BaseDefine(wx.Panel, BaseMCVersionAPI): def __init__( self, parent, diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py index f17eb611..bee7958d 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py @@ -9,10 +9,10 @@ from amulet_map_editor.api.wx.ui.mc.block.properties import ( BasePropertySelect, ) -from amulet_map_editor.api.wx.ui.mc.block.define.api import BaseBlockDefineAPI +from amulet_map_editor.api.wx.ui.mc.base import BaseMCBlockAPI -class BaseBlockDefine(BaseDefine, BaseBlockDefineAPI): +class BaseBlockDefine(BaseDefine, BaseMCBlockAPI): """ A UI that merges a version select widget with a block select widget and a property select. """ diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/block_define.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/block_define.py index e0ace339..a7c38674 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/widget/block_define.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/block_define.py @@ -11,10 +11,10 @@ EVT_SINGLE_PROPERTIES_CHANGE, ) from amulet_map_editor.api.wx.ui.mc.block.define.widget.base import BaseBlockDefine -from amulet_map_editor.api.wx.ui.mc.block.define.api import NormalBlockDefineAPI +from amulet_map_editor.api.wx.ui.mc.base import NormalMCBlockAPI -class BlockDefine(BaseBlockDefine, NormalBlockDefineAPI): +class BlockDefine(BaseBlockDefine, NormalMCBlockAPI): """ A UI that merges a version select widget with a block select widget and a property select. """ diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard_block_define.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard_block_define.py index 2d6d49da..b01638fc 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard_block_define.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard_block_define.py @@ -10,10 +10,10 @@ EVT_MULTIPLE_PROPERTIES_CHANGE, ) from amulet_map_editor.api.wx.ui.mc.block.define.widget.base import BaseBlockDefine -from amulet_map_editor.api.wx.ui.mc.block.define.api import WildcardBlockDefineAPI +from amulet_map_editor.api.wx.ui.mc.base import WildcardMCBlockAPI -class WildcardBlockDefine(BaseBlockDefine, WildcardBlockDefineAPI): +class WildcardBlockDefine(BaseBlockDefine, WildcardMCBlockAPI): """ A UI that merges a version select widget with a block select widget and a multi property select. """ From 6704887e135c9894094e772536defd77dc0ca20c Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Wed, 7 Jul 2021 10:19:43 +0100 Subject: [PATCH 039/139] Added a block define button and moved the block define widget Added a button that when clicked opens a dialog containing a block define widget and displays the selected block. Moved the block define widget down a package Renamed extra_properties to selected_properties to better describe what it does --- .../api/wx/ui/mc/base/api/block/base.py | 5 - .../api/wx/ui/mc/base/api/block/wildcard.py | 16 ++- .../api/wx/ui/mc/base/api/platform.py | 2 - .../api/wx/ui/mc/base/api/version.py | 5 - .../api/wx/ui/mc/block/__init__.py | 11 +- .../api/wx/ui/mc/block/define/__init__.py | 3 +- .../wx/ui/mc/block/define/button/__init__.py | 3 + .../api/wx/ui/mc/block/define/button/base.py | 86 ++++++++++++++ .../api/wx/ui/mc/block/define/button/demo.py | 26 +++++ .../wx/ui/mc/block/define/button/normal.py | 109 ++++++++++++++++++ .../wx/ui/mc/block/define/button/wildcard.py | 87 ++++++++++++++ .../api/wx/ui/mc/block/define/demo.py | 12 +- .../wx/ui/mc/block/define/widget/__init__.py | 6 +- .../api/wx/ui/mc/block/define/widget/demo.py | 12 +- .../widget/{block_define.py => normal.py} | 0 .../{wildcard_block_define.py => wildcard.py} | 19 ++- amulet_map_editor/api/wx/ui/mc/block/demo.py | 2 +- .../multiple/automatic/automatic.py | 8 +- .../ui/mc/block/properties/multiple/base.py | 18 ++- .../ui/mc/block/properties/multiple/main.py | 35 ++++-- .../ui/mc/block/properties/multiple/manual.py | 24 ++-- 21 files changed, 421 insertions(+), 68 deletions(-) create mode 100644 amulet_map_editor/api/wx/ui/mc/block/define/button/__init__.py create mode 100644 amulet_map_editor/api/wx/ui/mc/block/define/button/base.py create mode 100644 amulet_map_editor/api/wx/ui/mc/block/define/button/demo.py create mode 100644 amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py create mode 100644 amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py rename amulet_map_editor/api/wx/ui/mc/block/define/widget/{block_define.py => normal.py} (100%) rename amulet_map_editor/api/wx/ui/mc/block/define/widget/{wildcard_block_define.py => wildcard.py} (82%) diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/block/base.py b/amulet_map_editor/api/wx/ui/mc/base/api/block/base.py index 0a56fd9d..403a6e37 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/block/base.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/block/base.py @@ -12,9 +12,6 @@ def namespace(self) -> str: def namespace(self, namespace: str): raise NotImplementedError - def set_namespace(self, namespace: str): - raise NotImplementedError - @property def block_name(self) -> str: raise NotImplementedError @@ -23,5 +20,3 @@ def block_name(self) -> str: def block_name(self, block_name: str): raise NotImplementedError - def set_block_name(self, block_name: str): - raise NotImplementedError diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py b/amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py index e248e65a..68586d5d 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py @@ -5,9 +5,19 @@ class WildcardMCBlockAPI(BaseMCBlockAPI): @property - def extra_properties(self) -> PropertyTypeMultiple: + def all_properties(self) -> PropertyTypeMultiple: + """The values that exist for every property.""" raise NotImplementedError - @extra_properties.setter - def extra_properties(self, properties: PropertyTypeMultiple): + @all_properties.setter + def all_properties(self, properties: PropertyTypeMultiple): + raise NotImplementedError + + @property + def selected_properties(self) -> PropertyTypeMultiple: + """The values that are selected for every property.""" + raise NotImplementedError + + @selected_properties.setter + def selected_properties(self, properties: PropertyTypeMultiple): raise NotImplementedError diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/platform.py b/amulet_map_editor/api/wx/ui/mc/base/api/platform.py index 75e907e5..5ec4e0e6 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/platform.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/platform.py @@ -12,5 +12,3 @@ def platform(self) -> PlatformType: def platform(self, platform: PlatformType): raise NotImplementedError - def set_platform(self, platform: PlatformType): - raise NotImplementedError diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/version.py b/amulet_map_editor/api/wx/ui/mc/base/api/version.py index d5372ec4..fd6b7674 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/version.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/version.py @@ -11,9 +11,6 @@ def version_number(self) -> VersionNumberTuple: def version_number(self, version_number: VersionNumberTuple): raise NotImplementedError - def set_version_number(self, version_number: VersionNumberTuple): - raise NotImplementedError - @property def force_blockstate(self) -> bool: raise NotImplementedError @@ -22,5 +19,3 @@ def force_blockstate(self) -> bool: def force_blockstate(self, force_blockstate: bool): raise NotImplementedError - def set_force_blockstate(self, force_blockstate: bool): - raise NotImplementedError diff --git a/amulet_map_editor/api/wx/ui/mc/block/__init__.py b/amulet_map_editor/api/wx/ui/mc/block/__init__.py index 4dddc512..db43c5c7 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/__init__.py +++ b/amulet_map_editor/api/wx/ui/mc/block/__init__.py @@ -1,11 +1,4 @@ from .block_select import BlockSelect -from .define import BlockDefine +from .define import * from .multi_block_define import MultiBlockDefine -from .properties import ( - SinglePropertySelect, - SinglePropertiesChangeEvent, - EVT_SINGLE_PROPERTIES_CHANGE, - MultiplePropertySelect, - MultiplePropertiesChangeEvent, - EVT_MULTIPLE_PROPERTIES_CHANGE, -) +from .properties import * diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/__init__.py b/amulet_map_editor/api/wx/ui/mc/block/define/__init__.py index 57478642..1350a493 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/__init__.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/__init__.py @@ -1 +1,2 @@ -from .widget import BlockDefine, WildcardBlockDefine +from .widget import * +from .button import * diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/__init__.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/__init__.py new file mode 100644 index 00000000..c5290876 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/__init__.py @@ -0,0 +1,3 @@ +from .base import BaseBlockDefineButton +from .normal import BlockDefineButton +from .wildcard import WildcardBlockDefine diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py new file mode 100644 index 00000000..54ea2609 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py @@ -0,0 +1,86 @@ +import wx +from typing import Optional + +from amulet.api.data_types import PlatformType, VersionNumberTuple +from amulet_map_editor.api.wx.ui.mc.base import BaseMCBlockAPI +from amulet_map_editor.api.wx.ui.mc.block.define import BaseBlockDefine + + +class BaseBlockDefineButton(wx.Button, BaseMCBlockAPI): + def __init__( + self, + parent: wx.Window, + ): + super().__init__(parent) + self._dialog: Optional[wx.Dialog] = None + self._block_widget: Optional[BaseBlockDefine] = None + self.Bind(wx.EVT_BUTTON, self._on_press) + + def _on_press(self, evt): + self._dialog.Fit() + if self._dialog.ShowModal() == wx.ID_OK: + self.update_button() + + def update_button(self): + """Update the text on the button from the internal state.""" + raise NotImplementedError + + @property + def platform(self) -> PlatformType: + return self._block_widget.platform + + @platform.setter + def platform(self, platform: PlatformType): + self.set_platform(platform) + self.update_button() + + def set_platform(self, platform: PlatformType): + self._block_widget.platform = platform + + @property + def version_number(self) -> VersionNumberTuple: + return self._block_widget.version_number + + @version_number.setter + def version_number(self, version_number: VersionNumberTuple): + self.set_version_number(version_number) + self.update_button() + + def set_version_number(self, version_number: VersionNumberTuple): + self._block_widget.version_number = version_number + + @property + def namespace(self) -> str: + return self._block_widget.namespace + + @namespace.setter + def namespace(self, namespace: str): + self.set_namespace(namespace) + self.update_button() + + def set_namespace(self, namespace: str): + self._block_widget.namespace = namespace + + @property + def force_blockstate(self) -> bool: + return self._block_widget.force_blockstate + + @force_blockstate.setter + def force_blockstate(self, force_blockstate: bool): + self.set_force_blockstate(force_blockstate) + self.update_button() + + def set_force_blockstate(self, force_blockstate: bool): + self._block_widget.force_blockstate = force_blockstate + + @property + def block_name(self) -> str: + return self._block_widget.block_name + + @block_name.setter + def block_name(self, block_name: str): + self.set_block_name(block_name) + self.update_button() + + def set_block_name(self, block_name: str): + self._block_widget.block_name = block_name diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/demo.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/demo.py new file mode 100644 index 00000000..416cc806 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/demo.py @@ -0,0 +1,26 @@ +import wx +from amulet_map_editor.api.wx.ui.mc.block.define.button.normal import ( + demo as normal_button_demo, +) +from amulet_map_editor.api.wx.ui.mc.block.define.button.wildcard import ( + demo as wildcard_button_demo, +) + + +def demo(): + """ + Show a demo version of the UI. + An app instance must be created first. + """ + normal_button_demo() + wildcard_button_demo() + + +if __name__ == "__main__": + + def main(): + app = wx.App() + demo() + app.MainLoop() + + main() diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py new file mode 100644 index 00000000..14f408a5 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py @@ -0,0 +1,109 @@ +from typing import Tuple, Optional +import wx + +import PyMCTranslate +from amulet.api.block import Block, PropertyType +from amulet.api.block_entity import BlockEntity +from amulet_map_editor.api.wx.ui.simple import SimpleDialog +from amulet_map_editor.api.wx.ui.mc.base import NormalMCBlockAPI +from amulet_map_editor.api.wx.ui.mc.block.define.widget import BlockDefine + +from amulet_map_editor.api.wx.ui.mc.block.define.button.base import BaseBlockDefineButton + + +class BlockDefineButton(BaseBlockDefineButton, NormalMCBlockAPI): + def __init__(self, parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager, *args, **kwargs): + super().__init__(parent) + self._dialog = SimpleDialog(parent, "Pick a Block") + self._block_widget = BlockDefine(self._dialog, translation_manager, wx.HORIZONTAL, *args, **kwargs) + self._dialog.sizer.Add(self._block_widget) + self.update_button() + + def update_button(self): + """Update the text on the button from the internal state.""" + self.SetLabel(self.block.full_blockstate) + + @property + def properties(self) -> PropertyType: + return self._block_widget.properties + + @properties.setter + def properties(self, properties: PropertyType): + self.set_properties(properties) + self.update_button() + + def set_properties(self, properties: PropertyType): + self._block_widget.properties = properties + + @property + def block(self) -> Block: + """The block object stored in this button.""" + return Block(self.namespace, self.block_name, self.properties) + + @block.setter + def block(self, block: Block): + self.set_block(block) + self.update_button() + + def set_block(self, block: Block): + self.set_namespace(block.namespace) + self.set_block_name(block.base_name) + self.set_properties(block.properties) + + @property + def block_entity(self) -> Optional[BlockEntity]: + return self._block_widget.block_entity + + @block_entity.setter + def block_entity(self, block_entity: Optional[BlockEntity]): + self.set_block_entity(block_entity) + self.update_button() + + def set_block_entity(self, block_entity: Optional[BlockEntity]): + self._block_widget.block_entity = block_entity + + @property + def universal_block(self) -> Tuple[Block, Optional[BlockEntity]]: + return self._block_widget.universal_block + + @universal_block.setter + def universal_block(self, universal_block: Tuple[Block, Optional[BlockEntity]]): + self.set_universal_block(universal_block) + self.update_button() + + def set_universal_block(self, universal_block: Tuple[Block, Optional[BlockEntity]]): + self._block_widget.universal_block = universal_block + + +def demo(): + """ + Show a demo version of the UI. + An app instance must be created first. + """ + translation_manager = PyMCTranslate.new_translation_manager() + dialog = wx.Dialog( + None, + title="WildcardBlockDefine", + style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT, + ) + sizer = wx.BoxSizer() + dialog.SetSizer(sizer) + sizer.Add( + BlockDefineButton(dialog, translation_manager), + 0, + wx.ALL, + 5, + ) + dialog.Show() + dialog.Fit() + dialog.Bind(wx.EVT_CLOSE, lambda evt: dialog.Destroy()) + + +if __name__ == "__main__": + + def main(): + app = wx.App() + demo() + app.MainLoop() + + main() diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py new file mode 100644 index 00000000..9dcb3038 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py @@ -0,0 +1,87 @@ +import wx +import PyMCTranslate + +from amulet.api.block import PropertyTypeMultiple +from amulet_map_editor.api.wx.ui.simple import SimpleDialog +from amulet_map_editor.api.wx.ui.mc.base import WildcardMCBlockAPI +from amulet_map_editor.api.wx.ui.mc.block.define.widget import WildcardBlockDefine +from amulet_map_editor.api.wx.ui.mc.block.define.button.base import BaseBlockDefineButton + + +class WildcardBlockDefineButton(BaseBlockDefineButton, WildcardMCBlockAPI): + def __init__(self, parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager, *args, **kwargs): + super().__init__(parent) + self._dialog = SimpleDialog(parent, "Pick a Block") + self._block_widget = WildcardBlockDefine(self._dialog, translation_manager, wx.HORIZONTAL, *args, **kwargs) + self._dialog.sizer.Add(self._block_widget) + self.update_button() + + def update_button(self): + """Update the text on the button from the internal state.""" + if self.selected_properties: + properties = [f"{key}:({'|'.join([v.to_snbt() for v in val])})" for key, val in self.selected_properties.items()] + self.SetLabel(f"{self.namespace}:{self.block_name}[{','.join(properties)}]") + properties_str = ',\n'.join(properties) + self.SetToolTip(f"{self.namespace}:{self.block_name}[\n{properties_str}\n]") + else: + self.SetLabel(f"{self.namespace}:{self.block_name}") + self.SetToolTip(f"{self.namespace}:{self.block_name}") + + @property + def selected_properties(self) -> PropertyTypeMultiple: + return self._block_widget.selected_properties + + @selected_properties.setter + def selected_properties(self, selected_properties: PropertyTypeMultiple): + self.set_selected_properties(selected_properties) + self.update_button() + + def set_selected_properties(self, selected_properties: PropertyTypeMultiple): + self._block_widget.selected_properties = selected_properties + + @property + def all_properties(self) -> PropertyTypeMultiple: + """The values that exist for every property.""" + return self._block_widget.all_properties + + @all_properties.setter + def all_properties(self, all_properties: PropertyTypeMultiple): + self.set_all_properties(all_properties) + self.update_button() + + def set_all_properties(self, all_properties: PropertyTypeMultiple): + self._block_widget.all_properties = all_properties + + +def demo(): + """ + Show a demo version of the UI. + An app instance must be created first. + """ + translation_manager = PyMCTranslate.new_translation_manager() + dialog = wx.Dialog( + None, + title="WildcardBlockDefineButton", + style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT, + ) + sizer = wx.BoxSizer() + dialog.SetSizer(sizer) + sizer.Add( + WildcardBlockDefineButton(dialog, translation_manager), + 0, + wx.ALL, + 5, + ) + dialog.Show() + dialog.Fit() + dialog.Bind(wx.EVT_CLOSE, lambda evt: dialog.Destroy()) + + +if __name__ == "__main__": + + def main(): + app = wx.App() + demo() + app.MainLoop() + + main() diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/demo.py b/amulet_map_editor/api/wx/ui/mc/block/define/demo.py index 679118c7..517b1a52 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/demo.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/demo.py @@ -1,9 +1,9 @@ import wx -from amulet_map_editor.api.wx.ui.mc.block.define.widget import ( - demo as block_define_demo, +from amulet_map_editor.api.wx.ui.mc.block.define.button.demo import ( + demo as block_button_demo, ) -from amulet_map_editor.api.wx.ui.mc.block.define.button import ( - demo as block_define_button_demo, +from amulet_map_editor.api.wx.ui.mc.block.define.widget.demo import ( + demo as block_widget_demo, ) @@ -12,8 +12,8 @@ def demo(): Show a demo version of the UI. An app instance must be created first. """ - block_define_demo() - block_define_button_demo() + block_button_demo() + block_widget_demo() if __name__ == "__main__": diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/__init__.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/__init__.py index c651689f..a54df884 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/widget/__init__.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/__init__.py @@ -1,3 +1,3 @@ -from .block_define import BlockDefine -from .wildcard_block_define import WildcardBlockDefine -from .demo import demo +from .base import BaseBlockDefine +from .normal import BlockDefine +from .wildcard import WildcardBlockDefine diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/demo.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/demo.py index e7681479..92190cf0 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/widget/demo.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/demo.py @@ -1,9 +1,9 @@ import wx -from amulet_map_editor.api.wx.ui.mc.block.define.block_define import ( - demo as block_define_demo, +from amulet_map_editor.api.wx.ui.mc.block.define.widget.normal import ( + demo as block_widget_demo, ) -from amulet_map_editor.api.wx.ui.mc.block.define.wildcard_block_define import ( - demo as wildcard_block_define_demo, +from amulet_map_editor.api.wx.ui.mc.block.define.widget.wildcard import ( + demo as wildcard_widget_demo, ) @@ -12,8 +12,8 @@ def demo(): Show a demo version of the UI. An app instance must be created first. """ - block_define_demo() - wildcard_block_define_demo() + block_widget_demo() + wildcard_widget_demo() if __name__ == "__main__": diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/block_define.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py similarity index 100% rename from amulet_map_editor/api/wx/ui/mc/block/define/widget/block_define.py rename to amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard_block_define.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py similarity index 82% rename from amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard_block_define.py rename to amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py index b01638fc..a369fe3f 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard_block_define.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py @@ -66,16 +66,25 @@ def __init__( self.Layout() @property - def extra_properties(self) -> PropertyTypeMultiple: + def selected_properties(self) -> PropertyTypeMultiple: """ The values that are checked for each property. This UI can have more than one property value checked (ticked). """ - return self._property_picker.extra_properties + return self._property_picker.selected_properties - @extra_properties.setter - def extra_properties(self, extra_properties: PropertyTypeMultiple): - self._property_picker.extra_properties = extra_properties + @selected_properties.setter + def selected_properties(self, selected_properties: PropertyTypeMultiple): + self._property_picker.selected_properties = selected_properties + + @property + def all_properties(self) -> PropertyTypeMultiple: + """The values that exist for every property.""" + return self._property_picker.all_properties + + @all_properties.setter + def all_properties(self, all_properties: PropertyTypeMultiple): + self._property_picker.all_properties = all_properties def demo(): diff --git a/amulet_map_editor/api/wx/ui/mc/block/demo.py b/amulet_map_editor/api/wx/ui/mc/block/demo.py index cc4694dd..1786de93 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/demo.py +++ b/amulet_map_editor/api/wx/ui/mc/block/demo.py @@ -2,7 +2,7 @@ from amulet_map_editor.api.wx.ui.mc.block.block_select import ( demo as block_select_demo, ) -from amulet_map_editor.api.wx.ui.mc.block.define import ( +from amulet_map_editor.api.wx.ui.mc.block.define.demo import ( demo as block_define_demo, ) diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/automatic/automatic.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/automatic/automatic.py index 5068a68d..fc51222d 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/automatic/automatic.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/automatic/automatic.py @@ -59,7 +59,7 @@ def on_close(evt): choice.SetValue(popup.GetStringValue()) wx.PostEvent( self, - MultiplePropertiesChangeEvent(self.extra_properties), + MultiplePropertiesChangeEvent(self.selected_properties), ) choice.Bind( @@ -74,7 +74,7 @@ def on_close(evt): self.Thaw() @property - def extra_properties(self) -> PropertyTypeMultiple: + def selected_properties(self) -> PropertyTypeMultiple: """ The values that are checked for each property. This UI can have more than one property value checked (ticked). @@ -83,8 +83,8 @@ def extra_properties(self) -> PropertyTypeMultiple: prop: popup.checked_nbt for prop, (_, popup) in self._properties.items() } - @extra_properties.setter - def extra_properties(self, properties: PropertyTypeMultiple): + @selected_properties.setter + def selected_properties(self, properties: PropertyTypeMultiple): self.Freeze() for name, nbt_tuple in properties.items(): if name in self._properties: diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/base.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/base.py index ac28c089..c031874c 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/base.py @@ -16,13 +16,25 @@ def __init__( self._translation_manager = translation_manager @property - def extra_properties(self) -> PropertyTypeMultiple: + def all_properties(self) -> PropertyTypeMultiple: + """ + All the states defined for every property. + This contains all values regardless of if they are selected or not. + """ + raise NotImplementedError + + @all_properties.setter + def all_properties(self, all_properties: PropertyTypeMultiple): + raise NotImplementedError + + @property + def selected_properties(self) -> PropertyTypeMultiple: """ The values that are checked for each property. This UI can have more than one property value checked (ticked). """ raise NotImplementedError - @extra_properties.setter - def extra_properties(self, properties: PropertyTypeMultiple): + @selected_properties.setter + def selected_properties(self, selected_properties: PropertyTypeMultiple): raise NotImplementedError diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py index 11f455d6..bcc99c83 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py @@ -59,23 +59,44 @@ def _set_properties(self, properties: PropertyTypeMultiple): self.Thaw() @property - def extra_properties(self) -> PropertyTypeMultiple: + def selected_properties(self) -> PropertyTypeMultiple: """ The values that are checked for each property. This UI can have more than one property value checked (ticked). """ if self._manual_enabled: - return self._manual.extra_properties + return self._manual.selected_properties else: - return self._simple.extra_properties + return self._simple.selected_properties - @extra_properties.setter - def extra_properties(self, extra_properties: PropertyTypeMultiple): + @selected_properties.setter + def selected_properties(self, selected_properties: PropertyTypeMultiple): self.Freeze() if self._manual_enabled: - self._manual.extra_properties = extra_properties + self._manual.selected_properties = selected_properties else: - self._simple.extra_properties = extra_properties + self._simple.selected_properties = selected_properties + self.TopLevelParent.Layout() + self.Thaw() + + @property + def all_properties(self) -> PropertyTypeMultiple: + """ + The values that are checked for each property. + This UI can have more than one property value checked (ticked). + """ + if self._manual_enabled: + return self._manual.all_properties + else: + return self._simple.all_properties + + @all_properties.setter + def all_properties(self, all_properties: PropertyTypeMultiple): + self.Freeze() + if self._manual_enabled: + self._manual.all_properties = all_properties + else: + self._simple.all_properties = all_properties self.TopLevelParent.Layout() self.Thaw() diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/manual.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/manual.py index 11f7002c..74f235ad 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/manual.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/manual.py @@ -40,7 +40,7 @@ def __init__( self._properties: Dict[int, Tuple[wx.TextCtrl, wx.TextCtrl]] = {} def _post_property_change(self): - wx.PostEvent(self, MultiplePropertiesChangeEvent(self.extra_properties)) + wx.PostEvent(self, MultiplePropertiesChangeEvent(self.selected_properties)) def _add_property(self, name: str = "", value: SNBTType = ""): self.Freeze() @@ -122,17 +122,25 @@ def properties(self, properties: PropertyType): # TODO: implement this properly @property - def extra_properties(self) -> PropertyTypeMultiple: - """ - The values that are checked for each property. - This UI can have more than one property value checked (ticked). - """ + def selected_properties(self) -> PropertyTypeMultiple: return {prop: (val,) for prop, val in self.properties.items()} - @extra_properties.setter - def extra_properties(self, properties: PropertyTypeMultiple): + @selected_properties.setter + def selected_properties(self, properties: PropertyTypeMultiple): props = {} for prop, val in properties: if val: props[prop] = val[0] self.properties = props + + @property + def all_properties(self) -> PropertyTypeMultiple: + return {prop: (val,) for prop, val in self.properties.items()} + + @all_properties.setter + def all_properties(self, all_properties: PropertyTypeMultiple): + props = {} + for prop, val in all_properties: + if val: + props[prop] = val[0] + self.properties = props From 6c68f9aa0471218fbcfde35882680eebd5dd2f65 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Wed, 7 Jul 2021 10:30:59 +0100 Subject: [PATCH 040/139] Added a note about a bug --- amulet_map_editor/api/wx/ui/mc/block/define/button/base.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py index 54ea2609..610a0f7a 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py @@ -20,6 +20,9 @@ def _on_press(self, evt): self._dialog.Fit() if self._dialog.ShowModal() == wx.ID_OK: self.update_button() + # TODO: There is currently a bug if the user clicks cancel and clicks the button again the UI will not get reset. + # In order to fix this, this class will need to store the state outside of the UI. + # This should probably be done for all the UI elements def update_button(self): """Update the text on the button from the internal state.""" From 0d5ec45a0ae72be28307e711f850486e5d0aaa2d Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Wed, 7 Jul 2021 10:31:41 +0100 Subject: [PATCH 041/139] Reformatted --- .../wx/ui/mc/block/define/button/normal.py | 16 ++++++++++--- .../wx/ui/mc/block/define/button/wildcard.py | 23 +++++++++++++++---- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py index 14f408a5..b8a50e15 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py @@ -8,14 +8,24 @@ from amulet_map_editor.api.wx.ui.mc.base import NormalMCBlockAPI from amulet_map_editor.api.wx.ui.mc.block.define.widget import BlockDefine -from amulet_map_editor.api.wx.ui.mc.block.define.button.base import BaseBlockDefineButton +from amulet_map_editor.api.wx.ui.mc.block.define.button.base import ( + BaseBlockDefineButton, +) class BlockDefineButton(BaseBlockDefineButton, NormalMCBlockAPI): - def __init__(self, parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager, *args, **kwargs): + def __init__( + self, + parent: wx.Window, + translation_manager: PyMCTranslate.TranslationManager, + *args, + **kwargs + ): super().__init__(parent) self._dialog = SimpleDialog(parent, "Pick a Block") - self._block_widget = BlockDefine(self._dialog, translation_manager, wx.HORIZONTAL, *args, **kwargs) + self._block_widget = BlockDefine( + self._dialog, translation_manager, wx.HORIZONTAL, *args, **kwargs + ) self._dialog.sizer.Add(self._block_widget) self.update_button() diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py index 9dcb3038..de8d140b 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py @@ -5,23 +5,36 @@ from amulet_map_editor.api.wx.ui.simple import SimpleDialog from amulet_map_editor.api.wx.ui.mc.base import WildcardMCBlockAPI from amulet_map_editor.api.wx.ui.mc.block.define.widget import WildcardBlockDefine -from amulet_map_editor.api.wx.ui.mc.block.define.button.base import BaseBlockDefineButton +from amulet_map_editor.api.wx.ui.mc.block.define.button.base import ( + BaseBlockDefineButton, +) class WildcardBlockDefineButton(BaseBlockDefineButton, WildcardMCBlockAPI): - def __init__(self, parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager, *args, **kwargs): + def __init__( + self, + parent: wx.Window, + translation_manager: PyMCTranslate.TranslationManager, + *args, + **kwargs, + ): super().__init__(parent) self._dialog = SimpleDialog(parent, "Pick a Block") - self._block_widget = WildcardBlockDefine(self._dialog, translation_manager, wx.HORIZONTAL, *args, **kwargs) + self._block_widget = WildcardBlockDefine( + self._dialog, translation_manager, wx.HORIZONTAL, *args, **kwargs + ) self._dialog.sizer.Add(self._block_widget) self.update_button() def update_button(self): """Update the text on the button from the internal state.""" if self.selected_properties: - properties = [f"{key}:({'|'.join([v.to_snbt() for v in val])})" for key, val in self.selected_properties.items()] + properties = [ + f"{key}:({'|'.join([v.to_snbt() for v in val])})" + for key, val in self.selected_properties.items() + ] self.SetLabel(f"{self.namespace}:{self.block_name}[{','.join(properties)}]") - properties_str = ',\n'.join(properties) + properties_str = ",\n".join(properties) self.SetToolTip(f"{self.namespace}:{self.block_name}[\n{properties_str}\n]") else: self.SetLabel(f"{self.namespace}:{self.block_name}") From fe79faf4de295e2b3bac63a950708430255343fb Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Wed, 7 Jul 2021 10:54:22 +0100 Subject: [PATCH 042/139] Added a method to the wildcard properties to get all values defined --- .../properties/multiple/automatic/automatic.py | 8 ++++++++ .../block/properties/multiple/automatic/popup.py | 14 ++++++++++++++ .../api/wx/ui/mc/block/properties/multiple/main.py | 11 +---------- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/automatic/automatic.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/automatic/automatic.py index fc51222d..0b557b23 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/automatic/automatic.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/automatic/automatic.py @@ -92,3 +92,11 @@ def selected_properties(self, properties: PropertyTypeMultiple): popup.checked_nbt = nbt_tuple choice.SetValue(popup.GetStringValue()) self.Thaw() + + @property + def all_properties(self) -> PropertyTypeMultiple: + return {prop: popup.all_nbt for prop, (_, popup) in self._properties.items()} + + @all_properties.setter + def all_properties(self, all_properties: PropertyTypeMultiple): + pass diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/automatic/popup.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/automatic/popup.py index 3bf8407c..dcb57815 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/automatic/popup.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/automatic/popup.py @@ -42,6 +42,16 @@ def checked_nbt(self) -> Tuple[PropertyValueType, ...]: def checked_nbt(self, checked_nbt: Iterable[PropertyValueType]): self._check_list_box.SetCheckedStrings([v.to_snbt() for v in checked_nbt]) + @property + def all_nbt(self) -> Tuple[PropertyValueType, ...]: + nbt = [] + for entry in self._check_list_box.GetStrings(): + try: + nbt.append(amulet_nbt.from_snbt(entry)) + except: + pass + return tuple(nbt) + def _on_toggle(self, evt): if self._toggle_checkbox.GetValue(): self._check_list_box.SetCheckedItems( @@ -75,6 +85,10 @@ def __init__(self, values: Iterable[str]): self._check_list: Optional[PropertyValueCheckList] = None self._values = values + @property + def all_nbt(self) -> Tuple[PropertyValueType, ...]: + return self._check_list.all_nbt + @property def checked_nbt(self) -> Tuple[PropertyValueType, ...]: return self._check_list.checked_nbt diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py index bcc99c83..33b0a4e7 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py @@ -46,18 +46,9 @@ def __init__( if properties is None: properties = {} - self._set_properties(properties) + self.selected_properties = properties self._rebuild_ui() - def _set_properties(self, properties: PropertyTypeMultiple): - self.Freeze() - if self._manual_enabled: - self._manual.properties = properties - else: - self._simple.properties = properties - self.TopLevelParent.Layout() - self.Thaw() - @property def selected_properties(self) -> PropertyTypeMultiple: """ From a83f591f2e04bd5f945906b361d3208efaaf4229 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Sun, 11 Jul 2021 13:51:12 +0100 Subject: [PATCH 043/139] Cleaned up the platform and version select These were implemented kind of badly. The logic relied on events to propagate the changes which meant that if code changed something it would have to wait for the event loop to update before the UI and properties would update. This fixes all of that. The code methods update immediately and do not produce events. Only user created changes create events. VersionNumberChangeEvent has been removed and replaced with VersionChangeEvent PlatformChangeEvent will only be created in the standalone PlatformSelect widget. The state management logic is not managed by a separate group of classes which can be subclassed to implement the required methods and behaviour. UI events must change these states and the update method will push the state back to the UI. --- .../api/wx/ui/mc/base/api/platform.py | 74 +++++- .../api/wx/ui/mc/base/api/version.py | 99 ++++++- .../api/wx/ui/mc/version/__init__.py | 6 +- .../api/wx/ui/mc/version/events.py | 52 ++-- .../api/wx/ui/mc/version/platform_select.py | 68 +++-- .../api/wx/ui/mc/version/version_select.py | 247 ++++++++++++------ 6 files changed, 414 insertions(+), 132 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/platform.py b/amulet_map_editor/api/wx/ui/mc/base/api/platform.py index 5ec4e0e6..36bf15e5 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/platform.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/platform.py @@ -1,14 +1,86 @@ from typing import Optional import PyMCTranslate -from amulet.api.data_types import PlatformType, VersionNumberTuple +from amulet.api.data_types import PlatformType class BaseMCPlatformAPI: + def update(self) -> bool: + """ + Update the from the internal state. + No events should be created when calling this method. + + :return: True if data was changed + """ + return False + @property def platform(self) -> PlatformType: + """The active platform.""" raise NotImplementedError @platform.setter def platform(self, platform: PlatformType): + """ + Set the active platform. + Changes will propagate to the end of this UI. + No events will be created. + + :param platform: The platform string to set. + """ + raise NotImplementedError + + def set_platform(self, platform: Optional[PlatformType]): + """ + Set the active platform. + Changes will not propagate. + :meth:`update` must be called once all desired states are set. + + :param platform: The platform string to set. + """ raise NotImplementedError + +""" +update method should compare the internal state to the UI state and update as required. This should not create events. +Setter methods must change the set_x method and call the update logic to propagate the changes. +set_x methods should just change the internal state. Other code must call the update method +user changing the UI should propagate the changes and create one event at the end. +""" + + +class BaseMC: + """A class to store the translation manager.""" + + def __init__( + self, + translation_manager: PyMCTranslate.TranslationManager, + ): + self._translation_manager = translation_manager + + +class BaseMCPlatform(BaseMC, BaseMCPlatformAPI): + """A class to store the state of the platform.""" + + def __init__( + self, + translation_manager: PyMCTranslate.TranslationManager, + platform: str = None, + ): + super().__init__(translation_manager) + self._platform = None + self.set_platform(platform) + + @property + def platform(self) -> PlatformType: + return self._platform + + @platform.setter + def platform(self, platform: PlatformType): + self.set_platform(platform) + self.update() + + def set_platform(self, platform: Optional[PlatformType]): + if platform is not None and platform in self._translation_manager.platforms(): + self._platform = platform + else: + self._platform = self._translation_manager.platforms()[0] diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/version.py b/amulet_map_editor/api/wx/ui/mc/base/api/version.py index fd6b7674..067c048f 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/version.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/version.py @@ -1,21 +1,118 @@ +from typing import Optional +import PyMCTranslate from amulet.api.data_types import VersionNumberTuple -from .platform import BaseMCPlatformAPI +from .platform import BaseMCPlatformAPI, BaseMCPlatform class BaseMCVersionAPI(BaseMCPlatformAPI): @property def version_number(self) -> VersionNumberTuple: + """The active version tuple.""" raise NotImplementedError @version_number.setter def version_number(self, version_number: VersionNumberTuple): + """ + Set the active version tuple. + Changes will propagate to the end of this UI. + No events will be created. + + :param version_number: The version number to set. + """ + raise NotImplementedError + + def set_version_number(self, version_number: Optional[VersionNumberTuple]): + """ + Set the active version tuple. + Changes will not propagate. + :meth:`update` must be called once all desired states are set. + + :param version_number: The version number to set. + """ raise NotImplementedError @property def force_blockstate(self) -> bool: + """ + Is the block format native (False) or blockstate (True) + Note in cases where the native is blockstate this option does nothing. + """ raise NotImplementedError @force_blockstate.setter def force_blockstate(self, force_blockstate: bool): + """ + Set if blockstate is forced. + Changes will propagate to the end of this UI. + No events will be created. + + :param force_blockstate: False for the native format, True for the blockstate format. + """ + raise NotImplementedError + + def set_force_blockstate(self, force_blockstate: Optional[bool]): + """ + Set if blockstate is forced. + Changes will not propagate. + :meth:`update` must be called once all desired states are set. + + :param force_blockstate: False for the native format, True for the blockstate format. + """ raise NotImplementedError + +class BaseMCVersion(BaseMCPlatform, BaseMCVersionAPI): + """A class to store the state of the platform, version tuple and force blockstate.""" + + def __init__( + self, + translation_manager: PyMCTranslate.TranslationManager, + platform: str = None, + version_number: VersionNumberTuple = None, + force_blockstate: bool = False, + ): + super().__init__(translation_manager, platform) + self._version_number = None + self.set_version_number(version_number) + self._force_blockstate = None + self.set_force_blockstate(force_blockstate) + + @property + def version_number(self) -> VersionNumberTuple: + return self._version_number + + @version_number.setter + def version_number(self, version_number: VersionNumberTuple): + self.set_version_number(version_number) + self.update() + + def set_version_number(self, version_number: Optional[VersionNumberTuple]): + v = None + if version_number is not None: + if version_number in self._translation_manager.version_numbers( + self.platform + ): + self._version_number = v = version_number + else: + try: + self._version_number = v = self._translation_manager.get_version( + self.platform, version_number + ).version_number + except KeyError: + pass + if v is None: + self._version_number = self._translation_manager.version_numbers( + self.platform + )[-1] + + @property + def force_blockstate(self) -> bool: + return self._force_blockstate + + @force_blockstate.setter + def force_blockstate(self, force_blockstate: bool): + self.set_force_blockstate(force_blockstate) + self.update() + + def set_force_blockstate(self, force_blockstate: Optional[bool]): + self._force_blockstate = bool(force_blockstate) diff --git a/amulet_map_editor/api/wx/ui/mc/version/__init__.py b/amulet_map_editor/api/wx/ui/mc/version/__init__.py index 88bd7329..18eccbe6 100644 --- a/amulet_map_editor/api/wx/ui/mc/version/__init__.py +++ b/amulet_map_editor/api/wx/ui/mc/version/__init__.py @@ -1,10 +1,8 @@ -from .platform_select import PlatformSelect -from .version_select import VersionSelect from .events import ( PlatformChangeEvent, EVT_PLATFORM_CHANGE, - VersionNumberChangeEvent, - EVT_VERSION_NUMBER_CHANGE, VersionChangeEvent, EVT_VERSION_CHANGE, ) +from .platform_select import PlatformSelect +from .version_select import VersionSelect diff --git a/amulet_map_editor/api/wx/ui/mc/version/events.py b/amulet_map_editor/api/wx/ui/mc/version/events.py index 367149f8..bebbfd4b 100644 --- a/amulet_map_editor/api/wx/ui/mc/version/events.py +++ b/amulet_map_editor/api/wx/ui/mc/version/events.py @@ -11,34 +11,20 @@ class PlatformChangeEvent(wx.PyEvent): Is run when the user or code changes the platform. """ - def __init__(self, platform: str): + def __init__(self, platform: str, old_platform: str): wx.PyEvent.__init__(self, eventType=_PlatformEventType) self._platform = platform + self._old_platform = old_platform @property def platform(self) -> str: """The platform that the selection was changed to.""" return self._platform - -_VersionNumberChangeEventType = wx.NewEventType() -EVT_VERSION_NUMBER_CHANGE = wx.PyEventBinder(_VersionNumberChangeEventType) - - -class VersionNumberChangeEvent(wx.PyEvent): - """ - Event run when the version number input is changed. - Is run when the user or code changes the version number. - """ - - def __init__(self, version_number: Tuple[int, ...]): - wx.PyEvent.__init__(self, eventType=_VersionNumberChangeEventType) - self._version_number = version_number - @property - def version_number(self) -> Tuple[int, ...]: - """The version_number that the selection was changed to.""" - return self._version_number + def old_platform(self) -> str: + """The platform that was selected before it was changed.""" + return self._old_platform _VersionChangeEventType = wx.NewEventType() @@ -51,12 +37,21 @@ class VersionChangeEvent(wx.PyEvent): """ def __init__( - self, platform: str, version_number: Tuple[int, ...], force_blockstate: bool + self, + platform: str, + version_number: Tuple[int, ...], + force_blockstate: bool, + old_platform: str, + old_version_number: Tuple[int, ...], + old_force_blockstate: bool, ): wx.PyEvent.__init__(self, eventType=_VersionChangeEventType) self._platform = platform self._version_number = version_number self._force_blockstate = force_blockstate + self._old_platform = old_platform + self._old_version_number = old_version_number + self._old_force_blockstate = old_force_blockstate @property def platform(self) -> str: @@ -74,3 +69,20 @@ def force_blockstate(self) -> bool: True if the format is force blockstate, False otherwise. """ return self._force_blockstate + + @property + def old_platform(self) -> str: + """The platform that was selected before it was changed.""" + return self._old_platform + + @property + def old_version_number(self) -> Tuple[int, ...]: + """The version_number that was selected before it was changed.""" + return self._old_version_number + + @property + def old_force_blockstate(self) -> bool: + """ + True if the format was force blockstate, False otherwise. + """ + return self._old_force_blockstate diff --git a/amulet_map_editor/api/wx/ui/mc/version/platform_select.py b/amulet_map_editor/api/wx/ui/mc/version/platform_select.py index bc9ce547..d7601d77 100644 --- a/amulet_map_editor/api/wx/ui/mc/version/platform_select.py +++ b/amulet_map_editor/api/wx/ui/mc/version/platform_select.py @@ -1,13 +1,14 @@ from amulet_map_editor.api.wx.ui.simple import SimpleChoice import wx import PyMCTranslate -from typing import Tuple, Type, Any +from typing import Tuple, Type, Any, Dict from amulet.api.data_types import PlatformType from .events import PlatformChangeEvent +from amulet_map_editor.api.wx.ui.mc.base.api.platform import BaseMCPlatform -class PlatformSelect(wx.Panel): +class PlatformSelect(wx.Panel, BaseMCPlatform): """ A UI element that allows you to pick between the platforms in the translator. """ @@ -20,6 +21,7 @@ def __init__( allow_universal: bool = True, allow_vanilla: bool = True, allowed_platforms: Tuple[PlatformType, ...] = None, + state: Dict[str, Any] = None, **kwargs ): """ @@ -31,10 +33,20 @@ def __init__( :param allow_universal: If True the universal format will be included. :param allow_vanilla: If True the vanilla formats will be included. :param allowed_platforms: A whitelist of platforms. + :param state: A dictionary containing kwargs passed to the state manager. :param kwargs: Keyword args to be given to the Panel. """ + state = state or {} + state.setdefault("translation_manager", translation_manager) + state.setdefault("platform", platform) + # This is the init call to the class that stores the internal state of the data. + # This needs to be at the start to ensure that the internal state is set up before anything else is done. + # It is not a direct call to init so that subclasses of this class can substitute in which state subclass is used. + self._init_state(state) + + # Init the panel kwargs.setdefault("style", wx.BORDER_SIMPLE) - super().__init__(parent, **kwargs) + wx.Panel.__init__(self, parent, **kwargs) sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(sizer) self._sizer = wx.FlexGridSizer(2, 5, 5) @@ -43,7 +55,6 @@ def __init__( self._sizer.AddGrowableCol(0) self._sizer.AddGrowableCol(1) - self._translation_manager = translation_manager self._allow_universal = allow_universal self._allow_vanilla = allow_vanilla self._allowed_platforms = allowed_platforms @@ -51,12 +62,19 @@ def __init__( "Platform:", SimpleChoice ) self._populate_platform() - self._set_platform(platform) + self._push_platform() self._platform_choice.Bind( wx.EVT_CHOICE, - lambda evt: wx.PostEvent(self, PlatformChangeEvent(self.platform)), + self._on_platform_change, ) + def _init_state(self, state: Dict[str, Any]): + """ + Call the init method of the state manager. + This is here so that nested classes do not have to init the state managers multiple times. + """ + BaseMCPlatform.__init__(self, **state) + def _add_ui_element( self, label: str, obj: Type[wx.Control], shown=True, **kwargs ) -> Any: @@ -69,24 +87,8 @@ def _add_ui_element( wx_obj.Hide() return wx_obj - @property - def platform(self) -> PlatformType: - return self._platform_choice.GetCurrentString() - - @platform.setter - def platform(self, platform: PlatformType): - self._set_platform(platform) - wx.PostEvent(self, PlatformChangeEvent(self.platform)) - - def _set_platform(self, platform: PlatformType): - if platform and platform in self._platform_choice.GetItems(): - self._platform_choice.SetSelection( - self._platform_choice.GetItems().index(platform) - ) - else: - self._platform_choice.SetSelection(0) - def _populate_platform(self): + """Update the UI with the valid platforms.""" platforms = self._translation_manager.platforms() if self._allowed_platforms is not None: platforms = [p for p in platforms if p in self._allowed_platforms] @@ -96,6 +98,26 @@ def _populate_platform(self): platforms = [p for p in platforms if p == "universal"] self._platform_choice.SetItems(platforms) + def _push_platform(self): + """Push the internal platform state to the UI.""" + self._platform_choice.SetSelection( + self._platform_choice.GetItems().index(self.platform) + ) + + def _on_platform_change(self, evt): + """The event run when the platform choice is changed by a user.""" + old_platform = self.platform + new_platform = self._platform_choice.GetCurrentString() + # write the changes back to the internal state + self.set_platform(new_platform) + wx.PostEvent(self, PlatformChangeEvent(new_platform, old_platform)) + + def update(self): + if self.platform != self._platform_choice.GetCurrentString(): + self._push_platform() + return True + return False + def demo(): """ diff --git a/amulet_map_editor/api/wx/ui/mc/version/version_select.py b/amulet_map_editor/api/wx/ui/mc/version/version_select.py index acf0d101..e9f1c740 100644 --- a/amulet_map_editor/api/wx/ui/mc/version/version_select.py +++ b/amulet_map_editor/api/wx/ui/mc/version/version_select.py @@ -1,20 +1,17 @@ from amulet_map_editor.api.wx.ui.simple import SimpleChoice, SimpleChoiceAny import wx import PyMCTranslate -from typing import Optional +from typing import Optional, Dict, Any, Tuple -from amulet.api.data_types import VersionNumberTuple, PlatformType, VersionGroupType +from amulet.api.data_types import VersionNumberTuple, PlatformType from .platform_select import PlatformSelect +from amulet_map_editor.api.wx.ui.mc.base.api.version import BaseMCVersion from .events import ( - PlatformChangeEvent, - EVT_PLATFORM_CHANGE, - VersionNumberChangeEvent, - EVT_VERSION_NUMBER_CHANGE, VersionChangeEvent, ) -class VersionSelect(PlatformSelect): +class VersionSelect(PlatformSelect, BaseMCVersion): """ A UI element that allows you to pick between the platforms and versions in the translator. """ @@ -26,10 +23,14 @@ def __init__( platform: PlatformType = None, version_number: VersionNumberTuple = None, force_blockstate: bool = None, + allow_universal: bool = True, + allow_vanilla: bool = True, + allowed_platforms: Tuple[PlatformType, ...] = None, show_force_blockstate: bool = True, allow_numerical: bool = True, allow_blockstate: bool = True, - **kwargs + state: Dict[str, Any] = None, + **kwargs, ): """ Construct a :class:`VersionSelect` UI. @@ -39,97 +40,59 @@ def __init__( :param platform: The default platform (optional) :param version_number: The default version number (optional) :param force_blockstate: If True and the native format is numerical will use the custom blockstate format. Else will use the native format. + :param allow_universal: If True the universal format will be included. + :param allow_vanilla: If True the vanilla formats will be included. + :param allowed_platforms: A whitelist of platforms. :param show_force_blockstate: Should the format selection be shown to the user. :param allow_numerical: Should the numerical versions be shown to the user. :param allow_blockstate: Should the blockstate versions be shown to the user. + :param state: A dictionary containing kwargs passed to the state manager. :param kwargs: Keyword args to be given to the :class:`PlatformSelect` and Panel. """ - super().__init__(parent, translation_manager, platform, **kwargs) + state = state or {} + state.setdefault("version_number", version_number) + state.setdefault("force_blockstate", force_blockstate) + super().__init__( + parent, + translation_manager, + platform, + allow_universal=allow_universal, + allow_vanilla=allow_vanilla, + allowed_platforms=allowed_platforms, + state=state, + **kwargs, + ) self._allow_numerical = allow_numerical self._allow_blockstate = allow_blockstate - self.Bind(EVT_PLATFORM_CHANGE, self._on_platform_change) - self._version_choice: Optional[SimpleChoiceAny] = self._add_ui_element( "Version:", SimpleChoiceAny, reverse=True ) self._populate_version() - self._set_version_number(version_number) + self._push_version_number() self._version_choice.Bind( wx.EVT_CHOICE, - lambda evt: wx.PostEvent( - self, - VersionNumberChangeEvent(self.version_number), - ), + self._on_version_number_change, ) - self.Bind(EVT_VERSION_NUMBER_CHANGE, self._on_version_number_change) self._blockstate_choice: Optional[SimpleChoice] = self._add_ui_element( "Format:", SimpleChoice, shown=show_force_blockstate ) self._blockstate_choice.SetItems(["native", "blockstate"]) self._blockstate_choice.SetSelection(0) self._populate_blockstate() - self._set_force_blockstate(force_blockstate) - self._blockstate_choice.Bind( - wx.EVT_CHOICE, lambda evt: self._post_version_change() - ) - - def _post_version_change(self): - wx.PostEvent( - self, - VersionChangeEvent( - self.platform, - self.version_number, - self.force_blockstate, - ), - ) - - @property - def version_number(self) -> VersionNumberTuple: - return self._version_choice.GetCurrentObject() + self._push_force_blockstate() + self._blockstate_choice.Bind(wx.EVT_CHOICE, self._on_blockstate_change) - @version_number.setter - def version_number(self, version_number: VersionNumberTuple): - self._set_version_number(version_number) - wx.PostEvent( - self, - VersionNumberChangeEvent(self.version_number), - ) - - def _set_version_number(self, version_number: VersionNumberTuple): - if version_number and version_number in self._version_choice.values: - self._version_choice.SetSelection( - self._version_choice.values.index(version_number) - ) - else: - self._version_choice.SetSelection(0) - - @property - def force_blockstate(self) -> bool: - return self._blockstate_choice.GetCurrentString() == "blockstate" - - @force_blockstate.setter - def force_blockstate(self, force_blockstate: bool): - self._set_force_blockstate(force_blockstate) - self._post_version_change() - - def _set_force_blockstate(self, force_blockstate: bool): - if force_blockstate is not None: - self._blockstate_choice.SetSelection(int(force_blockstate)) - - @property - def version(self) -> VersionGroupType: - return self.platform, self.version_number, self.force_blockstate - - @version.setter - def version(self, version: VersionGroupType): - platform, version_number, force_blockstate = version - self._set_platform(platform) - self._set_version_number(version_number) - self.force_blockstate = force_blockstate + def _init_state(self, state: Dict[str, Any]): + """ + Call the init method of the state manager. + This is here so that nested classes do not have to init the state managers multiple times. + """ + BaseMCVersion.__init__(self, **state) def _populate_version(self): + """Populate the version UI element""" versions = self._translation_manager.version_numbers(self.platform) if not self._allow_blockstate: versions = [v for v in versions if v < (1, 13, 0)] @@ -145,15 +108,92 @@ def _populate_blockstate(self): else: self._blockstate_choice.Disable() - def _on_platform_change(self, evt: PlatformChangeEvent): - self._populate_version() - self.version_number = None - evt.Skip() + def _push_version_number(self): + """Push the internal version number state to the UI.""" + self._version_choice.SetSelection( + self._version_choice.values.index(self.version_number) + ) - def _on_version_number_change(self, evt: VersionChangeEvent): - self._populate_blockstate() - self.force_blockstate = None - evt.Skip() + def _push_force_blockstate(self): + """Push the internal block format state to the UI.""" + self._blockstate_choice.SetSelection(int(self.force_blockstate)) + + def _on_change(self, changed: int): + """ + Handle the UI changes and create an event. + + :param changed: 1=platform, 2=version, 3=blockstate + :return: + """ + old_platform = self.platform + old_version = self.version_number + old_force_blockstate = self.force_blockstate + + if changed <= 1: + # write the changes back to the internal state + new_platform = self._platform_choice.GetCurrentString() + self.set_platform(new_platform) + else: + new_platform = old_platform + + if changed <= 2: + if changed < 2: + self._populate_version() + self._version_choice.SetSelection(0) + new_version = self._version_choice.GetCurrentObject() + self.set_version_number(new_version) + else: + new_version = old_version + + if changed < 3: + self._populate_blockstate() + self._blockstate_choice.SetSelection(0) + new_force_blockstate = ( + self._blockstate_choice.GetCurrentString() == "blockstate" + ) + self.set_force_blockstate(new_force_blockstate) + + wx.PostEvent( + self, + VersionChangeEvent( + new_platform, + new_version, + new_force_blockstate, + old_platform, + old_version, + old_force_blockstate, + ), + ) + + def _on_platform_change(self, evt): + self._on_change(1) + + def _on_version_number_change(self, evt): + self._on_change(2) + + def _on_blockstate_change(self, evt): + self._on_change(3) + + def update(self) -> bool: + update = super().update() + # If the user set these out of order they may be messed up. + # This should fix that. + self.set_version_number(self.version_number) + self.set_force_blockstate(self.force_blockstate) + + if update: + self._populate_version() + if update or self.version_number != self._version_choice.GetCurrentObject(): + self._push_version_number() + update = True + if update: + self._populate_blockstate() + if update or self.force_blockstate != ( + self._blockstate_choice.GetCurrentString() == "blockstate" + ): + self._push_force_blockstate() + update = True + return update def demo(): @@ -174,7 +214,8 @@ def demo(): ) sizer = wx.BoxSizer() dialog.SetSizer(sizer) - sizer.Add(cls(dialog, translation_manager), 0, wx.ALL, 5) + select = cls(dialog, translation_manager) + sizer.Add(select, 0, wx.ALL, 5) dialog.Show() dialog.Fit() @@ -185,3 +226,43 @@ def on_close(evt): return on_close dialog.Bind(wx.EVT_CLOSE, get_on_close(dialog)) + + def set_version( + obj: VersionSelect, + platform: PlatformType, + version: VersionNumberTuple, + force_blockstate: bool, + ): + obj.set_platform(platform) + obj.set_version_number(version) + obj.set_force_blockstate(force_blockstate) + obj.update() + + interval = 1_000 + + wx.CallLater(interval * 1, set_version, select, "java", (1, 15, 0), False) + wx.CallLater(interval * 2, set_version, select, "java", (1, 17, 0), False) + wx.CallLater(interval * 3, set_version, select, "bedrock", (1, 15, 0), False) + wx.CallLater(interval * 4, set_version, select, "bedrock", (1, 17, 0), False) + + def set_version2( + obj: VersionSelect, + platform: PlatformType, + version: VersionNumberTuple, + force_blockstate: bool, + ): + obj.set_force_blockstate(force_blockstate) + obj.set_version_number(version) + obj.set_platform(platform) + obj.update() + + wx.CallLater(interval * 5, set_version2, select, "java", (1, 15, 0), False) + wx.CallLater(interval * 6, set_version2, select, "java", (1, 17, 0), False) + wx.CallLater(interval * 7, set_version2, select, "bedrock", (1, 15, 0), False) + wx.CallLater(interval * 8, set_version2, select, "bedrock", (1, 17, 0), False) + + def set_platform(obj: VersionSelect, platform: PlatformType): + obj.platform = platform + + wx.CallLater(interval * 9, set_platform, select, "java") + wx.CallLater(interval * 10, set_platform, select, "bedrock") From 589afaea6abefdbe5095ce048dd0c11a07b045d9 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Mon, 12 Jul 2021 09:50:06 +0100 Subject: [PATCH 044/139] Made a resource id manager and made block subclass that --- .../api/wx/ui/mc/base/api/__init__.py | 4 +- .../api/wx/ui/mc/base/api/block/base.py | 37 ++++--- .../api/wx/ui/mc/base/api/resource_id.py | 102 ++++++++++++++++++ 3 files changed, 126 insertions(+), 17 deletions(-) create mode 100644 amulet_map_editor/api/wx/ui/mc/base/api/resource_id.py diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/__init__.py b/amulet_map_editor/api/wx/ui/mc/base/api/__init__.py index 9f4eb0e0..ef9bea1c 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/__init__.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/__init__.py @@ -1,3 +1,3 @@ -from .platform import BaseMCPlatformAPI -from .version import BaseMCVersionAPI +from .platform import BaseMCPlatformAPI, BaseMC, BaseMCPlatform +from .version import BaseMCVersionAPI, BaseMCVersion from .block import * diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/block/base.py b/amulet_map_editor/api/wx/ui/mc/base/api/block/base.py index 403a6e37..2ea45bca 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/block/base.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/block/base.py @@ -1,22 +1,29 @@ from typing import Optional -from ..version import BaseMCVersionAPI +from ..resource_id import BaseMCResourceIDAPI, BaseMCResourceID -class BaseMCBlockAPI(BaseMCVersionAPI): - @property - def namespace(self) -> str: - raise NotImplementedError +class BaseMCBlockAPI(BaseMCResourceIDAPI): + pass - @namespace.setter - def namespace(self, namespace: str): - raise NotImplementedError - @property - def block_name(self) -> str: - raise NotImplementedError - - @block_name.setter - def block_name(self, block_name: str): - raise NotImplementedError +class BaseMCBlock(BaseMCResourceID, BaseMCBlockAPI): + def set_namespace(self, namespace: Optional[str]): + if namespace is None: + self._namespace = self._translation_manager.get_version( + self.platform, self.version_number + ).block.namespaces(self.force_blockstate)[0] + else: + self._namespace = str(namespace) + def set_base_name(self, base_name: Optional[str]): + if base_name is None: + blocks = self._translation_manager.get_version( + self.platform, self.version_number + ).block.base_names(self.namespace, self.force_blockstate) + if blocks: + self._block_name = blocks[0] + else: + self._block_name = "" + else: + self._block_name = str(base_name) diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/resource_id.py b/amulet_map_editor/api/wx/ui/mc/base/api/resource_id.py new file mode 100644 index 00000000..d24d2437 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/mc/base/api/resource_id.py @@ -0,0 +1,102 @@ +from typing import Optional + +import PyMCTranslate +from amulet.api.data_types import VersionNumberTuple +from .version import BaseMCVersionAPI, BaseMCVersion + + +class BaseMCResourceIDAPI(BaseMCVersionAPI): + @property + def namespace(self) -> str: + """The active namespace.""" + raise NotImplementedError + + @namespace.setter + def namespace(self, namespace: str): + """ + Set the active namespace. + Changes will propagate to the end of this UI. + No events will be created. + + :param namespace: The namespace to set. + """ + raise NotImplementedError + + def set_namespace(self, namespace: Optional[str]): + """ + Set the active namespace. + Changes will not propagate. + :meth:`update` must be called once all desired states are set. + + :param namespace: The namespace to set. + """ + raise NotImplementedError + + @property + def base_name(self) -> str: + """The active base name.""" + raise NotImplementedError + + @base_name.setter + def base_name(self, base_name: str): + """ + Set the active base name. + Changes will propagate to the end of this UI. + No events will be created. + + :param base_name: The base name to set. + """ + raise NotImplementedError + + def set_base_name(self, base_name: Optional[str]): + """ + Set the active base name. + Changes will not propagate. + :meth:`update` must be called once all desired states are set. + + :param base_name: The base name to set. + """ + raise NotImplementedError + + +class BaseMCResourceID(BaseMCVersion, BaseMCResourceIDAPI): + def __init__( + self, + translation_manager: PyMCTranslate.TranslationManager, + platform: str = None, + version_number: VersionNumberTuple = None, + force_blockstate: bool = False, + namespace: str = None, + base_name: str = None, + ): + super().__init__( + translation_manager, platform, version_number, force_blockstate + ) + self._namespace = None + self.set_namespace(namespace) + self._base_name = None + self.set_base_name(base_name) + + @property + def namespace(self) -> str: + return self._namespace + + @namespace.setter + def namespace(self, namespace: str): + self.set_namespace(namespace) + self.update() + + def set_namespace(self, namespace: Optional[str]): + raise NotImplementedError + + @property + def base_name(self) -> str: + return self._base_name + + @base_name.setter + def base_name(self, base_name: str): + self.set_base_name(base_name) + self.update() + + def set_base_name(self, base_name: Optional[str]): + raise NotImplementedError From b34fd718ef18bb79bae1a76c8bab81d025760813 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Mon, 12 Jul 2021 10:05:55 +0100 Subject: [PATCH 045/139] Moved panel style into a single input rather than in kwargs --- .../api/wx/ui/mc/version/platform_select.py | 9 +++++---- amulet_map_editor/api/wx/ui/mc/version/version_select.py | 6 +++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/version/platform_select.py b/amulet_map_editor/api/wx/ui/mc/version/platform_select.py index d7601d77..cf7c3d4b 100644 --- a/amulet_map_editor/api/wx/ui/mc/version/platform_select.py +++ b/amulet_map_editor/api/wx/ui/mc/version/platform_select.py @@ -22,7 +22,7 @@ def __init__( allow_vanilla: bool = True, allowed_platforms: Tuple[PlatformType, ...] = None, state: Dict[str, Any] = None, - **kwargs + style: Dict[str, Any] = None, ): """ Construct a :class:`PlatformSelect` UI. @@ -34,7 +34,7 @@ def __init__( :param allow_vanilla: If True the vanilla formats will be included. :param allowed_platforms: A whitelist of platforms. :param state: A dictionary containing kwargs passed to the state manager. - :param kwargs: Keyword args to be given to the Panel. + :param style: Dictionary of keyword args to be given to the Panel. """ state = state or {} state.setdefault("translation_manager", translation_manager) @@ -45,8 +45,9 @@ def __init__( self._init_state(state) # Init the panel - kwargs.setdefault("style", wx.BORDER_SIMPLE) - wx.Panel.__init__(self, parent, **kwargs) + style = style or {} + style.setdefault("style", wx.BORDER_SIMPLE) + wx.Panel.__init__(self, parent, **style) sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(sizer) self._sizer = wx.FlexGridSizer(2, 5, 5) diff --git a/amulet_map_editor/api/wx/ui/mc/version/version_select.py b/amulet_map_editor/api/wx/ui/mc/version/version_select.py index e9f1c740..7fca3f35 100644 --- a/amulet_map_editor/api/wx/ui/mc/version/version_select.py +++ b/amulet_map_editor/api/wx/ui/mc/version/version_select.py @@ -30,7 +30,7 @@ def __init__( allow_numerical: bool = True, allow_blockstate: bool = True, state: Dict[str, Any] = None, - **kwargs, + style: Dict[str, Any] = None, ): """ Construct a :class:`VersionSelect` UI. @@ -47,7 +47,7 @@ def __init__( :param allow_numerical: Should the numerical versions be shown to the user. :param allow_blockstate: Should the blockstate versions be shown to the user. :param state: A dictionary containing kwargs passed to the state manager. - :param kwargs: Keyword args to be given to the :class:`PlatformSelect` and Panel. + :param style: Dictionary of keyword args to be given to the Panel. """ state = state or {} state.setdefault("version_number", version_number) @@ -60,7 +60,7 @@ def __init__( allow_vanilla=allow_vanilla, allowed_platforms=allowed_platforms, state=state, - **kwargs, + style=style, ) self._allow_numerical = allow_numerical self._allow_blockstate = allow_blockstate From f025ebb36b3a2f635aae257901ec510f49915284 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Mon, 12 Jul 2021 10:07:46 +0100 Subject: [PATCH 046/139] Fixed an attribute name --- amulet_map_editor/api/wx/ui/mc/base/api/block/base.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/block/base.py b/amulet_map_editor/api/wx/ui/mc/base/api/block/base.py index 2ea45bca..33489df2 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/block/base.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/block/base.py @@ -22,8 +22,8 @@ def set_base_name(self, base_name: Optional[str]): self.platform, self.version_number ).block.base_names(self.namespace, self.force_blockstate) if blocks: - self._block_name = blocks[0] + self._base_name = blocks[0] else: - self._block_name = "" + self._base_name = "" else: - self._block_name = str(base_name) + self._base_name = str(base_name) From a46db27d7c5192b5f354581ec7c3d76f4aed5503 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Mon, 12 Jul 2021 14:41:23 +0100 Subject: [PATCH 047/139] Reworked the select classes These have been renamed to _____IdentifierSelect to better describe what it is. It has been rewritten to build upon the state classes and behave more like a wx widget should. --- .../api/wx/ui/mc/base/__init__.py | 2 +- .../api/wx/ui/mc/base/api/block/__init__.py | 2 +- .../base/base_identifier_select/__init__.py | 5 + .../base_identifier_select.py | 197 ++++++++++++++++ .../mc/base/base_identifier_select/events.py | 6 + .../api/wx/ui/mc/base/base_select/__init__.py | 11 - .../wx/ui/mc/base/base_select/base_select.py | 212 ------------------ .../api/wx/ui/mc/base/base_select/events.py | 20 -- .../api/wx/ui/mc/block/__init__.py | 2 +- .../api/wx/ui/mc/block/block_select.py | 85 ------- amulet_map_editor/api/wx/ui/mc/block/demo.py | 2 +- .../ui/mc/block/identifier_select/__init__.py | 2 + .../block_identifier_select.py | 104 +++++++++ .../ui/mc/block/identifier_select/events.py | 39 ++++ .../stock_plugins/operations/fill.py | 2 +- .../stock_plugins/operations/replace.py | 2 +- .../stock_plugins/operations/set_biome.py | 2 +- .../stock_plugins/operations/waterlog.py | 2 +- 18 files changed, 361 insertions(+), 336 deletions(-) create mode 100644 amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/__init__.py create mode 100644 amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py create mode 100644 amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/events.py delete mode 100644 amulet_map_editor/api/wx/ui/mc/base/base_select/__init__.py delete mode 100644 amulet_map_editor/api/wx/ui/mc/base/base_select/base_select.py delete mode 100644 amulet_map_editor/api/wx/ui/mc/base/base_select/events.py delete mode 100644 amulet_map_editor/api/wx/ui/mc/block/block_select.py create mode 100644 amulet_map_editor/api/wx/ui/mc/block/identifier_select/__init__.py create mode 100644 amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py create mode 100644 amulet_map_editor/api/wx/ui/mc/block/identifier_select/events.py diff --git a/amulet_map_editor/api/wx/ui/mc/base/__init__.py b/amulet_map_editor/api/wx/ui/mc/base/__init__.py index 3f13d3eb..21572238 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/__init__.py +++ b/amulet_map_editor/api/wx/ui/mc/base/__init__.py @@ -1,3 +1,3 @@ from .api import * -from .base_select import * +from .base_identifier_select import * from .base_define import * diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/block/__init__.py b/amulet_map_editor/api/wx/ui/mc/base/api/block/__init__.py index eafc6fd9..4fdc248b 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/block/__init__.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/block/__init__.py @@ -1,3 +1,3 @@ -from .base import BaseMCBlockAPI +from .base import BaseMCBlockAPI, BaseMCBlock from .normal import NormalMCBlockAPI from .wildcard import WildcardMCBlockAPI diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/__init__.py b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/__init__.py new file mode 100644 index 00000000..9307d193 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/__init__.py @@ -0,0 +1,5 @@ +from .events import ( + PickEvent, + EVT_PICK, +) +from .base_identifier_select import BaseIdentifierSelect diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py new file mode 100644 index 00000000..65de6ebd --- /dev/null +++ b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py @@ -0,0 +1,197 @@ +import wx +from typing import Tuple, List, Dict, Any + +import PyMCTranslate + +from amulet_map_editor.api.image import COLOUR_PICKER +from ..api.resource_id import BaseMCResourceID +from .events import ( + PickEvent, +) + + +class BaseIdentifierSelect(wx.Panel, BaseMCResourceID): + """ + BaseIdentifierSelect is a base class for a UI containing + a namespace choice + a base name search + a list of base names + """ + + def __init__( + self, + parent: wx.Window, + translation_manager: PyMCTranslate.TranslationManager, + platform: str, + version_number: Tuple[int, ...], + force_blockstate: bool = None, + namespace: str = None, + base_name: str = None, + show_pick: bool = False, + state: Dict[str, Any] = None, + ): + state = state or {} + state.setdefault("translation_manager", translation_manager) + state.setdefault("platform", platform) + state.setdefault("version_number", version_number) + state.setdefault("force_blockstate", force_blockstate) + state.setdefault("namespace", namespace) + state.setdefault("base_name", base_name) + # This is the init call to the class that stores the internal state of the data. + # This needs to be at the start to ensure that the internal state is set up before anything else is done. + # It is not a direct call to init so that subclasses of this class can substitute in which state subclass is used. + self._init_state(state) + + wx.Panel.__init__(self, parent, style=wx.BORDER_SIMPLE) + self._sizer = wx.BoxSizer(wx.VERTICAL) + self.SetSizer(self._sizer) + + sizer = wx.BoxSizer(wx.HORIZONTAL) + self._sizer.Add(sizer, 0, wx.EXPAND | wx.ALL, 5) + text = wx.StaticText(self, label="Namespace:", style=wx.ALIGN_CENTER) + sizer.Add(text, 1, wx.ALIGN_CENTER_VERTICAL) + self._namespace_combo = wx.ComboBox(self) + sizer.Add(self._namespace_combo, 2) + self._populate_namespace() + self._push_namespace() + + self._namespace_combo.Bind(wx.EVT_TEXT, self._on_namespace_change) + + sizer = wx.BoxSizer(wx.VERTICAL) + self._sizer.Add(sizer, 1, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, 5) + header_sizer = wx.BoxSizer(wx.HORIZONTAL) + sizer.Add(header_sizer, 0, wx.EXPAND | wx.BOTTOM, 5) + header_sizer.Add( + wx.StaticText( + self, + label=f"{self.type_name.capitalize()} name:", + style=wx.ALIGN_CENTER, + ), + 1, + wx.ALIGN_CENTER_VERTICAL, + ) + search_sizer = wx.BoxSizer(wx.HORIZONTAL) + header_sizer.Add(search_sizer, 2, wx.EXPAND) + self._search = wx.SearchCtrl(self) + search_sizer.Add(self._search, 1, wx.ALIGN_CENTER_VERTICAL) + self._search.Bind(wx.EVT_TEXT, self._on_search_change) + if show_pick: + pick_button = wx.BitmapButton(self, bitmap=COLOUR_PICKER.bitmap(22, 22)) + search_sizer.Add(pick_button, 0, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5) + pick_button.Bind( + wx.EVT_BUTTON, + lambda evt: wx.PostEvent(self, PickEvent(self.GetId(), widget=self)), + ) + self._base_name_list_box = wx.ListBox(self, style=wx.LB_SINGLE) + sizer.Add(self._base_name_list_box, 1, wx.EXPAND) + + self._base_names: List[str] = [] + self._populate_base_name() + self._push_base_name() + self._base_name_list_box.Bind(wx.EVT_LISTBOX, self._on_base_name_change) + + def _init_state(self, state: Dict[str, Any]): + """ + Call the init method of the state manager. + This is here so that nested classes do not have to init the state managers multiple times. + """ + BaseMCResourceID.__init__(self, **state) + + @property + def type_name(self) -> str: + raise NotImplementedError + + def _populate_namespace(self): + raise NotImplementedError("This method should be overridden in child classes.") + + def _populate_base_name(self): + raise NotImplementedError("This method should be overridden in child classes.") + + def _push_namespace(self): + """Push the internal namespace to the UI.""" + namespace = self.namespace + if namespace in self._namespace_combo.GetItems(): + self._namespace_combo.SetSelection( + self._namespace_combo.GetItems().index(namespace) + ) + else: + self._namespace_combo.ChangeValue(namespace) + + def _push_base_name(self): + """Push the internal base name to the UI.""" + self._search.ChangeValue(self.base_name) + self._update_from_search() + + def _update_from_search(self) -> bool: + """ + Update the base names based on the value in the search field. + + :return: True if the text in the field changed + """ + search_str = self._search.GetValue() + base_names = [bn for bn in self._base_names if search_str in bn] + exact = search_str in base_names + + # I originally wrote this in one line but it was rather difficult to read. + if exact: + # if we have an exact match + pass + elif not search_str and base_names: + # if the search string is blank but there are options + pass + else: + base_names.insert(0, f'"{search_str}"') + + # find the previously selected string + previous_string = None + selection = self._base_name_list_box.GetSelection() + if selection != wx.NOT_FOUND: + previous_string = self._base_name_list_box.GetString(selection) + + self._base_name_list_box.SetItems(base_names) + if exact: + # if the searched text perfectly matches select that + self._base_name_list_box.SetSelection(base_names.index(search_str)) + elif previous_string in base_names: + # if the previously selected string is in the list select that + index = base_names.index(previous_string) + self._base_name_list_box.SetSelection(index) + elif len(self._base_name_list_box.GetItems()) >= 2: + self._base_name_list_box.SetSelection(1) + else: + self._base_name_list_box.SetSelection(0) + + return previous_string != self._base_name_list_box.GetString( + self._base_name_list_box.GetSelection() + ) + + def _post_event(self, old_namespace: str, old_base_name: str, new_namespace: str, new_base_name: str): + raise NotImplementedError + + def _on_namespace_change(self, evt): + old_namespace = self.namespace + + new_namespace = self._namespace_combo.GetValue() + self.set_namespace(new_namespace) + + self._populate_base_name() + self._update_from_search() + + self._on_change(old_namespace) + + def _on_search_change(self, evt): + if self._update_from_search(): + self._on_change() + + def _on_base_name_change(self, evt): + self._on_change() + + def _on_change(self, old_namespace=None): + if old_namespace is None: + old_namespace = self.namespace + old_base_name = self.base_name + new_base_name = self._base_name_list_box.GetString(self._base_name_list_box.GetSelection()) + if self._base_name_list_box.GetSelection() == 0 and new_base_name.startswith('"'): + new_base_name = new_base_name[1:-1] + self.set_base_name(new_base_name) + self._post_event(old_namespace, old_base_name, self.namespace, new_base_name) diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/events.py b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/events.py new file mode 100644 index 00000000..d91b78fe --- /dev/null +++ b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/events.py @@ -0,0 +1,6 @@ +from wx.lib import newevent + +( + PickEvent, + EVT_PICK, +) = newevent.NewCommandEvent() # The pick button was pressed diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_select/__init__.py b/amulet_map_editor/api/wx/ui/mc/base/base_select/__init__.py deleted file mode 100644 index 1836d800..00000000 --- a/amulet_map_editor/api/wx/ui/mc/base/base_select/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -from .events import ( - ItemNamespaceChangeEvent, - EVT_ITEM_NAMESPACE_CHANGE, - ItemNameChangeEvent, - EVT_ITEM_NAME_CHANGE, - ItemChangeEvent, - EVT_ITEM_CHANGE, - PickEvent, - EVT_PICK, -) -from .base_select import BaseSelect diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_select/base_select.py b/amulet_map_editor/api/wx/ui/mc/base/base_select/base_select.py deleted file mode 100644 index 213287e4..00000000 --- a/amulet_map_editor/api/wx/ui/mc/base/base_select/base_select.py +++ /dev/null @@ -1,212 +0,0 @@ -import wx -from typing import Tuple, List, Optional - -import PyMCTranslate - -from amulet_map_editor.api.image import COLOUR_PICKER -from .events import ( - ItemNamespaceChangeEvent, - ItemNameChangeEvent, - ItemChangeEvent, - EVT_ITEM_NAMESPACE_CHANGE, - PickEvent, -) - - -class BaseSelect(wx.Panel): - """ - BaseSelect is a base class for a UI containing - a namespace choice - a base name search - a list of base names - """ - - def __init__( - self, - parent: wx.Window, - translation_manager: PyMCTranslate.TranslationManager, - platform: str, - version_number: Tuple[int, ...], - force_blockstate: bool = None, - namespace: str = None, - default_name: str = None, - show_pick: bool = False, - ): - super().__init__(parent, style=wx.BORDER_SIMPLE) - self._sizer = wx.BoxSizer(wx.VERTICAL) - self.SetSizer(self._sizer) - - self._translation_manager = translation_manager - - self._platform: Optional[str] = None - self._version_number: Optional[Tuple[int, int, int]] = None - self._force_blockstate: Optional[bool] = force_blockstate - - sizer = wx.BoxSizer(wx.HORIZONTAL) - self._sizer.Add(sizer, 0, wx.EXPAND | wx.ALL, 5) - text = wx.StaticText(self, label="Namespace:", style=wx.ALIGN_CENTER) - sizer.Add(text, 1, wx.ALIGN_CENTER_VERTICAL) - self._namespace_combo = wx.ComboBox(self) - sizer.Add(self._namespace_combo, 2) - self._set_version((platform, version_number, force_blockstate or False)) - self._populate_namespace() - self.set_namespace(namespace) - - self._namespace_combo.Bind( - wx.EVT_TEXT, lambda evt: self._post_namespace_change() - ) - self._do_text_event = ( - True # some widgets create events. This is used to suppress them - ) - - self.Bind(EVT_ITEM_NAMESPACE_CHANGE, self._on_namespace_change) - sizer = wx.BoxSizer(wx.VERTICAL) - self._sizer.Add(sizer, 1, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, 5) - header_sizer = wx.BoxSizer(wx.HORIZONTAL) - sizer.Add(header_sizer, 0, wx.EXPAND | wx.BOTTOM, 5) - header_sizer.Add( - wx.StaticText( - self, - label=f"{self.type_name.capitalize()} name:", - style=wx.ALIGN_CENTER, - ), - 1, - wx.ALIGN_CENTER_VERTICAL, - ) - search_sizer = wx.BoxSizer(wx.HORIZONTAL) - header_sizer.Add(search_sizer, 2, wx.EXPAND) - self._search = wx.SearchCtrl(self) - search_sizer.Add(self._search, 1, wx.ALIGN_CENTER_VERTICAL) - self._search.Bind(wx.EVT_TEXT, self._on_search_change) - if show_pick: - pick_button = wx.BitmapButton(self, bitmap=COLOUR_PICKER.bitmap(22, 22)) - search_sizer.Add(pick_button, 0, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5) - pick_button.Bind( - wx.EVT_BUTTON, - lambda evt: wx.PostEvent(self, PickEvent(self.GetId(), widget=self)), - ) - self._list_box = wx.ListBox(self, style=wx.LB_SINGLE) - sizer.Add(self._list_box, 1, wx.EXPAND) - - self._names: List[str] = [] - self._populate_item_name() - self.set_name(default_name) - self._list_box.Bind(wx.EVT_LISTBOX, lambda evt: self._post_item_change()) - - @property - def type_name(self) -> str: - raise NotImplementedError - - def _post_namespace_change(self): - if self._do_text_event: - wx.PostEvent( - self, ItemNamespaceChangeEvent(self.GetId(), namespace=self.namespace) - ) - self._do_text_event = True - - def _post_item_change(self): - wx.PostEvent(self, ItemNameChangeEvent(self.GetId(), name=self.name)), - wx.PostEvent( - self, - ItemChangeEvent(self.GetId(), namespace=self.namespace, name=self.name), - ) - - @property - def version(self) -> Tuple[str, Tuple[int, int, int], bool]: - return self._platform, self._version_number, self._force_blockstate - - @version.setter - def version(self, version: Tuple[str, Tuple[int, int, int], bool]): - self._set_version(version) - self._populate_namespace() - self.namespace = None - - def _set_version(self, version: Tuple[str, Tuple[int, ...], bool]): - assert ( - version[0] in self._translation_manager.platforms() - and version[1] in self._translation_manager.version_numbers(version[0]) - and isinstance(version[2], bool) - ), f"{version} is not a valid version" - self._platform, self._version_number, self._force_blockstate = version - - @property - def namespace(self) -> str: - return self._namespace_combo.GetValue() - - @namespace.setter - def namespace(self, namespace: str): - self.set_namespace(namespace) - wx.PostEvent( - self, ItemNamespaceChangeEvent(self.GetId(), namespace=self.namespace) - ) - - def set_namespace(self, namespace: str): - namespace = namespace or "minecraft" - if isinstance(namespace, str): - if namespace in self._namespace_combo.GetItems(): - self._namespace_combo.SetSelection( - self._namespace_combo.GetItems().index(namespace) - ) - else: - self._namespace_combo.ChangeValue(namespace) - - @property - def name(self) -> str: - name: str = self._list_box.GetString(self._list_box.GetSelection()) - if self._list_box.GetSelection() == 0 and name.startswith('"'): - name = name[1:-1] - return name - - @name.setter - def name(self, name: str): - if self.set_name(name): - self._post_item_change() - - def set_name(self, name: str) -> bool: - name = name or "" - self._search.ChangeValue(name) - return self._update_item_name(name) - - def _populate_namespace(self): - raise NotImplementedError("This method should be overridden in child classes.") - - def _populate_item_name(self): - raise NotImplementedError("This method should be overridden in child classes.") - - def _on_namespace_change(self, evt: ItemNamespaceChangeEvent): - self._populate_item_name() - self.name = None - evt.Skip() - - def _on_search_change(self, evt): - search_str = evt.GetString() - if self._update_item_name(search_str): - self._post_item_change() - - def _update_item_name(self, search_str: str) -> bool: - names = [bn for bn in self._names if search_str in bn] - if search_str not in names: - names.insert(0, f'"{search_str}"') - - index = 0 - selection = self._list_box.GetSelection() - if selection != wx.NOT_FOUND: - current_string = self._list_box.GetString(selection) - if current_string in names: - index = names.index(current_string) - - self._list_box.SetItems(names) - if index: - # if the previously selected string is in the list select that - self._list_box.SetSelection(index) - return False - elif search_str in names: - # if the searched text perfectly matches select that - self._list_box.SetSelection(names.index(search_str)) - return True - elif len(self._list_box.GetItems()) >= 2: - self._list_box.SetSelection(1) - return True - else: - self._list_box.SetSelection(0) - return True diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_select/events.py b/amulet_map_editor/api/wx/ui/mc/base/base_select/events.py deleted file mode 100644 index 6e209aa5..00000000 --- a/amulet_map_editor/api/wx/ui/mc/base/base_select/events.py +++ /dev/null @@ -1,20 +0,0 @@ -from wx.lib import newevent - -( - ItemNamespaceChangeEvent, - EVT_ITEM_NAMESPACE_CHANGE, -) = newevent.NewCommandEvent() # the namespace entry changed -( - ItemNameChangeEvent, - EVT_ITEM_NAME_CHANGE, -) = newevent.NewCommandEvent() # the name entry changed -( - ItemChangeEvent, - EVT_ITEM_CHANGE, -) = ( - newevent.NewCommandEvent() -) # the name or namespace changed. Generated after EVT_ITEM_NAME_CHANGE -( - PickEvent, - EVT_PICK, -) = newevent.NewCommandEvent() # The pick button was pressed diff --git a/amulet_map_editor/api/wx/ui/mc/block/__init__.py b/amulet_map_editor/api/wx/ui/mc/block/__init__.py index db43c5c7..1c8eec62 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/__init__.py +++ b/amulet_map_editor/api/wx/ui/mc/block/__init__.py @@ -1,4 +1,4 @@ -from .block_select import BlockSelect +from .identifier_select import BlockIdentifierSelect from .define import * from .multi_block_define import MultiBlockDefine from .properties import * diff --git a/amulet_map_editor/api/wx/ui/mc/block/block_select.py b/amulet_map_editor/api/wx/ui/mc/block/block_select.py deleted file mode 100644 index 53de46b9..00000000 --- a/amulet_map_editor/api/wx/ui/mc/block/block_select.py +++ /dev/null @@ -1,85 +0,0 @@ -import wx -from typing import Tuple - -import PyMCTranslate - -from amulet_map_editor.api.wx.ui.mc.base.base_select import BaseSelect - - -class BlockSelect(BaseSelect): - """ - A UI consisting of a namespace choice, block name search box and list of block names. - """ - - @property - def type_name(self) -> str: - return "Block" - - def __init__( - self, - parent: wx.Window, - translation_manager: PyMCTranslate.TranslationManager, - platform: str, - version_number: Tuple[int, int, int], - force_blockstate: bool, - namespace: str = None, - block_name: str = None, - show_pick_block: bool = False, - ): - super().__init__( - parent, - translation_manager, - platform, - version_number, - force_blockstate, - namespace, - block_name, - show_pick_block, - ) - - def _populate_namespace(self): - version = self._translation_manager.get_version( - self._platform, self._version_number - ) - namespaces = version.block.namespaces(self._force_blockstate) - self._do_text_event = False - self._namespace_combo.Set(namespaces) - - def _populate_item_name(self): - version = self._translation_manager.get_version( - self._platform, self._version_number - ) - self._names = version.block.base_names(self.namespace, self._force_blockstate) - self._list_box.SetItems(self._names) - - -def demo(): - """ - Show a demo version of the UI. - An app instance must be created first. - """ - translation_manager = PyMCTranslate.new_translation_manager() - dialog = wx.Dialog( - None, title="BlockSelect", style=wx.DEFAULT_DIALOG_STYLE | wx.DIALOG_NO_PARENT - ) - sizer = wx.BoxSizer() - dialog.SetSizer(sizer) - sizer.Add( - BlockSelect(dialog, translation_manager, "java", (1, 16, 0), False), - 1, - wx.ALL | wx.EXPAND, - 5, - ) - dialog.Bind(wx.EVT_CLOSE, lambda evt: dialog.Destroy()) - dialog.Show() - dialog.Fit() - - -if __name__ == "__main__": - - def main(): - app = wx.App() - demo() - app.MainLoop() - - main() diff --git a/amulet_map_editor/api/wx/ui/mc/block/demo.py b/amulet_map_editor/api/wx/ui/mc/block/demo.py index 1786de93..db9fa574 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/demo.py +++ b/amulet_map_editor/api/wx/ui/mc/block/demo.py @@ -1,5 +1,5 @@ import wx -from amulet_map_editor.api.wx.ui.mc.block.block_select import ( +from amulet_map_editor.api.wx.ui.mc.block.identifier_select.block_identifier_select import ( demo as block_select_demo, ) from amulet_map_editor.api.wx.ui.mc.block.define.demo import ( diff --git a/amulet_map_editor/api/wx/ui/mc/block/identifier_select/__init__.py b/amulet_map_editor/api/wx/ui/mc/block/identifier_select/__init__.py new file mode 100644 index 00000000..9885e07e --- /dev/null +++ b/amulet_map_editor/api/wx/ui/mc/block/identifier_select/__init__.py @@ -0,0 +1,2 @@ +from .block_identifier_select import BlockIdentifierSelect +from .events import BlockIDChangeEvent, EVT_BLOCK_ID_CHANGE diff --git a/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py b/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py new file mode 100644 index 00000000..2d446c47 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py @@ -0,0 +1,104 @@ +from typing import Dict, Any +import wx + +import PyMCTranslate +from amulet.api.data_types import VersionNumberTuple +from amulet_map_editor.api.wx.ui.mc.base.base_identifier_select import BaseIdentifierSelect +from amulet_map_editor.api.wx.ui.mc.base.api.block import BaseMCBlock +from amulet_map_editor.api.wx.ui.mc.block.identifier_select.events import BlockIDChangeEvent, EVT_BLOCK_ID_CHANGE + + +class BlockIdentifierSelect(BaseIdentifierSelect, BaseMCBlock): + """ + A UI consisting of a namespace choice, block name search box and list of block names. + """ + + @property + def type_name(self) -> str: + return "Block" + + def __init__( + self, + parent: wx.Window, + translation_manager: PyMCTranslate.TranslationManager, + platform: str, + version_number: VersionNumberTuple, + force_blockstate: bool, + namespace: str = None, + block_name: str = None, + show_pick_block: bool = False, + ): + super().__init__( + parent, + translation_manager, + platform, + version_number, + force_blockstate, + namespace, + block_name, + show_pick_block, + ) + + def _init_state(self, state: Dict[str, Any]): + BaseMCBlock.__init__(self, **state) + + def _populate_namespace(self): + version = self._translation_manager.get_version( + self.platform, self.version_number + ) + self._namespace_combo.Set(version.block.namespaces(self.force_blockstate)) + + def _populate_base_name(self): + version = self._translation_manager.get_version( + self.platform, self.version_number + ) + self._base_names = version.block.base_names(self.namespace, self.force_blockstate) + self._base_name_list_box.SetItems(self._base_names) + + def _post_event(self, old_namespace: str, old_base_name: str, new_namespace: str, new_base_name: str): + wx.PostEvent( + self, + BlockIDChangeEvent( + new_namespace, + new_base_name, + old_namespace, + old_base_name, + ), + ) + + +def demo(): + """ + Show a demo version of the UI. + An app instance must be created first. + """ + translation_manager = PyMCTranslate.new_translation_manager() + dialog = wx.Dialog( + None, title="BlockIdentifierSelect", style=wx.DEFAULT_DIALOG_STYLE | wx.DIALOG_NO_PARENT | wx.RESIZE_BORDER + ) + sizer = wx.BoxSizer() + dialog.SetSizer(sizer) + widget = BlockIdentifierSelect(dialog, translation_manager, "java", (1, 16, 0), False) + + def on_change(evt: BlockIDChangeEvent): + print(evt.old_namespace, evt.old_base_name, evt.namespace, evt.base_name) + widget.Bind(EVT_BLOCK_ID_CHANGE, on_change) + sizer.Add( + widget, + 1, + wx.ALL | wx.EXPAND, + 5, + ) + dialog.Bind(wx.EVT_CLOSE, lambda evt: dialog.Destroy()) + dialog.Show() + dialog.Fit() + + +if __name__ == "__main__": + + def main(): + app = wx.App() + demo() + app.MainLoop() + + main() diff --git a/amulet_map_editor/api/wx/ui/mc/block/identifier_select/events.py b/amulet_map_editor/api/wx/ui/mc/block/identifier_select/events.py new file mode 100644 index 00000000..fb4378fa --- /dev/null +++ b/amulet_map_editor/api/wx/ui/mc/block/identifier_select/events.py @@ -0,0 +1,39 @@ +import wx + +_BlockIDChangeEventType = wx.NewEventType() +EVT_BLOCK_ID_CHANGE = wx.PyEventBinder(_BlockIDChangeEventType) + + +class BlockIDChangeEvent(wx.PyEvent): + """ + Run when the block resource identifier changes. + """ + + def __init__( + self, + namespace: str, + base_name: str, + old_namespace: str, + old_base_name: str, + ): + wx.PyEvent.__init__(self, eventType=_BlockIDChangeEventType) + self._namespace = namespace + self._base_name = base_name + self._old_namespace = old_namespace + self._old_base_name = old_base_name + + @property + def namespace(self) -> str: + return self._namespace + + @property + def base_name(self) -> str: + return self._base_name + + @property + def old_namespace(self) -> str: + return self._old_namespace + + @property + def old_base_name(self) -> str: + return self._old_base_name diff --git a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/fill.py b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/fill.py index 635082fe..82193334 100644 --- a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/fill.py +++ b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/fill.py @@ -3,7 +3,7 @@ from amulet.operations.fill import fill -from amulet_map_editor.api.wx.ui.mc.base.base_select import EVT_PICK +from amulet_map_editor.api.wx.ui.mc.base.base_identifier_select import EVT_PICK from amulet_map_editor.api.wx.ui.mc.block import BlockDefine from amulet_map_editor.programs.edit.api.operations import DefaultOperationUI diff --git a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/replace.py b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/replace.py index 85685045..6e5e3254 100644 --- a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/replace.py +++ b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/replace.py @@ -4,7 +4,7 @@ from amulet.api.block import Block -from amulet_map_editor.api.wx.ui.mc.base.base_select import EVT_PICK +from amulet_map_editor.api.wx.ui.mc.base.base_identifier_select import EVT_PICK from amulet_map_editor.api.wx.ui.mc.block import BlockDefine from amulet_map_editor.api.wx.ui.simple import SimpleScrollablePanel from amulet_map_editor.programs.edit.api.operations import DefaultOperationUI diff --git a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/set_biome.py b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/set_biome.py index 338d0024..b56c01e7 100644 --- a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/set_biome.py +++ b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/set_biome.py @@ -5,7 +5,7 @@ from amulet.utils import block_coords_to_chunk_coords from amulet.api.chunk.biomes import BiomesShape -from amulet_map_editor.api.wx.ui.mc.base.base_select import EVT_PICK +from amulet_map_editor.api.wx.ui.mc.base.base_identifier_select import EVT_PICK from amulet_map_editor.api.wx.ui.mc.biome import BiomeDefine from amulet_map_editor.programs.edit.api.operations import SimpleOperationPanel from amulet_map_editor.api.wx.ui.simple import SimpleChoiceAny diff --git a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/waterlog.py b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/waterlog.py index d2e17ee2..c34eadad 100644 --- a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/waterlog.py +++ b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/waterlog.py @@ -2,7 +2,7 @@ from typing import TYPE_CHECKING, Tuple import wx -from amulet_map_editor.api.wx.ui.mc.base.base_select import EVT_PICK +from amulet_map_editor.api.wx.ui.mc.base.base_identifier_select import EVT_PICK from amulet_map_editor.api.wx.ui.simple import SimpleDialog from amulet_map_editor.api.wx.ui.mc.block import BlockDefine from amulet_map_editor.programs.edit.api.operations import DefaultOperationUI From 8e0c8305bc741a1955352f02190e5786a0281c2b Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Mon, 12 Jul 2021 14:49:15 +0100 Subject: [PATCH 048/139] Renamed the block identifier storage class --- amulet_map_editor/api/wx/ui/mc/base/api/block/__init__.py | 2 +- .../api/wx/ui/mc/base/api/block/{base.py => identifier.py} | 4 ++-- amulet_map_editor/api/wx/ui/mc/base/api/block/normal.py | 4 ++-- amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py | 4 ++-- amulet_map_editor/api/wx/ui/mc/block/define/button/base.py | 4 ++-- .../mc/block/identifier_select/block_identifier_select.py | 6 +++--- 6 files changed, 12 insertions(+), 12 deletions(-) rename amulet_map_editor/api/wx/ui/mc/base/api/block/{base.py => identifier.py} (88%) diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/block/__init__.py b/amulet_map_editor/api/wx/ui/mc/base/api/block/__init__.py index 4fdc248b..0c6d531b 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/block/__init__.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/block/__init__.py @@ -1,3 +1,3 @@ -from .base import BaseMCBlockAPI, BaseMCBlock +from .identifier import MCBlockIdentifierAPI, MCBlockIdentifier from .normal import NormalMCBlockAPI from .wildcard import WildcardMCBlockAPI diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/block/base.py b/amulet_map_editor/api/wx/ui/mc/base/api/block/identifier.py similarity index 88% rename from amulet_map_editor/api/wx/ui/mc/base/api/block/base.py rename to amulet_map_editor/api/wx/ui/mc/base/api/block/identifier.py index 33489df2..4af2079b 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/block/base.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/block/identifier.py @@ -3,11 +3,11 @@ from ..resource_id import BaseMCResourceIDAPI, BaseMCResourceID -class BaseMCBlockAPI(BaseMCResourceIDAPI): +class MCBlockIdentifierAPI(BaseMCResourceIDAPI): pass -class BaseMCBlock(BaseMCResourceID, BaseMCBlockAPI): +class MCBlockIdentifier(BaseMCResourceID, MCBlockIdentifierAPI): def set_namespace(self, namespace: Optional[str]): if namespace is None: self._namespace = self._translation_manager.get_version( diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/block/normal.py b/amulet_map_editor/api/wx/ui/mc/base/api/block/normal.py index 95cad4b6..d3bbd164 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/block/normal.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/block/normal.py @@ -3,10 +3,10 @@ from amulet.api.block import PropertyType, Block from amulet.api.block_entity import BlockEntity -from .base import BaseMCBlockAPI +from .identifier import MCBlockIdentifierAPI -class NormalMCBlockAPI(BaseMCBlockAPI): +class NormalMCBlockAPI(MCBlockIdentifierAPI): @property def properties(self) -> PropertyType: raise NotImplementedError diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py b/amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py index 68586d5d..b10e2b49 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py @@ -1,9 +1,9 @@ from amulet.api.block import PropertyTypeMultiple -from .base import BaseMCBlockAPI +from .identifier import MCBlockIdentifierAPI -class WildcardMCBlockAPI(BaseMCBlockAPI): +class WildcardMCBlockAPI(MCBlockIdentifierAPI): @property def all_properties(self) -> PropertyTypeMultiple: """The values that exist for every property.""" diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py index 610a0f7a..e7d527ad 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py @@ -2,11 +2,11 @@ from typing import Optional from amulet.api.data_types import PlatformType, VersionNumberTuple -from amulet_map_editor.api.wx.ui.mc.base import BaseMCBlockAPI +from amulet_map_editor.api.wx.ui.mc.base import MCBlockIdentifierAPI from amulet_map_editor.api.wx.ui.mc.block.define import BaseBlockDefine -class BaseBlockDefineButton(wx.Button, BaseMCBlockAPI): +class BaseBlockDefineButton(wx.Button, MCBlockIdentifierAPI): def __init__( self, parent: wx.Window, diff --git a/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py b/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py index 2d446c47..b9d7f81c 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py +++ b/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py @@ -4,11 +4,11 @@ import PyMCTranslate from amulet.api.data_types import VersionNumberTuple from amulet_map_editor.api.wx.ui.mc.base.base_identifier_select import BaseIdentifierSelect -from amulet_map_editor.api.wx.ui.mc.base.api.block import BaseMCBlock +from amulet_map_editor.api.wx.ui.mc.base.api.block import MCBlockIdentifier from amulet_map_editor.api.wx.ui.mc.block.identifier_select.events import BlockIDChangeEvent, EVT_BLOCK_ID_CHANGE -class BlockIdentifierSelect(BaseIdentifierSelect, BaseMCBlock): +class BlockIdentifierSelect(BaseIdentifierSelect, MCBlockIdentifier): """ A UI consisting of a namespace choice, block name search box and list of block names. """ @@ -40,7 +40,7 @@ def __init__( ) def _init_state(self, state: Dict[str, Any]): - BaseMCBlock.__init__(self, **state) + MCBlockIdentifier.__init__(self, **state) def _populate_namespace(self): version = self._translation_manager.get_version( From 2ee1b4b08742be0e6ae07c73ce766bc7378d1a86 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Mon, 12 Jul 2021 14:49:57 +0100 Subject: [PATCH 049/139] Reformatted --- .../base_identifier_select.py | 16 ++++++++-- .../block_identifier_select.py | 30 +++++++++++++++---- .../ui/mc/block/identifier_select/events.py | 10 +++---- 3 files changed, 42 insertions(+), 14 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py index 65de6ebd..53292d53 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py +++ b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py @@ -165,7 +165,13 @@ def _update_from_search(self) -> bool: self._base_name_list_box.GetSelection() ) - def _post_event(self, old_namespace: str, old_base_name: str, new_namespace: str, new_base_name: str): + def _post_event( + self, + old_namespace: str, + old_base_name: str, + new_namespace: str, + new_base_name: str, + ): raise NotImplementedError def _on_namespace_change(self, evt): @@ -190,8 +196,12 @@ def _on_change(self, old_namespace=None): if old_namespace is None: old_namespace = self.namespace old_base_name = self.base_name - new_base_name = self._base_name_list_box.GetString(self._base_name_list_box.GetSelection()) - if self._base_name_list_box.GetSelection() == 0 and new_base_name.startswith('"'): + new_base_name = self._base_name_list_box.GetString( + self._base_name_list_box.GetSelection() + ) + if self._base_name_list_box.GetSelection() == 0 and new_base_name.startswith( + '"' + ): new_base_name = new_base_name[1:-1] self.set_base_name(new_base_name) self._post_event(old_namespace, old_base_name, self.namespace, new_base_name) diff --git a/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py b/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py index b9d7f81c..c2d53c76 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py +++ b/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py @@ -3,9 +3,14 @@ import PyMCTranslate from amulet.api.data_types import VersionNumberTuple -from amulet_map_editor.api.wx.ui.mc.base.base_identifier_select import BaseIdentifierSelect +from amulet_map_editor.api.wx.ui.mc.base.base_identifier_select import ( + BaseIdentifierSelect, +) from amulet_map_editor.api.wx.ui.mc.base.api.block import MCBlockIdentifier -from amulet_map_editor.api.wx.ui.mc.block.identifier_select.events import BlockIDChangeEvent, EVT_BLOCK_ID_CHANGE +from amulet_map_editor.api.wx.ui.mc.block.identifier_select.events import ( + BlockIDChangeEvent, + EVT_BLOCK_ID_CHANGE, +) class BlockIdentifierSelect(BaseIdentifierSelect, MCBlockIdentifier): @@ -52,10 +57,18 @@ def _populate_base_name(self): version = self._translation_manager.get_version( self.platform, self.version_number ) - self._base_names = version.block.base_names(self.namespace, self.force_blockstate) + self._base_names = version.block.base_names( + self.namespace, self.force_blockstate + ) self._base_name_list_box.SetItems(self._base_names) - def _post_event(self, old_namespace: str, old_base_name: str, new_namespace: str, new_base_name: str): + def _post_event( + self, + old_namespace: str, + old_base_name: str, + new_namespace: str, + new_base_name: str, + ): wx.PostEvent( self, BlockIDChangeEvent( @@ -74,14 +87,19 @@ def demo(): """ translation_manager = PyMCTranslate.new_translation_manager() dialog = wx.Dialog( - None, title="BlockIdentifierSelect", style=wx.DEFAULT_DIALOG_STYLE | wx.DIALOG_NO_PARENT | wx.RESIZE_BORDER + None, + title="BlockIdentifierSelect", + style=wx.DEFAULT_DIALOG_STYLE | wx.DIALOG_NO_PARENT | wx.RESIZE_BORDER, ) sizer = wx.BoxSizer() dialog.SetSizer(sizer) - widget = BlockIdentifierSelect(dialog, translation_manager, "java", (1, 16, 0), False) + widget = BlockIdentifierSelect( + dialog, translation_manager, "java", (1, 16, 0), False + ) def on_change(evt: BlockIDChangeEvent): print(evt.old_namespace, evt.old_base_name, evt.namespace, evt.base_name) + widget.Bind(EVT_BLOCK_ID_CHANGE, on_change) sizer.Add( widget, diff --git a/amulet_map_editor/api/wx/ui/mc/block/identifier_select/events.py b/amulet_map_editor/api/wx/ui/mc/block/identifier_select/events.py index fb4378fa..48339726 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/identifier_select/events.py +++ b/amulet_map_editor/api/wx/ui/mc/block/identifier_select/events.py @@ -10,11 +10,11 @@ class BlockIDChangeEvent(wx.PyEvent): """ def __init__( - self, - namespace: str, - base_name: str, - old_namespace: str, - old_base_name: str, + self, + namespace: str, + base_name: str, + old_namespace: str, + old_base_name: str, ): wx.PyEvent.__init__(self, eventType=_BlockIDChangeEventType) self._namespace = namespace From e8520e7fa81756a86207320165407a636b711892 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Mon, 12 Jul 2021 16:25:14 +0100 Subject: [PATCH 050/139] Reworked the biome identifier select widget Added BaseIDChangeEvent which BlockIDChangeEvent and BiomeIDChangeEvent subclass Fixed an issue in the base class which only showed one item when first opened. --- .../api/wx/ui/mc/base/api/biome.py | 25 ++++ .../base/base_identifier_select/__init__.py | 1 + .../base_identifier_select.py | 12 +- .../mc/base/base_identifier_select/events.py | 36 ++++++ .../api/wx/ui/mc/biome/__init__.py | 2 +- .../api/wx/ui/mc/biome/biome_select.py | 71 ------------ amulet_map_editor/api/wx/ui/mc/biome/demo.py | 2 +- .../ui/mc/biome/identifier_select/__init__.py | 2 + .../biome_identifier_select.py | 108 ++++++++++++++++++ .../ui/mc/biome/identifier_select/events.py | 21 ++++ .../block_identifier_select.py | 26 +---- .../ui/mc/block/identifier_select/events.py | 26 +---- 12 files changed, 211 insertions(+), 121 deletions(-) create mode 100644 amulet_map_editor/api/wx/ui/mc/base/api/biome.py delete mode 100644 amulet_map_editor/api/wx/ui/mc/biome/biome_select.py create mode 100644 amulet_map_editor/api/wx/ui/mc/biome/identifier_select/__init__.py create mode 100644 amulet_map_editor/api/wx/ui/mc/biome/identifier_select/biome_identifier_select.py create mode 100644 amulet_map_editor/api/wx/ui/mc/biome/identifier_select/events.py diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/biome.py b/amulet_map_editor/api/wx/ui/mc/base/api/biome.py new file mode 100644 index 00000000..ecca57cb --- /dev/null +++ b/amulet_map_editor/api/wx/ui/mc/base/api/biome.py @@ -0,0 +1,25 @@ +from typing import Optional + +from .resource_id import BaseMCResourceIDAPI, BaseMCResourceID + + +class MCBiomeIdentifierAPI(BaseMCResourceIDAPI): + pass + + +class MCBiomeIdentifier(BaseMCResourceID, MCBiomeIdentifierAPI): + def set_namespace(self, namespace: Optional[str]): + if namespace is None: + self._namespace = self._translation_manager.get_version( + self.platform, self.version_number + ).biome.biome_ids[0].split(":", 1)[0] + else: + self._namespace = str(namespace) + + def set_base_name(self, base_name: Optional[str]): + if base_name is None: + self._base_name = self._translation_manager.get_version( + self.platform, self.version_number + ).biome.biome_ids[0].split(":", 1)[-1] + else: + self._base_name = str(base_name) diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/__init__.py b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/__init__.py index 9307d193..d3771987 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/__init__.py +++ b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/__init__.py @@ -1,5 +1,6 @@ from .events import ( PickEvent, EVT_PICK, + BaseIDChangeEvent, ) from .base_identifier_select import BaseIdentifierSelect diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py index 53292d53..a63e1c9c 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py +++ b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py @@ -119,8 +119,16 @@ def _push_namespace(self): def _push_base_name(self): """Push the internal base name to the UI.""" - self._search.ChangeValue(self.base_name) - self._update_from_search() + if self.base_name in self._base_names: + location = self._base_name_list_box.FindString(self.base_name) + if location == wx.NOT_FOUND: + self._search.ChangeValue("") + self._update_from_search() + location = self._base_name_list_box.FindString(self.base_name) + self._base_name_list_box.SetSelection(location) + else: + self._search.ChangeValue(self.base_name) + self._update_from_search() def _update_from_search(self) -> bool: """ diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/events.py b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/events.py index d91b78fe..caf809d7 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/events.py +++ b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/events.py @@ -1,6 +1,42 @@ +import wx from wx.lib import newevent ( PickEvent, EVT_PICK, ) = newevent.NewCommandEvent() # The pick button was pressed + + +class BaseIDChangeEvent(wx.PyEvent): + """ + Run when the identifier changes. + """ + + def __init__( + self, + namespace: str, + base_name: str, + old_namespace: str, + old_base_name: str, + ): + wx.PyEvent.__init__(self) + self._namespace = namespace + self._base_name = base_name + self._old_namespace = old_namespace + self._old_base_name = old_base_name + + @property + def namespace(self) -> str: + return self._namespace + + @property + def base_name(self) -> str: + return self._base_name + + @property + def old_namespace(self) -> str: + return self._old_namespace + + @property + def old_base_name(self) -> str: + return self._old_base_name diff --git a/amulet_map_editor/api/wx/ui/mc/biome/__init__.py b/amulet_map_editor/api/wx/ui/mc/biome/__init__.py index 174646e8..04dd142b 100644 --- a/amulet_map_editor/api/wx/ui/mc/biome/__init__.py +++ b/amulet_map_editor/api/wx/ui/mc/biome/__init__.py @@ -1,2 +1,2 @@ -from .biome_select import BiomeSelect +from .identifier_select import BiomeIdentifierSelect from .biome_define import BiomeDefine diff --git a/amulet_map_editor/api/wx/ui/mc/biome/biome_select.py b/amulet_map_editor/api/wx/ui/mc/biome/biome_select.py deleted file mode 100644 index bf1b8be7..00000000 --- a/amulet_map_editor/api/wx/ui/mc/biome/biome_select.py +++ /dev/null @@ -1,71 +0,0 @@ -import wx -from amulet_map_editor.api.wx.ui.mc.base.base_select import BaseSelect - - -class BiomeSelect(BaseSelect): - """ - A UI consisting of a namespace choice, biome name search box and list of biome names. - """ - - @property - def type_name(self) -> str: - return "Biome" - - def _populate_namespace(self): - version = self._translation_manager.get_version( - self._platform, self._version_number - ) - namespaces = list( - set( - [biome_id[: biome_id.find(":")] for biome_id in version.biome.biome_ids] - ) - ) - self._do_text_event = False - self._namespace_combo.Set(namespaces) - - def _populate_item_name(self): - version = self._translation_manager.get_version( - self._platform, self._version_number - ) - self._names = [ - biome_id[len(self.namespace) + 1 :] - for biome_id in version.biome.biome_ids - if biome_id.startswith(self.namespace) - ] - self._list_box.SetItems(self._names) - - -def demo(): - """ - Show a demo version of the UI. - An app instance must be created first. - """ - import PyMCTranslate - - translation_manager = PyMCTranslate.new_translation_manager() - dialog = wx.Dialog( - None, - title="BiomeSelect", - style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT, - ) - sizer = wx.BoxSizer() - dialog.SetSizer(sizer) - sizer.Add( - BiomeSelect(dialog, translation_manager, "java", (1, 16, 0), False), - 1, - wx.ALL | wx.EXPAND, - 5, - ) - dialog.Show() - dialog.Fit() - dialog.Bind(wx.EVT_CLOSE, lambda evt: dialog.Destroy()) - - -if __name__ == "__main__": - - def main(): - app = wx.App() - demo() - app.MainLoop() - - main() diff --git a/amulet_map_editor/api/wx/ui/mc/biome/demo.py b/amulet_map_editor/api/wx/ui/mc/biome/demo.py index 81e4d651..ce3ee07c 100644 --- a/amulet_map_editor/api/wx/ui/mc/biome/demo.py +++ b/amulet_map_editor/api/wx/ui/mc/biome/demo.py @@ -1,5 +1,5 @@ import wx -from amulet_map_editor.api.wx.ui.mc.biome.biome_select import ( +from amulet_map_editor.api.wx.ui.mc.biome.identifier_select.biome_identifier_select import ( demo as biome_select_demo, ) from amulet_map_editor.api.wx.ui.mc.biome.biome_define import ( diff --git a/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/__init__.py b/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/__init__.py new file mode 100644 index 00000000..2501953c --- /dev/null +++ b/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/__init__.py @@ -0,0 +1,2 @@ +from .biome_identifier_select import BiomeIdentifierSelect +from .events import BiomeIDChangeEvent, EVT_BIOME_ID_CHANGE diff --git a/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/biome_identifier_select.py b/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/biome_identifier_select.py new file mode 100644 index 00000000..648c039a --- /dev/null +++ b/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/biome_identifier_select.py @@ -0,0 +1,108 @@ +from typing import Dict, Any +import wx + +from amulet_map_editor.api.wx.ui.mc.base.base_identifier_select import ( + BaseIdentifierSelect, +) +from amulet_map_editor.api.wx.ui.mc.base.api.biome import MCBiomeIdentifier +from amulet_map_editor.api.wx.ui.mc.biome.identifier_select.events import ( + BiomeIDChangeEvent, + EVT_BIOME_ID_CHANGE, +) + + +class BiomeIdentifierSelect(BaseIdentifierSelect, MCBiomeIdentifier): + """ + A UI consisting of a namespace choice, biome name search box and list of biome names. + """ + + @property + def type_name(self) -> str: + return "Biome" + + def _init_state(self, state: Dict[str, Any]): + MCBiomeIdentifier.__init__(self, **state) + + def _populate_namespace(self): + version = self._translation_manager.get_version( + self.platform, self.version_number + ) + namespaces = list( + set( + [biome_id.split(":", 1)[0] for biome_id in version.biome.biome_ids] + ) + ) + self._namespace_combo.Set(namespaces) + + def _populate_base_name(self): + version = self._translation_manager.get_version( + self.platform, self.version_number + ) + namespace = f"{self.namespace}:" + namespace_size = len(namespace) + + self._base_names = [ + biome_id[namespace_size:] + for biome_id in version.biome.biome_ids + if biome_id.startswith(namespace) + ] + self._base_name_list_box.SetItems(self._base_names) + + def _post_event( + self, + old_namespace: str, + old_base_name: str, + new_namespace: str, + new_base_name: str, + ): + wx.PostEvent( + self, + BiomeIDChangeEvent( + new_namespace, + new_base_name, + old_namespace, + old_base_name, + ), + ) + + +def demo(): + """ + Show a demo version of the UI. + An app instance must be created first. + """ + import PyMCTranslate + + translation_manager = PyMCTranslate.new_translation_manager() + dialog = wx.Dialog( + None, + title="BiomeIdentifierSelect", + style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT, + ) + sizer = wx.BoxSizer() + dialog.SetSizer(sizer) + widget = BiomeIdentifierSelect(dialog, translation_manager, "java", (1, 16, 0), False) + + def on_change(evt: BiomeIDChangeEvent): + print(evt.old_namespace, evt.old_base_name, evt.namespace, evt.base_name) + + widget.Bind(EVT_BIOME_ID_CHANGE, on_change) + sizer.Add( + widget, + 1, + wx.ALL | wx.EXPAND, + 5, + ) + dialog.Show() + dialog.Fit() + dialog.Bind(wx.EVT_CLOSE, lambda evt: dialog.Destroy()) + + +if __name__ == "__main__": + + def main(): + app = wx.App() + demo() + app.MainLoop() + + main() diff --git a/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/events.py b/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/events.py new file mode 100644 index 00000000..0d69c4c6 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/events.py @@ -0,0 +1,21 @@ +import wx +from ...base.base_identifier_select import BaseIDChangeEvent + +_BiomeIDChangeEventType = wx.NewEventType() +EVT_BIOME_ID_CHANGE = wx.PyEventBinder(_BiomeIDChangeEventType) + + +class BiomeIDChangeEvent(BaseIDChangeEvent): + """ + Run when the biome resource identifier changes. + """ + + def __init__( + self, + namespace: str, + base_name: str, + old_namespace: str, + old_base_name: str, + ): + super().__init__(namespace, base_name, old_namespace, old_base_name) + self.SetEventType(_BiomeIDChangeEventType) diff --git a/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py b/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py index c2d53c76..d3899105 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py +++ b/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py @@ -1,8 +1,6 @@ from typing import Dict, Any import wx -import PyMCTranslate -from amulet.api.data_types import VersionNumberTuple from amulet_map_editor.api.wx.ui.mc.base.base_identifier_select import ( BaseIdentifierSelect, ) @@ -22,28 +20,6 @@ class BlockIdentifierSelect(BaseIdentifierSelect, MCBlockIdentifier): def type_name(self) -> str: return "Block" - def __init__( - self, - parent: wx.Window, - translation_manager: PyMCTranslate.TranslationManager, - platform: str, - version_number: VersionNumberTuple, - force_blockstate: bool, - namespace: str = None, - block_name: str = None, - show_pick_block: bool = False, - ): - super().__init__( - parent, - translation_manager, - platform, - version_number, - force_blockstate, - namespace, - block_name, - show_pick_block, - ) - def _init_state(self, state: Dict[str, Any]): MCBlockIdentifier.__init__(self, **state) @@ -85,6 +61,8 @@ def demo(): Show a demo version of the UI. An app instance must be created first. """ + import PyMCTranslate + translation_manager = PyMCTranslate.new_translation_manager() dialog = wx.Dialog( None, diff --git a/amulet_map_editor/api/wx/ui/mc/block/identifier_select/events.py b/amulet_map_editor/api/wx/ui/mc/block/identifier_select/events.py index 48339726..42f938b6 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/identifier_select/events.py +++ b/amulet_map_editor/api/wx/ui/mc/block/identifier_select/events.py @@ -1,10 +1,11 @@ import wx +from ...base.base_identifier_select import BaseIDChangeEvent _BlockIDChangeEventType = wx.NewEventType() EVT_BLOCK_ID_CHANGE = wx.PyEventBinder(_BlockIDChangeEventType) -class BlockIDChangeEvent(wx.PyEvent): +class BlockIDChangeEvent(BaseIDChangeEvent): """ Run when the block resource identifier changes. """ @@ -16,24 +17,5 @@ def __init__( old_namespace: str, old_base_name: str, ): - wx.PyEvent.__init__(self, eventType=_BlockIDChangeEventType) - self._namespace = namespace - self._base_name = base_name - self._old_namespace = old_namespace - self._old_base_name = old_base_name - - @property - def namespace(self) -> str: - return self._namespace - - @property - def base_name(self) -> str: - return self._base_name - - @property - def old_namespace(self) -> str: - return self._old_namespace - - @property - def old_base_name(self) -> str: - return self._old_base_name + super().__init__(namespace, base_name, old_namespace, old_base_name) + self.SetEventType(_BlockIDChangeEventType) From a3005e23541106c05d05ef012e4b59b0535d168b Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Mon, 12 Jul 2021 16:25:41 +0100 Subject: [PATCH 051/139] Reformatted --- .../api/wx/ui/mc/base/api/biome.py | 20 +++++++++++++------ .../biome_identifier_select.py | 8 ++++---- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/biome.py b/amulet_map_editor/api/wx/ui/mc/base/api/biome.py index ecca57cb..f1a9a602 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/biome.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/biome.py @@ -10,16 +10,24 @@ class MCBiomeIdentifierAPI(BaseMCResourceIDAPI): class MCBiomeIdentifier(BaseMCResourceID, MCBiomeIdentifierAPI): def set_namespace(self, namespace: Optional[str]): if namespace is None: - self._namespace = self._translation_manager.get_version( - self.platform, self.version_number - ).biome.biome_ids[0].split(":", 1)[0] + self._namespace = ( + self._translation_manager.get_version( + self.platform, self.version_number + ) + .biome.biome_ids[0] + .split(":", 1)[0] + ) else: self._namespace = str(namespace) def set_base_name(self, base_name: Optional[str]): if base_name is None: - self._base_name = self._translation_manager.get_version( - self.platform, self.version_number - ).biome.biome_ids[0].split(":", 1)[-1] + self._base_name = ( + self._translation_manager.get_version( + self.platform, self.version_number + ) + .biome.biome_ids[0] + .split(":", 1)[-1] + ) else: self._base_name = str(base_name) diff --git a/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/biome_identifier_select.py b/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/biome_identifier_select.py index 648c039a..8d0b0328 100644 --- a/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/biome_identifier_select.py +++ b/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/biome_identifier_select.py @@ -28,9 +28,7 @@ def _populate_namespace(self): self.platform, self.version_number ) namespaces = list( - set( - [biome_id.split(":", 1)[0] for biome_id in version.biome.biome_ids] - ) + set([biome_id.split(":", 1)[0] for biome_id in version.biome.biome_ids]) ) self._namespace_combo.Set(namespaces) @@ -81,7 +79,9 @@ def demo(): ) sizer = wx.BoxSizer() dialog.SetSizer(sizer) - widget = BiomeIdentifierSelect(dialog, translation_manager, "java", (1, 16, 0), False) + widget = BiomeIdentifierSelect( + dialog, translation_manager, "java", (1, 16, 0), False + ) def on_change(evt: BiomeIDChangeEvent): print(evt.old_namespace, evt.old_base_name, evt.namespace, evt.base_name) From 8e5ae2c13a561659ff7dd7bc9a38a6f9eb5cce89 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Mon, 12 Jul 2021 17:09:09 +0100 Subject: [PATCH 052/139] Added missing update method to identifier select --- .../api/wx/ui/mc/base/api/platform.py | 18 +++++++++--------- .../base_identifier_select.py | 7 +++++++ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/platform.py b/amulet_map_editor/api/wx/ui/mc/base/api/platform.py index 36bf15e5..ccd1d8ae 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/platform.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/platform.py @@ -4,15 +4,6 @@ class BaseMCPlatformAPI: - def update(self) -> bool: - """ - Update the from the internal state. - No events should be created when calling this method. - - :return: True if data was changed - """ - return False - @property def platform(self) -> PlatformType: """The active platform.""" @@ -70,6 +61,15 @@ def __init__( self._platform = None self.set_platform(platform) + def update(self) -> bool: + """ + Update the from the internal state. + No events should be created when calling this method. + + :return: True if data was changed + """ + raise NotImplementedError + @property def platform(self) -> PlatformType: return self._platform diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py index a63e1c9c..546b3bba 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py +++ b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py @@ -213,3 +213,10 @@ def _on_change(self, old_namespace=None): new_base_name = new_base_name[1:-1] self.set_base_name(new_base_name) self._post_event(old_namespace, old_base_name, self.namespace, new_base_name) + + def update(self) -> bool: + self._populate_namespace() + self._push_namespace() + self._populate_base_name() + self._push_base_name() + return True From da48d8ccb7640ad03b94ad4aec751a2a1dbb08ef Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Mon, 12 Jul 2021 17:15:01 +0100 Subject: [PATCH 053/139] Readded Base to some of the state classes These classes require another class to subclass them --- amulet_map_editor/api/wx/ui/mc/base/api/biome.py | 4 ++-- amulet_map_editor/api/wx/ui/mc/base/api/block/__init__.py | 2 +- amulet_map_editor/api/wx/ui/mc/base/api/block/identifier.py | 4 ++-- amulet_map_editor/api/wx/ui/mc/base/api/block/normal.py | 4 ++-- amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py | 4 ++-- .../mc/biome/identifier_select/biome_identifier_select.py | 6 +++--- amulet_map_editor/api/wx/ui/mc/block/define/button/base.py | 4 ++-- .../mc/block/identifier_select/block_identifier_select.py | 6 +++--- 8 files changed, 17 insertions(+), 17 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/biome.py b/amulet_map_editor/api/wx/ui/mc/base/api/biome.py index f1a9a602..4c64a175 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/biome.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/biome.py @@ -3,11 +3,11 @@ from .resource_id import BaseMCResourceIDAPI, BaseMCResourceID -class MCBiomeIdentifierAPI(BaseMCResourceIDAPI): +class BaseMCBiomeIdentifierAPI(BaseMCResourceIDAPI): pass -class MCBiomeIdentifier(BaseMCResourceID, MCBiomeIdentifierAPI): +class BaseMCBiomeIdentifier(BaseMCResourceID, BaseMCBiomeIdentifierAPI): def set_namespace(self, namespace: Optional[str]): if namespace is None: self._namespace = ( diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/block/__init__.py b/amulet_map_editor/api/wx/ui/mc/base/api/block/__init__.py index 0c6d531b..e985c4df 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/block/__init__.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/block/__init__.py @@ -1,3 +1,3 @@ -from .identifier import MCBlockIdentifierAPI, MCBlockIdentifier +from .identifier import BaseMCBlockIdentifierAPI, BaseMCBlockIdentifier from .normal import NormalMCBlockAPI from .wildcard import WildcardMCBlockAPI diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/block/identifier.py b/amulet_map_editor/api/wx/ui/mc/base/api/block/identifier.py index 4af2079b..137238e5 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/block/identifier.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/block/identifier.py @@ -3,11 +3,11 @@ from ..resource_id import BaseMCResourceIDAPI, BaseMCResourceID -class MCBlockIdentifierAPI(BaseMCResourceIDAPI): +class BaseMCBlockIdentifierAPI(BaseMCResourceIDAPI): pass -class MCBlockIdentifier(BaseMCResourceID, MCBlockIdentifierAPI): +class BaseMCBlockIdentifier(BaseMCResourceID, BaseMCBlockIdentifierAPI): def set_namespace(self, namespace: Optional[str]): if namespace is None: self._namespace = self._translation_manager.get_version( diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/block/normal.py b/amulet_map_editor/api/wx/ui/mc/base/api/block/normal.py index d3bbd164..efa4caff 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/block/normal.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/block/normal.py @@ -3,10 +3,10 @@ from amulet.api.block import PropertyType, Block from amulet.api.block_entity import BlockEntity -from .identifier import MCBlockIdentifierAPI +from .identifier import BaseMCBlockIdentifierAPI -class NormalMCBlockAPI(MCBlockIdentifierAPI): +class NormalMCBlockAPI(BaseMCBlockIdentifierAPI): @property def properties(self) -> PropertyType: raise NotImplementedError diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py b/amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py index b10e2b49..cbadc3f0 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py @@ -1,9 +1,9 @@ from amulet.api.block import PropertyTypeMultiple -from .identifier import MCBlockIdentifierAPI +from .identifier import BaseMCBlockIdentifierAPI -class WildcardMCBlockAPI(MCBlockIdentifierAPI): +class WildcardMCBlockAPI(BaseMCBlockIdentifierAPI): @property def all_properties(self) -> PropertyTypeMultiple: """The values that exist for every property.""" diff --git a/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/biome_identifier_select.py b/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/biome_identifier_select.py index 8d0b0328..7e1f2c20 100644 --- a/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/biome_identifier_select.py +++ b/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/biome_identifier_select.py @@ -4,14 +4,14 @@ from amulet_map_editor.api.wx.ui.mc.base.base_identifier_select import ( BaseIdentifierSelect, ) -from amulet_map_editor.api.wx.ui.mc.base.api.biome import MCBiomeIdentifier +from amulet_map_editor.api.wx.ui.mc.base.api.biome import BaseMCBiomeIdentifier from amulet_map_editor.api.wx.ui.mc.biome.identifier_select.events import ( BiomeIDChangeEvent, EVT_BIOME_ID_CHANGE, ) -class BiomeIdentifierSelect(BaseIdentifierSelect, MCBiomeIdentifier): +class BiomeIdentifierSelect(BaseIdentifierSelect, BaseMCBiomeIdentifier): """ A UI consisting of a namespace choice, biome name search box and list of biome names. """ @@ -21,7 +21,7 @@ def type_name(self) -> str: return "Biome" def _init_state(self, state: Dict[str, Any]): - MCBiomeIdentifier.__init__(self, **state) + BaseMCBiomeIdentifier.__init__(self, **state) def _populate_namespace(self): version = self._translation_manager.get_version( diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py index e7d527ad..267e43c4 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py @@ -2,11 +2,11 @@ from typing import Optional from amulet.api.data_types import PlatformType, VersionNumberTuple -from amulet_map_editor.api.wx.ui.mc.base import MCBlockIdentifierAPI +from amulet_map_editor.api.wx.ui.mc.base import BaseMCBlockIdentifierAPI from amulet_map_editor.api.wx.ui.mc.block.define import BaseBlockDefine -class BaseBlockDefineButton(wx.Button, MCBlockIdentifierAPI): +class BaseBlockDefineButton(wx.Button, BaseMCBlockIdentifierAPI): def __init__( self, parent: wx.Window, diff --git a/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py b/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py index d3899105..dd64ba60 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py +++ b/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py @@ -4,14 +4,14 @@ from amulet_map_editor.api.wx.ui.mc.base.base_identifier_select import ( BaseIdentifierSelect, ) -from amulet_map_editor.api.wx.ui.mc.base.api.block import MCBlockIdentifier +from amulet_map_editor.api.wx.ui.mc.base.api.block import BaseMCBlockIdentifier from amulet_map_editor.api.wx.ui.mc.block.identifier_select.events import ( BlockIDChangeEvent, EVT_BLOCK_ID_CHANGE, ) -class BlockIdentifierSelect(BaseIdentifierSelect, MCBlockIdentifier): +class BlockIdentifierSelect(BaseIdentifierSelect, BaseMCBlockIdentifier): """ A UI consisting of a namespace choice, block name search box and list of block names. """ @@ -21,7 +21,7 @@ def type_name(self) -> str: return "Block" def _init_state(self, state: Dict[str, Any]): - MCBlockIdentifier.__init__(self, **state) + BaseMCBlockIdentifier.__init__(self, **state) def _populate_namespace(self): version = self._translation_manager.get_version( From 3141011ae7e7cb9d1bb781445d28d071860297f5 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Sat, 24 Jul 2021 10:39:03 +0100 Subject: [PATCH 054/139] Renamed update method to push to avoid confusion with the wx Update method --- amulet_map_editor/api/wx/ui/mc/base/api/platform.py | 6 +++--- amulet_map_editor/api/wx/ui/mc/base/api/resource_id.py | 4 ++-- amulet_map_editor/api/wx/ui/mc/base/api/version.py | 4 ++-- .../base/base_identifier_select/base_identifier_select.py | 2 +- amulet_map_editor/api/wx/ui/mc/version/platform_select.py | 2 +- amulet_map_editor/api/wx/ui/mc/version/version_select.py | 8 ++++---- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/platform.py b/amulet_map_editor/api/wx/ui/mc/base/api/platform.py index ccd1d8ae..9fd9cc6e 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/platform.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/platform.py @@ -61,9 +61,9 @@ def __init__( self._platform = None self.set_platform(platform) - def update(self) -> bool: + def push(self) -> bool: """ - Update the from the internal state. + Push the internal state to the UI. No events should be created when calling this method. :return: True if data was changed @@ -77,7 +77,7 @@ def platform(self) -> PlatformType: @platform.setter def platform(self, platform: PlatformType): self.set_platform(platform) - self.update() + self.push() def set_platform(self, platform: Optional[PlatformType]): if platform is not None and platform in self._translation_manager.platforms(): diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/resource_id.py b/amulet_map_editor/api/wx/ui/mc/base/api/resource_id.py index d24d2437..d300441e 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/resource_id.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/resource_id.py @@ -84,7 +84,7 @@ def namespace(self) -> str: @namespace.setter def namespace(self, namespace: str): self.set_namespace(namespace) - self.update() + self.push() def set_namespace(self, namespace: Optional[str]): raise NotImplementedError @@ -96,7 +96,7 @@ def base_name(self) -> str: @base_name.setter def base_name(self, base_name: str): self.set_base_name(base_name) - self.update() + self.push() def set_base_name(self, base_name: Optional[str]): raise NotImplementedError diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/version.py b/amulet_map_editor/api/wx/ui/mc/base/api/version.py index 067c048f..d28fa425 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/version.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/version.py @@ -84,7 +84,7 @@ def version_number(self) -> VersionNumberTuple: @version_number.setter def version_number(self, version_number: VersionNumberTuple): self.set_version_number(version_number) - self.update() + self.push() def set_version_number(self, version_number: Optional[VersionNumberTuple]): v = None @@ -112,7 +112,7 @@ def force_blockstate(self) -> bool: @force_blockstate.setter def force_blockstate(self, force_blockstate: bool): self.set_force_blockstate(force_blockstate) - self.update() + self.push() def set_force_blockstate(self, force_blockstate: Optional[bool]): self._force_blockstate = bool(force_blockstate) diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py index 546b3bba..0692334c 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py +++ b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py @@ -214,7 +214,7 @@ def _on_change(self, old_namespace=None): self.set_base_name(new_base_name) self._post_event(old_namespace, old_base_name, self.namespace, new_base_name) - def update(self) -> bool: + def push(self) -> bool: self._populate_namespace() self._push_namespace() self._populate_base_name() diff --git a/amulet_map_editor/api/wx/ui/mc/version/platform_select.py b/amulet_map_editor/api/wx/ui/mc/version/platform_select.py index cf7c3d4b..2ce5da38 100644 --- a/amulet_map_editor/api/wx/ui/mc/version/platform_select.py +++ b/amulet_map_editor/api/wx/ui/mc/version/platform_select.py @@ -113,7 +113,7 @@ def _on_platform_change(self, evt): self.set_platform(new_platform) wx.PostEvent(self, PlatformChangeEvent(new_platform, old_platform)) - def update(self): + def push(self): if self.platform != self._platform_choice.GetCurrentString(): self._push_platform() return True diff --git a/amulet_map_editor/api/wx/ui/mc/version/version_select.py b/amulet_map_editor/api/wx/ui/mc/version/version_select.py index 7fca3f35..ae61cdc3 100644 --- a/amulet_map_editor/api/wx/ui/mc/version/version_select.py +++ b/amulet_map_editor/api/wx/ui/mc/version/version_select.py @@ -174,8 +174,8 @@ def _on_version_number_change(self, evt): def _on_blockstate_change(self, evt): self._on_change(3) - def update(self) -> bool: - update = super().update() + def push(self) -> bool: + update = super().push() # If the user set these out of order they may be messed up. # This should fix that. self.set_version_number(self.version_number) @@ -236,7 +236,7 @@ def set_version( obj.set_platform(platform) obj.set_version_number(version) obj.set_force_blockstate(force_blockstate) - obj.update() + obj.push() interval = 1_000 @@ -254,7 +254,7 @@ def set_version2( obj.set_force_blockstate(force_blockstate) obj.set_version_number(version) obj.set_platform(platform) - obj.update() + obj.push() wx.CallLater(interval * 5, set_version2, select, "java", (1, 15, 0), False) wx.CallLater(interval * 6, set_version2, select, "java", (1, 17, 0), False) From 750f4d3483cfaf4e71f72ea8c9465cc0dd36f7d9 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Sat, 24 Jul 2021 10:39:44 +0100 Subject: [PATCH 055/139] Converted base_define back to a module --- .../api/wx/ui/mc/base/base_define.py | 88 +++++++++++++++ .../api/wx/ui/mc/base/base_define/__init__.py | 1 - .../wx/ui/mc/base/base_define/base_define.py | 100 ------------------ 3 files changed, 88 insertions(+), 101 deletions(-) create mode 100644 amulet_map_editor/api/wx/ui/mc/base/base_define.py delete mode 100644 amulet_map_editor/api/wx/ui/mc/base/base_define/__init__.py delete mode 100644 amulet_map_editor/api/wx/ui/mc/base/base_define/base_define.py diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_define.py b/amulet_map_editor/api/wx/ui/mc/base/base_define.py new file mode 100644 index 00000000..872a218e --- /dev/null +++ b/amulet_map_editor/api/wx/ui/mc/base/base_define.py @@ -0,0 +1,88 @@ +import wx +import wx.lib.scrolledpanel +from typing import Optional, Dict, Any + +import PyMCTranslate +from amulet.api.data_types import VersionNumberTuple, PlatformType + +from amulet_map_editor.api.wx.ui.mc.base.base_identifier_select import ( + BaseIdentifierSelect, +) +from amulet_map_editor.api.wx.ui.mc import version as mc_version +from amulet_map_editor.api.wx.ui.mc.base.api import BaseMCVersion + + +class BaseDefine(wx.Panel, BaseMCVersion): + def __init__( + self, + parent, + translation_manager: PyMCTranslate.TranslationManager, + orientation=wx.VERTICAL, + platform: PlatformType = None, + version_number: VersionNumberTuple = None, + force_blockstate: bool = False, + state: Dict[str, Any] = None, + style: Dict[str, Any] = None, + **kwargs, + ): + state = state or {} + state.setdefault("translation_manager", translation_manager) + state.setdefault("platform", platform) + state.setdefault("version_number", version_number) + state.setdefault("force_blockstate", force_blockstate) + # This is the init call to the class that stores the internal state of the data. + # This needs to be at the start to ensure that the internal state is set up before anything else is done. + # It is not a direct call to init so that subclasses of this class can substitute in which state subclass is used. + self._init_state(state) + wx.Panel.__init__(self, parent) + + self._orientation = orientation + self._sizer = wx.BoxSizer(orientation) + self.SetSizer(self._sizer) + + self._top_sizer = wx.BoxSizer(wx.VERTICAL) + if orientation == wx.HORIZONTAL: + self._sizer.Add(self._top_sizer, 1, wx.EXPAND) + else: + self._sizer.Add(self._top_sizer, 2, wx.EXPAND) + + self._version_picker = mc_version.VersionSelect( + self, + translation_manager, + platform, + version_number, + force_blockstate, + style=style, + **kwargs, + ) + self._top_sizer.Add(self._version_picker, 0, wx.EXPAND) + self._version_picker.Bind( + mc_version.EVT_VERSION_CHANGE, self._on_version_change + ) + + self._picker: Optional[BaseIdentifierSelect] = None + + self.Layout() + + def _init_state(self, state: Dict[str, Any]): + """ + Call the init method of the state manager. + This is here so that nested classes do not have to init the state managers multiple times. + """ + BaseMCVersion.__init__(self, **state) + + def _on_version_change(self, evt: mc_version.VersionChangeEvent): + raise NotImplementedError + + def push(self) -> bool: + if ( + self.platform != self._version_picker.platform + or self.version_number != self._version_picker.version_number + or self.force_blockstate != self._version_picker.force_blockstate + ): + self._version_picker.platform = self.platform + self._version_picker.version_number = self.version_number + self._version_picker.force_blockstate = self.force_blockstate + self._version_picker.push() + return True + return False diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_define/__init__.py b/amulet_map_editor/api/wx/ui/mc/base/base_define/__init__.py deleted file mode 100644 index 42b06e1a..00000000 --- a/amulet_map_editor/api/wx/ui/mc/base/base_define/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .base_define import BaseDefine diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_define/base_define.py b/amulet_map_editor/api/wx/ui/mc/base/base_define/base_define.py deleted file mode 100644 index 411abf55..00000000 --- a/amulet_map_editor/api/wx/ui/mc/base/base_define/base_define.py +++ /dev/null @@ -1,100 +0,0 @@ -import wx -import wx.lib.scrolledpanel -from typing import Tuple, Type - -import PyMCTranslate -from amulet.api.data_types import VersionNumberTuple, PlatformType - -from amulet_map_editor.api.wx.ui.mc.base.base_select import EVT_ITEM_CHANGE, BaseSelect -from amulet_map_editor.api.wx.ui.mc.version import ( - VersionSelect, - EVT_VERSION_CHANGE, -) -from amulet_map_editor.api.wx.ui.mc.base.api import BaseMCVersionAPI - - -class BaseDefine(wx.Panel, BaseMCVersionAPI): - def __init__( - self, - parent, - translation_manager: PyMCTranslate.TranslationManager, - select_type: Type[BaseSelect], - orientation=wx.VERTICAL, - platform: str = None, - version_number: Tuple[int, int, int] = None, - namespace: str = None, - default_name: str = None, - show_pick: bool = False, - **kwargs, - ): - super().__init__(parent) - - self._translation_manager = translation_manager - self._orientation = orientation - self._sizer = wx.BoxSizer(orientation) - left_sizer = wx.BoxSizer(wx.VERTICAL) - if orientation == wx.HORIZONTAL: - self._sizer.Add(left_sizer, 1, wx.EXPAND) - else: - self._sizer.Add(left_sizer, 2, wx.EXPAND) - - self._version_picker = VersionSelect( - self, - translation_manager, - platform, - version_number, - **kwargs, - ) - left_sizer.Add(self._version_picker, 0, wx.EXPAND) - self._version_picker.Bind(EVT_VERSION_CHANGE, self._on_version_change) - - self._picker = select_type( - self, - translation_manager, - self._version_picker.platform, - self._version_picker.version_number, - self._version_picker.force_blockstate, - namespace, - default_name, - show_pick, - ) - left_sizer.Add(self._picker, 1, wx.EXPAND | wx.TOP, 5) - self._picker.Bind(EVT_ITEM_CHANGE, self._on_picker_change) - - self.SetSizer(self._sizer) - self.Layout() - - def _on_picker_change(self, evt): - raise NotImplementedError("This method should be overridden in child classes.") - - def _on_version_change(self, evt): - self._picker.version = ( - evt.platform, - evt.version_number, - evt.force_blockstate, - ) - evt.Skip() - - @property - def platform(self) -> PlatformType: - return self._version_picker.platform - - @platform.setter - def platform(self, platform: PlatformType): - self._version_picker.platform = platform - - @property - def version_number(self) -> VersionNumberTuple: - return self._version_picker.version_number - - @version_number.setter - def version_number(self, version_number: VersionNumberTuple): - self._version_picker.version_number = version_number - - @property - def namespace(self) -> str: - return self._picker.namespace - - @namespace.setter - def namespace(self, namespace: str): - self._picker.namespace = namespace From 24cfca54b4e1e0eb09ccf5ca660d028f36fe91c5 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Sat, 24 Jul 2021 10:41:27 +0100 Subject: [PATCH 056/139] Fixed the comboxbox set method generating events Only run the namespace change logic if the namespace actually changed --- .../base_identifier_select.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py index 0692334c..a37fe341 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py +++ b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py @@ -50,12 +50,14 @@ def __init__( self._sizer.Add(sizer, 0, wx.EXPAND | wx.ALL, 5) text = wx.StaticText(self, label="Namespace:", style=wx.ALIGN_CENTER) sizer.Add(text, 1, wx.ALIGN_CENTER_VERTICAL) - self._namespace_combo = wx.ComboBox(self) + self._namespace_combo = wx.ComboBox(self, style=wx.TE_PROCESS_ENTER) sizer.Add(self._namespace_combo, 2) self._populate_namespace() self._push_namespace() - self._namespace_combo.Bind(wx.EVT_TEXT, self._on_namespace_change) + self._namespace_combo.Bind(wx.EVT_COMBOBOX, self._on_namespace_change) + self._namespace_combo.Bind(wx.EVT_TEXT_ENTER, self._on_namespace_change) + self._namespace_combo.Bind(wx.EVT_KILL_FOCUS, self._on_namespace_change) sizer = wx.BoxSizer(wx.VERTICAL) self._sizer.Add(sizer, 1, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, 5) @@ -186,12 +188,15 @@ def _on_namespace_change(self, evt): old_namespace = self.namespace new_namespace = self._namespace_combo.GetValue() - self.set_namespace(new_namespace) + if new_namespace != old_namespace: + self.set_namespace(new_namespace) - self._populate_base_name() - self._update_from_search() + self._populate_base_name() + self._update_from_search() - self._on_change(old_namespace) + self._on_change(old_namespace) + if isinstance(evt, wx.FocusEvent): + evt.Skip() def _on_search_change(self, evt): if self._update_from_search(): From 3f6bb02fe097fe3ab20b60286a7661b5216a99d3 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Sat, 24 Jul 2021 11:36:57 +0100 Subject: [PATCH 057/139] Fixed setting through property rather than method --- amulet_map_editor/api/wx/ui/mc/base/base_define.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_define.py b/amulet_map_editor/api/wx/ui/mc/base/base_define.py index 872a218e..21bfb9f5 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/base_define.py +++ b/amulet_map_editor/api/wx/ui/mc/base/base_define.py @@ -75,14 +75,17 @@ def _on_version_change(self, evt: mc_version.VersionChangeEvent): raise NotImplementedError def push(self) -> bool: + self.set_platform(self.platform) + self.set_version_number(self.version_number) + self.set_force_blockstate(self.force_blockstate) if ( self.platform != self._version_picker.platform or self.version_number != self._version_picker.version_number or self.force_blockstate != self._version_picker.force_blockstate ): - self._version_picker.platform = self.platform - self._version_picker.version_number = self.version_number - self._version_picker.force_blockstate = self.force_blockstate + self._version_picker.set_platform(self.platform) + self._version_picker.set_version_number(self.version_number) + self._version_picker.set_force_blockstate(self.force_blockstate) self._version_picker.push() return True return False From 0ac8a2e5ddf747564f08db1c9916bb4edc4fbf36 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Sat, 24 Jul 2021 11:37:38 +0100 Subject: [PATCH 058/139] Fixed the populate base name method not using the search entry --- .../wx/ui/mc/biome/identifier_select/biome_identifier_select.py | 1 + 1 file changed, 1 insertion(+) diff --git a/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/biome_identifier_select.py b/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/biome_identifier_select.py index 7e1f2c20..ccdc1052 100644 --- a/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/biome_identifier_select.py +++ b/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/biome_identifier_select.py @@ -45,6 +45,7 @@ def _populate_base_name(self): if biome_id.startswith(namespace) ] self._base_name_list_box.SetItems(self._base_names) + self._update_from_search() def _post_event( self, From 859423f31db64aa5cd0c29d9b58ccb437d960284 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Sat, 24 Jul 2021 11:38:49 +0100 Subject: [PATCH 059/139] Rewritten biome define --- .../api/wx/ui/mc/biome/biome_define.py | 133 ++++++++++++------ 1 file changed, 91 insertions(+), 42 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py b/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py index f812a4ba..17d5dc3a 100644 --- a/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py +++ b/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py @@ -1,13 +1,18 @@ -from typing import Tuple +from typing import Tuple, Dict, Any import PyMCTranslate import wx from amulet_map_editor.api.wx.ui.mc.base.base_define import BaseDefine -from amulet_map_editor.api.wx.ui.mc.biome.biome_select import BiomeSelect +from amulet_map_editor.api.wx.ui.mc.base.api.biome import BaseMCBiomeIdentifier +from amulet_map_editor.api.wx.ui.mc.biome.identifier_select.biome_identifier_select import ( + BiomeIdentifierSelect, +) +from amulet_map_editor.api.wx.ui.mc.biome.identifier_select.events import BiomeIDChangeEvent, EVT_BIOME_ID_CHANGE +from amulet_map_editor.api.wx.ui.mc.version.events import VersionChangeEvent -class BiomeDefine(BaseDefine): +class BiomeDefine(BaseDefine, BaseMCBiomeIdentifier): """ A UI that merges a version select widget with a biome select widget. """ @@ -20,56 +25,100 @@ def __init__( platform: str = None, version_number: Tuple[int, int, int] = None, namespace: str = None, - biome_name: str = None, + base_name: str = None, show_pick_biome: bool = False, - **kwargs, + state: Dict[str, Any] = None, ): + state = state or {} + state.setdefault("namespace", namespace) + state.setdefault("base_name", base_name) super().__init__( parent, translation_manager, - BiomeSelect, orientation, platform, version_number, + False, + state=state, + show_force_blockstate=False + ) + self._picker = BiomeIdentifierSelect( + self, + translation_manager, + self.platform, + self.version_number, + self.force_blockstate, namespace, - default_name=biome_name, - show_pick=show_pick_biome, - show_force_blockstate=False, - **kwargs, + base_name, + show_pick_biome, + ) + self._picker.Bind(EVT_BIOME_ID_CHANGE, self._on_biome_id_change) + self._top_sizer.Add(self._picker, 1, wx.EXPAND | wx.TOP, 5) + + def _init_state(self, state: Dict[str, Any]): + BaseMCBiomeIdentifier.__init__(self, **state) + + def _on_version_change(self, evt: VersionChangeEvent): + old_platform, old_version = self.platform, self.version_number + old_namespace, old_base_name = self.namespace, self.base_name + + self.set_platform(evt.platform) + self.set_version_number(evt.version_number) + self.set_force_blockstate(evt.force_blockstate) + + universal_biome = self._translation_manager.get_version(old_platform, old_version).biome.to_universal(f"{self.namespace}:{self.base_name}") + new_biome = self._translation_manager.get_version(self.platform, self.version_number).biome.from_universal(universal_biome) + namespace, base_name = new_biome.split(":", 1) + + self.set_namespace(namespace) + self.set_base_name(base_name) + + self._picker.set_platform(self.platform) + self._picker.set_version_number(self.version_number) + self._picker.set_force_blockstate(self.force_blockstate) + self._picker.set_namespace(self.namespace) + self._picker.set_base_name(self.base_name) + self._picker.push() + + wx.PostEvent( + self, + BiomeIDChangeEvent( + self.namespace, + self.base_name, + old_namespace, + old_base_name, + ), + ) + + def _on_biome_id_change(self, evt: BiomeIDChangeEvent): + self.set_namespace(evt.namespace) + self.set_base_name(evt.base_name) + wx.PostEvent( + self, + BiomeIDChangeEvent( + evt.namespace, + evt.base_name, + evt.old_namespace, + evt.old_base_name, + ), ) - def _on_picker_change(self, evt): - evt.Skip() - - @property - def biome_name(self) -> str: - return self._picker.name - - @biome_name.setter - def biome_name(self, biome_name: str): - self._picker.name = biome_name - - @property - def biome(self) -> str: - return f"{self.namespace}:{self.biome_name}" - - @biome.setter - def biome(self, biome: str): - namespace, biome_name = biome.split(":") - self._picker.set_namespace(namespace) - self._picker.set_name(biome_name) - - @property - def universal_biome(self) -> str: - return self._translation_manager.get_version( - self.platform, self.version_number - ).biome.to_universal(self.biome) - - @universal_biome.setter - def universal_biome(self, universal_biome: str): - self.biome = self._translation_manager.get_version( - self.platform, self.version_number - ).biome.from_universal(universal_biome) + def push(self) -> bool: + update = super().push() + self.set_namespace(self.namespace) + self.set_base_name(self.base_name) + if update: + # The version changed + self._picker.set_platform(self.platform) + self._picker.set_version_number(self.version_number) + self._picker.set_force_blockstate(self.force_blockstate) + if self.namespace != self._picker.namespace or self.base_name != self._picker.base_name: + update = True + self._picker.set_namespace(self.namespace) + self._picker.set_base_name(self.base_name) + if update: + self._picker.push() + return update def demo(): From cfaa19df188a5df3acc4205c76cd4b8fab1b87b1 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Sat, 24 Jul 2021 11:43:20 +0100 Subject: [PATCH 060/139] If no version is given to the state manager it will use the newest version The version numbers are not always ordered --- amulet_map_editor/api/wx/ui/mc/base/api/version.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/version.py b/amulet_map_editor/api/wx/ui/mc/base/api/version.py index d28fa425..a6a365d7 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/version.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/version.py @@ -101,9 +101,9 @@ def set_version_number(self, version_number: Optional[VersionNumberTuple]): except KeyError: pass if v is None: - self._version_number = self._translation_manager.version_numbers( - self.platform - )[-1] + self._version_number = max( + self._translation_manager.version_numbers(self.platform) + ) @property def force_blockstate(self) -> bool: From 7f00b24b6cb11571427a5fff46ecdd978babdf0d Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Sat, 24 Jul 2021 11:43:32 +0100 Subject: [PATCH 061/139] Reformatted --- .../api/wx/ui/mc/biome/biome_define.py | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py b/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py index 17d5dc3a..5038e3e5 100644 --- a/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py +++ b/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py @@ -8,7 +8,10 @@ from amulet_map_editor.api.wx.ui.mc.biome.identifier_select.biome_identifier_select import ( BiomeIdentifierSelect, ) -from amulet_map_editor.api.wx.ui.mc.biome.identifier_select.events import BiomeIDChangeEvent, EVT_BIOME_ID_CHANGE +from amulet_map_editor.api.wx.ui.mc.biome.identifier_select.events import ( + BiomeIDChangeEvent, + EVT_BIOME_ID_CHANGE, +) from amulet_map_editor.api.wx.ui.mc.version.events import VersionChangeEvent @@ -40,7 +43,7 @@ def __init__( version_number, False, state=state, - show_force_blockstate=False + show_force_blockstate=False, ) self._picker = BiomeIdentifierSelect( self, @@ -66,8 +69,12 @@ def _on_version_change(self, evt: VersionChangeEvent): self.set_version_number(evt.version_number) self.set_force_blockstate(evt.force_blockstate) - universal_biome = self._translation_manager.get_version(old_platform, old_version).biome.to_universal(f"{self.namespace}:{self.base_name}") - new_biome = self._translation_manager.get_version(self.platform, self.version_number).biome.from_universal(universal_biome) + universal_biome = self._translation_manager.get_version( + old_platform, old_version + ).biome.to_universal(f"{self.namespace}:{self.base_name}") + new_biome = self._translation_manager.get_version( + self.platform, self.version_number + ).biome.from_universal(universal_biome) namespace, base_name = new_biome.split(":", 1) self.set_namespace(namespace) @@ -112,7 +119,10 @@ def push(self) -> bool: self._picker.set_platform(self.platform) self._picker.set_version_number(self.version_number) self._picker.set_force_blockstate(self.force_blockstate) - if self.namespace != self._picker.namespace or self.base_name != self._picker.base_name: + if ( + self.namespace != self._picker.namespace + or self.base_name != self._picker.base_name + ): update = True self._picker.set_namespace(self.namespace) self._picker.set_base_name(self.base_name) From 9d5fbe44f0718520f0476f1b4b76f960643772f4 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Sat, 24 Jul 2021 14:00:06 +0100 Subject: [PATCH 062/139] Improved UI updating and added some demo code for biome define --- .../api/wx/ui/mc/biome/biome_define.py | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py b/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py index 5038e3e5..3d6550ef 100644 --- a/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py +++ b/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py @@ -3,6 +3,7 @@ import PyMCTranslate import wx +from amulet.api.data_types import VersionNumberTuple, PlatformType from amulet_map_editor.api.wx.ui.mc.base.base_define import BaseDefine from amulet_map_editor.api.wx.ui.mc.base.api.biome import BaseMCBiomeIdentifier from amulet_map_editor.api.wx.ui.mc.biome.identifier_select.biome_identifier_select import ( @@ -62,6 +63,7 @@ def _init_state(self, state: Dict[str, Any]): BaseMCBiomeIdentifier.__init__(self, **state) def _on_version_change(self, evt: VersionChangeEvent): + self.Freeze() old_platform, old_version = self.platform, self.version_number old_namespace, old_base_name = self.namespace, self.base_name @@ -96,6 +98,7 @@ def _on_version_change(self, evt: VersionChangeEvent): old_base_name, ), ) + self.Thaw() def _on_biome_id_change(self, evt: BiomeIDChangeEvent): self.set_namespace(evt.namespace) @@ -111,6 +114,7 @@ def _on_biome_id_change(self, evt: BiomeIDChangeEvent): ) def push(self) -> bool: + self.Freeze() update = super().push() self.set_namespace(self.namespace) self.set_base_name(self.base_name) @@ -128,6 +132,7 @@ def push(self) -> bool: self._picker.set_base_name(self.base_name) if update: self._picker.push() + self.Thaw() return update @@ -144,8 +149,16 @@ def demo(): ) sizer = wx.BoxSizer() dialog.SetSizer(sizer) + obj = BiomeDefine(dialog, translation_manager, wx.HORIZONTAL) + + def on_biome_change(evt: BiomeIDChangeEvent): + print(obj.platform, obj.version_number, obj.force_blockstate, obj.namespace, obj.base_name) + print(evt.namespace, evt.base_name, evt.old_namespace, evt.old_base_name) + + obj.Bind(EVT_BIOME_ID_CHANGE, on_biome_change) + sizer.Add( - BiomeDefine(dialog, translation_manager, wx.HORIZONTAL), + obj, 1, wx.ALL | wx.EXPAND, 5, @@ -154,6 +167,26 @@ def demo(): dialog.Fit() dialog.Bind(wx.EVT_CLOSE, lambda evt: dialog.Destroy()) + def set( + platform: PlatformType, + version: VersionNumberTuple, + force_blockstate: bool, + namespace: str, + base_name: str, + ): + obj.set_platform(platform) + obj.set_version_number(version) + obj.set_force_blockstate(force_blockstate) + obj.set_namespace(namespace) + obj.set_base_name(base_name) + obj.push() + + interval = 1_000 + wx.CallLater(interval * 1, set, "java", (1, 17, 0), False, "minecraft", "end_highlands") + wx.CallLater(interval * 2, set, "bedrock", (1, 17, 0), False, "minecraft", "birch_forest_hills_mutated") + wx.CallLater(interval * 3, set, "java", (1, 17, 0), False, "minecraft", "random") + wx.CallLater(interval * 4, set, "bedrock", (1, 17, 0), False, "minecraft", "random") + if __name__ == "__main__": From aeab7b8918e017463c80bedeb36b929cf937354c Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Sat, 24 Jul 2021 15:17:12 +0100 Subject: [PATCH 063/139] Fixed some issues with the base identifier select There was previously a crash if you clicked on one of the base names after updating the text. This was because the kill focus event would update the UI but a listbox event would also be generated. The UI now updates as you type the text. This is done by detecting the char event and calling the update logic after it is processed. EVT_TEXT is triggered by the Set method --- .../base_identifier_select.py | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py index a37fe341..b824b265 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py +++ b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py @@ -50,14 +50,15 @@ def __init__( self._sizer.Add(sizer, 0, wx.EXPAND | wx.ALL, 5) text = wx.StaticText(self, label="Namespace:", style=wx.ALIGN_CENTER) sizer.Add(text, 1, wx.ALIGN_CENTER_VERTICAL) - self._namespace_combo = wx.ComboBox(self, style=wx.TE_PROCESS_ENTER) + self._namespace_combo = wx.ComboBox(self) sizer.Add(self._namespace_combo, 2) self._populate_namespace() self._push_namespace() + # This was previously done with EVT_TEXT but that is also triggered by the Set method. + # This is a workaround so that it is only triggered by user input. + self._namespace_combo.Bind(wx.EVT_CHAR, self._on_namespace_char) self._namespace_combo.Bind(wx.EVT_COMBOBOX, self._on_namespace_change) - self._namespace_combo.Bind(wx.EVT_TEXT_ENTER, self._on_namespace_change) - self._namespace_combo.Bind(wx.EVT_KILL_FOCUS, self._on_namespace_change) sizer = wx.BoxSizer(wx.VERTICAL) self._sizer.Add(sizer, 1, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, 5) @@ -185,8 +186,12 @@ def _post_event( raise NotImplementedError def _on_namespace_change(self, evt): - old_namespace = self.namespace + self._handle_namespace_change() + if isinstance(evt, wx.KeyEvent): + evt.Skip() + def _handle_namespace_change(self): + old_namespace = self.namespace new_namespace = self._namespace_combo.GetValue() if new_namespace != old_namespace: self.set_namespace(new_namespace) @@ -195,8 +200,10 @@ def _on_namespace_change(self, evt): self._update_from_search() self._on_change(old_namespace) - if isinstance(evt, wx.FocusEvent): - evt.Skip() + + def _on_namespace_char(self, evt): + wx.CallAfter(self._handle_namespace_change) + evt.Skip() def _on_search_change(self, evt): if self._update_from_search(): @@ -216,8 +223,9 @@ def _on_change(self, old_namespace=None): '"' ): new_base_name = new_base_name[1:-1] - self.set_base_name(new_base_name) - self._post_event(old_namespace, old_base_name, self.namespace, new_base_name) + if old_namespace != self.namespace or old_base_name != new_base_name: + self.set_base_name(new_base_name) + self._post_event(old_namespace, old_base_name, self.namespace, new_base_name) def push(self) -> bool: self._populate_namespace() From a22ca9a8926be4069201c54e9a562c709d7ba5bc Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Sat, 24 Jul 2021 16:41:22 +0100 Subject: [PATCH 064/139] Added another freeze and thaw to the base identifier select --- .../ui/mc/base/base_identifier_select/base_identifier_select.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py index b824b265..9f2f3638 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py +++ b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py @@ -191,6 +191,7 @@ def _on_namespace_change(self, evt): evt.Skip() def _handle_namespace_change(self): + self.Freeze() old_namespace = self.namespace new_namespace = self._namespace_combo.GetValue() if new_namespace != old_namespace: @@ -200,6 +201,7 @@ def _handle_namespace_change(self): self._update_from_search() self._on_change(old_namespace) + self.Thaw() def _on_namespace_char(self, evt): wx.CallAfter(self._handle_namespace_change) From 8d2e45c659ef7be217511e524ac2707c62521b10 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Sun, 25 Jul 2021 11:43:21 +0100 Subject: [PATCH 065/139] Reverted to use of properties for external use The property setter schedules the push method so that the properties are all pushed to the UI when the setting is finished. The push method can still be manually called to push it automatically --- .../api/wx/ui/mc/base/api/biome.py | 4 +- .../api/wx/ui/mc/base/api/block/identifier.py | 4 +- .../api/wx/ui/mc/base/api/platform.py | 26 +++++- .../api/wx/ui/mc/base/api/resource_id.py | 20 ++-- .../api/wx/ui/mc/base/api/version.py | 20 ++-- .../api/wx/ui/mc/base/base_define.py | 14 +-- .../base_identifier_select.py | 8 +- .../api/wx/ui/mc/biome/biome_define.py | 91 +++++++++++++------ .../api/wx/ui/mc/block/define/button/base.py | 16 ++-- .../wx/ui/mc/block/define/button/normal.py | 2 +- .../wx/ui/mc/block/define/widget/normal.py | 6 +- .../api/wx/ui/mc/version/platform_select.py | 2 +- .../api/wx/ui/mc/version/version_select.py | 26 +++--- 13 files changed, 150 insertions(+), 89 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/biome.py b/amulet_map_editor/api/wx/ui/mc/base/api/biome.py index 4c64a175..8e71f922 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/biome.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/biome.py @@ -8,7 +8,7 @@ class BaseMCBiomeIdentifierAPI(BaseMCResourceIDAPI): class BaseMCBiomeIdentifier(BaseMCResourceID, BaseMCBiomeIdentifierAPI): - def set_namespace(self, namespace: Optional[str]): + def _set_namespace(self, namespace: Optional[str]): if namespace is None: self._namespace = ( self._translation_manager.get_version( @@ -20,7 +20,7 @@ def set_namespace(self, namespace: Optional[str]): else: self._namespace = str(namespace) - def set_base_name(self, base_name: Optional[str]): + def _set_base_name(self, base_name: Optional[str]): if base_name is None: self._base_name = ( self._translation_manager.get_version( diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/block/identifier.py b/amulet_map_editor/api/wx/ui/mc/base/api/block/identifier.py index 137238e5..4bcfd9d7 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/block/identifier.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/block/identifier.py @@ -8,7 +8,7 @@ class BaseMCBlockIdentifierAPI(BaseMCResourceIDAPI): class BaseMCBlockIdentifier(BaseMCResourceID, BaseMCBlockIdentifierAPI): - def set_namespace(self, namespace: Optional[str]): + def _set_namespace(self, namespace: Optional[str]): if namespace is None: self._namespace = self._translation_manager.get_version( self.platform, self.version_number @@ -16,7 +16,7 @@ def set_namespace(self, namespace: Optional[str]): else: self._namespace = str(namespace) - def set_base_name(self, base_name: Optional[str]): + def _set_base_name(self, base_name: Optional[str]): if base_name is None: blocks = self._translation_manager.get_version( self.platform, self.version_number diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/platform.py b/amulet_map_editor/api/wx/ui/mc/base/api/platform.py index 9fd9cc6e..90811fee 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/platform.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/platform.py @@ -1,4 +1,5 @@ from typing import Optional +import wx import PyMCTranslate from amulet.api.data_types import PlatformType @@ -20,7 +21,7 @@ def platform(self, platform: PlatformType): """ raise NotImplementedError - def set_platform(self, platform: Optional[PlatformType]): + def _set_platform(self, platform: Optional[PlatformType]): """ Set the active platform. Changes will not propagate. @@ -47,6 +48,7 @@ def __init__( translation_manager: PyMCTranslate.TranslationManager, ): self._translation_manager = translation_manager + self._changed = False class BaseMCPlatform(BaseMC, BaseMCPlatformAPI): @@ -59,7 +61,21 @@ def __init__( ): super().__init__(translation_manager) self._platform = None - self.set_platform(platform) + self._set_platform(platform) + + def _schedule_push(self): + """Schedule the pushing of the internal data to the UI.""" + if isinstance(self, wx.Window): + self._changed = True + + def on_push(): + if self._changed: + self.Freeze() + self.push() + self.Thaw() + self._changed = False + + wx.CallAfter(on_push) def push(self) -> bool: """ @@ -76,10 +92,10 @@ def platform(self) -> PlatformType: @platform.setter def platform(self, platform: PlatformType): - self.set_platform(platform) - self.push() + self._set_platform(platform) + self._schedule_push() - def set_platform(self, platform: Optional[PlatformType]): + def _set_platform(self, platform: Optional[PlatformType]): if platform is not None and platform in self._translation_manager.platforms(): self._platform = platform else: diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/resource_id.py b/amulet_map_editor/api/wx/ui/mc/base/api/resource_id.py index d300441e..db67303c 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/resource_id.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/resource_id.py @@ -22,7 +22,7 @@ def namespace(self, namespace: str): """ raise NotImplementedError - def set_namespace(self, namespace: Optional[str]): + def _set_namespace(self, namespace: Optional[str]): """ Set the active namespace. Changes will not propagate. @@ -48,7 +48,7 @@ def base_name(self, base_name: str): """ raise NotImplementedError - def set_base_name(self, base_name: Optional[str]): + def _set_base_name(self, base_name: Optional[str]): """ Set the active base name. Changes will not propagate. @@ -73,9 +73,9 @@ def __init__( translation_manager, platform, version_number, force_blockstate ) self._namespace = None - self.set_namespace(namespace) + self._set_namespace(namespace) self._base_name = None - self.set_base_name(base_name) + self._set_base_name(base_name) @property def namespace(self) -> str: @@ -83,10 +83,10 @@ def namespace(self) -> str: @namespace.setter def namespace(self, namespace: str): - self.set_namespace(namespace) - self.push() + self._set_namespace(namespace) + self._schedule_push() - def set_namespace(self, namespace: Optional[str]): + def _set_namespace(self, namespace: Optional[str]): raise NotImplementedError @property @@ -95,8 +95,8 @@ def base_name(self) -> str: @base_name.setter def base_name(self, base_name: str): - self.set_base_name(base_name) - self.push() + self._set_base_name(base_name) + self._schedule_push() - def set_base_name(self, base_name: Optional[str]): + def _set_base_name(self, base_name: Optional[str]): raise NotImplementedError diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/version.py b/amulet_map_editor/api/wx/ui/mc/base/api/version.py index a6a365d7..6abbe7d9 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/version.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/version.py @@ -21,7 +21,7 @@ def version_number(self, version_number: VersionNumberTuple): """ raise NotImplementedError - def set_version_number(self, version_number: Optional[VersionNumberTuple]): + def _set_version_number(self, version_number: Optional[VersionNumberTuple]): """ Set the active version tuple. Changes will not propagate. @@ -50,7 +50,7 @@ def force_blockstate(self, force_blockstate: bool): """ raise NotImplementedError - def set_force_blockstate(self, force_blockstate: Optional[bool]): + def _set_force_blockstate(self, force_blockstate: Optional[bool]): """ Set if blockstate is forced. Changes will not propagate. @@ -73,9 +73,9 @@ def __init__( ): super().__init__(translation_manager, platform) self._version_number = None - self.set_version_number(version_number) + self._set_version_number(version_number) self._force_blockstate = None - self.set_force_blockstate(force_blockstate) + self._set_force_blockstate(force_blockstate) @property def version_number(self) -> VersionNumberTuple: @@ -83,10 +83,10 @@ def version_number(self) -> VersionNumberTuple: @version_number.setter def version_number(self, version_number: VersionNumberTuple): - self.set_version_number(version_number) - self.push() + self._set_version_number(version_number) + self._schedule_push() - def set_version_number(self, version_number: Optional[VersionNumberTuple]): + def _set_version_number(self, version_number: Optional[VersionNumberTuple]): v = None if version_number is not None: if version_number in self._translation_manager.version_numbers( @@ -111,8 +111,8 @@ def force_blockstate(self) -> bool: @force_blockstate.setter def force_blockstate(self, force_blockstate: bool): - self.set_force_blockstate(force_blockstate) - self.push() + self._set_force_blockstate(force_blockstate) + self._schedule_push() - def set_force_blockstate(self, force_blockstate: Optional[bool]): + def _set_force_blockstate(self, force_blockstate: Optional[bool]): self._force_blockstate = bool(force_blockstate) diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_define.py b/amulet_map_editor/api/wx/ui/mc/base/base_define.py index 21bfb9f5..99f26224 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/base_define.py +++ b/amulet_map_editor/api/wx/ui/mc/base/base_define.py @@ -75,17 +75,19 @@ def _on_version_change(self, evt: mc_version.VersionChangeEvent): raise NotImplementedError def push(self) -> bool: - self.set_platform(self.platform) - self.set_version_number(self.version_number) - self.set_force_blockstate(self.force_blockstate) + self._set_platform(self.platform) + self._set_version_number(self.version_number) + self._set_force_blockstate(self.force_blockstate) if ( self.platform != self._version_picker.platform or self.version_number != self._version_picker.version_number or self.force_blockstate != self._version_picker.force_blockstate ): - self._version_picker.set_platform(self.platform) - self._version_picker.set_version_number(self.version_number) - self._version_picker.set_force_blockstate(self.force_blockstate) + ( + self._version_picker.platform, + self._version_picker.version_number, + self._version_picker.force_blockstate, + ) = (self.platform, self.version_number, self.force_blockstate) self._version_picker.push() return True return False diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py index 9f2f3638..efd239a4 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py +++ b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py @@ -195,7 +195,7 @@ def _handle_namespace_change(self): old_namespace = self.namespace new_namespace = self._namespace_combo.GetValue() if new_namespace != old_namespace: - self.set_namespace(new_namespace) + self._set_namespace(new_namespace) self._populate_base_name() self._update_from_search() @@ -226,8 +226,10 @@ def _on_change(self, old_namespace=None): ): new_base_name = new_base_name[1:-1] if old_namespace != self.namespace or old_base_name != new_base_name: - self.set_base_name(new_base_name) - self._post_event(old_namespace, old_base_name, self.namespace, new_base_name) + self._set_base_name(new_base_name) + self._post_event( + old_namespace, old_base_name, self.namespace, new_base_name + ) def push(self) -> bool: self._populate_namespace() diff --git a/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py b/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py index 3d6550ef..38f580a8 100644 --- a/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py +++ b/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py @@ -67,9 +67,9 @@ def _on_version_change(self, evt: VersionChangeEvent): old_platform, old_version = self.platform, self.version_number old_namespace, old_base_name = self.namespace, self.base_name - self.set_platform(evt.platform) - self.set_version_number(evt.version_number) - self.set_force_blockstate(evt.force_blockstate) + self._set_platform(evt.platform) + self._set_version_number(evt.version_number) + self._set_force_blockstate(evt.force_blockstate) universal_biome = self._translation_manager.get_version( old_platform, old_version @@ -79,15 +79,22 @@ def _on_version_change(self, evt: VersionChangeEvent): ).biome.from_universal(universal_biome) namespace, base_name = new_biome.split(":", 1) - self.set_namespace(namespace) - self.set_base_name(base_name) + self._set_namespace(namespace) + self._set_base_name(base_name) - self._picker.set_platform(self.platform) - self._picker.set_version_number(self.version_number) - self._picker.set_force_blockstate(self.force_blockstate) - self._picker.set_namespace(self.namespace) - self._picker.set_base_name(self.base_name) - self._picker.push() + ( + self._picker.platform, + self._picker.version_number, + self._picker.force_blockstate, + self._picker.namespace, + self._picker.base_name, + ) = ( + self.platform, + self.version_number, + self.force_blockstate, + self.namespace, + self.base_name, + ) wx.PostEvent( self, @@ -101,8 +108,8 @@ def _on_version_change(self, evt: VersionChangeEvent): self.Thaw() def _on_biome_id_change(self, evt: BiomeIDChangeEvent): - self.set_namespace(evt.namespace) - self.set_base_name(evt.base_name) + self._set_namespace(evt.namespace) + self._set_base_name(evt.base_name) wx.PostEvent( self, BiomeIDChangeEvent( @@ -116,20 +123,24 @@ def _on_biome_id_change(self, evt: BiomeIDChangeEvent): def push(self) -> bool: self.Freeze() update = super().push() - self.set_namespace(self.namespace) - self.set_base_name(self.base_name) + self._set_namespace(self.namespace) + self._set_base_name(self.base_name) if update: # The version changed - self._picker.set_platform(self.platform) - self._picker.set_version_number(self.version_number) - self._picker.set_force_blockstate(self.force_blockstate) + ( + self._picker.platform, + self._picker.version_number, + self._picker.force_blockstate, + ) = (self.platform, self.version_number, self.force_blockstate) if ( self.namespace != self._picker.namespace or self.base_name != self._picker.base_name ): update = True - self._picker.set_namespace(self.namespace) - self._picker.set_base_name(self.base_name) + self._picker.namespace, self._picker.base_name = ( + self.namespace, + self.base_name, + ) if update: self._picker.push() self.Thaw() @@ -152,7 +163,13 @@ def demo(): obj = BiomeDefine(dialog, translation_manager, wx.HORIZONTAL) def on_biome_change(evt: BiomeIDChangeEvent): - print(obj.platform, obj.version_number, obj.force_blockstate, obj.namespace, obj.base_name) + print( + obj.platform, + obj.version_number, + obj.force_blockstate, + obj.namespace, + obj.base_name, + ) print(evt.namespace, evt.base_name, evt.old_namespace, evt.old_base_name) obj.Bind(EVT_BIOME_ID_CHANGE, on_biome_change) @@ -174,16 +191,34 @@ def set( namespace: str, base_name: str, ): - obj.set_platform(platform) - obj.set_version_number(version) - obj.set_force_blockstate(force_blockstate) - obj.set_namespace(namespace) - obj.set_base_name(base_name) + ( + obj.platform, + obj.version_number, + obj.force_blockstate, + obj.namespace, + obj.base_name, + ) = ( + platform, + version, + force_blockstate, + namespace, + base_name, + ) obj.push() interval = 1_000 - wx.CallLater(interval * 1, set, "java", (1, 17, 0), False, "minecraft", "end_highlands") - wx.CallLater(interval * 2, set, "bedrock", (1, 17, 0), False, "minecraft", "birch_forest_hills_mutated") + wx.CallLater( + interval * 1, set, "java", (1, 17, 0), False, "minecraft", "end_highlands" + ) + wx.CallLater( + interval * 2, + set, + "bedrock", + (1, 17, 0), + False, + "minecraft", + "birch_forest_hills_mutated", + ) wx.CallLater(interval * 3, set, "java", (1, 17, 0), False, "minecraft", "random") wx.CallLater(interval * 4, set, "bedrock", (1, 17, 0), False, "minecraft", "random") diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py index 267e43c4..9bb2e302 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py @@ -34,10 +34,10 @@ def platform(self) -> PlatformType: @platform.setter def platform(self, platform: PlatformType): - self.set_platform(platform) + self._set_platform(platform) self.update_button() - def set_platform(self, platform: PlatformType): + def _set_platform(self, platform: PlatformType): self._block_widget.platform = platform @property @@ -46,10 +46,10 @@ def version_number(self) -> VersionNumberTuple: @version_number.setter def version_number(self, version_number: VersionNumberTuple): - self.set_version_number(version_number) + self._set_version_number(version_number) self.update_button() - def set_version_number(self, version_number: VersionNumberTuple): + def _set_version_number(self, version_number: VersionNumberTuple): self._block_widget.version_number = version_number @property @@ -58,10 +58,10 @@ def namespace(self) -> str: @namespace.setter def namespace(self, namespace: str): - self.set_namespace(namespace) + self._set_namespace(namespace) self.update_button() - def set_namespace(self, namespace: str): + def _set_namespace(self, namespace: str): self._block_widget.namespace = namespace @property @@ -70,10 +70,10 @@ def force_blockstate(self) -> bool: @force_blockstate.setter def force_blockstate(self, force_blockstate: bool): - self.set_force_blockstate(force_blockstate) + self._set_force_blockstate(force_blockstate) self.update_button() - def set_force_blockstate(self, force_blockstate: bool): + def _set_force_blockstate(self, force_blockstate: bool): self._block_widget.force_blockstate = force_blockstate @property diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py index b8a50e15..b91feacb 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py @@ -56,7 +56,7 @@ def block(self, block: Block): self.update_button() def set_block(self, block: Block): - self.set_namespace(block.namespace) + self._set_namespace(block.namespace) self.set_block_name(block.base_name) self.set_properties(block.properties) diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py index a7c38674..064f5239 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py @@ -80,8 +80,10 @@ def block(self) -> Block: @block.setter def block(self, block: Block): - self._picker.set_namespace(block.namespace) - self._picker.set_name(block.base_name) + self._picker.namespace, self._picker.base_name = ( + block.namespace, + block.base_name, + ) self._update_properties() self.properties = block.properties diff --git a/amulet_map_editor/api/wx/ui/mc/version/platform_select.py b/amulet_map_editor/api/wx/ui/mc/version/platform_select.py index 2ce5da38..77ad8d34 100644 --- a/amulet_map_editor/api/wx/ui/mc/version/platform_select.py +++ b/amulet_map_editor/api/wx/ui/mc/version/platform_select.py @@ -110,7 +110,7 @@ def _on_platform_change(self, evt): old_platform = self.platform new_platform = self._platform_choice.GetCurrentString() # write the changes back to the internal state - self.set_platform(new_platform) + self._set_platform(new_platform) wx.PostEvent(self, PlatformChangeEvent(new_platform, old_platform)) def push(self): diff --git a/amulet_map_editor/api/wx/ui/mc/version/version_select.py b/amulet_map_editor/api/wx/ui/mc/version/version_select.py index ae61cdc3..9d021636 100644 --- a/amulet_map_editor/api/wx/ui/mc/version/version_select.py +++ b/amulet_map_editor/api/wx/ui/mc/version/version_select.py @@ -132,7 +132,7 @@ def _on_change(self, changed: int): if changed <= 1: # write the changes back to the internal state new_platform = self._platform_choice.GetCurrentString() - self.set_platform(new_platform) + self._set_platform(new_platform) else: new_platform = old_platform @@ -141,7 +141,7 @@ def _on_change(self, changed: int): self._populate_version() self._version_choice.SetSelection(0) new_version = self._version_choice.GetCurrentObject() - self.set_version_number(new_version) + self._set_version_number(new_version) else: new_version = old_version @@ -151,7 +151,7 @@ def _on_change(self, changed: int): new_force_blockstate = ( self._blockstate_choice.GetCurrentString() == "blockstate" ) - self.set_force_blockstate(new_force_blockstate) + self._set_force_blockstate(new_force_blockstate) wx.PostEvent( self, @@ -178,8 +178,8 @@ def push(self) -> bool: update = super().push() # If the user set these out of order they may be messed up. # This should fix that. - self.set_version_number(self.version_number) - self.set_force_blockstate(self.force_blockstate) + self._set_version_number(self.version_number) + self._set_force_blockstate(self.force_blockstate) if update: self._populate_version() @@ -233,9 +233,11 @@ def set_version( version: VersionNumberTuple, force_blockstate: bool, ): - obj.set_platform(platform) - obj.set_version_number(version) - obj.set_force_blockstate(force_blockstate) + obj.platform, obj.version_number, obj.force_blockstate = ( + platform, + version, + force_blockstate, + ) obj.push() interval = 1_000 @@ -251,9 +253,11 @@ def set_version2( version: VersionNumberTuple, force_blockstate: bool, ): - obj.set_force_blockstate(force_blockstate) - obj.set_version_number(version) - obj.set_platform(platform) + obj.force_blockstate, obj.version_number, obj.platform = ( + force_blockstate, + version, + platform, + ) obj.push() wx.CallLater(interval * 5, set_version2, select, "java", (1, 15, 0), False) From f802ae3fe75c3480dd2812862247322bfa30a389 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Sun, 25 Jul 2021 12:09:23 +0100 Subject: [PATCH 066/139] Moved the freeze thaw and changed logic into the push method The old push method is now private and called _on_push The new push method will only run if the contents have changed and will freeze and thaw the window if it is an instance of a wx window. Once done it will mark the window as unchanged. --- .../api/wx/ui/mc/base/api/platform.py | 30 ++++++++++++------- .../api/wx/ui/mc/base/base_define.py | 3 +- .../base_identifier_select.py | 2 +- .../api/wx/ui/mc/biome/biome_define.py | 7 ++--- .../api/wx/ui/mc/version/platform_select.py | 2 +- .../api/wx/ui/mc/version/version_select.py | 6 ++-- 6 files changed, 27 insertions(+), 23 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/platform.py b/amulet_map_editor/api/wx/ui/mc/base/api/platform.py index 90811fee..46536873 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/platform.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/platform.py @@ -65,23 +65,33 @@ def __init__( def _schedule_push(self): """Schedule the pushing of the internal data to the UI.""" + self._changed = True + push = self.push if isinstance(self, wx.Window): - self._changed = True - - def on_push(): - if self._changed: - self.Freeze() - self.push() - self.Thaw() - self._changed = False - - wx.CallAfter(on_push) + wx.CallAfter(push) def push(self) -> bool: """ Push the internal state to the UI. No events should be created when calling this method. + :return: True if data was changed + """ + ret = False + if self._changed: + if isinstance(self, wx.Window): + self.Freeze() + ret = self._on_push() + if isinstance(self, wx.Window): + self.Thaw() + self._changed = False + return ret + + def _on_push(self) -> bool: + """ + Push the internal state to the UI. + No events should be created when calling this method. + :return: True if data was changed """ raise NotImplementedError diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_define.py b/amulet_map_editor/api/wx/ui/mc/base/base_define.py index 99f26224..ba789520 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/base_define.py +++ b/amulet_map_editor/api/wx/ui/mc/base/base_define.py @@ -74,7 +74,7 @@ def _init_state(self, state: Dict[str, Any]): def _on_version_change(self, evt: mc_version.VersionChangeEvent): raise NotImplementedError - def push(self) -> bool: + def _on_push(self) -> bool: self._set_platform(self.platform) self._set_version_number(self.version_number) self._set_force_blockstate(self.force_blockstate) @@ -88,6 +88,5 @@ def push(self) -> bool: self._version_picker.version_number, self._version_picker.force_blockstate, ) = (self.platform, self.version_number, self.force_blockstate) - self._version_picker.push() return True return False diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py index efd239a4..44749311 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py +++ b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py @@ -231,7 +231,7 @@ def _on_change(self, old_namespace=None): old_namespace, old_base_name, self.namespace, new_base_name ) - def push(self) -> bool: + def _on_push(self) -> bool: self._populate_namespace() self._push_namespace() self._populate_base_name() diff --git a/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py b/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py index 38f580a8..379a78ca 100644 --- a/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py +++ b/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py @@ -120,9 +120,9 @@ def _on_biome_id_change(self, evt: BiomeIDChangeEvent): ), ) - def push(self) -> bool: + def _on_push(self) -> bool: self.Freeze() - update = super().push() + update = super()._on_push() self._set_namespace(self.namespace) self._set_base_name(self.base_name) if update: @@ -141,8 +141,6 @@ def push(self) -> bool: self.namespace, self.base_name, ) - if update: - self._picker.push() self.Thaw() return update @@ -204,7 +202,6 @@ def set( namespace, base_name, ) - obj.push() interval = 1_000 wx.CallLater( diff --git a/amulet_map_editor/api/wx/ui/mc/version/platform_select.py b/amulet_map_editor/api/wx/ui/mc/version/platform_select.py index 77ad8d34..de3f967f 100644 --- a/amulet_map_editor/api/wx/ui/mc/version/platform_select.py +++ b/amulet_map_editor/api/wx/ui/mc/version/platform_select.py @@ -113,7 +113,7 @@ def _on_platform_change(self, evt): self._set_platform(new_platform) wx.PostEvent(self, PlatformChangeEvent(new_platform, old_platform)) - def push(self): + def _on_push(self): if self.platform != self._platform_choice.GetCurrentString(): self._push_platform() return True diff --git a/amulet_map_editor/api/wx/ui/mc/version/version_select.py b/amulet_map_editor/api/wx/ui/mc/version/version_select.py index 9d021636..05400c6a 100644 --- a/amulet_map_editor/api/wx/ui/mc/version/version_select.py +++ b/amulet_map_editor/api/wx/ui/mc/version/version_select.py @@ -174,8 +174,8 @@ def _on_version_number_change(self, evt): def _on_blockstate_change(self, evt): self._on_change(3) - def push(self) -> bool: - update = super().push() + def _on_push(self) -> bool: + update = super()._on_push() # If the user set these out of order they may be messed up. # This should fix that. self._set_version_number(self.version_number) @@ -238,7 +238,6 @@ def set_version( version, force_blockstate, ) - obj.push() interval = 1_000 @@ -258,7 +257,6 @@ def set_version2( version, platform, ) - obj.push() wx.CallLater(interval * 5, set_version2, select, "java", (1, 15, 0), False) wx.CallLater(interval * 6, set_version2, select, "java", (1, 17, 0), False) From f3233a2a34db90204804297d0db190f851d062e9 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Sun, 25 Jul 2021 12:10:51 +0100 Subject: [PATCH 067/139] Modified the version and platform on change logic It will now only create and event if the entry in the choice has actually changed. Previously an event was created even if the user selected the same value that was previously selected. --- .../api/wx/ui/mc/version/platform_select.py | 17 +++++++++---- .../api/wx/ui/mc/version/version_select.py | 25 ++++++++++++++++--- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/version/platform_select.py b/amulet_map_editor/api/wx/ui/mc/version/platform_select.py index de3f967f..03741792 100644 --- a/amulet_map_editor/api/wx/ui/mc/version/platform_select.py +++ b/amulet_map_editor/api/wx/ui/mc/version/platform_select.py @@ -4,7 +4,7 @@ from typing import Tuple, Type, Any, Dict from amulet.api.data_types import PlatformType -from .events import PlatformChangeEvent +from .events import PlatformChangeEvent, EVT_PLATFORM_CHANGE from amulet_map_editor.api.wx.ui.mc.base.api.platform import BaseMCPlatform @@ -109,9 +109,10 @@ def _on_platform_change(self, evt): """The event run when the platform choice is changed by a user.""" old_platform = self.platform new_platform = self._platform_choice.GetCurrentString() - # write the changes back to the internal state - self._set_platform(new_platform) - wx.PostEvent(self, PlatformChangeEvent(new_platform, old_platform)) + if old_platform != new_platform: + # write the changes back to the internal state + self._set_platform(new_platform) + wx.PostEvent(self, PlatformChangeEvent(new_platform, old_platform)) def _on_push(self): if self.platform != self._platform_choice.GetCurrentString(): @@ -133,12 +134,18 @@ def demo(): ) sizer = wx.BoxSizer() dialog.SetSizer(sizer) + obj = PlatformSelect(dialog, translation_manager, "java") sizer.Add( - PlatformSelect(dialog, translation_manager, "java"), + obj, 1, wx.ALL | wx.EXPAND, 5, ) + + def on_change(evt: PlatformChangeEvent): + print(evt.platform, evt.old_platform) + + obj.Bind(EVT_PLATFORM_CHANGE, on_change) dialog.Bind(wx.EVT_CLOSE, lambda evt: dialog.Destroy()) dialog.Show() dialog.Fit() diff --git a/amulet_map_editor/api/wx/ui/mc/version/version_select.py b/amulet_map_editor/api/wx/ui/mc/version/version_select.py index 05400c6a..ced426db 100644 --- a/amulet_map_editor/api/wx/ui/mc/version/version_select.py +++ b/amulet_map_editor/api/wx/ui/mc/version/version_select.py @@ -6,9 +6,7 @@ from amulet.api.data_types import VersionNumberTuple, PlatformType from .platform_select import PlatformSelect from amulet_map_editor.api.wx.ui.mc.base.api.version import BaseMCVersion -from .events import ( - VersionChangeEvent, -) +from .events import VersionChangeEvent, EVT_VERSION_CHANGE class VersionSelect(PlatformSelect, BaseMCVersion): @@ -132,6 +130,9 @@ def _on_change(self, changed: int): if changed <= 1: # write the changes back to the internal state new_platform = self._platform_choice.GetCurrentString() + if new_platform == old_platform: + # nothing changed + return self._set_platform(new_platform) else: new_platform = old_platform @@ -141,6 +142,8 @@ def _on_change(self, changed: int): self._populate_version() self._version_choice.SetSelection(0) new_version = self._version_choice.GetCurrentObject() + if changed == 2 and new_version == old_version: + return self._set_version_number(new_version) else: new_version = old_version @@ -151,6 +154,8 @@ def _on_change(self, changed: int): new_force_blockstate = ( self._blockstate_choice.GetCurrentString() == "blockstate" ) + if changed == 3 and new_force_blockstate == old_force_blockstate: + return self._set_force_blockstate(new_force_blockstate) wx.PostEvent( @@ -214,11 +219,23 @@ def demo(): ) sizer = wx.BoxSizer() dialog.SetSizer(sizer) - select = cls(dialog, translation_manager) + select: VersionSelect = cls(dialog, translation_manager) sizer.Add(select, 0, wx.ALL, 5) dialog.Show() dialog.Fit() + def on_change(evt: VersionChangeEvent): + print( + evt.platform, + evt.version_number, + evt.force_blockstate, + evt.old_platform, + evt.old_version_number, + evt.force_blockstate, + ) + + select.Bind(EVT_VERSION_CHANGE, on_change) + def get_on_close(dialog_): def on_close(evt): dialog_.Destroy() From 3905d6480c2e67d2e245297a2eaa30c645d11aa2 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Tue, 27 Jul 2021 12:22:41 +0100 Subject: [PATCH 068/139] Added a state manager for normal and wildcard properties --- .../api/wx/ui/mc/base/api/block/normal.py | 111 +++++++++++++++--- .../api/wx/ui/mc/base/api/block/wildcard.py | 104 +++++++++++++++- 2 files changed, 199 insertions(+), 16 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/block/normal.py b/amulet_map_editor/api/wx/ui/mc/base/api/block/normal.py index efa4caff..c1c6be9a 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/block/normal.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/block/normal.py @@ -1,9 +1,13 @@ -from typing import Optional, Tuple +from typing import Optional -from amulet.api.block import PropertyType, Block -from amulet.api.block_entity import BlockEntity +import PyMCTranslate +import amulet_nbt +from amulet.api.data_types import VersionNumberTuple +from amulet.api.block import PropertyType, Block, PropertyDataTypes -from .identifier import BaseMCBlockIdentifierAPI +# from amulet.api.block_entity import BlockEntity + +from .identifier import BaseMCBlockIdentifierAPI, BaseMCBlockIdentifier class NormalMCBlockAPI(BaseMCBlockIdentifierAPI): @@ -23,18 +27,97 @@ def block(self) -> Block: def block(self, block: Block): raise NotImplementedError + +class NormalMCBlock(BaseMCBlockIdentifier, NormalMCBlockAPI): + def __init__( + self, + translation_manager: PyMCTranslate.TranslationManager, + platform: str = None, + version_number: VersionNumberTuple = None, + force_blockstate: bool = False, + namespace: str = None, + base_name: str = None, + properties: PropertyType = None, + ): + super().__init__( + translation_manager, + platform, + version_number, + force_blockstate, + namespace, + base_name, + ) + self._properties = None + self._set_properties(properties) + @property - def block_entity(self) -> Optional[BlockEntity]: - raise NotImplementedError + def properties(self) -> PropertyType: + return self._properties - @block_entity.setter - def block_entity(self, block_entity: Optional[BlockEntity]): - raise NotImplementedError + @properties.setter + def properties(self, properties: PropertyType): + self._set_properties(properties) + self._schedule_push() + + def _set_properties(self, properties: Optional[PropertyType]): + """ + Set the active properties. + Changes will not propagate. + :meth:`push` must be called once all desired states are set. + + :param properties: A dictionary mapping the property name to the property value. + """ + self._properties = {} + block_manager = self._translation_manager.get_version( + self.platform, self.version_number + ).block + if self.platform in block_manager.namespaces( + self.force_blockstate + ) and self.base_name in block_manager.base_names( + self.namespace, self.force_blockstate + ): + # Vanilla block. Make sure the property is valid + block_spec = block_manager.get_specification( + self.namespace, self.base_name, self.force_blockstate + ) + props = block_spec.get("properties", {}) + defaults = block_spec.get("defaults", {}) + if isinstance(properties, dict): + for name, ps in props.items(): + if ( + name in properties + and isinstance(properties[name], PropertyDataTypes) + and properties[name].to_snbt() in ps + ): + self._properties[name] = properties[name] + else: + self._properties[name] = amulet_nbt.from_snbt(defaults[name]) + else: + for name, snbt in defaults.items(): + self._properties[name] = amulet_nbt.from_snbt(snbt) + elif isinstance(properties, dict): + for name, nbt in properties.items(): + if isinstance(nbt, PropertyDataTypes): + self._properties[name] = nbt @property - def universal_block(self) -> Tuple[Block, Optional[BlockEntity]]: - raise NotImplementedError + def block(self) -> Block: + return Block(self.namespace, self.base_name, self.properties) - @universal_block.setter - def universal_block(self, universal_block: Tuple[Block, Optional[BlockEntity]]): - raise NotImplementedError + @block.setter + def block(self, block: Block): + self._set_block(block) + self._schedule_push() + + def _set_block(self, block: Optional[Block]): + """ + Set the active block. + Namespace, base name and properties will be pulled from this. + Changes will not propagate. + :meth:`push` must be called once all desired states are set. + + :param block: The block to set. + """ + self._set_namespace(block.namespace) + self._set_base_name(block.base_name) + self._set_properties(block.properties) diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py b/amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py index cbadc3f0..a24a8715 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py @@ -1,6 +1,10 @@ -from amulet.api.block import PropertyTypeMultiple +from typing import Optional +import PyMCTranslate +import amulet_nbt +from amulet.api.data_types import VersionNumberTuple +from amulet.api.block import PropertyTypeMultiple, PropertyDataTypes -from .identifier import BaseMCBlockIdentifierAPI +from .identifier import BaseMCBlockIdentifierAPI, BaseMCBlockIdentifier class WildcardMCBlockAPI(BaseMCBlockIdentifierAPI): @@ -21,3 +25,99 @@ def selected_properties(self) -> PropertyTypeMultiple: @selected_properties.setter def selected_properties(self, properties: PropertyTypeMultiple): raise NotImplementedError + + +class WildcardMCBlock(BaseMCBlockIdentifier, WildcardMCBlockAPI): + def __init__( + self, + translation_manager: PyMCTranslate.TranslationManager, + platform: str = None, + version_number: VersionNumberTuple = None, + force_blockstate: bool = False, + namespace: str = None, + base_name: str = None, + selected_properties: PropertyTypeMultiple = None, + all_properties: PropertyTypeMultiple = None, + ): + super().__init__( + translation_manager, + platform, + version_number, + force_blockstate, + namespace, + base_name, + ) + self._all_properties = None + self._set_all_properties(all_properties) + self._selected_properties = None + self._set_selected_properties(selected_properties) + + @property + def all_properties(self) -> PropertyTypeMultiple: + return self._all_properties + + @all_properties.setter + def all_properties(self, all_properties: PropertyTypeMultiple): + self._set_all_properties(all_properties) + self._schedule_push() + + def _clean_properties( + self, properties: Optional[PropertyTypeMultiple] + ) -> PropertyTypeMultiple: + out_properties: PropertyTypeMultiple = {} + block_manager = self._translation_manager.get_version( + self.platform, self.version_number + ).block + if self.platform in block_manager.namespaces( + self.force_blockstate + ) and self.base_name in block_manager.base_names( + self.namespace, self.force_blockstate + ): + # Vanilla block. Make sure the property is valid + block_spec = block_manager.get_specification( + self.namespace, self.base_name, self.force_blockstate + ) + props = block_spec.get("properties", {}) + if isinstance(properties, dict): + for name, ps in props.items(): + if name in properties: + out_properties[name] = tuple( + nbt + for nbt in properties[name] + if isinstance(nbt, PropertyDataTypes) + and nbt.to_snbt() in ps + ) + else: + for name, snbts in props.items(): + out_properties[name] = tuple( + amulet_nbt.from_snbt(snbt) for snbt in snbts + ) + elif isinstance(properties, dict): + for name, nbts in properties.items(): + out_properties[name] = tuple( + nbt for nbt in nbts if isinstance(nbt, PropertyDataTypes) + ) + return out_properties + + def _set_all_properties(self, all_properties: Optional[PropertyTypeMultiple]): + self._all_properties: PropertyTypeMultiple = self._clean_properties( + all_properties + ) + + @property + def selected_properties(self) -> PropertyTypeMultiple: + return self._selected_properties + + @selected_properties.setter + def selected_properties(self, selected_properties: PropertyTypeMultiple): + self._set_selected_properties(selected_properties) + self._schedule_push() + + def _set_selected_properties( + self, selected_properties: Optional[PropertyTypeMultiple] + ): + self._selected_properties = { + name: tuple(nbt for nbt in nbts if nbt in self.all_properties[name]) + for name, nbts in self._clean_properties(selected_properties).items() + if name in self.all_properties + } From 4a3ce0886034695320f652851ada71e049307dc7 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Tue, 27 Jul 2021 12:24:54 +0100 Subject: [PATCH 069/139] Moved the private setter methods out of the abstract API These used to be public but they do not make much sense here any more --- .../api/wx/ui/mc/base/api/block/normal.py | 17 ++++++++++ .../api/wx/ui/mc/base/api/platform.py | 17 ++++------ .../api/wx/ui/mc/base/api/resource_id.py | 34 ++++++++----------- .../api/wx/ui/mc/base/api/version.py | 34 ++++++++----------- 4 files changed, 52 insertions(+), 50 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/block/normal.py b/amulet_map_editor/api/wx/ui/mc/base/api/block/normal.py index c1c6be9a..78cae0ee 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/block/normal.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/block/normal.py @@ -13,18 +13,35 @@ class NormalMCBlockAPI(BaseMCBlockIdentifierAPI): @property def properties(self) -> PropertyType: + """The active properties.""" raise NotImplementedError @properties.setter def properties(self, properties: PropertyType): + """ + Set the active properties. + Changes will propagate to the end of this UI. + No events will be created. + + :param properties: The properties to set. + """ raise NotImplementedError @property def block(self) -> Block: + """The active block.""" raise NotImplementedError @block.setter def block(self, block: Block): + """ + Set the active block. + Namespace, base name and properties will be pulled from this. + Changes will propagate to the end of this UI. + No events will be created. + + :param block: The block to set. + """ raise NotImplementedError diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/platform.py b/amulet_map_editor/api/wx/ui/mc/base/api/platform.py index 46536873..ce78d052 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/platform.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/platform.py @@ -21,16 +21,6 @@ def platform(self, platform: PlatformType): """ raise NotImplementedError - def _set_platform(self, platform: Optional[PlatformType]): - """ - Set the active platform. - Changes will not propagate. - :meth:`update` must be called once all desired states are set. - - :param platform: The platform string to set. - """ - raise NotImplementedError - """ update method should compare the internal state to the UI state and update as required. This should not create events. @@ -106,6 +96,13 @@ def platform(self, platform: PlatformType): self._schedule_push() def _set_platform(self, platform: Optional[PlatformType]): + """ + Set the active platform. + Changes will not propagate. + :meth:`push` must be called once all desired states are set. + + :param platform: The platform string to set. + """ if platform is not None and platform in self._translation_manager.platforms(): self._platform = platform else: diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/resource_id.py b/amulet_map_editor/api/wx/ui/mc/base/api/resource_id.py index db67303c..c2e1d8c5 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/resource_id.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/resource_id.py @@ -22,16 +22,6 @@ def namespace(self, namespace: str): """ raise NotImplementedError - def _set_namespace(self, namespace: Optional[str]): - """ - Set the active namespace. - Changes will not propagate. - :meth:`update` must be called once all desired states are set. - - :param namespace: The namespace to set. - """ - raise NotImplementedError - @property def base_name(self) -> str: """The active base name.""" @@ -48,16 +38,6 @@ def base_name(self, base_name: str): """ raise NotImplementedError - def _set_base_name(self, base_name: Optional[str]): - """ - Set the active base name. - Changes will not propagate. - :meth:`update` must be called once all desired states are set. - - :param base_name: The base name to set. - """ - raise NotImplementedError - class BaseMCResourceID(BaseMCVersion, BaseMCResourceIDAPI): def __init__( @@ -87,6 +67,13 @@ def namespace(self, namespace: str): self._schedule_push() def _set_namespace(self, namespace: Optional[str]): + """ + Set the active namespace. + Changes will not propagate. + :meth:`push` must be called once all desired states are set. + + :param namespace: The namespace to set. + """ raise NotImplementedError @property @@ -99,4 +86,11 @@ def base_name(self, base_name: str): self._schedule_push() def _set_base_name(self, base_name: Optional[str]): + """ + Set the active base name. + Changes will not propagate. + :meth:`push` must be called once all desired states are set. + + :param base_name: The base name to set. + """ raise NotImplementedError diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/version.py b/amulet_map_editor/api/wx/ui/mc/base/api/version.py index 6abbe7d9..1c488478 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/version.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/version.py @@ -21,16 +21,6 @@ def version_number(self, version_number: VersionNumberTuple): """ raise NotImplementedError - def _set_version_number(self, version_number: Optional[VersionNumberTuple]): - """ - Set the active version tuple. - Changes will not propagate. - :meth:`update` must be called once all desired states are set. - - :param version_number: The version number to set. - """ - raise NotImplementedError - @property def force_blockstate(self) -> bool: """ @@ -50,16 +40,6 @@ def force_blockstate(self, force_blockstate: bool): """ raise NotImplementedError - def _set_force_blockstate(self, force_blockstate: Optional[bool]): - """ - Set if blockstate is forced. - Changes will not propagate. - :meth:`update` must be called once all desired states are set. - - :param force_blockstate: False for the native format, True for the blockstate format. - """ - raise NotImplementedError - class BaseMCVersion(BaseMCPlatform, BaseMCVersionAPI): """A class to store the state of the platform, version tuple and force blockstate.""" @@ -87,6 +67,13 @@ def version_number(self, version_number: VersionNumberTuple): self._schedule_push() def _set_version_number(self, version_number: Optional[VersionNumberTuple]): + """ + Set the active version tuple. + Changes will not propagate. + :meth:`push` must be called once all desired states are set. + + :param version_number: The version number to set. + """ v = None if version_number is not None: if version_number in self._translation_manager.version_numbers( @@ -115,4 +102,11 @@ def force_blockstate(self, force_blockstate: bool): self._schedule_push() def _set_force_blockstate(self, force_blockstate: Optional[bool]): + """ + Set if blockstate is forced. + Changes will not propagate. + :meth:`push` must be called once all desired states are set. + + :param force_blockstate: False for the native format, True for the blockstate format. + """ self._force_blockstate = bool(force_blockstate) From 9d4ac34322102ee14b14ee7142a317d9aac4b217 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Fri, 30 Jul 2021 11:21:37 +0100 Subject: [PATCH 070/139] Reworked the single properties select UI --- .../mc/block/properties/single/automatic.py | 74 ++++++----- .../wx/ui/mc/block/properties/single/base.py | 8 +- .../wx/ui/mc/block/properties/single/main.py | 117 +++++++++--------- .../ui/mc/block/properties/single/manual.py | 6 +- 4 files changed, 105 insertions(+), 100 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/single/automatic.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/automatic.py index 66fb5300..244bb267 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/single/automatic.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/single/automatic.py @@ -1,12 +1,13 @@ import wx -from typing import Dict, List +from typing import Dict, Tuple -import PyMCTranslate -import amulet_nbt -from amulet.api.block import PropertyDataTypes, PropertyType + +from amulet.api.block import PropertyDataTypes, PropertyType, PropertyValueType from .events import SinglePropertiesChangeEvent from .base import BaseSingleProperty +StatesType = Dict[str, Tuple[Tuple[PropertyValueType, ...], int]] + class AutomaticSingleProperty(BaseSingleProperty): """ @@ -15,12 +16,13 @@ class AutomaticSingleProperty(BaseSingleProperty): The UI is automatically populated from the given specification. """ + _states: StatesType + def __init__( self, parent: wx.Window, - translation_manager: PyMCTranslate.TranslationManager, ): - super().__init__(parent, translation_manager) + super().__init__(parent) header_sizer = wx.BoxSizer(wx.HORIZONTAL) self._sizer.Add(header_sizer, 0, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, 5) @@ -33,26 +35,40 @@ def __init__( self._property_sizer = wx.GridSizer(2, 5, 5) self._sizer.Add(self._property_sizer, 0, wx.ALL | wx.EXPAND, 5) + self._states: StatesType = {} self._properties: Dict[str, wx.Choice] = {} - self._specification: dict = {} - def rebuild_ui(self, specification: dict): + @property + def states(self) -> StatesType: """ - Rebuild the UI from the given specification. - Run when the version or block is changed. + A dictionary mapping the string property names to the valid states and the index of the default state. """ - self._specification = specification + return self._states + + @states.setter + def states(self, states: StatesType): + self._states = {} + for key, (choices, default) in states.items(): + if isinstance(key, str): + valid_choices = tuple( + choice + for choice in choices + if isinstance(choice, PropertyDataTypes) + ) + if valid_choices: + self._states[key] = ( + valid_choices, + default if default < len(valid_choices) else 0, + ) self.Freeze() self._properties.clear() self._property_sizer.Clear(True) - spec_properties: Dict[str, List[str]] = self._specification.get( - "properties", {} - ) - for name, choices in spec_properties.items(): + props = {} + for name, (choices, default) in self._states.items(): label = wx.StaticText(self, label=name) self._property_sizer.Add(label, 0, wx.ALIGN_CENTER) - choice = wx.Choice(self, choices=choices) + choice = wx.Choice(self, choices=[c.to_snbt() for c in choices]) self._property_sizer.Add(choice, 0, wx.EXPAND) choice.Bind( wx.EVT_CHOICE, @@ -62,7 +78,8 @@ def rebuild_ui(self, specification: dict): ), ) self._properties[name] = choice - self.properties = self._specification.get("defaults", {}) + props[name] = choices[default] + self.properties = props self.Fit() self.GetTopLevelParent().Layout() self.Thaw() @@ -70,23 +87,22 @@ def rebuild_ui(self, specification: dict): @property def properties(self) -> PropertyType: return { - name: amulet_nbt.from_snbt(choice.GetString(choice.GetSelection())) + name: self._states[name][0][choice.GetSelection()] for name, choice in self._properties.items() } @properties.setter def properties(self, properties: PropertyType): - self.Freeze() + is_frozen = self.IsFrozen() + if not is_frozen: + self.Freeze() for name, nbt in properties.items(): if name in self._properties: - if isinstance(nbt, PropertyDataTypes): - snbt = nbt.to_snbt() - elif isinstance(nbt, str): - snbt = nbt + if isinstance(nbt, PropertyDataTypes) and nbt in self._states[name][0]: + self._properties[name].SetSelection( + self._states[name][0].index(nbt) + ) else: - continue - choice = self._properties[name] - index = choice.FindString(snbt) - if index != wx.NOT_FOUND: - choice.SetSelection(index) - self.Thaw() + self._properties[name].SetSelection(self._states[name][1]) + if not is_frozen: + self.Thaw() diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/single/base.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/base.py index 5be5d6f8..0a2112d0 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/single/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/single/base.py @@ -1,6 +1,5 @@ import wx -import PyMCTranslate from amulet.api.block import PropertyType @@ -12,15 +11,10 @@ class BaseSingleProperty(wx.Panel): Subclasses must implement the logic. """ - def __init__( - self, - parent: wx.Window, - translation_manager: PyMCTranslate.TranslationManager, - ): + def __init__(self, parent: wx.Window): super().__init__(parent) self._sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(self._sizer) - self._translation_manager = translation_manager @property def properties(self) -> PropertyType: diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py index 69b5be8b..9366a4a2 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py @@ -1,15 +1,17 @@ import wx -from typing import Tuple +from typing import Tuple, Dict, Any, List import PyMCTranslate +import amulet_nbt from amulet.api.block import PropertyType from ..base import BasePropertySelect -from .events import SinglePropertiesChangeEvent +from .events import SinglePropertiesChangeEvent, EVT_SINGLE_PROPERTIES_CHANGE from .automatic import AutomaticSingleProperty from .manual import ManualSingleProperty +from amulet_map_editor.api.wx.ui.mc.base import NormalMCBlock -class SinglePropertySelect(BasePropertySelect): +class SinglePropertySelect(BasePropertySelect, NormalMCBlock): """ This is a UI which lets the user pick one value for each property for a given block. If the block is known it will be populated from the specification. @@ -23,84 +25,73 @@ def __init__( platform: str, version_number: Tuple[int, int, int], force_blockstate: bool, - namespace: str, - block_name: str, + namespace: str = None, + base_name: str = None, properties: PropertyType = None, + state: Dict[str, Any] = None, ): - super().__init__( + state = state or {} + state.setdefault("properties", properties) + BasePropertySelect.__init__( + self, parent, translation_manager, platform, version_number, force_blockstate, namespace, - block_name, - style=wx.BORDER_SIMPLE, + base_name, + state, ) self._manual_enabled = False - self._simple = AutomaticSingleProperty(self, translation_manager) - self._sizer.Add(self._simple, 1, wx.EXPAND) - self._manual = ManualSingleProperty(self, translation_manager) - self._sizer.Add(self._manual, 1, wx.EXPAND) - - if properties is None: - properties = {} - self._set_properties(properties) - self._rebuild_ui() - - @property - def properties(self) -> PropertyType: - """ - The selected values for each property. - - :return: A dictionary mapping property name to the nbt value. - """ - return self._get_properties() - - @properties.setter - def properties(self, properties: PropertyType): - self._set_properties(properties) - wx.PostEvent(self, SinglePropertiesChangeEvent(self.properties)) - - def _get_properties(self) -> PropertyType: - """Get the selected values for each property.""" - if self._manual_enabled: - return self._manual.properties - else: - return self._simple.properties - - def _set_properties(self, properties: PropertyType): - """Set the selected values for each property.""" - self.Freeze() - if self._manual_enabled: - self._manual.properties = properties - else: - self._simple.properties = properties - self.TopLevelParent.Layout() - self.Thaw() - - def _rebuild_ui(self): - self.Freeze() + self._simple = AutomaticSingleProperty(self) + self._sizer.Add(self._simple, 0, wx.EXPAND) + self._manual = ManualSingleProperty(self) + self._sizer.Add(self._manual, 0, wx.EXPAND) + self._simple.Bind(EVT_SINGLE_PROPERTIES_CHANGE, self._on_change) + self._manual.Bind(EVT_SINGLE_PROPERTIES_CHANGE, self._on_change) + self.push(True) + + def _init_state(self, state: Dict[str, Any]): + NormalMCBlock.__init__(self, **state) + + def _on_push(self) -> bool: translator = self._translation_manager.get_version( self._platform, self._version_number ).block - self._manual_enabled = self._block_name not in translator.base_names( - self._namespace, self._force_blockstate + self._manual_enabled = self.base_name not in translator.base_names( + self.namespace, self._force_blockstate ) if self._manual_enabled: self._simple.Hide() self._manual.Show() + self._manual.properties = self.properties else: self._manual.Hide() self._simple.Show() - self._simple.rebuild_ui( - translator.get_specification( - self._namespace, self._block_name, self._force_blockstate + spec = translator.get_specification( + self.namespace, self.base_name, self._force_blockstate + ) + properties: Dict[str, List[str]] = spec.get("properties", {}) + defaults = spec.get("defaults", {}) + self._simple.states = { + name: ( + [amulet_nbt.from_snbt(p) for p in properties[name]], + properties[name].index(defaults[name]), ) + for name in properties + } + return True + + def _on_change(self, evt: SinglePropertiesChangeEvent): + if evt.properties != self.properties: + self._set_properties(evt.properties) + wx.PostEvent( + self, + SinglePropertiesChangeEvent(self.properties), ) - self.Thaw() def demo(): @@ -117,15 +108,21 @@ def demo(): ) sizer = wx.BoxSizer() dialog.SetSizer(sizer) + obj = SinglePropertySelect( + dialog, translation_manager, "java", (1, 16, 0), False, *block + ) sizer.Add( - SinglePropertySelect( - dialog, translation_manager, "java", (1, 16, 0), False, *block - ), + obj, 1, wx.ALL, 5, ) + def on_change(evt: SinglePropertiesChangeEvent): + print(evt.properties) + + obj.Bind(EVT_SINGLE_PROPERTIES_CHANGE, on_change) + def get_on_close(dialog_): def on_close(evt): dialog_.Destroy() diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/single/manual.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/manual.py index 7fe4bae9..0fd6bed1 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/single/manual.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/single/manual.py @@ -17,10 +17,8 @@ class ManualSingleProperty(BaseSingleProperty): This is used when the block is not know so the user can define the properties themselves. """ - def __init__( - self, parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager - ): - super().__init__(parent, translation_manager) + def __init__(self, parent: wx.Window): + super().__init__(parent) header_sizer = wx.BoxSizer(wx.HORIZONTAL) add_button = wx.BitmapButton( self, bitmap=ADD_ICON.bitmap(30, 30), size=(30, 30) From dbc0864dad6eda4e954b1a9a698b5f4e63ef1e3f Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Fri, 30 Jul 2021 11:23:20 +0100 Subject: [PATCH 071/139] Added some imports to the init file --- amulet_map_editor/api/wx/ui/mc/base/api/block/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/block/__init__.py b/amulet_map_editor/api/wx/ui/mc/base/api/block/__init__.py index e985c4df..59d3c708 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/block/__init__.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/block/__init__.py @@ -1,3 +1,3 @@ from .identifier import BaseMCBlockIdentifierAPI, BaseMCBlockIdentifier -from .normal import NormalMCBlockAPI -from .wildcard import WildcardMCBlockAPI +from .normal import NormalMCBlockAPI, NormalMCBlock +from .wildcard import WildcardMCBlockAPI, WildcardMCBlock From 247e0288309865fc15b46eb3a1dd7beb11ab0e86 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Fri, 30 Jul 2021 11:23:31 +0100 Subject: [PATCH 072/139] Cleaned up some typing --- .../mc/base/base_identifier_select/base_identifier_select.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py index 44749311..4f216bf2 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py +++ b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py @@ -3,6 +3,7 @@ import PyMCTranslate +from amulet.api.data_types import VersionNumberTuple from amulet_map_editor.api.image import COLOUR_PICKER from ..api.resource_id import BaseMCResourceID from .events import ( @@ -23,7 +24,7 @@ def __init__( parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager, platform: str, - version_number: Tuple[int, ...], + version_number: VersionNumberTuple, force_blockstate: bool = None, namespace: str = None, base_name: str = None, From d8454cdc655aa08037896e788911418eeff2fe5d Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Fri, 30 Jul 2021 11:24:25 +0100 Subject: [PATCH 073/139] Added a force option to push method Previously the push method would only do something if the data had actually changed. This gives the option to force it. --- amulet_map_editor/api/wx/ui/mc/base/api/platform.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/platform.py b/amulet_map_editor/api/wx/ui/mc/base/api/platform.py index ce78d052..f1dec330 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/platform.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/platform.py @@ -60,7 +60,7 @@ def _schedule_push(self): if isinstance(self, wx.Window): wx.CallAfter(push) - def push(self) -> bool: + def push(self, force=False) -> bool: """ Push the internal state to the UI. No events should be created when calling this method. @@ -68,7 +68,7 @@ def push(self) -> bool: :return: True if data was changed """ ret = False - if self._changed: + if self._changed or force: if isinstance(self, wx.Window): self.Freeze() ret = self._on_push() From 7fb4f610fda0bc4e5d8b64c2503dadbdb4ccf68a Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Fri, 30 Jul 2021 11:25:14 +0100 Subject: [PATCH 074/139] Cleaned up the base property select class --- .../api/wx/ui/mc/block/properties/base.py | 78 ++++++------------- 1 file changed, 23 insertions(+), 55 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/base.py b/amulet_map_editor/api/wx/ui/mc/block/properties/base.py index 66fc8d3f..23693a93 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/base.py @@ -1,10 +1,11 @@ import wx -from typing import Tuple +from typing import Tuple, Dict, Any import PyMCTranslate +from amulet_map_editor.api.wx.ui.mc.base.api.block import BaseMCBlockIdentifier -class BasePropertySelect(wx.Panel): +class BasePropertySelect(wx.Panel, BaseMCBlockIdentifier): def __init__( self, parent: wx.Window, @@ -12,65 +13,32 @@ def __init__( platform: str, version_number: Tuple[int, int, int], force_blockstate: bool, - namespace: str, - block_name: str, - **kwargs, + namespace: str = None, + base_name: str = None, + state: Dict[str, Any] = None, ): - super().__init__(parent, **kwargs) + state = state or {} + state.setdefault("translation_manager", translation_manager) + state.setdefault("platform", platform) + state.setdefault("version_number", version_number) + state.setdefault("force_blockstate", force_blockstate) + state.setdefault("namespace", namespace) + state.setdefault("base_name", base_name) + # This is the init call to the class that stores the internal state of the data. + # This needs to be at the start to ensure that the internal state is set up before anything else is done. + # It is not a direct call to init so that subclasses of this class can substitute in which state subclass is used. + self._init_state(state) + + wx.Panel.__init__(self, parent) self._sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(self._sizer) - self._translation_manager = translation_manager - - self._platform: str = platform - self._version_number: Tuple[int, int, int] = version_number - self._force_blockstate: bool = force_blockstate - self._namespace: str = namespace - self._block_name: str = block_name - - self._set_version_block( - (platform, version_number, force_blockstate, namespace, block_name) - ) - - @property - def version_block(self) -> Tuple[str, Tuple[int, int, int], bool, str, str]: - """The version and block these properties relate to.""" - return ( - self._platform, - self._version_number, - self._force_blockstate, - self._namespace, - self._block_name, - ) - - @version_block.setter - def version_block( - self, version_block: Tuple[str, Tuple[int, int, int], bool, str, str] - ): - """Set the version and block and update the UI.""" - self._set_version_block(version_block) - self._rebuild_ui() - - def _set_version_block( - self, version_block: Tuple[str, Tuple[int, int, int], bool, str, str] - ): + def _init_state(self, state: Dict[str, Any]): """ - Set the version and block these properties relate to. - - :param version_block: The platform, version number, format, namespace and block name + Call the init method of the state manager. + This is here so that nested classes do not have to init the state managers multiple times. """ - version = version_block[:3] - assert ( - version[0] in self._translation_manager.platforms() - and version[1] in self._translation_manager.version_numbers(version[0]) - and isinstance(version[2], bool) - ), f"{version} is not a valid version" - self._platform, self._version_number, self._force_blockstate = version - block = version_block[3:5] - assert isinstance(block[0], str) and isinstance( - block[1], str - ), "The block namespace and block name must be strings" - self._namespace, self._block_name = block + BaseMCBlockIdentifier.__init__(self, **state) def _rebuild_ui(self): """ From a9c0e8a1d02b2aba55b5de32ae5ad0eaff79cf74 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Fri, 30 Jul 2021 11:34:40 +0100 Subject: [PATCH 075/139] Fixed some issues in the single properties select --- .../api/wx/ui/mc/block/properties/single/main.py | 13 ++++++++++++- .../api/wx/ui/mc/block/properties/single/manual.py | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py index 9366a4a2..af9b8319 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py @@ -83,6 +83,7 @@ def _on_push(self) -> bool: ) for name in properties } + self._simple.properties = self.properties return True def _on_change(self, evt: SinglePropertiesChangeEvent): @@ -100,7 +101,17 @@ def demo(): An app instance must be created first. """ translation_manager = PyMCTranslate.new_translation_manager() - for block in (("minecraft", "oak_fence"), ("modded", "block")): + for block in ( + ("minecraft", "oak_fence", { + 'east': amulet_nbt.TAG_String("false"), + 'north': amulet_nbt.TAG_String("true"), + 'south': amulet_nbt.TAG_String("false"), + 'west': amulet_nbt.TAG_String("false") + }), + ("modded", "block", { + 'test': amulet_nbt.TAG_String("hello"), + }) + ): dialog = wx.Dialog( None, title=f"SinglePropertySelect with block {block[0]}:{block[1]}", diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/single/manual.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/manual.py index 0fd6bed1..2e1dacde 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/single/manual.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/single/manual.py @@ -72,7 +72,7 @@ def _add_property(self, name: str = "", value: SNBTType = ""): sizer.Add(value_entry, 1, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) snbt_text = wx.StaticText(self, style=wx.ALIGN_CENTER) sizer.Add(snbt_text, 1, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) - self._change_value("", snbt_text) + self._change_value(value, snbt_text) value_entry.Bind(wx.EVT_TEXT, lambda evt: self._on_value_change(evt, snbt_text)) self._property_sizer.Add(sizer, 1, wx.TOP | wx.EXPAND, 5) From 956085afdf4e493e10442cc173c351362006f3a6 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Mon, 2 Aug 2021 17:03:28 +0100 Subject: [PATCH 076/139] Fixed the wrong property reference --- amulet_map_editor/api/wx/ui/mc/base/api/block/normal.py | 2 +- amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/block/normal.py b/amulet_map_editor/api/wx/ui/mc/base/api/block/normal.py index 78cae0ee..ad623d24 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/block/normal.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/block/normal.py @@ -88,7 +88,7 @@ def _set_properties(self, properties: Optional[PropertyType]): block_manager = self._translation_manager.get_version( self.platform, self.version_number ).block - if self.platform in block_manager.namespaces( + if self.namespace in block_manager.namespaces( self.force_blockstate ) and self.base_name in block_manager.base_names( self.namespace, self.force_blockstate diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py b/amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py index a24a8715..bb00abc9 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py @@ -68,7 +68,7 @@ def _clean_properties( block_manager = self._translation_manager.get_version( self.platform, self.version_number ).block - if self.platform in block_manager.namespaces( + if self.namespace in block_manager.namespaces( self.force_blockstate ) and self.base_name in block_manager.base_names( self.namespace, self.force_blockstate From fb9a77377efafbc90ca4bde817ba409169acfaaf Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Mon, 2 Aug 2021 17:04:20 +0100 Subject: [PATCH 077/139] Reformatted --- .../wx/ui/mc/block/properties/single/main.py | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py index af9b8319..d9d87c2d 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py @@ -102,15 +102,23 @@ def demo(): """ translation_manager = PyMCTranslate.new_translation_manager() for block in ( - ("minecraft", "oak_fence", { - 'east': amulet_nbt.TAG_String("false"), - 'north': amulet_nbt.TAG_String("true"), - 'south': amulet_nbt.TAG_String("false"), - 'west': amulet_nbt.TAG_String("false") - }), - ("modded", "block", { - 'test': amulet_nbt.TAG_String("hello"), - }) + ( + "minecraft", + "oak_fence", + { + "east": amulet_nbt.TAG_String("false"), + "north": amulet_nbt.TAG_String("true"), + "south": amulet_nbt.TAG_String("false"), + "west": amulet_nbt.TAG_String("false"), + }, + ), + ( + "modded", + "block", + { + "test": amulet_nbt.TAG_String("hello"), + }, + ), ): dialog = wx.Dialog( None, From c56df6dc6dbd01ff68ca181282a1e503f1d1747b Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Mon, 2 Aug 2021 17:36:28 +0100 Subject: [PATCH 078/139] Fixed the multiple property select --- .../api/wx/ui/mc/base/api/block/wildcard.py | 46 ++++-- .../multiple/automatic/automatic.py | 55 +++---- .../ui/mc/block/properties/multiple/base.py | 8 +- .../ui/mc/block/properties/multiple/events.py | 8 +- .../ui/mc/block/properties/multiple/main.py | 148 ++++++++++-------- .../ui/mc/block/properties/multiple/manual.py | 27 ++-- 6 files changed, 157 insertions(+), 135 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py b/amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py index bb00abc9..56bea3d4 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py @@ -52,6 +52,20 @@ def __init__( self._selected_properties = None self._set_selected_properties(selected_properties) + def _block_manager(self): + return self._translation_manager.get_version( + self.platform, self.version_number + ).block + + @property + def is_vanilla(self): + block_manager = self._block_manager() + return self.namespace in block_manager.namespaces( + self.force_blockstate + ) and self.base_name in block_manager.base_names( + self.namespace, self.force_blockstate + ) + @property def all_properties(self) -> PropertyTypeMultiple: return self._all_properties @@ -65,16 +79,9 @@ def _clean_properties( self, properties: Optional[PropertyTypeMultiple] ) -> PropertyTypeMultiple: out_properties: PropertyTypeMultiple = {} - block_manager = self._translation_manager.get_version( - self.platform, self.version_number - ).block - if self.namespace in block_manager.namespaces( - self.force_blockstate - ) and self.base_name in block_manager.base_names( - self.namespace, self.force_blockstate - ): + if self.is_vanilla: # Vanilla block. Make sure the property is valid - block_spec = block_manager.get_specification( + block_spec = self._block_manager().get_specification( self.namespace, self.base_name, self.force_blockstate ) props = block_spec.get("properties", {}) @@ -116,8 +123,19 @@ def selected_properties(self, selected_properties: PropertyTypeMultiple): def _set_selected_properties( self, selected_properties: Optional[PropertyTypeMultiple] ): - self._selected_properties = { - name: tuple(nbt for nbt in nbts if nbt in self.all_properties[name]) - for name, nbts in self._clean_properties(selected_properties).items() - if name in self.all_properties - } + if self.is_vanilla: + self._selected_properties = { + name: tuple(nbt for nbt in nbts if nbt in self.all_properties[name]) + for name, nbts in self._clean_properties(selected_properties).items() + if name in self.all_properties + } + else: + props = self._clean_properties(selected_properties) + all_props = self.all_properties + for name, nbts in props.items(): + if name in all_props: + all_props[name] = tuple(set(all_props[name] + nbts)) + else: + all_props[name] = nbts + self.all_properties = all_props + self._selected_properties = props diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/automatic/automatic.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/automatic/automatic.py index 0b557b23..59840d9e 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/automatic/automatic.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/automatic/automatic.py @@ -1,20 +1,15 @@ import wx -from typing import Dict, List, Tuple +from typing import Dict, Tuple -import PyMCTranslate -from amulet.api.block import PropertyTypeMultiple +from amulet.api.block import PropertyTypeMultiple, PropertyDataTypes from ..events import MultiplePropertiesChangeEvent from ..base import BaseMultipleProperty from .popup import PropertyValueComboPopup class AutomaticMultipleProperty(BaseMultipleProperty): - def __init__( - self, - parent: wx.Window, - translation_manager: PyMCTranslate.TranslationManager, - ): - super().__init__(parent, translation_manager) + def __init__(self, parent: wx.Window): + super().__init__(parent) header_sizer = wx.BoxSizer(wx.HORIZONTAL) self._sizer.Add(header_sizer, 0, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, 5) @@ -27,29 +22,37 @@ def __init__( self._property_sizer = wx.GridSizer(2, 5, 5) self._sizer.Add(self._property_sizer, 0, wx.ALL | wx.EXPAND, 5) + self._states: PropertyTypeMultiple = {} self._properties: Dict[str, Tuple[wx.ComboCtrl, PropertyValueComboPopup]] = {} - self._specification: dict = {} - def rebuild_ui(self, specification: dict): - """ - Rebuild the UI. - Run when the version or block is changed. - """ - self._specification = specification + @property + def all_properties(self) -> PropertyTypeMultiple: + return self._states + + @all_properties.setter + def all_properties(self, all_properties: PropertyTypeMultiple): + self._states = {} + for key, choices in all_properties.items(): + if isinstance(key, str): + valid_choices = tuple( + choice + for choice in choices + if isinstance(choice, PropertyDataTypes) + ) + if valid_choices: + self._states[key] = valid_choices self.Freeze() self._properties.clear() self._property_sizer.Clear(True) - spec_properties: Dict[str, List[str]] = self._specification.get( - "properties", {} - ) - for name, choices in spec_properties.items(): + props = {} + for name, choices in self._states.items(): label = wx.StaticText(self, label=name) self._property_sizer.Add(label, 0, wx.ALIGN_CENTER) def create_choice(): choice = wx.ComboCtrl(self, style=wx.CB_READONLY) - popup = PropertyValueComboPopup(choices) + popup = PropertyValueComboPopup([c.to_snbt() for c in choices]) choice.SetPopupControl(popup) choice.SetValue(popup.GetStringValue()) @@ -69,6 +72,8 @@ def on_close(evt): self._properties[name] = (choice, popup) create_choice() + props[name] = choices + self.selected_properties = props self.Fit() self.GetTopLevelParent().Layout() self.Thaw() @@ -92,11 +97,3 @@ def selected_properties(self, properties: PropertyTypeMultiple): popup.checked_nbt = nbt_tuple choice.SetValue(popup.GetStringValue()) self.Thaw() - - @property - def all_properties(self) -> PropertyTypeMultiple: - return {prop: popup.all_nbt for prop, (_, popup) in self._properties.items()} - - @all_properties.setter - def all_properties(self, all_properties: PropertyTypeMultiple): - pass diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/base.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/base.py index c031874c..f0dcd63c 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/base.py @@ -1,19 +1,13 @@ import wx -import PyMCTranslate from amulet.api.block import PropertyTypeMultiple class BaseMultipleProperty(wx.Panel): - def __init__( - self, - parent: wx.Window, - translation_manager: PyMCTranslate.TranslationManager, - ): + def __init__(self, parent: wx.Window): super().__init__(parent) self._sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(self._sizer) - self._translation_manager = translation_manager @property def all_properties(self) -> PropertyTypeMultiple: diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/events.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/events.py index 21390572..f4f45269 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/events.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/events.py @@ -10,10 +10,10 @@ class MultiplePropertiesChangeEvent(wx.PyEvent): Run when the properties UI changes. """ - def __init__(self, properties: PropertyTypeMultiple): + def __init__(self, selected_properties: PropertyTypeMultiple): wx.PyEvent.__init__(self, eventType=_MultiplePropertiesChangeEventType) - self._properties = properties + self._selected_properties = selected_properties @property - def properties(self) -> PropertyTypeMultiple: - return self._properties + def selected_properties(self) -> PropertyTypeMultiple: + return self._selected_properties diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py index 33b0a4e7..8af2a875 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py @@ -1,15 +1,18 @@ import wx -from typing import Tuple +from typing import Tuple, Dict, Any, List import PyMCTranslate +import amulet_nbt from amulet.api.block import PropertyTypeMultiple +from amulet_map_editor.api.wx.ui.mc.base import WildcardMCBlock from ..base import BasePropertySelect from .automatic import AutomaticMultipleProperty from .manual import ManualMultipleProperty +from .events import MultiplePropertiesChangeEvent, EVT_MULTIPLE_PROPERTIES_CHANGE -class MultiplePropertySelect(BasePropertySelect): +class MultiplePropertySelect(BasePropertySelect, WildcardMCBlock): """ This is a UI which lets the user pick zero or more values for each property. If the block is known it will be populated from the specification. @@ -24,94 +27,74 @@ def __init__( version_number: Tuple[int, int, int], force_blockstate: bool, namespace: str, - block_name: str, - properties: PropertyTypeMultiple = None, + base_name: str, + selected_properties: PropertyTypeMultiple = None, + all_properties: PropertyTypeMultiple = None, + state: Dict[str, Any] = None, ): - super().__init__( + state = state or {} + state.setdefault("selected_properties", selected_properties or {}) + state.setdefault("all_properties", all_properties) + BasePropertySelect.__init__( + self, parent, translation_manager, platform, version_number, force_blockstate, namespace, - block_name, - style=wx.BORDER_SIMPLE, + base_name, + state, ) self._manual_enabled = False - self._simple = AutomaticMultipleProperty(self, translation_manager) + self._simple = AutomaticMultipleProperty(self) self._sizer.Add(self._simple, 1, wx.EXPAND) - self._manual = ManualMultipleProperty(self, translation_manager) + self._manual = ManualMultipleProperty(self) self._sizer.Add(self._manual, 1, wx.EXPAND) + self._simple.Bind(EVT_MULTIPLE_PROPERTIES_CHANGE, self._on_change) + self._manual.Bind(EVT_MULTIPLE_PROPERTIES_CHANGE, self._on_change) + self.push(True) - if properties is None: - properties = {} - self.selected_properties = properties - self._rebuild_ui() - - @property - def selected_properties(self) -> PropertyTypeMultiple: - """ - The values that are checked for each property. - This UI can have more than one property value checked (ticked). - """ - if self._manual_enabled: - return self._manual.selected_properties - else: - return self._simple.selected_properties - - @selected_properties.setter - def selected_properties(self, selected_properties: PropertyTypeMultiple): - self.Freeze() - if self._manual_enabled: - self._manual.selected_properties = selected_properties - else: - self._simple.selected_properties = selected_properties - self.TopLevelParent.Layout() - self.Thaw() - - @property - def all_properties(self) -> PropertyTypeMultiple: - """ - The values that are checked for each property. - This UI can have more than one property value checked (ticked). - """ - if self._manual_enabled: - return self._manual.all_properties - else: - return self._simple.all_properties + def _init_state(self, state: Dict[str, Any]): + WildcardMCBlock.__init__(self, **state) - @all_properties.setter - def all_properties(self, all_properties: PropertyTypeMultiple): - self.Freeze() - if self._manual_enabled: - self._manual.all_properties = all_properties - else: - self._simple.all_properties = all_properties - self.TopLevelParent.Layout() - self.Thaw() - - def _rebuild_ui(self): + def _on_push(self) -> bool: self.Freeze() translator = self._translation_manager.get_version( self._platform, self._version_number ).block - self._manual_enabled = self._block_name not in translator.base_names( - self._namespace, self._force_blockstate + self._manual_enabled = self.base_name not in translator.base_names( + self.namespace, self.force_blockstate ) if self._manual_enabled: self._simple.Hide() self._manual.Show() + self._manual.all_properties = self.all_properties + self._manual.selected_properties = self.selected_properties else: self._manual.Hide() self._simple.Show() - self._simple.rebuild_ui( - translator.get_specification( - self._namespace, self._block_name, self._force_blockstate - ) + spec = translator.get_specification( + self.namespace, self.base_name, self._force_blockstate ) + properties: Dict[str, List[str]] = spec.get("properties", {}) + self._simple.all_properties = { + name: [amulet_nbt.from_snbt(p) for p in properties[name]] + for name in properties + } + self._simple.selected_properties = self.selected_properties self.Thaw() + return True + + def _on_change(self, evt: MultiplePropertiesChangeEvent): + if evt.selected_properties != self.selected_properties: + self._set_selected_properties(evt.selected_properties) + wx.PostEvent( + self, + MultiplePropertiesChangeEvent(self.selected_properties), + ) def demo(): @@ -120,7 +103,34 @@ def demo(): An app instance must be created first. """ translation_manager = PyMCTranslate.new_translation_manager() - for block in (("minecraft", "oak_fence"), ("modded", "block")): + for block in ( + ( + "minecraft", + "oak_fence", + { + "east": ( + amulet_nbt.TAG_String("true"), + amulet_nbt.TAG_String("false"), + ), + "north": ( + amulet_nbt.TAG_String("true"), + amulet_nbt.TAG_String("false"), + ), + "south": (amulet_nbt.TAG_String("false"),), + "west": (amulet_nbt.TAG_String("true"),), + }, + ), + ( + "modded", + "block", + { + "test": ( + amulet_nbt.TAG_String("hello"), + amulet_nbt.TAG_String("hello2"), + ), + }, + ), + ): dialog = wx.Dialog( None, title=f"MultiplePropertySelect with block {block[0]}:{block[1]}", @@ -128,21 +138,27 @@ def demo(): ) sizer = wx.BoxSizer() dialog.SetSizer(sizer) + obj = MultiplePropertySelect( + dialog, translation_manager, "java", (1, 16, 0), False, *block + ) + sizer.Add( - MultiplePropertySelect( - dialog, translation_manager, "java", (1, 16, 0), False, *block - ), + obj, 1, wx.ALL, 5, ) + def on_change(evt: MultiplePropertiesChangeEvent): + print(evt.selected_properties) + def get_on_close(dialog_): def on_close(evt): dialog_.Destroy() return on_close + obj.Bind(EVT_MULTIPLE_PROPERTIES_CHANGE, on_change) dialog.Bind(wx.EVT_CLOSE, get_on_close(dialog)) dialog.Show() dialog.Fit() diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/manual.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/manual.py index 74f235ad..816c7dcd 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/manual.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/manual.py @@ -1,7 +1,6 @@ import wx from typing import Tuple, Dict -import PyMCTranslate import amulet_nbt from amulet_nbt import SNBTType from amulet.api.block import PropertyDataTypes, PropertyType, PropertyTypeMultiple @@ -11,10 +10,8 @@ class ManualMultipleProperty(BaseMultipleProperty): - def __init__( - self, parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager - ): - super().__init__(parent, translation_manager) + def __init__(self, parent: wx.Window): + super().__init__(parent) header_sizer = wx.BoxSizer(wx.HORIZONTAL) add_button = wx.BitmapButton( self, bitmap=ADD_ICON.bitmap(30, 30), size=(30, 30) @@ -61,7 +58,7 @@ def _add_property(self, name: str = "", value: SNBTType = ""): sizer.Add(value_entry, 1, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) snbt_text = wx.StaticText(self, style=wx.ALIGN_CENTER) sizer.Add(snbt_text, 1, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) - self._change_value("", snbt_text) + self._change_value(value, snbt_text) value_entry.Bind(wx.EVT_TEXT, lambda evt: self._on_value_change(evt, snbt_text)) self._property_sizer.Add(sizer, 1, wx.TOP | wx.EXPAND, 5) @@ -120,27 +117,27 @@ def properties(self, properties: PropertyType): for name, value in properties.items(): self._add_property(name, value.to_snbt()) - # TODO: implement this properly @property - def selected_properties(self) -> PropertyTypeMultiple: + def all_properties(self) -> PropertyTypeMultiple: return {prop: (val,) for prop, val in self.properties.items()} - @selected_properties.setter - def selected_properties(self, properties: PropertyTypeMultiple): + @all_properties.setter + def all_properties(self, all_properties: PropertyTypeMultiple): props = {} - for prop, val in properties: + for prop, val in all_properties.items(): if val: props[prop] = val[0] self.properties = props + # TODO: implement this properly @property - def all_properties(self) -> PropertyTypeMultiple: + def selected_properties(self) -> PropertyTypeMultiple: return {prop: (val,) for prop, val in self.properties.items()} - @all_properties.setter - def all_properties(self, all_properties: PropertyTypeMultiple): + @selected_properties.setter + def selected_properties(self, properties: PropertyTypeMultiple): props = {} - for prop, val in all_properties: + for prop, val in properties.items(): if val: props[prop] = val[0] self.properties = props From 7760fe14d51f69ebbee24d5c97512003e669ad85 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Tue, 3 Aug 2021 13:09:44 +0100 Subject: [PATCH 079/139] Fixed an issue with the multi custom block property ui The old code would rebuild the UI each time the user changed the code meaning the user could only change one character at a time --- .../api/wx/ui/mc/base/api/block/wildcard.py | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py b/amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py index 56bea3d4..2334f00e 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py @@ -48,8 +48,17 @@ def __init__( base_name, ) self._all_properties = None - self._set_all_properties(all_properties) self._selected_properties = None + if not self.is_vanilla: + selected_properties = self._clean_properties(selected_properties) + all_properties = self._clean_properties(all_properties) + for name, nbts in selected_properties.items(): + if name in all_properties: + ap = all_properties[name] + all_properties[name] = ap + tuple(nbt for nbt in nbts if nbt not in ap) + else: + all_properties[name] = nbts + self._set_all_properties(all_properties) self._set_selected_properties(selected_properties) def _block_manager(self): @@ -123,19 +132,8 @@ def selected_properties(self, selected_properties: PropertyTypeMultiple): def _set_selected_properties( self, selected_properties: Optional[PropertyTypeMultiple] ): - if self.is_vanilla: - self._selected_properties = { - name: tuple(nbt for nbt in nbts if nbt in self.all_properties[name]) - for name, nbts in self._clean_properties(selected_properties).items() - if name in self.all_properties - } - else: - props = self._clean_properties(selected_properties) - all_props = self.all_properties - for name, nbts in props.items(): - if name in all_props: - all_props[name] = tuple(set(all_props[name] + nbts)) - else: - all_props[name] = nbts - self.all_properties = all_props - self._selected_properties = props + self._selected_properties = { + name: tuple(nbt for nbt in nbts if nbt in self.all_properties[name]) + for name, nbts in self._clean_properties(selected_properties).items() + if name in self.all_properties + } From ce07e384e086a582f4f1cf02071491cde4780bc8 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Tue, 3 Aug 2021 16:35:06 +0100 Subject: [PATCH 080/139] Reworked the BlockDefine UIs --- .../api/wx/ui/mc/base/api/block/wildcard.py | 4 +- .../api/wx/ui/mc/base/base_define.py | 18 +- .../api/wx/ui/mc/block/__init__.py | 4 +- .../api/wx/ui/mc/block/define/widget/base.py | 91 ++++--- .../wx/ui/mc/block/define/widget/normal.py | 233 +++++++++++------ .../wx/ui/mc/block/define/widget/wildcard.py | 238 ++++++++++++++---- .../block_identifier_select.py | 1 + .../api/wx/ui/mc/block/properties/base.py | 9 +- .../ui/mc/block/properties/multiple/main.py | 4 +- .../wx/ui/mc/block/properties/single/main.py | 4 +- 10 files changed, 420 insertions(+), 186 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py b/amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py index 2334f00e..3dbb6f25 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py @@ -55,7 +55,9 @@ def __init__( for name, nbts in selected_properties.items(): if name in all_properties: ap = all_properties[name] - all_properties[name] = ap + tuple(nbt for nbt in nbts if nbt not in ap) + all_properties[name] = ap + tuple( + nbt for nbt in nbts if nbt not in ap + ) else: all_properties[name] = nbts self._set_all_properties(all_properties) diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_define.py b/amulet_map_editor/api/wx/ui/mc/base/base_define.py index ba789520..5e3b0990 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/base_define.py +++ b/amulet_map_editor/api/wx/ui/mc/base/base_define.py @@ -8,7 +8,11 @@ from amulet_map_editor.api.wx.ui.mc.base.base_identifier_select import ( BaseIdentifierSelect, ) -from amulet_map_editor.api.wx.ui.mc import version as mc_version +from amulet_map_editor.api.wx.ui.mc.version import ( + VersionSelect, + EVT_VERSION_CHANGE, + VersionChangeEvent, +) from amulet_map_editor.api.wx.ui.mc.base.api import BaseMCVersion @@ -42,11 +46,11 @@ def __init__( self._top_sizer = wx.BoxSizer(wx.VERTICAL) if orientation == wx.HORIZONTAL: - self._sizer.Add(self._top_sizer, 1, wx.EXPAND) + self._sizer.Add(self._top_sizer, 0, wx.EXPAND) else: - self._sizer.Add(self._top_sizer, 2, wx.EXPAND) + self._sizer.Add(self._top_sizer, 0, wx.EXPAND) - self._version_picker = mc_version.VersionSelect( + self._version_picker = VersionSelect( self, translation_manager, platform, @@ -56,9 +60,7 @@ def __init__( **kwargs, ) self._top_sizer.Add(self._version_picker, 0, wx.EXPAND) - self._version_picker.Bind( - mc_version.EVT_VERSION_CHANGE, self._on_version_change - ) + self._version_picker.Bind(EVT_VERSION_CHANGE, self._on_version_change) self._picker: Optional[BaseIdentifierSelect] = None @@ -71,7 +73,7 @@ def _init_state(self, state: Dict[str, Any]): """ BaseMCVersion.__init__(self, **state) - def _on_version_change(self, evt: mc_version.VersionChangeEvent): + def _on_version_change(self, evt: VersionChangeEvent): raise NotImplementedError def _on_push(self) -> bool: diff --git a/amulet_map_editor/api/wx/ui/mc/block/__init__.py b/amulet_map_editor/api/wx/ui/mc/block/__init__.py index 1c8eec62..106599cb 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/__init__.py +++ b/amulet_map_editor/api/wx/ui/mc/block/__init__.py @@ -1,4 +1,4 @@ -from .identifier_select import BlockIdentifierSelect +from .properties import * +from .identifier_select import * from .define import * from .multi_block_define import MultiBlockDefine -from .properties import * diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py index bee7958d..5a171c17 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py @@ -1,18 +1,21 @@ import wx.lib.scrolledpanel -from typing import Tuple, Optional +from typing import Tuple, Optional, Dict, Any import PyMCTranslate from amulet_map_editor.api.wx.ui.mc.base.base_define import BaseDefine -from amulet_map_editor.api.wx.ui.mc.block import BlockSelect +from amulet_map_editor.api.wx.ui.mc.block import BlockIdentifierSelect -from amulet_map_editor.api.wx.ui.mc.block.properties import ( +from amulet_map_editor.api.wx.ui.mc.block import ( BasePropertySelect, + BlockIDChangeEvent, + EVT_BLOCK_ID_CHANGE, ) -from amulet_map_editor.api.wx.ui.mc.base import BaseMCBlockAPI +from amulet_map_editor.api.wx.ui.mc.base import BaseMCBlockIdentifier +from amulet_map_editor.api.wx.ui.mc.version import VersionChangeEvent -class BaseBlockDefine(BaseDefine, BaseMCBlockAPI): +class BaseBlockDefine(BaseDefine, BaseMCBlockIdentifier): """ A UI that merges a version select widget with a block select widget and a property select. """ @@ -26,54 +29,62 @@ def __init__( version_number: Tuple[int, int, int] = None, force_blockstate: bool = None, namespace: str = None, - block_name: str = None, + base_name: str = None, show_pick_block: bool = False, - **kwargs, + state: Dict[str, Any] = None, ): - super().__init__( + state = state or {} + state.setdefault("translation_manager", translation_manager) + state.setdefault("platform", platform) + state.setdefault("version_number", version_number) + state.setdefault("force_blockstate", force_blockstate) + state.setdefault("namespace", namespace) + state.setdefault("base_name", base_name) + BaseDefine.__init__( + self, parent, translation_manager, - BlockSelect, orientation, platform, version_number, + force_blockstate, + state=state, + ) + self._picker = BlockIdentifierSelect( + self, + translation_manager, + self.platform, + self.version_number, + self.force_blockstate, namespace, - default_name=block_name, - show_pick=show_pick_block, - force_blockstate=force_blockstate, - **kwargs, + base_name, + show_pick_block, ) + self._top_sizer.Add(self._picker, 0, wx.EXPAND | wx.TOP, 5) + self._picker.Bind(EVT_BLOCK_ID_CHANGE, self._on_block_change) self._property_picker: Optional[BasePropertySelect] = None - def _on_picker_change(self, evt): - self._update_properties() - evt.Skip() + def _init_state(self, state: Dict[str, Any]): + raise NotImplementedError + + def _on_version_change(self, evt: VersionChangeEvent): + # TODO: Translate the block and properties to the new version + raise NotImplementedError + + def _on_block_change(self, evt: BlockIDChangeEvent): + self._set_namespace(evt.namespace) + self._set_base_name(evt.base_name) + self._property_picker.namespace = self.namespace + self._property_picker.base_name = self.base_name def _on_property_change(self, evt): self.Layout() evt.Skip() - def _update_properties(self): - self._property_picker.version_block = ( - self._version_picker.platform, - self._version_picker.version_number, - self._version_picker.force_blockstate, - self._picker.namespace, - self._picker.name, - ) - - @property - def force_blockstate(self) -> bool: - return self._version_picker.force_blockstate - - @force_blockstate.setter - def force_blockstate(self, force_blockstate: bool): - self._version_picker.force_blockstate = force_blockstate - - @property - def block_name(self) -> str: - return self._picker.name - - @block_name.setter - def block_name(self, block_name: str): - self._picker.name = block_name + def _on_push(self) -> bool: + update = super()._on_push() + if update: + self._property_picker.platform = self.platform + self._property_picker.version_number = self.version_number + self._property_picker.force_blockstate = self.force_blockstate + return update diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py index 064f5239..8eb43cfb 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py @@ -1,20 +1,22 @@ import wx import wx.lib.scrolledpanel -from typing import Tuple, Optional +from typing import Tuple, Dict, Any import PyMCTranslate from amulet.api.block import PropertyType, Block -from amulet.api.block_entity import BlockEntity -from amulet_map_editor.api.wx.ui.mc.block.properties import ( +from amulet_map_editor.api.wx.ui.mc.block import ( + BlockIDChangeEvent, SinglePropertySelect, EVT_SINGLE_PROPERTIES_CHANGE, + SinglePropertiesChangeEvent, ) from amulet_map_editor.api.wx.ui.mc.block.define.widget.base import BaseBlockDefine -from amulet_map_editor.api.wx.ui.mc.base import NormalMCBlockAPI +from amulet_map_editor.api.wx.ui.mc.base import NormalMCBlock +from amulet_map_editor.api.wx.ui.mc.version import VersionChangeEvent -class BlockDefine(BaseBlockDefine, NormalMCBlockAPI): +class BlockDefine(BaseBlockDefine, NormalMCBlock): """ A UI that merges a version select widget with a block select widget and a property select. """ @@ -28,12 +30,15 @@ def __init__( version_number: Tuple[int, int, int] = None, force_blockstate: bool = None, namespace: str = None, - block_name: str = None, + base_name: str = None, properties: PropertyType = None, show_pick_block: bool = False, - **kwargs, + state: Dict[str, Any] = None, ): - super().__init__( + state = state or {} + state.setdefault("properties", properties) + BaseBlockDefine.__init__( + self, parent, translation_manager, orientation, @@ -41,9 +46,9 @@ def __init__( version_number, force_blockstate, namespace, - block_name, + base_name, show_pick_block=show_pick_block, - **kwargs, + state=state, ) right_sizer = wx.BoxSizer(wx.VERTICAL) @@ -52,65 +57,104 @@ def __init__( self._property_picker = SinglePropertySelect( self, translation_manager, - self._version_picker.platform, - self._version_picker.version_number, - self._version_picker.force_blockstate, - self._picker.namespace, - self._picker.name, - properties, + self.platform, + self.version_number, + self.force_blockstate, + self.namespace, + self.base_name, + self.properties, ) right_sizer.Add(self._property_picker, 1, wx.EXPAND) self._property_picker.Bind( EVT_SINGLE_PROPERTIES_CHANGE, self._on_property_change ) - self.Layout() - @property - def properties(self) -> PropertyType: - return self._property_picker.properties + def _init_state(self, state: Dict[str, Any]): + NormalMCBlock.__init__(self, **state) - @properties.setter - def properties(self, properties: PropertyType): - self._property_picker.properties = properties - - @property - def block(self) -> Block: - return Block(self.namespace, self.block_name, self.properties) - - @block.setter - def block(self, block: Block): - self._picker.namespace, self._picker.base_name = ( - block.namespace, - block.base_name, + def _on_version_change(self, evt: VersionChangeEvent): + self.Freeze() + old_platform, old_version = self.platform, self.version_number + old_namespace, old_base_name, old_properties = ( + self.namespace, + self.base_name, + self.properties, ) - self._update_properties() - self.properties = block.properties - - @property - def block_entity(self) -> Optional[BlockEntity]: - return None # TODO - @block_entity.setter - def block_entity(self, block_entity: Optional[BlockEntity]): - if block_entity is not None: - pass # TODO - - @property - def universal_block(self) -> Tuple[Block, Optional[BlockEntity]]: - return self._translation_manager.get_version( + self._set_platform(evt.platform) + self._set_version_number(evt.version_number) + self._set_force_blockstate(evt.force_blockstate) + + ( + universal_block, + universal_block_entity, + _, + ) = self._translation_manager.get_version( + old_platform, old_version + ).block.to_universal( + Block(old_namespace, old_base_name, old_properties), + force_blockstate=self.force_blockstate, + ) + new_block, _, _ = self._translation_manager.get_version( self.platform, self.version_number - ).block.to_universal(self.block, self.block_entity, self.force_blockstate)[:2] + ).block.from_universal( + universal_block, universal_block_entity, self.force_blockstate + ) - @universal_block.setter - def universal_block(self, universal_block: Tuple[Block, Optional[BlockEntity]]): - block, block_entity = universal_block - v_block, v_block_entity = self._translation_manager.get_version( - self.platform, self.version_number - ).block.from_universal(block, block_entity, self.force_blockstate)[:2] - if isinstance(v_block, Block): - self.block = v_block - self.block_entity = v_block_entity + if isinstance(new_block, Block): + self._set_namespace(new_block.namespace) + self._set_base_name(new_block.base_name) + self._set_properties(new_block.properties) + else: + self._set_namespace(None) + self._set_base_name(None) + self._set_properties(None) + + ( + self._picker.platform, + self._picker.version_number, + self._picker.force_blockstate, + self._picker.namespace, + self._picker.base_name, + ) = ( + self._property_picker.platform, + self._property_picker.version_number, + self._property_picker.force_blockstate, + self._property_picker.namespace, + self._property_picker.base_name, + ) = ( + self.platform, + self.version_number, + self.force_blockstate, + self.namespace, + self.base_name, + ) + self._property_picker.properties = self.properties + + # wx.PostEvent( + # self, + # BiomeIDChangeEvent( + # self.namespace, + # self.base_name, + # old_namespace, + # old_base_name, + # ), + # ) + self.Thaw() + + def _on_block_change(self, evt: BlockIDChangeEvent): + super()._on_block_change(evt) + self._set_properties(None) + self._property_picker.properties = self.properties + + def _on_push(self) -> bool: + update = super()._on_push() + self._set_properties(self.properties) + if update or self.properties != self._property_picker.properties: + update = True + self._property_picker.properties = self.properties + return update def demo(): @@ -118,23 +162,66 @@ def demo(): Show a demo version of the UI. An app instance must be created first. """ + import amulet_nbt + translation_manager = PyMCTranslate.new_translation_manager() - dialog = wx.Dialog( - None, - title="BlockDefine", - style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT, - ) - sizer = wx.BoxSizer() - dialog.SetSizer(sizer) - sizer.Add( - BlockDefine(dialog, translation_manager, wx.HORIZONTAL), - 1, - wx.ALL | wx.EXPAND, - 5, - ) - dialog.Show() - dialog.Fit() - dialog.Bind(wx.EVT_CLOSE, lambda evt: dialog.Destroy()) + for block in ( + ( + "minecraft", + "oak_fence", + { + "east": amulet_nbt.TAG_String("false"), + "north": amulet_nbt.TAG_String("true"), + "south": amulet_nbt.TAG_String("false"), + "west": amulet_nbt.TAG_String("false"), + }, + ), + ( + "modded", + "block", + { + "test": amulet_nbt.TAG_String("hello"), + }, + ), + ): + + dialog = wx.Dialog( + None, + title=f"BlockDefine with block {block[0]}:{block[1]}", + style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT, + ) + sizer = wx.BoxSizer() + dialog.SetSizer(sizer) + obj = BlockDefine( + dialog, + translation_manager, + wx.HORIZONTAL, + "java", + (1, 16, 0), + False, + *block, + ) + sizer.Add( + obj, + 1, + wx.ALL | wx.EXPAND, + 5, + ) + + def on_change(evt: SinglePropertiesChangeEvent): + print(evt.properties) + + obj.Bind(EVT_SINGLE_PROPERTIES_CHANGE, on_change) + + def get_on_close(dialog_): + def on_close(evt): + dialog_.Destroy() + + return on_close + + dialog.Bind(wx.EVT_CLOSE, get_on_close(dialog)) + dialog.Show() + dialog.Fit() if __name__ == "__main__": diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py index a369fe3f..9b047fe6 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py @@ -1,19 +1,22 @@ import wx import wx.lib.scrolledpanel -from typing import Tuple +from typing import Tuple, Dict, Any import PyMCTranslate -from amulet.api.block import PropertyTypeMultiple +from amulet.api.block import PropertyTypeMultiple, Block -from amulet_map_editor.api.wx.ui.mc.block.properties import ( +from amulet_map_editor.api.wx.ui.mc.block import ( + BlockIDChangeEvent, MultiplePropertySelect, EVT_MULTIPLE_PROPERTIES_CHANGE, + MultiplePropertiesChangeEvent, ) from amulet_map_editor.api.wx.ui.mc.block.define.widget.base import BaseBlockDefine -from amulet_map_editor.api.wx.ui.mc.base import WildcardMCBlockAPI +from amulet_map_editor.api.wx.ui.mc.base import WildcardMCBlock +from amulet_map_editor.api.wx.ui.mc.version import VersionChangeEvent -class WildcardBlockDefine(BaseBlockDefine, WildcardMCBlockAPI): +class WildcardBlockDefine(BaseBlockDefine, WildcardMCBlock): """ A UI that merges a version select widget with a block select widget and a multi property select. """ @@ -27,12 +30,17 @@ def __init__( version_number: Tuple[int, int, int] = None, force_blockstate: bool = None, namespace: str = None, - block_name: str = None, - properties: PropertyTypeMultiple = None, + base_name: str = None, + selected_properties: PropertyTypeMultiple = None, + all_properties: PropertyTypeMultiple = None, show_pick_block: bool = False, - **kwargs, + state: Dict[str, Any] = None, ): - super().__init__( + state = state or {} + state.setdefault("selected_properties", selected_properties) + state.setdefault("all_properties", all_properties) + BaseBlockDefine.__init__( + self, parent, translation_manager, orientation, @@ -40,9 +48,9 @@ def __init__( version_number, force_blockstate, namespace, - block_name, + base_name, show_pick_block=show_pick_block, - **kwargs, + state=state, ) right_sizer = wx.BoxSizer(wx.VERTICAL) @@ -51,12 +59,13 @@ def __init__( self._property_picker = MultiplePropertySelect( self, translation_manager, - self._version_picker.platform, - self._version_picker.version_number, - self._version_picker.force_blockstate, - self._picker.namespace, - self._picker.name, - properties, + self.platform, + self.version_number, + self.force_blockstate, + self.namespace, + self.base_name, + self.selected_properties, + self.all_properties, ) right_sizer.Add(self._property_picker, 1, wx.EXPAND) self._property_picker.Bind( @@ -65,26 +74,104 @@ def __init__( self.Layout() - @property - def selected_properties(self) -> PropertyTypeMultiple: - """ - The values that are checked for each property. - This UI can have more than one property value checked (ticked). - """ - return self._property_picker.selected_properties + def _init_state(self, state: Dict[str, Any]): + WildcardMCBlock.__init__(self, **state) + + def _on_version_change(self, evt: VersionChangeEvent): + self.Freeze() + old_platform, old_version = self.platform, self.version_number + old_namespace, old_base_name, old_all_properties = ( + self.namespace, + self.base_name, + self.all_properties, + ) + + self._set_platform(evt.platform) + self._set_version_number(evt.version_number) + self._set_force_blockstate(evt.force_blockstate) + + old_properties = { + name: values[0] for name, values in old_all_properties.items() if values + } + + ( + universal_block, + universal_block_entity, + _, + ) = self._translation_manager.get_version( + old_platform, old_version + ).block.to_universal( + Block(old_namespace, old_base_name, old_properties), + force_blockstate=self.force_blockstate, + ) + new_block, _, _ = self._translation_manager.get_version( + self.platform, self.version_number + ).block.from_universal( + universal_block, universal_block_entity, self.force_blockstate + ) + + if isinstance(new_block, Block): + self._set_namespace(new_block.namespace) + self._set_base_name(new_block.base_name) + else: + self._set_namespace(None) + self._set_base_name(None) + self._set_all_properties(None) + self._set_selected_properties(None) + + ( + self._picker.platform, + self._picker.version_number, + self._picker.force_blockstate, + self._picker.namespace, + self._picker.base_name, + ) = ( + self._property_picker.platform, + self._property_picker.version_number, + self._property_picker.force_blockstate, + self._property_picker.namespace, + self._property_picker.base_name, + ) = ( + self.platform, + self.version_number, + self.force_blockstate, + self.namespace, + self.base_name, + ) + self._property_picker.all_properties = self.all_properties + self._property_picker.selected_properties = self.selected_properties - @selected_properties.setter - def selected_properties(self, selected_properties: PropertyTypeMultiple): - self._property_picker.selected_properties = selected_properties + # wx.PostEvent( + # self, + # BiomeIDChangeEvent( + # self.namespace, + # self.base_name, + # old_namespace, + # old_base_name, + # ), + # ) + self.Thaw() - @property - def all_properties(self) -> PropertyTypeMultiple: - """The values that exist for every property.""" - return self._property_picker.all_properties + def _on_block_change(self, evt: BlockIDChangeEvent): + super()._on_block_change(evt) + self._set_all_properties(None) + self._set_selected_properties(None) + self._property_picker.all_properties = self.all_properties + self._property_picker.selected_properties = self.selected_properties - @all_properties.setter - def all_properties(self, all_properties: PropertyTypeMultiple): - self._property_picker.all_properties = all_properties + def _on_push(self) -> bool: + update = super()._on_push() + self._set_all_properties(self.all_properties) + self._set_selected_properties(self.selected_properties) + if ( + update + or self.all_properties != self._property_picker.all_properties + or self.selected_properties != self._property_picker.selected_properties + ): + update = True + self._property_picker.all_properties = self.all_properties + self._property_picker.selected_properties = self.selected_properties + return update def demo(): @@ -92,23 +179,74 @@ def demo(): Show a demo version of the UI. An app instance must be created first. """ + import amulet_nbt + translation_manager = PyMCTranslate.new_translation_manager() - dialog = wx.Dialog( - None, - title="WildcardBlockDefine", - style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT, - ) - sizer = wx.BoxSizer() - dialog.SetSizer(sizer) - sizer.Add( - WildcardBlockDefine(dialog, translation_manager, wx.HORIZONTAL), - 1, - wx.ALL | wx.EXPAND, - 5, - ) - dialog.Show() - dialog.Fit() - dialog.Bind(wx.EVT_CLOSE, lambda evt: dialog.Destroy()) + for block in ( + ( + "minecraft", + "oak_fence", + { + "east": ( + amulet_nbt.TAG_String("true"), + amulet_nbt.TAG_String("false"), + ), + "north": ( + amulet_nbt.TAG_String("true"), + amulet_nbt.TAG_String("false"), + ), + "south": (amulet_nbt.TAG_String("false"),), + "west": (amulet_nbt.TAG_String("true"),), + }, + ), + ( + "modded", + "block", + { + "test": ( + amulet_nbt.TAG_String("hello"), + amulet_nbt.TAG_String("hello2"), + ), + }, + ), + ): + dialog = wx.Dialog( + None, + title=f"WildcardBlockDefine with block {block[0]}:{block[1]}", + style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT, + ) + sizer = wx.BoxSizer() + dialog.SetSizer(sizer) + obj = WildcardBlockDefine( + dialog, + translation_manager, + wx.HORIZONTAL, + "java", + (1, 16, 0), + False, + *block, + ) + + sizer.Add( + obj, + 1, + wx.ALL, + 5, + ) + + def on_change(evt: MultiplePropertiesChangeEvent): + print(evt.selected_properties) + + def get_on_close(dialog_): + def on_close(evt): + dialog_.Destroy() + + return on_close + + obj.Bind(EVT_MULTIPLE_PROPERTIES_CHANGE, on_change) + dialog.Bind(wx.EVT_CLOSE, get_on_close(dialog)) + dialog.Show() + dialog.Fit() if __name__ == "__main__": diff --git a/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py b/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py index dd64ba60..cea42fbe 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py +++ b/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py @@ -37,6 +37,7 @@ def _populate_base_name(self): self.namespace, self.force_blockstate ) self._base_name_list_box.SetItems(self._base_names) + self._update_from_search() def _post_event( self, diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/base.py b/amulet_map_editor/api/wx/ui/mc/block/properties/base.py index 23693a93..bbbae155 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/base.py @@ -11,7 +11,7 @@ def __init__( parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager, platform: str, - version_number: Tuple[int, int, int], + version_number: Tuple[int, ...], force_blockstate: bool, namespace: str = None, base_name: str = None, @@ -39,10 +39,3 @@ def _init_state(self, state: Dict[str, Any]): This is here so that nested classes do not have to init the state managers multiple times. """ BaseMCBlockIdentifier.__init__(self, **state) - - def _rebuild_ui(self): - """ - Rebuild the UI. - Run when the version or block is changed. - """ - raise NotImplementedError diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py index 8af2a875..bc91341d 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py @@ -24,7 +24,7 @@ def __init__( parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager, platform: str, - version_number: Tuple[int, int, int], + version_number: Tuple[int, ...], force_blockstate: bool, namespace: str, base_name: str, @@ -44,7 +44,7 @@ def __init__( force_blockstate, namespace, base_name, - state, + state=state, ) self._manual_enabled = False diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py index d9d87c2d..3eb6034e 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py @@ -23,7 +23,7 @@ def __init__( parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager, platform: str, - version_number: Tuple[int, int, int], + version_number: Tuple[int, ...], force_blockstate: bool, namespace: str = None, base_name: str = None, @@ -41,7 +41,7 @@ def __init__( force_blockstate, namespace, base_name, - state, + state=state, ) self._manual_enabled = False From 9fa6964f39828ecfd688ecdf0187a0f9f4a9627a Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Wed, 4 Aug 2021 11:21:54 +0100 Subject: [PATCH 081/139] Added custom events for the block change UI --- .../api/wx/ui/mc/block/define/__init__.py | 6 + .../api/wx/ui/mc/block/define/events.py | 169 ++++++++++++++++++ .../api/wx/ui/mc/block/define/widget/base.py | 5 - .../wx/ui/mc/block/define/widget/normal.py | 99 ++++++++-- .../wx/ui/mc/block/define/widget/wildcard.py | 102 +++++++++-- 5 files changed, 349 insertions(+), 32 deletions(-) create mode 100644 amulet_map_editor/api/wx/ui/mc/block/define/events.py diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/__init__.py b/amulet_map_editor/api/wx/ui/mc/block/define/__init__.py index 1350a493..cfe9599c 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/__init__.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/__init__.py @@ -1,2 +1,8 @@ from .widget import * from .button import * +from .events import ( + BlockChangeEvent, + EVT_BLOCK_CHANGE, + WildcardBlockChangeEvent, + EVT_WILDCARD_BLOCK_CHANGE, +) diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/events.py b/amulet_map_editor/api/wx/ui/mc/block/define/events.py new file mode 100644 index 00000000..8a6bf062 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/mc/block/define/events.py @@ -0,0 +1,169 @@ +import wx +from amulet.api.block import PropertyType, PropertyTypeMultiple +from amulet.api.data_types import VersionNumberTuple, PlatformType + + +class BaseBlockChangeEvent: + def __init__( + self, + platform: PlatformType, + version_number: VersionNumberTuple, + force_blockstate: bool, + namespace: str, + base_name: str, + old_platform: str, + old_version_number: VersionNumberTuple, + old_force_blockstate: bool, + old_namespace: str, + old_base_name: str, + ): + self._platform = platform + self._version_number = version_number + self._force_blockstate = force_blockstate + self._namespace = namespace + self._base_name = base_name + self._old_platform = old_platform + self._old_version_number = old_version_number + self._old_force_blockstate = old_force_blockstate + self._old_namespace = old_namespace + self._old_base_name = old_base_name + + @property + def platform(self) -> PlatformType: + return self._platform + + @property + def version_number(self) -> VersionNumberTuple: + return self._version_number + + @property + def force_blockstate(self) -> bool: + return self._force_blockstate + + @property + def namespace(self) -> str: + return self._namespace + + @property + def base_name(self) -> str: + return self._base_name + + @property + def old_platform(self) -> str: + return self._old_platform + + @property + def old_version_number(self) -> VersionNumberTuple: + return self._old_version_number + + @property + def old_force_blockstate(self) -> bool: + return self._old_force_blockstate + + @property + def old_namespace(self) -> str: + return self._old_namespace + + @property + def old_base_name(self) -> str: + return self._old_base_name + + +_BlockChangeEventType = wx.NewEventType() +EVT_BLOCK_CHANGE = wx.PyEventBinder(_BlockChangeEventType) + + +class BlockChangeEvent(wx.PyEvent, BaseBlockChangeEvent): + """ + Run when the block define UI changes. + """ + + def __init__( + self, + platform: PlatformType, + version_number: VersionNumberTuple, + force_blockstate: bool, + namespace: str, + base_name: str, + properties: PropertyType, + old_platform: str, + old_version_number: VersionNumberTuple, + old_force_blockstate: bool, + old_namespace: str, + old_base_name: str, + old_properties: PropertyType, + ): + wx.PyEvent.__init__(self, eventType=_BlockChangeEventType) + BaseBlockChangeEvent.__init__( + self, + platform, + version_number, + force_blockstate, + namespace, + base_name, + old_platform, + old_version_number, + old_force_blockstate, + old_namespace, + old_base_name, + ) + self._properties = properties + self._old_properties = old_properties + + @property + def properties(self) -> PropertyType: + return self._properties + + @property + def old_properties(self) -> PropertyType: + return self._old_properties + + +_WildcardBlockChangeEventType = wx.NewEventType() +EVT_WILDCARD_BLOCK_CHANGE = wx.PyEventBinder(_WildcardBlockChangeEventType) + + +class WildcardBlockChangeEvent(wx.PyEvent, BaseBlockChangeEvent): + """ + Run when the wildcard block define UI changes. + """ + + def __init__( + self, + platform: PlatformType, + version_number: VersionNumberTuple, + force_blockstate: bool, + namespace: str, + base_name: str, + selected_properties: PropertyTypeMultiple, + old_platform: str, + old_version_number: VersionNumberTuple, + old_force_blockstate: bool, + old_namespace: str, + old_base_name: str, + old_selected_properties: PropertyTypeMultiple, + ): + wx.PyEvent.__init__(self, eventType=_WildcardBlockChangeEventType) + BaseBlockChangeEvent.__init__( + self, + platform, + version_number, + force_blockstate, + namespace, + base_name, + old_platform, + old_version_number, + old_force_blockstate, + old_namespace, + old_base_name, + ) + self._selected_properties = selected_properties + self._old_selected_properties = old_selected_properties + + @property + def selected_properties(self) -> PropertyTypeMultiple: + return self._selected_properties + + @property + def old_selected_properties(self) -> PropertyTypeMultiple: + return self._old_selected_properties diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py index 5a171c17..919fdfdb 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py @@ -68,7 +68,6 @@ def _init_state(self, state: Dict[str, Any]): raise NotImplementedError def _on_version_change(self, evt: VersionChangeEvent): - # TODO: Translate the block and properties to the new version raise NotImplementedError def _on_block_change(self, evt: BlockIDChangeEvent): @@ -77,10 +76,6 @@ def _on_block_change(self, evt: BlockIDChangeEvent): self._property_picker.namespace = self.namespace self._property_picker.base_name = self.base_name - def _on_property_change(self, evt): - self.Layout() - evt.Skip() - def _on_push(self) -> bool: update = super()._on_push() if update: diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py index 8eb43cfb..ddb95c65 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py @@ -12,6 +12,10 @@ SinglePropertiesChangeEvent, ) from amulet_map_editor.api.wx.ui.mc.block.define.widget.base import BaseBlockDefine +from amulet_map_editor.api.wx.ui.mc.block.define.events import ( + BlockChangeEvent, + EVT_BLOCK_CHANGE, +) from amulet_map_editor.api.wx.ui.mc.base import NormalMCBlock from amulet_map_editor.api.wx.ui.mc.version import VersionChangeEvent @@ -75,7 +79,11 @@ def _init_state(self, state: Dict[str, Any]): def _on_version_change(self, evt: VersionChangeEvent): self.Freeze() - old_platform, old_version = self.platform, self.version_number + old_platform, old_version, old_force_blockstate = ( + self.platform, + self.version_number, + self.force_blockstate, + ) old_namespace, old_base_name, old_properties = ( self.namespace, self.base_name, @@ -132,21 +140,73 @@ def _on_version_change(self, evt: VersionChangeEvent): ) self._property_picker.properties = self.properties - # wx.PostEvent( - # self, - # BiomeIDChangeEvent( - # self.namespace, - # self.base_name, - # old_namespace, - # old_base_name, - # ), - # ) + wx.PostEvent( + self, + BlockChangeEvent( + self.platform, + self.version_number, + self.force_blockstate, + self.namespace, + self.base_name, + self.properties, + old_platform, + old_version, + old_force_blockstate, + old_namespace, + old_base_name, + old_properties, + ), + ) self.Thaw() def _on_block_change(self, evt: BlockIDChangeEvent): + old_namespace, old_base_name, old_properties = ( + self.namespace, + self.base_name, + self.properties, + ) super()._on_block_change(evt) self._set_properties(None) self._property_picker.properties = self.properties + wx.PostEvent( + self, + BlockChangeEvent( + self.platform, + self.version_number, + self.force_blockstate, + self.namespace, + self.base_name, + self.properties, + self.platform, + self.version_number, + self.force_blockstate, + old_namespace, + old_base_name, + old_properties, + ), + ) + + def _on_property_change(self, evt: SinglePropertiesChangeEvent): + self.Layout() + old_properties = self.properties + self._set_properties(evt.properties) + wx.PostEvent( + self, + BlockChangeEvent( + self.platform, + self.version_number, + self.force_blockstate, + self.namespace, + self.base_name, + self.properties, + self.platform, + self.version_number, + self.force_blockstate, + self.namespace, + self.base_name, + old_properties, + ), + ) def _on_push(self) -> bool: update = super()._on_push() @@ -208,10 +268,23 @@ def demo(): 5, ) - def on_change(evt: SinglePropertiesChangeEvent): - print(evt.properties) + def on_change(evt: BlockChangeEvent): + print( + evt.platform, + evt.version_number, + evt.force_blockstate, + evt.namespace, + evt.base_name, + evt.properties, + evt.old_platform, + evt.old_version_number, + evt.old_force_blockstate, + evt.old_namespace, + evt.old_base_name, + evt.old_properties, + ) - obj.Bind(EVT_SINGLE_PROPERTIES_CHANGE, on_change) + obj.Bind(EVT_BLOCK_CHANGE, on_change) def get_on_close(dialog_): def on_close(evt): diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py index 9b047fe6..7a64812e 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py @@ -12,6 +12,10 @@ MultiplePropertiesChangeEvent, ) from amulet_map_editor.api.wx.ui.mc.block.define.widget.base import BaseBlockDefine +from amulet_map_editor.api.wx.ui.mc.block.define.events import ( + WildcardBlockChangeEvent, + EVT_WILDCARD_BLOCK_CHANGE, +) from amulet_map_editor.api.wx.ui.mc.base import WildcardMCBlock from amulet_map_editor.api.wx.ui.mc.version import VersionChangeEvent @@ -79,11 +83,16 @@ def _init_state(self, state: Dict[str, Any]): def _on_version_change(self, evt: VersionChangeEvent): self.Freeze() - old_platform, old_version = self.platform, self.version_number - old_namespace, old_base_name, old_all_properties = ( + old_platform, old_version, old_force_blockstate = ( + self.platform, + self.version_number, + self.force_blockstate, + ) + old_namespace, old_base_name, old_all_properties, old_selected_properties = ( self.namespace, self.base_name, self.all_properties, + self.selected_properties, ) self._set_platform(evt.platform) @@ -141,23 +150,75 @@ def _on_version_change(self, evt: VersionChangeEvent): self._property_picker.all_properties = self.all_properties self._property_picker.selected_properties = self.selected_properties - # wx.PostEvent( - # self, - # BiomeIDChangeEvent( - # self.namespace, - # self.base_name, - # old_namespace, - # old_base_name, - # ), - # ) + wx.PostEvent( + self, + WildcardBlockChangeEvent( + self.platform, + self.version_number, + self.force_blockstate, + self.namespace, + self.base_name, + self.selected_properties, + old_platform, + old_version, + old_force_blockstate, + old_namespace, + old_base_name, + old_selected_properties, + ), + ) self.Thaw() def _on_block_change(self, evt: BlockIDChangeEvent): + old_namespace, old_base_name, old_selected_properties = ( + self.namespace, + self.base_name, + self.selected_properties, + ) super()._on_block_change(evt) self._set_all_properties(None) self._set_selected_properties(None) self._property_picker.all_properties = self.all_properties self._property_picker.selected_properties = self.selected_properties + wx.PostEvent( + self, + WildcardBlockChangeEvent( + self.platform, + self.version_number, + self.force_blockstate, + self.namespace, + self.base_name, + self.selected_properties, + self.platform, + self.version_number, + self.force_blockstate, + old_namespace, + old_base_name, + old_selected_properties, + ), + ) + + def _on_property_change(self, evt: MultiplePropertiesChangeEvent): + self.Layout() + old_selected_properties = self.selected_properties + self._set_selected_properties(evt.selected_properties) + wx.PostEvent( + self, + WildcardBlockChangeEvent( + self.platform, + self.version_number, + self.force_blockstate, + self.namespace, + self.base_name, + self.selected_properties, + self.platform, + self.version_number, + self.force_blockstate, + self.namespace, + self.base_name, + old_selected_properties, + ), + ) def _on_push(self) -> bool: update = super()._on_push() @@ -234,8 +295,21 @@ def demo(): 5, ) - def on_change(evt: MultiplePropertiesChangeEvent): - print(evt.selected_properties) + def on_change(evt: WildcardBlockChangeEvent): + print( + evt.platform, + evt.version_number, + evt.force_blockstate, + evt.namespace, + evt.base_name, + evt.selected_properties, + evt.old_platform, + evt.old_version_number, + evt.old_force_blockstate, + evt.old_namespace, + evt.old_base_name, + evt.old_selected_properties, + ) def get_on_close(dialog_): def on_close(evt): @@ -243,7 +317,7 @@ def on_close(evt): return on_close - obj.Bind(EVT_MULTIPLE_PROPERTIES_CHANGE, on_change) + obj.Bind(EVT_WILDCARD_BLOCK_CHANGE, on_change) dialog.Bind(wx.EVT_CLOSE, get_on_close(dialog)) dialog.Show() dialog.Fit() From 0cc5796df9b829711177ddeede7a046493e8f2c4 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Wed, 4 Aug 2021 11:35:37 +0100 Subject: [PATCH 082/139] Fixed an issue with the wildcard define UI The event was not showing the correct value because the all properties state was not being updated --- .../api/wx/ui/mc/base/api/block/wildcard.py | 35 ++++++++++--------- .../wx/ui/mc/block/define/widget/wildcard.py | 1 + 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py b/amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py index 3dbb6f25..eb34e0db 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py +++ b/amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py @@ -49,17 +49,6 @@ def __init__( ) self._all_properties = None self._selected_properties = None - if not self.is_vanilla: - selected_properties = self._clean_properties(selected_properties) - all_properties = self._clean_properties(all_properties) - for name, nbts in selected_properties.items(): - if name in all_properties: - ap = all_properties[name] - all_properties[name] = ap + tuple( - nbt for nbt in nbts if nbt not in ap - ) - else: - all_properties[name] = nbts self._set_all_properties(all_properties) self._set_selected_properties(selected_properties) @@ -134,8 +123,22 @@ def selected_properties(self, selected_properties: PropertyTypeMultiple): def _set_selected_properties( self, selected_properties: Optional[PropertyTypeMultiple] ): - self._selected_properties = { - name: tuple(nbt for nbt in nbts if nbt in self.all_properties[name]) - for name, nbts in self._clean_properties(selected_properties).items() - if name in self.all_properties - } + if self.is_vanilla: + self._selected_properties = { + name: tuple(nbt for nbt in nbts if nbt in self.all_properties[name]) + for name, nbts in self._clean_properties(selected_properties).items() + if name in self.all_properties + } + else: + selected_properties = self._clean_properties(selected_properties) + all_properties = self.all_properties + for name, nbts in selected_properties.items(): + if name in all_properties: + ap = all_properties[name] + all_properties[name] = ap + tuple( + nbt for nbt in nbts if nbt not in ap + ) + else: + all_properties[name] = nbts + self._all_properties = all_properties + self._selected_properties = selected_properties diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py index 7a64812e..a8c0ce9b 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py @@ -201,6 +201,7 @@ def _on_block_change(self, evt: BlockIDChangeEvent): def _on_property_change(self, evt: MultiplePropertiesChangeEvent): self.Layout() old_selected_properties = self.selected_properties + self._set_all_properties(self._property_picker.all_properties) self._set_selected_properties(evt.selected_properties) wx.PostEvent( self, From 680b8d1446a3b9813b14d199e05e5d1acf698e83 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Thu, 26 Aug 2021 09:55:39 +0100 Subject: [PATCH 083/139] Moved api package up a level to avoid a circular import --- .../api/wx/ui/mc/{base => }/api/__init__.py | 0 .../api/wx/ui/mc/{base => }/api/biome.py | 0 .../wx/ui/mc/{base => }/api/block/__init__.py | 0 .../ui/mc/{base => }/api/block/identifier.py | 0 .../wx/ui/mc/{base => }/api/block/normal.py | 28 +++++++++++++++++++ .../wx/ui/mc/{base => }/api/block/wildcard.py | 0 .../api/wx/ui/mc/{base => }/api/platform.py | 0 .../wx/ui/mc/{base => }/api/resource_id.py | 0 .../api/wx/ui/mc/{base => }/api/version.py | 0 .../api/wx/ui/mc/base/__init__.py | 1 - .../api/wx/ui/mc/base/base_define.py | 2 +- .../base_identifier_select.py | 4 +-- .../api/wx/ui/mc/biome/biome_define.py | 2 +- .../biome_identifier_select.py | 2 +- .../api/wx/ui/mc/block/define/button/base.py | 2 +- .../wx/ui/mc/block/define/button/normal.py | 2 +- .../wx/ui/mc/block/define/button/wildcard.py | 2 +- .../api/wx/ui/mc/block/define/widget/base.py | 2 +- .../wx/ui/mc/block/define/widget/normal.py | 2 +- .../wx/ui/mc/block/define/widget/wildcard.py | 2 +- .../block_identifier_select.py | 2 +- .../api/wx/ui/mc/block/properties/base.py | 2 +- .../ui/mc/block/properties/multiple/main.py | 2 +- .../wx/ui/mc/block/properties/single/main.py | 2 +- .../api/wx/ui/mc/version/platform_select.py | 2 +- .../api/wx/ui/mc/version/version_select.py | 2 +- 26 files changed, 45 insertions(+), 18 deletions(-) rename amulet_map_editor/api/wx/ui/mc/{base => }/api/__init__.py (100%) rename amulet_map_editor/api/wx/ui/mc/{base => }/api/biome.py (100%) rename amulet_map_editor/api/wx/ui/mc/{base => }/api/block/__init__.py (100%) rename amulet_map_editor/api/wx/ui/mc/{base => }/api/block/identifier.py (100%) rename amulet_map_editor/api/wx/ui/mc/{base => }/api/block/normal.py (83%) rename amulet_map_editor/api/wx/ui/mc/{base => }/api/block/wildcard.py (100%) rename amulet_map_editor/api/wx/ui/mc/{base => }/api/platform.py (100%) rename amulet_map_editor/api/wx/ui/mc/{base => }/api/resource_id.py (100%) rename amulet_map_editor/api/wx/ui/mc/{base => }/api/version.py (100%) diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/__init__.py b/amulet_map_editor/api/wx/ui/mc/api/__init__.py similarity index 100% rename from amulet_map_editor/api/wx/ui/mc/base/api/__init__.py rename to amulet_map_editor/api/wx/ui/mc/api/__init__.py diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/biome.py b/amulet_map_editor/api/wx/ui/mc/api/biome.py similarity index 100% rename from amulet_map_editor/api/wx/ui/mc/base/api/biome.py rename to amulet_map_editor/api/wx/ui/mc/api/biome.py diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/block/__init__.py b/amulet_map_editor/api/wx/ui/mc/api/block/__init__.py similarity index 100% rename from amulet_map_editor/api/wx/ui/mc/base/api/block/__init__.py rename to amulet_map_editor/api/wx/ui/mc/api/block/__init__.py diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/block/identifier.py b/amulet_map_editor/api/wx/ui/mc/api/block/identifier.py similarity index 100% rename from amulet_map_editor/api/wx/ui/mc/base/api/block/identifier.py rename to amulet_map_editor/api/wx/ui/mc/api/block/identifier.py diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/block/normal.py b/amulet_map_editor/api/wx/ui/mc/api/block/normal.py similarity index 83% rename from amulet_map_editor/api/wx/ui/mc/base/api/block/normal.py rename to amulet_map_editor/api/wx/ui/mc/api/block/normal.py index ad623d24..e470463a 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/api/block/normal.py +++ b/amulet_map_editor/api/wx/ui/mc/api/block/normal.py @@ -44,6 +44,22 @@ def block(self, block: Block): """ raise NotImplementedError + # @property + # def block_entity(self) -> Optional[BlockEntity]: + # raise NotImplementedError + # + # @block_entity.setter + # def block_entity(self, block_entity: Optional[BlockEntity]): + # raise NotImplementedError + + # @property + # def universal_block(self) -> Tuple[Block, Optional[BlockEntity]]: + # raise NotImplementedError + # + # @universal_block.setter + # def universal_block(self, universal_block: Tuple[Block, Optional[BlockEntity]]): + # raise NotImplementedError + class NormalMCBlock(BaseMCBlockIdentifier, NormalMCBlockAPI): def __init__( @@ -138,3 +154,15 @@ def _set_block(self, block: Optional[Block]): self._set_namespace(block.namespace) self._set_base_name(block.base_name) self._set_properties(block.properties) + + # @property + # def block_entity(self) -> Optional[BlockEntity]: + # return self._block_entity + # + # @block_entity.setter + # def block_entity(self, block_entity: Optional[BlockEntity]): + # self._set_block_entity(block_entity) + # self._schedule_push() + # + # def _set_block_entity(self, block_entity: Optional[BlockEntity]): + # raise NotImplementedError diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py b/amulet_map_editor/api/wx/ui/mc/api/block/wildcard.py similarity index 100% rename from amulet_map_editor/api/wx/ui/mc/base/api/block/wildcard.py rename to amulet_map_editor/api/wx/ui/mc/api/block/wildcard.py diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/platform.py b/amulet_map_editor/api/wx/ui/mc/api/platform.py similarity index 100% rename from amulet_map_editor/api/wx/ui/mc/base/api/platform.py rename to amulet_map_editor/api/wx/ui/mc/api/platform.py diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/resource_id.py b/amulet_map_editor/api/wx/ui/mc/api/resource_id.py similarity index 100% rename from amulet_map_editor/api/wx/ui/mc/base/api/resource_id.py rename to amulet_map_editor/api/wx/ui/mc/api/resource_id.py diff --git a/amulet_map_editor/api/wx/ui/mc/base/api/version.py b/amulet_map_editor/api/wx/ui/mc/api/version.py similarity index 100% rename from amulet_map_editor/api/wx/ui/mc/base/api/version.py rename to amulet_map_editor/api/wx/ui/mc/api/version.py diff --git a/amulet_map_editor/api/wx/ui/mc/base/__init__.py b/amulet_map_editor/api/wx/ui/mc/base/__init__.py index 21572238..31653a0a 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/__init__.py +++ b/amulet_map_editor/api/wx/ui/mc/base/__init__.py @@ -1,3 +1,2 @@ -from .api import * from .base_identifier_select import * from .base_define import * diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_define.py b/amulet_map_editor/api/wx/ui/mc/base/base_define.py index 5e3b0990..dd571e29 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/base_define.py +++ b/amulet_map_editor/api/wx/ui/mc/base/base_define.py @@ -13,7 +13,7 @@ EVT_VERSION_CHANGE, VersionChangeEvent, ) -from amulet_map_editor.api.wx.ui.mc.base.api import BaseMCVersion +from amulet_map_editor.api.wx.ui.mc.api import BaseMCVersion class BaseDefine(wx.Panel, BaseMCVersion): diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py index 4f216bf2..159aaead 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py +++ b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py @@ -1,11 +1,11 @@ import wx -from typing import Tuple, List, Dict, Any +from typing import List, Dict, Any import PyMCTranslate from amulet.api.data_types import VersionNumberTuple from amulet_map_editor.api.image import COLOUR_PICKER -from ..api.resource_id import BaseMCResourceID +from amulet_map_editor.api.wx.ui.mc.api.resource_id import BaseMCResourceID from .events import ( PickEvent, ) diff --git a/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py b/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py index 379a78ca..3ee3e7fe 100644 --- a/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py +++ b/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py @@ -5,7 +5,7 @@ from amulet.api.data_types import VersionNumberTuple, PlatformType from amulet_map_editor.api.wx.ui.mc.base.base_define import BaseDefine -from amulet_map_editor.api.wx.ui.mc.base.api.biome import BaseMCBiomeIdentifier +from amulet_map_editor.api.wx.ui.mc.api.biome import BaseMCBiomeIdentifier from amulet_map_editor.api.wx.ui.mc.biome.identifier_select.biome_identifier_select import ( BiomeIdentifierSelect, ) diff --git a/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/biome_identifier_select.py b/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/biome_identifier_select.py index ccdc1052..87d164c4 100644 --- a/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/biome_identifier_select.py +++ b/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/biome_identifier_select.py @@ -4,7 +4,7 @@ from amulet_map_editor.api.wx.ui.mc.base.base_identifier_select import ( BaseIdentifierSelect, ) -from amulet_map_editor.api.wx.ui.mc.base.api.biome import BaseMCBiomeIdentifier +from amulet_map_editor.api.wx.ui.mc.api.biome import BaseMCBiomeIdentifier from amulet_map_editor.api.wx.ui.mc.biome.identifier_select.events import ( BiomeIDChangeEvent, EVT_BIOME_ID_CHANGE, diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py index 9bb2e302..6328d468 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py @@ -2,7 +2,7 @@ from typing import Optional from amulet.api.data_types import PlatformType, VersionNumberTuple -from amulet_map_editor.api.wx.ui.mc.base import BaseMCBlockIdentifierAPI +from amulet_map_editor.api.wx.ui.mc.api import BaseMCBlockIdentifierAPI from amulet_map_editor.api.wx.ui.mc.block.define import BaseBlockDefine diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py index b91feacb..a22311eb 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py @@ -5,7 +5,7 @@ from amulet.api.block import Block, PropertyType from amulet.api.block_entity import BlockEntity from amulet_map_editor.api.wx.ui.simple import SimpleDialog -from amulet_map_editor.api.wx.ui.mc.base import NormalMCBlockAPI +from amulet_map_editor.api.wx.ui.mc.api import NormalMCBlockAPI from amulet_map_editor.api.wx.ui.mc.block.define.widget import BlockDefine from amulet_map_editor.api.wx.ui.mc.block.define.button.base import ( diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py index de8d140b..d37fdc0c 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py @@ -3,7 +3,7 @@ from amulet.api.block import PropertyTypeMultiple from amulet_map_editor.api.wx.ui.simple import SimpleDialog -from amulet_map_editor.api.wx.ui.mc.base import WildcardMCBlockAPI +from amulet_map_editor.api.wx.ui.mc.api import WildcardMCBlockAPI from amulet_map_editor.api.wx.ui.mc.block.define.widget import WildcardBlockDefine from amulet_map_editor.api.wx.ui.mc.block.define.button.base import ( BaseBlockDefineButton, diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py index 919fdfdb..41d62090 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py @@ -11,7 +11,7 @@ BlockIDChangeEvent, EVT_BLOCK_ID_CHANGE, ) -from amulet_map_editor.api.wx.ui.mc.base import BaseMCBlockIdentifier +from amulet_map_editor.api.wx.ui.mc.api import BaseMCBlockIdentifier from amulet_map_editor.api.wx.ui.mc.version import VersionChangeEvent diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py index ddb95c65..d2f0ef5a 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py @@ -16,7 +16,7 @@ BlockChangeEvent, EVT_BLOCK_CHANGE, ) -from amulet_map_editor.api.wx.ui.mc.base import NormalMCBlock +from amulet_map_editor.api.wx.ui.mc.api import NormalMCBlock from amulet_map_editor.api.wx.ui.mc.version import VersionChangeEvent diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py index a8c0ce9b..feddb251 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py @@ -16,7 +16,7 @@ WildcardBlockChangeEvent, EVT_WILDCARD_BLOCK_CHANGE, ) -from amulet_map_editor.api.wx.ui.mc.base import WildcardMCBlock +from amulet_map_editor.api.wx.ui.mc.api import WildcardMCBlock from amulet_map_editor.api.wx.ui.mc.version import VersionChangeEvent diff --git a/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py b/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py index cea42fbe..4dfc2d9e 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py +++ b/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py @@ -4,7 +4,7 @@ from amulet_map_editor.api.wx.ui.mc.base.base_identifier_select import ( BaseIdentifierSelect, ) -from amulet_map_editor.api.wx.ui.mc.base.api.block import BaseMCBlockIdentifier +from amulet_map_editor.api.wx.ui.mc.api.block import BaseMCBlockIdentifier from amulet_map_editor.api.wx.ui.mc.block.identifier_select.events import ( BlockIDChangeEvent, EVT_BLOCK_ID_CHANGE, diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/base.py b/amulet_map_editor/api/wx/ui/mc/block/properties/base.py index bbbae155..1ba13d43 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/base.py @@ -2,7 +2,7 @@ from typing import Tuple, Dict, Any import PyMCTranslate -from amulet_map_editor.api.wx.ui.mc.base.api.block import BaseMCBlockIdentifier +from amulet_map_editor.api.wx.ui.mc.api.block import BaseMCBlockIdentifier class BasePropertySelect(wx.Panel, BaseMCBlockIdentifier): diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py index bc91341d..11ee62c2 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py @@ -4,7 +4,7 @@ import PyMCTranslate import amulet_nbt from amulet.api.block import PropertyTypeMultiple -from amulet_map_editor.api.wx.ui.mc.base import WildcardMCBlock +from amulet_map_editor.api.wx.ui.mc.api import WildcardMCBlock from ..base import BasePropertySelect from .automatic import AutomaticMultipleProperty diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py index 3eb6034e..5ebd6325 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py @@ -8,7 +8,7 @@ from .events import SinglePropertiesChangeEvent, EVT_SINGLE_PROPERTIES_CHANGE from .automatic import AutomaticSingleProperty from .manual import ManualSingleProperty -from amulet_map_editor.api.wx.ui.mc.base import NormalMCBlock +from amulet_map_editor.api.wx.ui.mc.api import NormalMCBlock class SinglePropertySelect(BasePropertySelect, NormalMCBlock): diff --git a/amulet_map_editor/api/wx/ui/mc/version/platform_select.py b/amulet_map_editor/api/wx/ui/mc/version/platform_select.py index 03741792..3c09df3b 100644 --- a/amulet_map_editor/api/wx/ui/mc/version/platform_select.py +++ b/amulet_map_editor/api/wx/ui/mc/version/platform_select.py @@ -5,7 +5,7 @@ from amulet.api.data_types import PlatformType from .events import PlatformChangeEvent, EVT_PLATFORM_CHANGE -from amulet_map_editor.api.wx.ui.mc.base.api.platform import BaseMCPlatform +from amulet_map_editor.api.wx.ui.mc.api.platform import BaseMCPlatform class PlatformSelect(wx.Panel, BaseMCPlatform): diff --git a/amulet_map_editor/api/wx/ui/mc/version/version_select.py b/amulet_map_editor/api/wx/ui/mc/version/version_select.py index ced426db..de841eea 100644 --- a/amulet_map_editor/api/wx/ui/mc/version/version_select.py +++ b/amulet_map_editor/api/wx/ui/mc/version/version_select.py @@ -5,7 +5,7 @@ from amulet.api.data_types import VersionNumberTuple, PlatformType from .platform_select import PlatformSelect -from amulet_map_editor.api.wx.ui.mc.base.api.version import BaseMCVersion +from amulet_map_editor.api.wx.ui.mc.api.version import BaseMCVersion from .events import VersionChangeEvent, EVT_VERSION_CHANGE From c3cd1afd71b31187f44daed1cd670995b318da96 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Thu, 26 Aug 2021 12:54:19 +0100 Subject: [PATCH 084/139] Added the block select buttons --- .../api/wx/ui/mc/block/define/button/base.py | 123 +++++++---------- .../wx/ui/mc/block/define/button/normal.py | 130 +++++++++--------- .../wx/ui/mc/block/define/button/wildcard.py | 115 ++++++++++------ 3 files changed, 191 insertions(+), 177 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py index 6328d468..ffdc7495 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py @@ -1,89 +1,64 @@ import wx -from typing import Optional +from typing import Tuple, Optional, Dict, Any -from amulet.api.data_types import PlatformType, VersionNumberTuple -from amulet_map_editor.api.wx.ui.mc.api import BaseMCBlockIdentifierAPI +import PyMCTranslate + +from amulet_map_editor.api.wx.ui.mc.api import BaseMCBlockIdentifier from amulet_map_editor.api.wx.ui.mc.block.define import BaseBlockDefine -class BaseBlockDefineButton(wx.Button, BaseMCBlockIdentifierAPI): +class BaseBlockDefineButton(wx.Button, BaseMCBlockIdentifier): def __init__( self, parent: wx.Window, + translation_manager: PyMCTranslate.TranslationManager, + platform: str = None, + version_number: Tuple[int, int, int] = None, + force_blockstate: bool = None, + namespace: str = None, + base_name: str = None, + show_pick_block: bool = False, + state: Dict[str, Any] = None, ): - super().__init__(parent) - self._dialog: Optional[wx.Dialog] = None + state = state or {} + state.setdefault("translation_manager", translation_manager) + state.setdefault("platform", platform) + state.setdefault("version_number", version_number) + state.setdefault("force_blockstate", force_blockstate) + state.setdefault("namespace", namespace) + state.setdefault("base_name", base_name) + # This is the init call to the class that stores the internal state of the data. + # This needs to be at the start to ensure that the internal state is set up before anything else is done. + # It is not a direct call to init so that subclasses of this class can substitute in which state subclass is used. + self._init_state(state) + wx.Button.__init__(self, parent) self._block_widget: Optional[BaseBlockDefine] = None self.Bind(wx.EVT_BUTTON, self._on_press) + self._show_pick_block = show_pick_block - def _on_press(self, evt): - self._dialog.Fit() - if self._dialog.ShowModal() == wx.ID_OK: - self.update_button() - # TODO: There is currently a bug if the user clicks cancel and clicks the button again the UI will not get reset. - # In order to fix this, this class will need to store the state outside of the UI. - # This should probably be done for all the UI elements + def _init_state(self, state: Dict[str, Any]): + """ + Call the init method of the state manager. + This is here so that nested classes do not have to init the state managers multiple times. + """ + BaseMCBlockIdentifier.__init__(self, **state) - def update_button(self): - """Update the text on the button from the internal state.""" + def _on_press(self, evt): raise NotImplementedError - @property - def platform(self) -> PlatformType: - return self._block_widget.platform - - @platform.setter - def platform(self, platform: PlatformType): - self._set_platform(platform) - self.update_button() - - def _set_platform(self, platform: PlatformType): - self._block_widget.platform = platform - - @property - def version_number(self) -> VersionNumberTuple: - return self._block_widget.version_number - - @version_number.setter - def version_number(self, version_number: VersionNumberTuple): - self._set_version_number(version_number) - self.update_button() - - def _set_version_number(self, version_number: VersionNumberTuple): - self._block_widget.version_number = version_number - - @property - def namespace(self) -> str: - return self._block_widget.namespace - - @namespace.setter - def namespace(self, namespace: str): - self._set_namespace(namespace) - self.update_button() - - def _set_namespace(self, namespace: str): - self._block_widget.namespace = namespace - - @property - def force_blockstate(self) -> bool: - return self._block_widget.force_blockstate - - @force_blockstate.setter - def force_blockstate(self, force_blockstate: bool): - self._set_force_blockstate(force_blockstate) - self.update_button() - - def _set_force_blockstate(self, force_blockstate: bool): - self._block_widget.force_blockstate = force_blockstate - - @property - def block_name(self) -> str: - return self._block_widget.block_name - - @block_name.setter - def block_name(self, block_name: str): - self.set_block_name(block_name) - self.update_button() - - def set_block_name(self, block_name: str): - self._block_widget.block_name = block_name + def _on_push(self) -> bool: + self._set_platform(self.platform) + self._set_version_number(self.version_number) + self._set_force_blockstate(self.force_blockstate) + if self._block_widget is not None and ( + self.platform != self._block_widget.platform + or self.version_number != self._block_widget.version_number + or self.force_blockstate != self._block_widget.force_blockstate + ): + ( + self._block_widget.platform, + self._block_widget.version_number, + self._block_widget.force_blockstate, + ) = (self.platform, self.version_number, self.force_blockstate) + return True + return False diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py index a22311eb..db50271b 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py @@ -1,88 +1,90 @@ -from typing import Tuple, Optional +from typing import Tuple, Dict, Any import wx import PyMCTranslate -from amulet.api.block import Block, PropertyType -from amulet.api.block_entity import BlockEntity + from amulet_map_editor.api.wx.ui.simple import SimpleDialog -from amulet_map_editor.api.wx.ui.mc.api import NormalMCBlockAPI +from amulet_map_editor.api.wx.ui.mc.api import NormalMCBlock from amulet_map_editor.api.wx.ui.mc.block.define.widget import BlockDefine - from amulet_map_editor.api.wx.ui.mc.block.define.button.base import ( BaseBlockDefineButton, ) +from amulet.api.block import PropertyType -class BlockDefineButton(BaseBlockDefineButton, NormalMCBlockAPI): +class BlockDefineButton(BaseBlockDefineButton, NormalMCBlock): def __init__( self, parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager, - *args, - **kwargs + platform: str = None, + version_number: Tuple[int, int, int] = None, + force_blockstate: bool = None, + namespace: str = None, + base_name: str = None, + properties: PropertyType = None, + show_pick_block: bool = False, + state: Dict[str, Any] = None, ): - super().__init__(parent) - self._dialog = SimpleDialog(parent, "Pick a Block") - self._block_widget = BlockDefine( - self._dialog, translation_manager, wx.HORIZONTAL, *args, **kwargs + state = state or {} + state.setdefault("properties", properties) + BaseBlockDefineButton.__init__( + self, + parent, + translation_manager, + platform, + version_number, + force_blockstate, + namespace, + base_name, + show_pick_block, + state=state ) - self._dialog.sizer.Add(self._block_widget) self.update_button() + def _init_state(self, state: Dict[str, Any]): + NormalMCBlock.__init__(self, **state) + + def _on_press(self, evt): + dialog = SimpleDialog(self, "Pick a Block") + self._block_widget = BlockDefine( + dialog, self._translation_manager, wx.HORIZONTAL, self.platform, self.version_number, self.force_blockstate, self.namespace, self.base_name, self.properties + ) + dialog.sizer.Add(self._block_widget) + dialog.Fit() + if dialog.ShowModal() == wx.ID_OK: + self._set_platform(self._block_widget.platform) + self._set_version_number(self._block_widget.version_number) + self._set_force_blockstate(self._block_widget.force_blockstate) + self._set_namespace(self._block_widget.namespace) + self._set_base_name(self._block_widget.base_name) + self._set_properties(self._block_widget.properties) + self.update_button() + self._block_widget = None + dialog.Destroy() + def update_button(self): """Update the text on the button from the internal state.""" self.SetLabel(self.block.full_blockstate) - @property - def properties(self) -> PropertyType: - return self._block_widget.properties - - @properties.setter - def properties(self, properties: PropertyType): - self.set_properties(properties) - self.update_button() - - def set_properties(self, properties: PropertyType): - self._block_widget.properties = properties - - @property - def block(self) -> Block: - """The block object stored in this button.""" - return Block(self.namespace, self.block_name, self.properties) - - @block.setter - def block(self, block: Block): - self.set_block(block) - self.update_button() - - def set_block(self, block: Block): - self._set_namespace(block.namespace) - self.set_block_name(block.base_name) - self.set_properties(block.properties) - - @property - def block_entity(self) -> Optional[BlockEntity]: - return self._block_widget.block_entity - - @block_entity.setter - def block_entity(self, block_entity: Optional[BlockEntity]): - self.set_block_entity(block_entity) - self.update_button() - - def set_block_entity(self, block_entity: Optional[BlockEntity]): - self._block_widget.block_entity = block_entity - - @property - def universal_block(self) -> Tuple[Block, Optional[BlockEntity]]: - return self._block_widget.universal_block - - @universal_block.setter - def universal_block(self, universal_block: Tuple[Block, Optional[BlockEntity]]): - self.set_universal_block(universal_block) - self.update_button() - - def set_universal_block(self, universal_block: Tuple[Block, Optional[BlockEntity]]): - self._block_widget.universal_block = universal_block + def _on_push(self) -> bool: + update = super()._on_push() + self._set_namespace(self.namespace) + self._set_base_name(self.base_name) + self._set_properties(self.properties) + if self._block_widget is not None and ( + update or + self.namespace != self._block_widget.namespace or + self.base_name != self._block_widget.base_name or + self.properties != self._block_widget.properties + ): + ( + self._block_widget.namespace, + self._block_widget.base_name, + self._block_widget.properties, + ) = (self.namespace, self.base_name, self.properties) + return True + return False def demo(): @@ -93,7 +95,7 @@ def demo(): translation_manager = PyMCTranslate.new_translation_manager() dialog = wx.Dialog( None, - title="WildcardBlockDefine", + title="BlockDefineButton", style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT, ) sizer = wx.BoxSizer() diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py index d37fdc0c..fe49b366 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py @@ -1,31 +1,71 @@ +from typing import Tuple, Dict, Any import wx + import PyMCTranslate -from amulet.api.block import PropertyTypeMultiple from amulet_map_editor.api.wx.ui.simple import SimpleDialog -from amulet_map_editor.api.wx.ui.mc.api import WildcardMCBlockAPI +from amulet_map_editor.api.wx.ui.mc.api import WildcardMCBlock from amulet_map_editor.api.wx.ui.mc.block.define.widget import WildcardBlockDefine from amulet_map_editor.api.wx.ui.mc.block.define.button.base import ( BaseBlockDefineButton, ) +from amulet.api.block import PropertyTypeMultiple -class WildcardBlockDefineButton(BaseBlockDefineButton, WildcardMCBlockAPI): +class WildcardBlockDefineButton(BaseBlockDefineButton, WildcardMCBlock): def __init__( self, parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager, - *args, - **kwargs, + platform: str = None, + version_number: Tuple[int, int, int] = None, + force_blockstate: bool = None, + namespace: str = None, + base_name: str = None, + selected_properties: PropertyTypeMultiple = None, + all_properties: PropertyTypeMultiple = None, + show_pick_block: bool = False, + state: Dict[str, Any] = None, ): - super().__init__(parent) - self._dialog = SimpleDialog(parent, "Pick a Block") - self._block_widget = WildcardBlockDefine( - self._dialog, translation_manager, wx.HORIZONTAL, *args, **kwargs + state = state or {} + state.setdefault("selected_properties", selected_properties) + state.setdefault("all_properties", all_properties) + BaseBlockDefineButton.__init__( + self, + parent, + translation_manager, + platform, + version_number, + force_blockstate, + namespace, + base_name, + show_pick_block, + state=state ) - self._dialog.sizer.Add(self._block_widget) self.update_button() + def _init_state(self, state: Dict[str, Any]): + WildcardMCBlock.__init__(self, **state) + + def _on_press(self, evt): + dialog = SimpleDialog(self, "Pick a Block") + self._block_widget = WildcardBlockDefine( + dialog, self._translation_manager, wx.HORIZONTAL, self.platform, self.version_number, self.force_blockstate, self.namespace, self.base_name, self.selected_properties, self.all_properties + ) + dialog.sizer.Add(self._block_widget) + dialog.Fit() + if dialog.ShowModal() == wx.ID_OK: + self._set_platform(self._block_widget.platform) + self._set_version_number(self._block_widget.version_number) + self._set_force_blockstate(self._block_widget.force_blockstate) + self._set_namespace(self._block_widget.namespace) + self._set_base_name(self._block_widget.base_name) + self._set_all_properties(self._block_widget.all_properties) + self._set_selected_properties(self._block_widget.selected_properties) + self.update_button() + self._block_widget = None + dialog.Destroy() + def update_button(self): """Update the text on the button from the internal state.""" if self.selected_properties: @@ -33,37 +73,34 @@ def update_button(self): f"{key}:({'|'.join([v.to_snbt() for v in val])})" for key, val in self.selected_properties.items() ] - self.SetLabel(f"{self.namespace}:{self.block_name}[{','.join(properties)}]") + self.SetLabel(f"{self.namespace}:{self.base_name}[{','.join(properties)}]") properties_str = ",\n".join(properties) - self.SetToolTip(f"{self.namespace}:{self.block_name}[\n{properties_str}\n]") + self.SetToolTip(f"{self.namespace}:{self.base_name}[\n{properties_str}\n]") else: - self.SetLabel(f"{self.namespace}:{self.block_name}") - self.SetToolTip(f"{self.namespace}:{self.block_name}") - - @property - def selected_properties(self) -> PropertyTypeMultiple: - return self._block_widget.selected_properties - - @selected_properties.setter - def selected_properties(self, selected_properties: PropertyTypeMultiple): - self.set_selected_properties(selected_properties) - self.update_button() - - def set_selected_properties(self, selected_properties: PropertyTypeMultiple): - self._block_widget.selected_properties = selected_properties - - @property - def all_properties(self) -> PropertyTypeMultiple: - """The values that exist for every property.""" - return self._block_widget.all_properties - - @all_properties.setter - def all_properties(self, all_properties: PropertyTypeMultiple): - self.set_all_properties(all_properties) - self.update_button() - - def set_all_properties(self, all_properties: PropertyTypeMultiple): - self._block_widget.all_properties = all_properties + self.SetLabel(f"{self.namespace}:{self.base_name}") + self.SetToolTip(f"{self.namespace}:{self.base_name}") + + def _on_push(self) -> bool: + update = super()._on_push() + self._set_namespace(self.namespace) + self._set_base_name(self.base_name) + self._set_all_properties(self.all_properties) + self._set_selected_properties(self.selected_properties) + if self._block_widget is not None and ( + update or + self.namespace != self._block_widget.namespace or + self.base_name != self._block_widget.base_name or + self.all_properties != self._block_widget.all_properties or + self.selected_properties != self._block_widget.selected_properties + ): + ( + self._block_widget.namespace, + self._block_widget.base_name, + self._block_widget.all_properties, + self._block_widget.selected_properties + ) = (self.namespace, self.base_name, self.all_properties, self.selected_properties) + return True + return False def demo(): From 95c41528e4025a329f845fc862a4f1dff27aa240 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Thu, 26 Aug 2021 13:04:27 +0100 Subject: [PATCH 085/139] Made the text in the button left aligned This means the button will always show the start of the label and cut off if it is too small --- amulet_map_editor/api/wx/ui/mc/block/define/button/base.py | 2 +- amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py | 4 +++- .../api/wx/ui/mc/block/define/button/wildcard.py | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py index ffdc7495..a85f98e5 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py @@ -31,7 +31,7 @@ def __init__( # This needs to be at the start to ensure that the internal state is set up before anything else is done. # It is not a direct call to init so that subclasses of this class can substitute in which state subclass is used. self._init_state(state) - wx.Button.__init__(self, parent) + wx.Button.__init__(self, parent, style=wx.BU_LEFT) self._block_widget: Optional[BaseBlockDefine] = None self.Bind(wx.EVT_BUTTON, self._on_press) self._show_pick_block = show_pick_block diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py index db50271b..d150fdf8 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py @@ -65,7 +65,9 @@ def _on_press(self, evt): def update_button(self): """Update the text on the button from the internal state.""" - self.SetLabel(self.block.full_blockstate) + blockstate = self.block.full_blockstate + self.SetLabel(f" {blockstate}") + self.SetToolTip(blockstate) def _on_push(self) -> bool: update = super()._on_push() diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py index fe49b366..32046534 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py @@ -73,11 +73,11 @@ def update_button(self): f"{key}:({'|'.join([v.to_snbt() for v in val])})" for key, val in self.selected_properties.items() ] - self.SetLabel(f"{self.namespace}:{self.base_name}[{','.join(properties)}]") + self.SetLabel(f" {self.namespace}:{self.base_name}[{','.join(properties)}]") properties_str = ",\n".join(properties) self.SetToolTip(f"{self.namespace}:{self.base_name}[\n{properties_str}\n]") else: - self.SetLabel(f"{self.namespace}:{self.base_name}") + self.SetLabel(f" {self.namespace}:{self.base_name}") self.SetToolTip(f"{self.namespace}:{self.base_name}") def _on_push(self) -> bool: From bd133f0ac3ef99b6ec6aae2b21cbc1f46de27129 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Thu, 26 Aug 2021 13:04:35 +0100 Subject: [PATCH 086/139] Reformatted --- .../wx/ui/mc/block/define/button/normal.py | 20 ++++++++---- .../wx/ui/mc/block/define/button/wildcard.py | 32 +++++++++++++------ 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py index d150fdf8..408e1753 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py @@ -38,7 +38,7 @@ def __init__( namespace, base_name, show_pick_block, - state=state + state=state, ) self.update_button() @@ -48,7 +48,15 @@ def _init_state(self, state: Dict[str, Any]): def _on_press(self, evt): dialog = SimpleDialog(self, "Pick a Block") self._block_widget = BlockDefine( - dialog, self._translation_manager, wx.HORIZONTAL, self.platform, self.version_number, self.force_blockstate, self.namespace, self.base_name, self.properties + dialog, + self._translation_manager, + wx.HORIZONTAL, + self.platform, + self.version_number, + self.force_blockstate, + self.namespace, + self.base_name, + self.properties, ) dialog.sizer.Add(self._block_widget) dialog.Fit() @@ -75,10 +83,10 @@ def _on_push(self) -> bool: self._set_base_name(self.base_name) self._set_properties(self.properties) if self._block_widget is not None and ( - update or - self.namespace != self._block_widget.namespace or - self.base_name != self._block_widget.base_name or - self.properties != self._block_widget.properties + update + or self.namespace != self._block_widget.namespace + or self.base_name != self._block_widget.base_name + or self.properties != self._block_widget.properties ): ( self._block_widget.namespace, diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py index 32046534..528169e6 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py @@ -40,7 +40,7 @@ def __init__( namespace, base_name, show_pick_block, - state=state + state=state, ) self.update_button() @@ -50,7 +50,16 @@ def _init_state(self, state: Dict[str, Any]): def _on_press(self, evt): dialog = SimpleDialog(self, "Pick a Block") self._block_widget = WildcardBlockDefine( - dialog, self._translation_manager, wx.HORIZONTAL, self.platform, self.version_number, self.force_blockstate, self.namespace, self.base_name, self.selected_properties, self.all_properties + dialog, + self._translation_manager, + wx.HORIZONTAL, + self.platform, + self.version_number, + self.force_blockstate, + self.namespace, + self.base_name, + self.selected_properties, + self.all_properties, ) dialog.sizer.Add(self._block_widget) dialog.Fit() @@ -87,18 +96,23 @@ def _on_push(self) -> bool: self._set_all_properties(self.all_properties) self._set_selected_properties(self.selected_properties) if self._block_widget is not None and ( - update or - self.namespace != self._block_widget.namespace or - self.base_name != self._block_widget.base_name or - self.all_properties != self._block_widget.all_properties or - self.selected_properties != self._block_widget.selected_properties + update + or self.namespace != self._block_widget.namespace + or self.base_name != self._block_widget.base_name + or self.all_properties != self._block_widget.all_properties + or self.selected_properties != self._block_widget.selected_properties ): ( self._block_widget.namespace, self._block_widget.base_name, self._block_widget.all_properties, - self._block_widget.selected_properties - ) = (self.namespace, self.base_name, self.all_properties, self.selected_properties) + self._block_widget.selected_properties, + ) = ( + self.namespace, + self.base_name, + self.all_properties, + self.selected_properties, + ) return True return False From 711a50ef8202d16c44e9500596cdb4f15f19d2de Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Thu, 26 Aug 2021 13:23:39 +0100 Subject: [PATCH 087/139] Cleaned up version number and block location typing --- .../api/wx/ui/mc/biome/biome_define.py | 2 +- .../api/wx/ui/mc/block/define/button/base.py | 3 ++- .../api/wx/ui/mc/block/define/button/normal.py | 3 ++- .../api/wx/ui/mc/block/define/button/wildcard.py | 3 ++- .../api/wx/ui/mc/block/define/widget/base.py | 3 ++- .../api/wx/ui/mc/block/define/widget/normal.py | 3 ++- .../api/wx/ui/mc/block/define/widget/wildcard.py | 3 ++- .../api/wx/ui/mc/block/properties/base.py | 3 ++- .../api/wx/ui/mc/block/properties/multiple/main.py | 3 ++- .../api/wx/ui/mc/block/properties/single/main.py | 3 ++- amulet_map_editor/api/wx/ui/mc/version/events.py | 9 +++++---- .../edit/api/behaviour/block_selection_behaviour.py | 4 ++-- amulet_map_editor/programs/edit/api/selection.py | 13 +++++-------- .../programs/edit/api/ui/nudge_button.py | 5 +++-- .../edit/api/ui/select_location/select_location.py | 2 +- .../programs/edit/plugins/tools/paste.py | 8 ++++---- .../programs/edit/plugins/tools/select.py | 6 +++--- 17 files changed, 42 insertions(+), 34 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py b/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py index 3ee3e7fe..cc683904 100644 --- a/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py +++ b/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py @@ -27,7 +27,7 @@ def __init__( translation_manager: PyMCTranslate.TranslationManager, orientation=wx.VERTICAL, platform: str = None, - version_number: Tuple[int, int, int] = None, + version_number: VersionNumberTuple = None, namespace: str = None, base_name: str = None, show_pick_biome: bool = False, diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py index a85f98e5..d2fb6828 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py @@ -2,6 +2,7 @@ from typing import Tuple, Optional, Dict, Any import PyMCTranslate +from amulet.api.data_types import VersionNumberTuple from amulet_map_editor.api.wx.ui.mc.api import BaseMCBlockIdentifier from amulet_map_editor.api.wx.ui.mc.block.define import BaseBlockDefine @@ -13,7 +14,7 @@ def __init__( parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager, platform: str = None, - version_number: Tuple[int, int, int] = None, + version_number: VersionNumberTuple = None, force_blockstate: bool = None, namespace: str = None, base_name: str = None, diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py index 408e1753..d1b46c5b 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py @@ -2,6 +2,7 @@ import wx import PyMCTranslate +from amulet.api.data_types import VersionNumberTuple from amulet_map_editor.api.wx.ui.simple import SimpleDialog from amulet_map_editor.api.wx.ui.mc.api import NormalMCBlock @@ -18,7 +19,7 @@ def __init__( parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager, platform: str = None, - version_number: Tuple[int, int, int] = None, + version_number: VersionNumberTuple = None, force_blockstate: bool = None, namespace: str = None, base_name: str = None, diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py index 528169e6..a26f25c7 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py @@ -2,6 +2,7 @@ import wx import PyMCTranslate +from amulet.api.data_types import VersionNumberTuple from amulet_map_editor.api.wx.ui.simple import SimpleDialog from amulet_map_editor.api.wx.ui.mc.api import WildcardMCBlock @@ -18,7 +19,7 @@ def __init__( parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager, platform: str = None, - version_number: Tuple[int, int, int] = None, + version_number: VersionNumberTuple = None, force_blockstate: bool = None, namespace: str = None, base_name: str = None, diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py index 41d62090..c142039d 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py @@ -2,6 +2,7 @@ from typing import Tuple, Optional, Dict, Any import PyMCTranslate +from amulet.api.data_types import VersionNumberTuple from amulet_map_editor.api.wx.ui.mc.base.base_define import BaseDefine from amulet_map_editor.api.wx.ui.mc.block import BlockIdentifierSelect @@ -26,7 +27,7 @@ def __init__( translation_manager: PyMCTranslate.TranslationManager, orientation=wx.VERTICAL, platform: str = None, - version_number: Tuple[int, int, int] = None, + version_number: VersionNumberTuple = None, force_blockstate: bool = None, namespace: str = None, base_name: str = None, diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py index d2f0ef5a..48bf12ad 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py @@ -3,6 +3,7 @@ from typing import Tuple, Dict, Any import PyMCTranslate +from amulet.api.data_types import VersionNumberTuple from amulet.api.block import PropertyType, Block from amulet_map_editor.api.wx.ui.mc.block import ( @@ -31,7 +32,7 @@ def __init__( translation_manager: PyMCTranslate.TranslationManager, orientation=wx.VERTICAL, platform: str = None, - version_number: Tuple[int, int, int] = None, + version_number: VersionNumberTuple = None, force_blockstate: bool = None, namespace: str = None, base_name: str = None, diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py index feddb251..9a8a1cf4 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py @@ -3,6 +3,7 @@ from typing import Tuple, Dict, Any import PyMCTranslate +from amulet.api.data_types import VersionNumberTuple from amulet.api.block import PropertyTypeMultiple, Block from amulet_map_editor.api.wx.ui.mc.block import ( @@ -31,7 +32,7 @@ def __init__( translation_manager: PyMCTranslate.TranslationManager, orientation=wx.VERTICAL, platform: str = None, - version_number: Tuple[int, int, int] = None, + version_number: VersionNumberTuple = None, force_blockstate: bool = None, namespace: str = None, base_name: str = None, diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/base.py b/amulet_map_editor/api/wx/ui/mc/block/properties/base.py index 1ba13d43..ebddd122 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/base.py @@ -2,6 +2,7 @@ from typing import Tuple, Dict, Any import PyMCTranslate +from amulet.api.data_types import VersionNumberTuple from amulet_map_editor.api.wx.ui.mc.api.block import BaseMCBlockIdentifier @@ -11,7 +12,7 @@ def __init__( parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager, platform: str, - version_number: Tuple[int, ...], + version_number: VersionNumberTuple, force_blockstate: bool, namespace: str = None, base_name: str = None, diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py index 11ee62c2..2ac44d01 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py @@ -3,6 +3,7 @@ import PyMCTranslate import amulet_nbt +from amulet.api.data_types import VersionNumberTuple from amulet.api.block import PropertyTypeMultiple from amulet_map_editor.api.wx.ui.mc.api import WildcardMCBlock from ..base import BasePropertySelect @@ -24,7 +25,7 @@ def __init__( parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager, platform: str, - version_number: Tuple[int, ...], + version_number: VersionNumberTuple, force_blockstate: bool, namespace: str, base_name: str, diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py index 5ebd6325..2f893737 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py @@ -3,6 +3,7 @@ import PyMCTranslate import amulet_nbt +from amulet.api.data_types import VersionNumberTuple from amulet.api.block import PropertyType from ..base import BasePropertySelect from .events import SinglePropertiesChangeEvent, EVT_SINGLE_PROPERTIES_CHANGE @@ -23,7 +24,7 @@ def __init__( parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager, platform: str, - version_number: Tuple[int, ...], + version_number: VersionNumberTuple, force_blockstate: bool, namespace: str = None, base_name: str = None, diff --git a/amulet_map_editor/api/wx/ui/mc/version/events.py b/amulet_map_editor/api/wx/ui/mc/version/events.py index bebbfd4b..2902548d 100644 --- a/amulet_map_editor/api/wx/ui/mc/version/events.py +++ b/amulet_map_editor/api/wx/ui/mc/version/events.py @@ -1,5 +1,6 @@ import wx from typing import Tuple +from amulet.api.data_types import VersionNumberTuple _PlatformEventType = wx.NewEventType() EVT_PLATFORM_CHANGE = wx.PyEventBinder(_PlatformEventType) @@ -39,10 +40,10 @@ class VersionChangeEvent(wx.PyEvent): def __init__( self, platform: str, - version_number: Tuple[int, ...], + version_number: VersionNumberTuple, force_blockstate: bool, old_platform: str, - old_version_number: Tuple[int, ...], + old_version_number: VersionNumberTuple, old_force_blockstate: bool, ): wx.PyEvent.__init__(self, eventType=_VersionChangeEventType) @@ -59,7 +60,7 @@ def platform(self) -> str: return self._platform @property - def version_number(self) -> Tuple[int, ...]: + def version_number(self) -> VersionNumberTuple: """The version_number that the selection was changed to.""" return self._version_number @@ -76,7 +77,7 @@ def old_platform(self) -> str: return self._old_platform @property - def old_version_number(self) -> Tuple[int, ...]: + def old_version_number(self) -> VersionNumberTuple: """The version_number that was selected before it was changed.""" return self._old_version_number diff --git a/amulet_map_editor/programs/edit/api/behaviour/block_selection_behaviour.py b/amulet_map_editor/programs/edit/api/behaviour/block_selection_behaviour.py index 14696d69..7d9d03dd 100644 --- a/amulet_map_editor/programs/edit/api/behaviour/block_selection_behaviour.py +++ b/amulet_map_editor/programs/edit/api/behaviour/block_selection_behaviour.py @@ -299,7 +299,7 @@ def _get_active_points(self) -> Tuple[NPArray2x3, NPArray2x3]: @property def active_block_positions( self, - ) -> Tuple[Tuple[int, int, int], Tuple[int, int, int]]: + ) -> Tuple[BlockCoordinates, BlockCoordinates]: """Get the active box positions. The coordinates for the maximum point of the box will be one less because this is the block position.""" if self._active_selection is None: @@ -310,7 +310,7 @@ def active_block_positions( @active_block_positions.setter def active_block_positions( - self, positions: Tuple[Tuple[int, int, int], Tuple[int, int, int]] + self, positions: Tuple[BlockCoordinates, BlockCoordinates] ): """Set the active box positions. This should only be used when not editing. diff --git a/amulet_map_editor/programs/edit/api/selection.py b/amulet_map_editor/programs/edit/api/selection.py index c87dbcc4..ea8a53ec 100644 --- a/amulet_map_editor/programs/edit/api/selection.py +++ b/amulet_map_editor/programs/edit/api/selection.py @@ -1,6 +1,7 @@ from typing import Tuple, Optional, Any, TYPE_CHECKING import wx import weakref +from amulet.api.data_types import BlockCoordinates from amulet.api.selection import SelectionGroup, SelectionBox from amulet.api.history.history_manager import ObjectHistoryManager from amulet.api.history import Changeable @@ -10,7 +11,7 @@ if TYPE_CHECKING: from amulet_map_editor.programs.edit.api.canvas import EditCanvas -BoxType = Tuple[Tuple[int, int, int], Tuple[int, int, int]] # min and max positions +BoxType = Tuple[BlockCoordinates, BlockCoordinates] # min and max positions _SelectionChangeEventType = wx.NewEventType() @@ -60,7 +61,7 @@ def _on_destroy(self, evt): @property def selection_corners( self, - ) -> Tuple[Tuple[Tuple[int, int, int], Tuple[int, int, int]], ...]: + ) -> Tuple[BoxType, ...]: """Get the minimum and maximum points of each selection :return: The minimum and maximum points of each selection """ @@ -69,9 +70,7 @@ def selection_corners( @selection_corners.setter def selection_corners( self, - selection_corners: Tuple[ - Tuple[Tuple[int, int, int], Tuple[int, int, int]], ... - ], + selection_corners: Tuple[BoxType, ...], ): """Set the minimum and maximum points of each selection Will create events that allow the program to update. @@ -84,9 +83,7 @@ def selection_corners( def set_selection_corners( self, - selection_corners: Tuple[ - Tuple[Tuple[int, int, int], Tuple[int, int, int]], ... - ], + selection_corners: Tuple[BoxType, ...], ): """Set the minimum and maximum points of each selection Note this method will not trigger the history logic. diff --git a/amulet_map_editor/programs/edit/api/ui/nudge_button.py b/amulet_map_editor/programs/edit/api/ui/nudge_button.py index 6ac469dd..31094f8b 100644 --- a/amulet_map_editor/programs/edit/api/ui/nudge_button.py +++ b/amulet_map_editor/programs/edit/api/ui/nudge_button.py @@ -5,6 +5,7 @@ import numpy import math +from amulet.api.data_types import BlockCoordinates from amulet_map_editor.api.opengl.camera import Camera from amulet_map_editor.api.opengl.matrix import rotation_matrix_xy from amulet_map_editor.programs.edit.api.key_config import ( @@ -102,7 +103,7 @@ def _on_held(self, evt: InputHeldEvent): if self._timeout: self._timeout -= 1 - def _rotate(self, offset: Tuple[int, int, int]) -> Tuple[int, int, int]: + def _rotate(self, offset: BlockCoordinates) -> BlockCoordinates: x, y, z = offset ry = self.camera.rotation[0] x, y, z, _ = ( @@ -117,5 +118,5 @@ def _rotate(self, offset: Tuple[int, int, int]) -> Tuple[int, int, int]: ) return x, y, z - def _move(self, offset: Tuple[int, int, int]): + def _move(self, offset: BlockCoordinates): pass diff --git a/amulet_map_editor/programs/edit/api/ui/select_location/select_location.py b/amulet_map_editor/programs/edit/api/ui/select_location/select_location.py index 4a4fa877..94189c90 100644 --- a/amulet_map_editor/programs/edit/api/ui/select_location/select_location.py +++ b/amulet_map_editor/programs/edit/api/ui/select_location/select_location.py @@ -62,7 +62,7 @@ def location(self) -> BlockCoordinates: return self._x.GetValue(), self._y.GetValue(), self._z.GetValue() @location.setter - def location(self, location: Tuple[int, int, int]): + def location(self, location: BlockCoordinates): x, y, z = location self._x.SetValue(x) self._y.SetValue(y) diff --git a/amulet_map_editor/programs/edit/plugins/tools/paste.py b/amulet_map_editor/programs/edit/plugins/tools/paste.py index 53161159..537e09b4 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/paste.py +++ b/amulet_map_editor/programs/edit/plugins/tools/paste.py @@ -8,7 +8,7 @@ import numpy import weakref -from amulet.api.data_types import PointCoordinates +from amulet.api.data_types import PointCoordinates, BlockCoordinates from amulet.operations.paste import paste_iter from amulet.utils.matrix import ( rotation_matrix_xyz, @@ -101,11 +101,11 @@ class TupleIntInput(TupleInput): WindowCls = wx.SpinCtrl @property - def value(self) -> Tuple[int, int, int]: + def value(self) -> BlockCoordinates: return self.x.GetValue(), self.y.GetValue(), self.z.GetValue() @value.setter - def value(self, value: Tuple[int, int, int]): + def value(self, value: BlockCoordinates): self.x.SetValue(value[0]) self.y.SetValue(value[1]) self.z.SetValue(value[2]) @@ -233,7 +233,7 @@ def __init__( super().__init__(parent, camera, keybinds, label, tooltip) self._paste_tool = weakref.ref(paste_tool) - def _move(self, offset: Tuple[int, int, int]): + def _move(self, offset: BlockCoordinates): ox, oy, oz = offset x, y, z = self._paste_tool().location self._paste_tool().location = x + ox, y + oy, z + oz diff --git a/amulet_map_editor/programs/edit/plugins/tools/select.py b/amulet_map_editor/programs/edit/plugins/tools/select.py index f4228b2c..b603f0f2 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/select.py +++ b/amulet_map_editor/programs/edit/plugins/tools/select.py @@ -46,21 +46,21 @@ def __init__( class Point1MoveButton(BaseSelectionMoveButton): - def _move(self, offset: Tuple[int, int, int]): + def _move(self, offset: BlockCoordinates): ox, oy, oz = offset (x, y, z), point2 = self._selection.active_block_positions self._selection.active_block_positions = (x + ox, y + oy, z + oz), point2 class Point2MoveButton(BaseSelectionMoveButton): - def _move(self, offset: Tuple[int, int, int]): + def _move(self, offset: BlockCoordinates): ox, oy, oz = offset point1, (x, y, z) = self._selection.active_block_positions self._selection.active_block_positions = point1, (x + ox, y + oy, z + oz) class SelectionMoveButton(BaseSelectionMoveButton): - def _move(self, offset: Tuple[int, int, int]): + def _move(self, offset: BlockCoordinates): ox, oy, oz = offset (x1, y1, z1), (x2, y2, z2) = self._selection.active_block_positions self._selection.active_block_positions = (x1 + ox, y1 + oy, z1 + oz), ( From 174adcc32229ca1c3de3cea135e0fe982d841eac Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Mon, 30 Aug 2021 14:26:45 +0100 Subject: [PATCH 088/139] Cleaned up and fixed some issues in the fill and new block UIs Restructured the old block container UIs into a package format for readability. Rewritten to work with the new block UIs --- .../api/wx/ui/mc/api/block/wildcard.py | 8 +- .../wx/ui/mc/block/define/button/__init__.py | 2 +- .../wx/ui/mc/block/define/button/normal.py | 9 +- .../wx/ui/mc/block/define/button/wildcard.py | 11 +- .../fill_replace/block_container/__init__.py | 3 + .../fill_replace/block_container/base.py | 103 ++++++ .../block_container/block_entry/__init__.py | 4 + .../block_container/block_entry/base.py | 60 ++++ .../block_container/block_entry/events.py | 3 + .../block_container/block_entry/fill.py | 62 ++++ .../block_container/block_entry/find.py | 48 +++ .../fill_replace/block_container/fill.py | 38 +++ .../fill_replace/block_container/find.py | 17 + .../tools/fill_replace/fill_replace_widget.py | 68 ++-- .../tools/fill_replace/replace_widget.py | 311 +----------------- 15 files changed, 415 insertions(+), 332 deletions(-) create mode 100644 amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/__init__.py create mode 100644 amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/base.py create mode 100644 amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/__init__.py create mode 100644 amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/base.py create mode 100644 amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/events.py create mode 100644 amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/fill.py create mode 100644 amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/find.py create mode 100644 amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/fill.py create mode 100644 amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/find.py diff --git a/amulet_map_editor/api/wx/ui/mc/api/block/wildcard.py b/amulet_map_editor/api/wx/ui/mc/api/block/wildcard.py index eb34e0db..ea087db4 100644 --- a/amulet_map_editor/api/wx/ui/mc/api/block/wildcard.py +++ b/amulet_map_editor/api/wx/ui/mc/api/block/wildcard.py @@ -86,13 +86,17 @@ def _clean_properties( ) props = block_spec.get("properties", {}) if isinstance(properties, dict): - for name, ps in props.items(): + for name, snbts in props.items(): if name in properties: out_properties[name] = tuple( nbt for nbt in properties[name] if isinstance(nbt, PropertyDataTypes) - and nbt.to_snbt() in ps + and nbt.to_snbt() in snbts + ) + else: + out_properties[name] = tuple( + amulet_nbt.from_snbt(snbt) for snbt in snbts ) else: for name, snbts in props.items(): diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/__init__.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/__init__.py index c5290876..61e32fc4 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/button/__init__.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/__init__.py @@ -1,3 +1,3 @@ from .base import BaseBlockDefineButton from .normal import BlockDefineButton -from .wildcard import WildcardBlockDefine +from .wildcard import WildcardBlockDefineButton diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py index d1b46c5b..ddaf07ab 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py @@ -38,7 +38,7 @@ def __init__( force_blockstate, namespace, base_name, - show_pick_block, + show_pick_block=show_pick_block, state=state, ) self.update_button() @@ -83,18 +83,19 @@ def _on_push(self) -> bool: self._set_namespace(self.namespace) self._set_base_name(self.base_name) self._set_properties(self.properties) - if self._block_widget is not None and ( + update = self._block_widget is not None and ( update or self.namespace != self._block_widget.namespace or self.base_name != self._block_widget.base_name or self.properties != self._block_widget.properties - ): + ) + if update: ( self._block_widget.namespace, self._block_widget.base_name, self._block_widget.properties, ) = (self.namespace, self.base_name, self.properties) - return True + self.update_button() return False diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py index a26f25c7..95148165 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py @@ -40,7 +40,7 @@ def __init__( force_blockstate, namespace, base_name, - show_pick_block, + show_pick_block=show_pick_block, state=state, ) self.update_button() @@ -96,13 +96,14 @@ def _on_push(self) -> bool: self._set_base_name(self.base_name) self._set_all_properties(self.all_properties) self._set_selected_properties(self.selected_properties) - if self._block_widget is not None and ( + update = self._block_widget is not None and ( update or self.namespace != self._block_widget.namespace or self.base_name != self._block_widget.base_name or self.all_properties != self._block_widget.all_properties or self.selected_properties != self._block_widget.selected_properties - ): + ) + if update: ( self._block_widget.namespace, self._block_widget.base_name, @@ -114,8 +115,8 @@ def _on_push(self) -> bool: self.all_properties, self.selected_properties, ) - return True - return False + self.update_button() + return update def demo(): diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/__init__.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/__init__.py new file mode 100644 index 00000000..abcc4b80 --- /dev/null +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/__init__.py @@ -0,0 +1,3 @@ +from .base import BaseBlockContainer +from .find import FindBlockContainer +from .fill import FillBlockContainer diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/base.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/base.py new file mode 100644 index 00000000..038ff12e --- /dev/null +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/base.py @@ -0,0 +1,103 @@ +from typing import List, Dict, Any +import wx + +import PyMCTranslate +from amulet.api.data_types import VersionNumberTuple +from .block_entry import BaseBlockEntry, EVT_BLOCK_CLOSE + + +class BaseBlockContainer(wx.Panel): + """This is a UI element that contains one or more block buttons.""" + _blocks: List[BaseBlockEntry] + + def __init__( + self, + parent: wx.Window, + translation_manager: PyMCTranslate.TranslationManager, + platform: str = None, + version_number: VersionNumberTuple = None, + force_blockstate: bool = None, + **kwargs, + ): + super().__init__(parent, **kwargs) + self._translation_manager = translation_manager + self._version = (platform, version_number, force_blockstate) + self._expert = False + + sizer = wx.BoxSizer(wx.VERTICAL) + self.SetSizer(sizer) + + top_sizer = wx.BoxSizer(wx.HORIZONTAL) + sizer.Add(top_sizer, 0, wx.EXPAND, 5) + find_label = wx.StaticText( + self, label=self.name, style=wx.ALIGN_CENTER_HORIZONTAL + ) + top_sizer.Add(find_label, 1, wx.ALIGN_CENTER_VERTICAL) + + self._add_button = wx.Button(self, label="➕") + self._add_button.Bind(wx.EVT_BUTTON, lambda evt: self._add_block()) + self._add_button.SetMinSize((28, 28)) + self._add_button.Show(self._expert) + top_sizer.Add(self._add_button) + + self._block_sizer = wx.BoxSizer(wx.VERTICAL) + sizer.Add(self._block_sizer, 0, wx.EXPAND, 0) + + self._blocks = [] + self._add_block() + + @property + def name(self) -> str: + raise NotImplementedError + + def set_expert(self, expert: bool): + self._expert = expert + self._add_button.Show(expert) + for block in self._blocks: + block.show_close(expert) + if not expert: + while len(self._blocks) > 1: + self._destroy_block_entry(self._blocks[-1]) + + def _create_block(self) -> BaseBlockEntry: + raise NotImplementedError + + def _add_block(self): + block = self._create_block() + block.Bind(EVT_BLOCK_CLOSE, self._on_block_entry_close) + self._block_sizer.Add(block, 0, wx.EXPAND | wx.TOP, 5) + self._blocks.append(block) + if len(self._blocks) == 1: + # if there is only one block it cannot be removed. + self._blocks[-1].enable_close(False) + else: + if len(self._blocks) == 2: + # if this is the second block that was added enable the first + self._blocks[0].enable_close() + # enable the block that was just added + self._blocks[-1].enable_close() + block.show_close(self._expert) + self.GetTopLevelParent().Layout() + + def _on_block_entry_close(self, evt): + window = evt.GetEventObject() + if isinstance(window, BaseBlockEntry): + self._destroy_block_entry(window) + + def _destroy_block_entry(self, window: BaseBlockEntry): + if window in self._blocks: + self._blocks.remove(window) + window.Destroy() + if len(self._blocks) == 1: + block = self._blocks[-1] + block.enable_close(False) + self.GetTopLevelParent().Layout() + + @property + def states(self) -> List[Dict[str, Any]]: + return [block.state for block in self._blocks] + + @states.setter + def states(self, states: List[Dict[str, Any]]): + for state, block in zip(states, self._blocks): + block.state = state diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/__init__.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/__init__.py new file mode 100644 index 00000000..c2ee39eb --- /dev/null +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/__init__.py @@ -0,0 +1,4 @@ +from .events import BlockCloseEvent, EVT_BLOCK_CLOSE +from .base import BaseBlockEntry +from .find import FindBlockEntry +from .fill import FillBlockEntry diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/base.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/base.py new file mode 100644 index 00000000..ad33b179 --- /dev/null +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/base.py @@ -0,0 +1,60 @@ +import wx +from typing import Optional, Dict, Any + +from amulet_map_editor.api.wx.ui.mc.block import BaseBlockDefineButton +from .events import BlockCloseEvent + + +class BaseBlockEntry(wx.Panel): + """A UI element that holds a block button, weight entry and close button""" + + _button_props = ( + "platform", + "version_number", + "force_blockstate", + "namespace", + "base_name", + ) + + def __init__( + self, + parent: wx.Window, + **kwargs, + ): + super().__init__(parent, **kwargs) + self._sizer = wx.BoxSizer(wx.HORIZONTAL) + self.SetSizer(self._sizer) + self._block_button: Optional[BaseBlockDefineButton] = None + self._close_button: Optional[wx.Button] = None + + def _init_close_button(self): + self._close_button = wx.Button(self, label="❌") + self._close_button.Bind(wx.EVT_BUTTON, self._on_close) + self._close_button.SetMinSize((28, 28)) + self._sizer.Add(self._close_button) + + def _on_close(self, evt): + evt2 = BlockCloseEvent() + evt2.SetEventObject(self) + wx.PostEvent(self, evt2) + + @property + def block_button(self) -> BaseBlockDefineButton: + raise NotImplementedError + + def show_close(self, show: bool = True): + """Show or hide the close button.""" + self._close_button.Show(show) + self.Layout() + + def enable_close(self, enable: bool = True): + self._close_button.Enable(enable) + + @property + def state(self) -> Dict[str, Any]: + return {prop: getattr(self.block_button, prop) for prop in self._button_props} + + @state.setter + def state(self, state: Dict[str, Any]): + for prop in self._button_props: + setattr(self.block_button, prop, state.get(prop, None)) diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/events.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/events.py new file mode 100644 index 00000000..7e889230 --- /dev/null +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/events.py @@ -0,0 +1,3 @@ +from wx.lib import newevent + +BlockCloseEvent, EVT_BLOCK_CLOSE = newevent.NewEvent() diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/fill.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/fill.py new file mode 100644 index 00000000..2b973a32 --- /dev/null +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/fill.py @@ -0,0 +1,62 @@ +import wx + +import PyMCTranslate +from amulet.api.data_types import VersionNumberTuple +from amulet.api.block import PropertyType +from amulet_map_editor.api.wx.ui.mc.block import BlockDefineButton +from .base import BaseBlockEntry + + +class FillBlockEntry(BaseBlockEntry): + """A UI element that holds a block button, weight entry and close button""" + + _button_props = BaseBlockEntry._button_props + ("properties",) + + def __init__( + self, + parent: wx.Window, + translation_manager: PyMCTranslate.TranslationManager, + platform: str = None, + version_number: VersionNumberTuple = None, + force_blockstate: bool = None, + namespace: str = None, + base_name: str = None, + properties: PropertyType = None, + **kwargs, + ): + super().__init__(parent, **kwargs) + + self._block_button = BlockDefineButton( + self, + translation_manager, + platform, + version_number, + force_blockstate, + namespace, + base_name, + properties, + ) + self._sizer.Add(self._block_button, 1) + self._weight = wx.SpinCtrlDouble(self, initial=1.0, min=0.0, max=1.0, inc=0.1) + self._weight.SetDigits(2) + self._sizer.Add(self._weight) + self.show_weight(False) + self._init_close_button() + + @property + def block_button(self) -> BlockDefineButton: + return self._block_button + + @property + def weight(self) -> float: + """The weighting value for this entry.""" + return self._weight.GetValue() + + @weight.setter + def weight(self, weight: float): + self._weight.SetValue(weight) + + def show_weight(self, show: bool = True): + """Show or hide the weight entry.""" + self._weight.Show(show) + self.Layout() diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/find.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/find.py new file mode 100644 index 00000000..aebfe534 --- /dev/null +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/find.py @@ -0,0 +1,48 @@ +import wx + +import PyMCTranslate +from amulet.api.data_types import VersionNumberTuple +from amulet.api.block import PropertyTypeMultiple +from amulet_map_editor.api.wx.ui.mc.block import WildcardBlockDefineButton +from .base import BaseBlockEntry + + +class FindBlockEntry(BaseBlockEntry): + """A UI element that holds a wildcard block button and close button""" + + _button_props = BaseBlockEntry._button_props + ( + "all_properties", + "selected_properties", + ) + + def __init__( + self, + parent: wx.Window, + translation_manager: PyMCTranslate.TranslationManager, + platform: str = None, + version_number: VersionNumberTuple = None, + force_blockstate: bool = None, + namespace: str = None, + base_name: str = None, + selected_properties: PropertyTypeMultiple = None, + all_properties: PropertyTypeMultiple = None, + **kwargs, + ): + super().__init__(parent, **kwargs) + self._block_button = WildcardBlockDefineButton( + self, + translation_manager, + platform, + version_number, + force_blockstate, + namespace, + base_name, + selected_properties, + all_properties, + ) + self._sizer.Add(self._block_button, 1) + self._init_close_button() + + @property + def block_button(self) -> WildcardBlockDefineButton: + return self._block_button diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/fill.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/fill.py new file mode 100644 index 00000000..51c2df9e --- /dev/null +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/fill.py @@ -0,0 +1,38 @@ +from typing import Tuple, List +from amulet_map_editor import lang +from .base import BaseBlockContainer +from .block_entry import FillBlockEntry + + +class FillBlockContainer(BaseBlockContainer): + _blocks: List[FillBlockEntry] + + @property + def name(self) -> str: + return lang.get("program_3d_edit.fill_tool.fill") + + def _add_block(self): + super()._add_block() + if len(self._blocks) == 2: + for block in self._blocks: + block.show_weight() + elif len(self._blocks) >= 2: + self._blocks[-1].show_weight() + self.GetTopLevelParent().Layout() + + def _create_block(self) -> FillBlockEntry: + return FillBlockEntry( + self, self._translation_manager, *self._version + ) + + def _destroy_block_entry(self, window: FillBlockEntry): + super()._destroy_block_entry(window) + if len(self._blocks) == 1: + block = self._blocks[-1] + block.show_weight(False) + self.GetTopLevelParent().Layout() + + @property + def weights(self) -> Tuple[float, ...]: + """The weighting values for the blocks contained in this widget. May be unused.""" + return tuple(entry.weight for entry in self._blocks) diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/find.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/find.py new file mode 100644 index 00000000..389f22df --- /dev/null +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/find.py @@ -0,0 +1,17 @@ +from typing import List +from amulet_map_editor import lang +from .base import BaseBlockContainer +from .block_entry import FindBlockEntry + + +class FindBlockContainer(BaseBlockContainer): + _blocks: List[FindBlockEntry] + + @property + def name(self) -> str: + return lang.get("program_3d_edit.fill_tool.find") + + def _create_block(self) -> FindBlockEntry: + return FindBlockEntry( + self, self._translation_manager, *self._version + ) diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_widget.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_widget.py index c7e7aeba..c48a9ded 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_widget.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_widget.py @@ -1,13 +1,14 @@ from enum import Enum -from typing import List +from typing import List, Optional import wx import PyMCTranslate +from amulet.api.data_types import VersionGroupType from amulet_map_editor import lang from amulet_map_editor.api.wx.ui.simple import SimpleChoiceAny, SimpleScrollablePanel -from amulet_map_editor.api.wx.ui.version_select import ( +from amulet_map_editor.api.wx.ui.mc.version import ( VersionSelect, VersionChangeEvent, EVT_VERSION_CHANGE, @@ -22,36 +23,49 @@ class ReplaceMode(Enum): Map = 2 -LeftRightBorder = wx.LEFT | wx.RIGHT -BottomBorder = LeftRightBorder | wx.BOTTOM - - class FillReplaceWidget(wx.Panel): def __init__( self, parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager, + default_version: Optional[VersionGroupType] = None, **kwargs ): super().__init__(parent, **kwargs) self._translation_manager = translation_manager + if default_version is None: + default_version = ( + "java", + max(translation_manager.version_numbers("java")), + False, + ) + self._default_version = default_version sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(sizer) - self._version_select = VersionSelect(self, translation_manager) - self._version_select.Bind(EVT_VERSION_CHANGE, self._on_version_change) - sizer.Add(self._version_select, 0, wx.EXPAND) - top_sizer = wx.BoxSizer(wx.HORIZONTAL) - sizer.Add(top_sizer, 0, wx.ALL | wx.EXPAND, 5) + sizer.Add(top_sizer, 0, wx.BOTTOM | wx.EXPAND, 5) self._replace = wx.CheckBox( self, wx.ID_ANY, lang.get("program_3d_edit.fill_tool.replace") ) - self._replace.Bind(wx.EVT_CHECKBOX, self._on_replace_change) + self._replace.Bind(wx.EVT_CHECKBOX, self._on_check_change) top_sizer.Add(self._replace, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) + self._expert = wx.CheckBox(self, wx.ID_ANY, "Expert") + self._expert.Bind(wx.EVT_CHECKBOX, self._on_check_change) + top_sizer.Add(self._expert, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) + + self._pull_source = wx.CheckBox(self, wx.ID_ANY, "Pull From Source") + self._pull_source.Hide() + top_sizer.Add(self._pull_source, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) + + self._multiple = wx.CheckBox(self, wx.ID_ANY, "Multiple") + self._multiple.Hide() + self._multiple.Bind(wx.EVT_CHECKBOX, self._on_check_change) + top_sizer.Add(self._multiple, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) + self._replace_mode = SimpleChoiceAny(self, sort=False) self._replace_mode.Hide() self._replace_mode.SetItems( @@ -83,27 +97,35 @@ def _add_operation(self): replace_operation = ReplaceOperationWidget( self._operation_panel, self._translation_manager, - self._version_select.version, + *self._default_version, ) + replace_operation.set_expert(self.is_expert) + replace_operation.set_replace(self.is_replace) self._operations.append(replace_operation) - self._operation_sizer.Add(replace_operation, 0, wx.EXPAND | BottomBorder, 5) - - def _on_version_change(self, evt: VersionChangeEvent): - version = evt.platform, evt.version_number, evt.force_blockstate - for op in self._operations: - op.version = version + self._operation_sizer.Add(replace_operation, 0, wx.EXPAND | wx.BOTTOM, 5) - def _on_replace_change(self, evt): - self._replace_mode.Show(self.replace) + def _update_buttons(self): + self._pull_source.Show(self.is_expert) + # TODO: when multiple support is added uncomment this + # self._multiple.Show(self.is_expert) + # self._replace_mode.Show(self.is_expert and self._multiple.GetValue()) for operation in self._operations: - operation.replace(self.replace) + operation.set_replace(self.is_replace) + operation.set_expert(self.is_expert) self.GetTopLevelParent().Layout() + def _on_check_change(self, evt): + self._update_buttons() + @property - def replace(self) -> bool: + def is_replace(self) -> bool: """Is the replace check box ticked.""" return self._replace.GetValue() + @property + def is_expert(self) -> bool: + return self._expert.GetValue() + @property def replace_mode(self) -> ReplaceMode: return self._replace_mode.GetCurrentObject() diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/replace_widget.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/replace_widget.py index 101433ad..09647a06 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/replace_widget.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/replace_widget.py @@ -1,298 +1,20 @@ -from typing import List, Iterable, Tuple, Optional import wx -from wx.lib import newevent import PyMCTranslate -from amulet.api.block import Block, UniversalAirBlock -from amulet.api.block_entity import BlockEntity +from amulet.api.data_types import VersionNumberTuple from amulet_map_editor import lang -from .block_dialog import BlockSelectDialog - -LeftRightBorder = wx.LEFT | wx.RIGHT -BottomBorder = LeftRightBorder | wx.BOTTOM - -BlockCloseEvent, EVT_BLOCK_CLOSE = newevent.NewEvent() - -VersionType = Tuple[str, Tuple[int, ...], bool] - -BlockStorage = Tuple[Block, Optional[BlockEntity]] - - -def _check_version(version: VersionType): - assert isinstance(version, tuple) and len(version) == 3 - assert isinstance(version[0], str) - assert isinstance(version[1], tuple) and all(isinstance(v, int) for v in version[1]) - assert isinstance(version[2], bool) - - -class BlockPickButton(wx.Button): - def __init__( - self, - parent: wx.Window, - translation_manager: PyMCTranslate.TranslationManager, - version: VersionType, - block: BlockStorage, - **kwargs, - ): - super().__init__(parent, **kwargs) - self._translation_manager = translation_manager - self._version = version - self._block = block - self._update_button() - self.Bind(wx.EVT_BUTTON, self._on_press) - - @property - def version(self) -> VersionType: - return self._version - - @version.setter - def version(self, version: VersionType): - _check_version(version) - self._version = version - self._update_button() - - def _on_press(self, evt): - platform, verison_number, force_blockstate = self._version - dialog = BlockSelectDialog( - self, - self._translation_manager, - self._block, - platform, - verison_number, - force_blockstate, - ) - response = dialog.ShowModal() - if response == wx.ID_OK: - self.block = dialog.universal_block - dialog.Destroy() - - def _update_button(self): - platform, version, force_blockstate = self._version - block, block_entity = self._block - block, block_entity, _ = self._translation_manager.get_version( - platform, version - ).block.from_universal(block, block_entity, force_blockstate=force_blockstate) - if not isinstance(block, Block): - block, block_entity, _ = self._translation_manager.get_version( - platform, version - ).block.from_universal(UniversalAirBlock, force_blockstate=force_blockstate) - self.SetLabel(block.full_blockstate) - - @property - def block(self) -> BlockStorage: - """The universal block object stored in this button.""" - return self._block - - @block.setter - def block(self, block: BlockStorage): - if not ( - isinstance(block, tuple) - and len(block) == 2 - and isinstance(block[0], Block) - and block[1] is None - or (isinstance(block[1], BlockEntity)) - ): - raise ValueError(f"block must be a tuple of Block and BlockEntity.") - self._block = block - self._update_button() - - -class BlockEntry(wx.Panel): - def __init__( - self, - parent: wx.Window, - translation_manager: PyMCTranslate.TranslationManager, - version: VersionType, - block: BlockStorage, - show_weight=False, - **kwargs, - ): - super().__init__(parent, **kwargs) - sizer = wx.BoxSizer(wx.HORIZONTAL) - self.SetSizer(sizer) - self._block_button = BlockPickButton(self, translation_manager, version, block) - sizer.Add(self._block_button, 1) - self._weight = wx.SpinCtrlDouble(self, initial=100.0, min=0.0, max=100.0) - self._weight.SetDigits(2) - sizer.Add(self._weight) - self._close_button = wx.Button(self, label="❌") - self._close_button.Bind(wx.EVT_BUTTON, self._on_close) - self._close_button.SetMinSize((28, 28)) - sizer.Add(self._close_button) - self.show_weight(show_weight) - - @property - def version(self) -> VersionType: - return self._block_button.version - - @version.setter - def version(self, version: VersionType): - self._block_button.version = version - - def _on_close(self, evt): - evt2 = BlockCloseEvent() - evt2.SetEventObject(self) - wx.PostEvent(self, evt2) - - @property - def block(self) -> BlockStorage: - """The universal block contained within this widget.""" - return self._block_button.block - - @block.setter - def block(self, block: BlockStorage): - self._block_button.block = block - - @property - def weight(self) -> float: - """The weighting value for this entry. May be unused.""" - return self._weight.GetValue() - - @weight.setter - def weight(self, weight: float): - self._weight.SetValue(weight) - - def show_weight(self, show: bool = True): - """Show or hide the weight entry.""" - self._weight.Show(show) - self.Layout() - - def show_close(self, show: bool = True): - """Show or hide the close button.""" - self._close_button.Show(show) - self.Layout() - - def enable_close(self, enable: bool = True): - self._close_button.Enable(enable) - - -class BaseBlockContainer(wx.Panel): - def __init__( - self, - parent: wx.Window, - translation_manager: PyMCTranslate.TranslationManager, - version: VersionType, - **kwargs, - ): - super().__init__(parent, **kwargs) - self._translation_manager = translation_manager - self._version = version - - sizer = wx.BoxSizer(wx.VERTICAL) - self.SetSizer(sizer) - - top_sizer = wx.BoxSizer(wx.HORIZONTAL) - sizer.Add(top_sizer, 0, wx.EXPAND, 5) - find_label = wx.StaticText( - self, label=self.name, style=wx.ALIGN_CENTER_HORIZONTAL - ) - top_sizer.Add(find_label, 1, wx.ALIGN_CENTER_VERTICAL) - - self._add_button = wx.Button(self, label="➕") - self._add_button.Bind(wx.EVT_BUTTON, lambda evt: self._add_block()) - self._add_button.SetMinSize((28, 28)) - top_sizer.Add(self._add_button) - - self._block_sizer = wx.BoxSizer(wx.VERTICAL) - sizer.Add(self._block_sizer, 0, wx.EXPAND, 0) - - self._blocks: List[BlockEntry] = [] - self._add_block() - - @property - def version(self) -> VersionType: - return self._version - - @version.setter - def version(self, version: VersionType): - _check_version(version) - self._version = version - for block in self._blocks: - block.version = version - - def _add_block(self): - block = BlockEntry( - self, self._translation_manager, self._version, (UniversalAirBlock, None) - ) - block.Bind(EVT_BLOCK_CLOSE, self._remove_block) - self._block_sizer.Add(block, 0, wx.EXPAND | wx.TOP, 5) - self._blocks.append(block) - if len(self._blocks) == 1: - self._blocks[-1].enable_close(False) - if len(self._blocks) == 2: - for block in self._blocks: - block.enable_close() - elif len(self._blocks) >= 2: - self._blocks[-1].enable_close() - self.GetTopLevelParent().Layout() - - def _remove_block(self, evt): - window = evt.GetEventObject() - if isinstance(window, BlockEntry): - self._destroy_window(window) - - def _destroy_window(self, window: BlockEntry): - if window in self._blocks: - self._blocks.remove(window) - window.Destroy() - if len(self._blocks) == 1: - block = self._blocks[-1] - block.enable_close(False) - block.show_weight(False) - self.GetTopLevelParent().Layout() - - @property - def name(self) -> str: - raise NotImplementedError - - @property - def blocks(self) -> Tuple[BlockStorage, ...]: - """The universal blocks contained within this widget.""" - return tuple(entry.block for entry in self._blocks) - - @blocks.setter - def blocks(self, blocks: Iterable[BlockStorage]): - blocks = tuple(blocks) - while len(self._blocks) > len(blocks): - self._destroy_window(self._blocks[-1]) - while len(self._blocks) < len(blocks): - self._add_block() - for block, window in zip(blocks, self._blocks): - window.block = block - - -class FindWidget(BaseBlockContainer): - @property - def name(self) -> str: - return lang.get("program_3d_edit.fill_tool.find") - - -class FillWidget(BaseBlockContainer): - def _add_block(self): - super()._add_block() - if len(self._blocks) == 2: - for block in self._blocks: - block.show_weight() - elif len(self._blocks) >= 2: - self._blocks[-1].show_weight() - self.GetTopLevelParent().Layout() - - @property - def name(self) -> str: - return lang.get("program_3d_edit.fill_tool.fill") - - @property - def weights(self) -> Tuple[float]: - """The weighting values for the blocks contained in this widget. May be unused.""" - return tuple(entry.weight for entry in self._blocks) +from .block_container import FillBlockContainer, FindBlockContainer class ReplaceOperationWidget(wx.Panel): + """A widget containing a single Fill and optional Find widget.""" def __init__( self, parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager, - version: VersionType, + platform: str = None, + version_number: VersionNumberTuple = None, + force_blockstate: bool = None, **kwargs, ): kwargs["style"] = kwargs.get("style", 0) | wx.BORDER_SIMPLE @@ -302,7 +24,7 @@ def __init__( sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(sizer) - self._find = FindWidget(self, translation_manager, version) + self._find = FindBlockContainer(self, translation_manager, platform, version_number, force_blockstate) self._find.Hide() sizer.Add(self._find, 0, wx.EXPAND | wx.ALL, 5) @@ -313,26 +35,21 @@ def __init__( self._swap_button.Hide() sizer.Add(self._swap_button, 0, wx.EXPAND | wx.ALL, 5) - self._fill = FillWidget(self, translation_manager, version) + self._fill = FillBlockContainer(self, translation_manager, platform, version_number, force_blockstate) sizer.Add(self._fill, 0, wx.EXPAND | wx.ALL, 5) self.Layout() - @property - def version(self) -> VersionType: - return self._fill.version - - @version.setter - def version(self, version: VersionType): - self._find.version = version - self._fill.version = version - def _swap_blocks(self, evt): self.Freeze() - self._find.blocks, self._fill.blocks = self._fill.blocks, self._find.blocks + self._find.states, self._fill.states = self._fill.states, self._find.states self.GetTopLevelParent().Layout() self.Thaw() - def replace(self, replace: bool): + def set_replace(self, replace: bool): self._find.Show(replace) self._swap_button.Show(replace) + + def set_expert(self, expert: bool): + self._find.set_expert(expert) + self._fill.set_expert(expert) From cc2c41ed9170e6198888d4fc4e1e41847163d1e3 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Mon, 30 Aug 2021 14:27:07 +0100 Subject: [PATCH 089/139] Removed the old block dialog --- .../tools/fill_replace/block_dialog.py | 67 ------------------- 1 file changed, 67 deletions(-) delete mode 100644 amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_dialog.py diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_dialog.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_dialog.py deleted file mode 100644 index b3e21f7e..00000000 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_dialog.py +++ /dev/null @@ -1,67 +0,0 @@ -from typing import Tuple, Optional -import wx -import PyMCTranslate -from amulet.api.block import Block -from amulet.api.block_entity import BlockEntity -from amulet_map_editor.api.wx.ui.mc.block import BlockDefine - - -class BlockSelectDialog(wx.Dialog): - def __init__( - self, - parent: wx.Window, - translation_manager: PyMCTranslate.TranslationManager, - block: Tuple[Block, Optional[BlockEntity]], - platform: str, - version_number: Tuple[int, ...], - force_blockstate: bool, - *args, - **kwargs - ): - # begin wxGlade: BlockSelectDialog.__init__ - kwargs["style"] = ( - kwargs.get("style", 0) | wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER - ) - super().__init__(parent, *args, **kwargs) - self.SetTitle("Pick Block") - - sizer = wx.BoxSizer(wx.VERTICAL) - self.SetSizer(sizer) - - self._block_define = BlockDefine( - self, - translation_manager, - wx.HORIZONTAL, - platform, - version_number, - force_blockstate, - ) - self._block_define.universal_block = block - sizer.Add(self._block_define, 1, wx.EXPAND | wx.LEFT | wx.TOP | wx.RIGHT, 5) - - button_sizer = wx.StdDialogButtonSizer() - sizer.Add(button_sizer, 0, wx.ALIGN_RIGHT | wx.ALL, 4) - - self.button_ok = wx.Button(self, wx.ID_OK, "") - self.button_ok.SetDefault() - button_sizer.AddButton(self.button_ok) - - self.button_cancel = wx.Button(self, wx.ID_CANCEL, "") - button_sizer.AddButton(self.button_cancel) - - button_sizer.Realize() - - sizer.Fit(self) - - self.SetAffirmativeId(self.button_ok.GetId()) - self.SetEscapeId(self.button_cancel.GetId()) - - self.Layout() - - @property - def universal_block(self) -> Tuple[Block, Optional[BlockEntity]]: - return self._block_define.universal_block - - @universal_block.setter - def universal_block(self, universal_block: Tuple[Block, Optional[BlockEntity]]): - self._block_define.universal_block = universal_block From 8620696242942c0a897cbb9c270a32fb5bb939ac Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Wed, 1 Sep 2021 14:04:07 +0100 Subject: [PATCH 090/139] Cleaned up the state management a little --- amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py index c142039d..6ff0c0ff 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py @@ -35,10 +35,6 @@ def __init__( state: Dict[str, Any] = None, ): state = state or {} - state.setdefault("translation_manager", translation_manager) - state.setdefault("platform", platform) - state.setdefault("version_number", version_number) - state.setdefault("force_blockstate", force_blockstate) state.setdefault("namespace", namespace) state.setdefault("base_name", base_name) BaseDefine.__init__( From 78c059eef22bdc03e8c521a4d93109c3cb076757 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Wed, 1 Sep 2021 14:04:48 +0100 Subject: [PATCH 091/139] Reformatted --- .../plugins/tools/fill_replace/block_container/base.py | 1 + .../plugins/tools/fill_replace/block_container/fill.py | 4 +--- .../plugins/tools/fill_replace/block_container/find.py | 4 +--- .../edit/plugins/tools/fill_replace/replace_widget.py | 9 +++++++-- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/base.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/base.py index 038ff12e..020f88de 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/base.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/base.py @@ -8,6 +8,7 @@ class BaseBlockContainer(wx.Panel): """This is a UI element that contains one or more block buttons.""" + _blocks: List[BaseBlockEntry] def __init__( diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/fill.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/fill.py index 51c2df9e..efb4c461 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/fill.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/fill.py @@ -21,9 +21,7 @@ def _add_block(self): self.GetTopLevelParent().Layout() def _create_block(self) -> FillBlockEntry: - return FillBlockEntry( - self, self._translation_manager, *self._version - ) + return FillBlockEntry(self, self._translation_manager, *self._version) def _destroy_block_entry(self, window: FillBlockEntry): super()._destroy_block_entry(window) diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/find.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/find.py index 389f22df..ec7c91d4 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/find.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/find.py @@ -12,6 +12,4 @@ def name(self) -> str: return lang.get("program_3d_edit.fill_tool.find") def _create_block(self) -> FindBlockEntry: - return FindBlockEntry( - self, self._translation_manager, *self._version - ) + return FindBlockEntry(self, self._translation_manager, *self._version) diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/replace_widget.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/replace_widget.py index 09647a06..51531b29 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/replace_widget.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/replace_widget.py @@ -8,6 +8,7 @@ class ReplaceOperationWidget(wx.Panel): """A widget containing a single Fill and optional Find widget.""" + def __init__( self, parent: wx.Window, @@ -24,7 +25,9 @@ def __init__( sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(sizer) - self._find = FindBlockContainer(self, translation_manager, platform, version_number, force_blockstate) + self._find = FindBlockContainer( + self, translation_manager, platform, version_number, force_blockstate + ) self._find.Hide() sizer.Add(self._find, 0, wx.EXPAND | wx.ALL, 5) @@ -35,7 +38,9 @@ def __init__( self._swap_button.Hide() sizer.Add(self._swap_button, 0, wx.EXPAND | wx.ALL, 5) - self._fill = FillBlockContainer(self, translation_manager, platform, version_number, force_blockstate) + self._fill = FillBlockContainer( + self, translation_manager, platform, version_number, force_blockstate + ) sizer.Add(self._fill, 0, wx.EXPAND | wx.ALL, 5) self.Layout() From 4bb188b420a35c1b30f1ed00785ab45b5705b839 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Sat, 4 Sep 2021 11:05:02 +0100 Subject: [PATCH 092/139] Deferred tool creation until they are first shown Each tool takes a non-zero amount of time to create. Previously this was all done when the world was first loaded which slowed down loading time. This is now done when the tool is first shown to distribute the loading and only load the tools the user wants to use. --- .../edit/api/ui/tool/base_operation_choice.py | 6 ++++ .../programs/edit/api/ui/tool/base_tool_ui.py | 20 ++++++++++++- .../edit/api/ui/tool/default_base_tool_ui.py | 6 +++- .../programs/edit/api/ui/tool_manager.py | 2 ++ .../programs/edit/plugins/tools/chunk.py | 10 +++++-- .../edit/plugins/tools/import_tool.py | 8 ++++-- .../programs/edit/plugins/tools/paste.py | 28 +++++++++++++++---- .../programs/edit/plugins/tools/select.py | 23 +++++++++++++-- 8 files changed, 89 insertions(+), 14 deletions(-) diff --git a/amulet_map_editor/programs/edit/api/ui/tool/base_operation_choice.py b/amulet_map_editor/programs/edit/api/ui/tool/base_operation_choice.py index 9dc0f35f..c483e022 100644 --- a/amulet_map_editor/programs/edit/api/ui/tool/base_operation_choice.py +++ b/amulet_map_editor/programs/edit/api/ui/tool/base_operation_choice.py @@ -25,7 +25,13 @@ def __init__(self, canvas: "EditCanvas"): self._active_operation: Optional[OperationUIType] = None self._last_active_operation_id: Optional[str] = None + self._operation_choice: Optional[SimpleChoiceAny] = None + self._reload_operation: Optional[wx.BitmapButton] = None + self._operations: Optional[UIOperationManager] = None + self._operation_sizer: Optional[wx.BoxSizer] = None + def setup(self): + super().setup() horizontal_sizer = wx.BoxSizer(wx.HORIZONTAL) self._operation_choice = SimpleChoiceAny(self.canvas) diff --git a/amulet_map_editor/programs/edit/api/ui/tool/base_tool_ui.py b/amulet_map_editor/programs/edit/api/ui/tool/base_tool_ui.py index 469c4610..474976ff 100644 --- a/amulet_map_editor/programs/edit/api/ui/tool/base_tool_ui.py +++ b/amulet_map_editor/programs/edit/api/ui/tool/base_tool_ui.py @@ -1,5 +1,5 @@ import wx -from typing import Union +from typing import Union, TYPE_CHECKING from amulet_map_editor.programs.edit.api.edit_canvas_container import ( EditCanvasContainer, @@ -8,9 +8,27 @@ BaseToolUIType = Union[wx.Window, wx.Sizer, "BaseToolUI"] +if TYPE_CHECKING: + from amulet_map_editor.programs.edit.api.canvas import EditCanvas + class BaseToolUI(EditCanvasContainer, CanvasToggleElement): """The abstract base class for all tools that are to be loaded into the canvas.""" + def __init__(self, canvas: "EditCanvas"): + super().__init__(canvas) + self._is_setup = False + + @property + def is_setup(self) -> bool: + return self._is_setup + + def setup(self): + """ + Behaviour run before showing the tool for the first time. + This is useful so tools only need creating when they are first used. + Reduces the delay when first loading. + """ + self._is_setup = True @property def name(self) -> str: diff --git a/amulet_map_editor/programs/edit/api/ui/tool/default_base_tool_ui.py b/amulet_map_editor/programs/edit/api/ui/tool/default_base_tool_ui.py index 1f07c08d..c7f4f46b 100644 --- a/amulet_map_editor/programs/edit/api/ui/tool/default_base_tool_ui.py +++ b/amulet_map_editor/programs/edit/api/ui/tool/default_base_tool_ui.py @@ -1,5 +1,5 @@ import wx -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Optional from OpenGL.GL import ( glClear, GL_DEPTH_BUFFER_BIT, @@ -26,6 +26,10 @@ class DefaultBaseToolUI(BaseToolUI): def __init__(self, canvas: "EditCanvas"): super().__init__(canvas) + self._camera_behaviour: Optional[CameraBehaviour] + + def setup(self): + super().setup() self._camera_behaviour = CameraBehaviour(self.canvas) @property diff --git a/amulet_map_editor/programs/edit/api/ui/tool_manager.py b/amulet_map_editor/programs/edit/api/ui/tool_manager.py index 5a3c6555..c0df819a 100644 --- a/amulet_map_editor/programs/edit/api/ui/tool_manager.py +++ b/amulet_map_editor/programs/edit/api/ui/tool_manager.py @@ -110,6 +110,8 @@ def _enable_tool(self, tool: str): self._active_tool.Show() elif isinstance(self._active_tool, wx.Sizer): self._active_tool.ShowItems(show=True) + if not self._active_tool.is_setup: + self._active_tool.setup() self._active_tool.enable() self.canvas.reset_bound_events() self.canvas.Layout() diff --git a/amulet_map_editor/programs/edit/plugins/tools/chunk.py b/amulet_map_editor/programs/edit/plugins/tools/chunk.py index f3907994..46b1bfa7 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/chunk.py +++ b/amulet_map_editor/programs/edit/plugins/tools/chunk.py @@ -27,10 +27,17 @@ class ChunkTool(wx.BoxSizer, DefaultBaseToolUI): def __init__(self, canvas: "EditCanvas"): wx.BoxSizer.__init__(self, wx.HORIZONTAL) DefaultBaseToolUI.__init__(self, canvas) + self._selection: Optional[ChunkSelectionBehaviour] = None + self._button_panel: Optional[wx.Panel] = None + self._min_y: Optional[wx.SpinCtrl] = None + self._max_y: Optional[wx.SpinCtrl] = None + self._dimensions: Dict[Dimension, Tuple[int, int]] = {} + def setup(self): + super().setup() self._selection = ChunkSelectionBehaviour(self.canvas) - self._button_panel = wx.Panel(canvas) + self._button_panel = wx.Panel(self.canvas) button_sizer = wx.BoxSizer(wx.VERTICAL) self._button_panel.SetSizer(button_sizer) @@ -63,7 +70,6 @@ def __init__(self, canvas: "EditCanvas"): self._max_y.SetToolTip(lang.get("program_3d_edit.chunk_tool.max_y_tooltip")) y_sizer.Add(self._max_y, flag=wx.ALIGN_CENTER) self._max_y.Bind(wx.EVT_SPINCTRL, self._on_update_clipping) - self._dimensions: Dict[Dimension, Tuple[int, int]] = {} delete_button = wx.Button( self._button_panel, diff --git a/amulet_map_editor/programs/edit/plugins/tools/import_tool.py b/amulet_map_editor/programs/edit/plugins/tools/import_tool.py index 32eb1d9f..d58d0a95 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/import_tool.py +++ b/amulet_map_editor/programs/edit/plugins/tools/import_tool.py @@ -1,5 +1,5 @@ import wx -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Optional import traceback import amulet @@ -19,10 +19,14 @@ class ImportTool(wx.BoxSizer, DefaultBaseToolUI): def __init__(self, canvas: "EditCanvas"): wx.BoxSizer.__init__(self, wx.VERTICAL) DefaultBaseToolUI.__init__(self, canvas) + self._selection: Optional[StaticSelectionBehaviour] = None + self._open_file_button: Optional[wx.Button] = None + def setup(self): + super().setup() self._selection = StaticSelectionBehaviour(self.canvas) - self._open_file_button = wx.Button(canvas, label="Import File") + self._open_file_button = wx.Button(self.canvas, label="Import File") self._open_file_button.Bind(wx.EVT_BUTTON, self._on_open_file) self.AddStretchSpacer() self.Add(self._open_file_button, flag=wx.ALL, border=10) diff --git a/amulet_map_editor/programs/edit/plugins/tools/paste.py b/amulet_map_editor/programs/edit/plugins/tools/paste.py index 537e09b4..99242743 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/paste.py +++ b/amulet_map_editor/programs/edit/plugins/tools/paste.py @@ -1,5 +1,5 @@ import wx -from typing import TYPE_CHECKING, Tuple, Union, Type +from typing import TYPE_CHECKING, Tuple, Union, Type, Optional from OpenGL.GL import ( glClear, GL_DEPTH_BUFFER_BIT, @@ -243,13 +243,31 @@ class PasteTool(wx.BoxSizer, DefaultBaseToolUI): def __init__(self, canvas: "EditCanvas"): wx.BoxSizer.__init__(self, wx.HORIZONTAL) DefaultBaseToolUI.__init__(self, canvas) - - self._selection = StaticSelectionBehaviour(self.canvas) - self._cursor = PointerBehaviour(self.canvas) + self._selection: Optional[StaticSelectionBehaviour] = None + self._cursor: Optional[PointerBehaviour] = None self._moving = False self._is_enabled = False + self._paste_panel: Optional[wx.Panel] = None + self._paste_sizer: Optional[wx.BoxSizer] = None + self._location: Optional[TupleIntInput] = None + self._move_button: Optional[MoveButton] = None + self._free_rotation: Optional[wx.CheckBox] = None + self._rotation: Optional[RotationTupleInput] = None + self._rotate_left_button: Optional[wx.BitmapButton] = None + self._rotate_right_button: Optional[wx.BitmapButton] = None + self._scale: Optional[TupleFloatInput] = None + self._mirror_horizontal_button: Optional[wx.BitmapButton] = None + self._mirror_vertical_button: Optional[wx.BitmapButton] = None + self._copy_air: Optional[wx.CheckBox] = None + self._copy_water: Optional[wx.CheckBox] = None + self._copy_lava: Optional[wx.CheckBox] = None + + def setup(self): + super().setup() + self._selection = StaticSelectionBehaviour(self.canvas) + self._cursor = PointerBehaviour(self.canvas) - self._paste_panel = wx.Panel(canvas) + self._paste_panel = wx.Panel(self.canvas) self._paste_sizer = wx.BoxSizer(wx.VERTICAL) self._paste_panel.SetSizer(self._paste_sizer) self.Add(self._paste_panel, 0, wx.ALIGN_CENTER_VERTICAL) diff --git a/amulet_map_editor/programs/edit/plugins/tools/select.py b/amulet_map_editor/programs/edit/plugins/tools/select.py index b603f0f2..395f7f2a 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/select.py +++ b/amulet_map_editor/programs/edit/plugins/tools/select.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Type, Any, Callable, Tuple +from typing import TYPE_CHECKING, Type, Any, Callable, Optional import wx from OpenGL.GL import ( glClear, @@ -74,11 +74,28 @@ class SelectTool(wx.BoxSizer, DefaultBaseToolUI): def __init__(self, canvas: "EditCanvas"): wx.BoxSizer.__init__(self, wx.HORIZONTAL) DefaultBaseToolUI.__init__(self, canvas) - + self._selection: Optional[BlockSelectionBehaviour] = None + self._inspect_block: Optional[InspectBlockBehaviour] = None + self._button_panel: Optional[wx.Panel] = None + self._x1: Optional[wx.SpinCtrl] = None + self._y1: Optional[wx.SpinCtrl] = None + self._z1: Optional[wx.SpinCtrl] = None + self._x2: Optional[wx.SpinCtrl] = None + self._y2: Optional[wx.SpinCtrl] = None + self._z2: Optional[wx.SpinCtrl] = None + self._box_size_selector_fstring: Optional[str] = None + self._box_size_selector_text: Optional[wx.StaticText] = None + self._box_volume_text: Optional[wx.StaticText] = None + self._point1_move: Optional[Point1MoveButton] = None + self._point2_move: Optional[Point2MoveButton] = None + self._selection_move: Optional[SelectionMoveButton] = None + + def setup(self): + super().setup() self._selection = BlockSelectionBehaviour(self.canvas) self._inspect_block = InspectBlockBehaviour(self.canvas, self._selection) - self._button_panel = wx.Panel(canvas) + self._button_panel = wx.Panel(self.canvas) button_sizer = wx.BoxSizer(wx.VERTICAL) self._button_panel.SetSizer(button_sizer) From 244488f625e708587e3e7dcc232c7248eb50f414 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Wed, 8 Sep 2021 11:46:19 +0100 Subject: [PATCH 093/139] Added an event to enable size propagation upwards wx only propagates size changes down the UI hierarchy. Size changes in nested elements do not propagate upwards. This will serve as the event for UI elements to fire to trigger the canvas/containing UI to do a layout. This stops UI elements from having to be aware of their parents. --- amulet_map_editor/api/wx/ui/events.py | 5 +++++ .../programs/edit/api/canvas/base_edit_canvas.py | 6 ++++++ 2 files changed, 11 insertions(+) create mode 100644 amulet_map_editor/api/wx/ui/events.py diff --git a/amulet_map_editor/api/wx/ui/events.py b/amulet_map_editor/api/wx/ui/events.py new file mode 100644 index 00000000..26300969 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/events.py @@ -0,0 +1,5 @@ +from wx.lib import newevent + +# Sizers only propagate size changes downwards +# This event can be used to propagate those changes upwards so that parent elements know of the size change +ChildSizeEvent, EVT_CHILD_SIZE = newevent.NewCommandEvent() diff --git a/amulet_map_editor/programs/edit/api/canvas/base_edit_canvas.py b/amulet_map_editor/programs/edit/api/canvas/base_edit_canvas.py index 0b56a97e..712885f9 100644 --- a/amulet_map_editor/programs/edit/api/canvas/base_edit_canvas.py +++ b/amulet_map_editor/programs/edit/api/canvas/base_edit_canvas.py @@ -30,6 +30,7 @@ from amulet.api.data_types import OperationYieldType, Dimension from amulet_map_editor import experimental_bedrock_resources +from amulet_map_editor.api.wx.ui.events import EVT_CHILD_SIZE from amulet_map_editor.api.opengl.canvas import EventCanvas from amulet_map_editor.api.opengl.resource_pack.resource_pack import OpenGLResourcePack from amulet_map_editor.programs.edit.api.selection import ( @@ -223,6 +224,7 @@ def bind_events(self): self.buttons.bind_events() self.mouse.bind_events() self.renderer.bind_events() + self.Bind(EVT_CHILD_SIZE, self._do_layout) def enable(self): """Enable the canvas and start it working.""" @@ -284,6 +286,10 @@ def selection(self) -> SelectionManager: """A simple class for storing the selection state.""" return self._selection.value + def _do_layout(self, evt): + self.Layout() + evt.Skip() + def _on_size(self, evt): wx.CallAfter(self._set_size) evt.Skip() From 5b15785d76d97d81ea1b5a36cc6b2061a18bb16a Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Wed, 8 Sep 2021 11:48:35 +0100 Subject: [PATCH 094/139] Added default DoGetBestSize method to SimpleScrollablePanel --- amulet_map_editor/api/wx/ui/simple.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/amulet_map_editor/api/wx/ui/simple.py b/amulet_map_editor/api/wx/ui/simple.py index 8fa98c60..93e4a965 100644 --- a/amulet_map_editor/api/wx/ui/simple.py +++ b/amulet_map_editor/api/wx/ui/simple.py @@ -38,6 +38,17 @@ def __init__(self, parent: wx.Window, sizer_dir=wx.VERTICAL, **kwargs): self.SetupScrolling() self.SetAutoLayout(1) + def DoGetBestSize(self): + sizer = self.GetSizer() + if sizer is None: + return -1, -1 + else: + sx, sy = sizer.CalcMin() + return ( + sx + wx.SystemSettings.GetMetric(wx.SYS_VSCROLL_X), + sy + wx.SystemSettings.GetMetric(wx.SYS_HSCROLL_Y), + ) + class SimpleChoice(wx.Choice): """A wrapper for wx.Choice that sets up the UI for you.""" From 1bf487e6598aed1368cb42221746f8cacbf7c257 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Wed, 8 Sep 2021 11:49:09 +0100 Subject: [PATCH 095/139] Added optional max character length to the block buttons --- amulet_map_editor/api/wx/ui/mc/block/define/button/base.py | 7 +++++++ .../api/wx/ui/mc/block/define/button/normal.py | 2 ++ .../api/wx/ui/mc/block/define/button/wildcard.py | 2 ++ 3 files changed, 11 insertions(+) diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py index d2fb6828..4766da85 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py @@ -19,6 +19,7 @@ def __init__( namespace: str = None, base_name: str = None, show_pick_block: bool = False, + max_char_length: int = 99999, state: Dict[str, Any] = None, ): state = state or {} @@ -36,6 +37,7 @@ def __init__( self._block_widget: Optional[BaseBlockDefine] = None self.Bind(wx.EVT_BUTTON, self._on_press) self._show_pick_block = show_pick_block + self._max_char_length = max(3, max_char_length) def _init_state(self, state: Dict[str, Any]): """ @@ -44,6 +46,11 @@ def _init_state(self, state: Dict[str, Any]): """ BaseMCBlockIdentifier.__init__(self, **state) + def SetLabel(self, label: str): + if len(label) > self._max_char_length: + label = f"{label[:self._max_char_length]}..." + super().SetLabel(label) + def _on_press(self, evt): raise NotImplementedError diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py index ddaf07ab..c2714612 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py @@ -25,6 +25,7 @@ def __init__( base_name: str = None, properties: PropertyType = None, show_pick_block: bool = False, + max_char_length: int = 99999, state: Dict[str, Any] = None, ): state = state or {} @@ -39,6 +40,7 @@ def __init__( namespace, base_name, show_pick_block=show_pick_block, + max_char_length=max_char_length, state=state, ) self.update_button() diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py index 95148165..c048c511 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py @@ -26,6 +26,7 @@ def __init__( selected_properties: PropertyTypeMultiple = None, all_properties: PropertyTypeMultiple = None, show_pick_block: bool = False, + max_char_length: int = 99999, state: Dict[str, Any] = None, ): state = state or {} @@ -41,6 +42,7 @@ def __init__( namespace, base_name, show_pick_block=show_pick_block, + max_char_length=max_char_length, state=state, ) self.update_button() From 7e2aaec28d836c2c8393723bbd628f0f7b418c37 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Wed, 8 Sep 2021 11:50:01 +0100 Subject: [PATCH 096/139] Removed old replace operation DoGetBestClientSize method This is now handled in the base class --- .../operations/stock_plugins/operations/replace.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/replace.py b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/replace.py index 6e5e3254..fe37bb89 100644 --- a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/replace.py +++ b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/replace.py @@ -178,17 +178,6 @@ def _replace(self): count += 1 yield count / iter_count - def DoGetBestClientSize(self): - sizer = self.GetSizer() - if sizer is None: - return -1, -1 - else: - sx, sy = self.GetSizer().CalcMin() - return ( - sx + wx.SystemSettings.GetMetric(wx.SYS_VSCROLL_X), - sy + wx.SystemSettings.GetMetric(wx.SYS_HSCROLL_Y), - ) - export = { "name": "Replace", # the name of the plugin From 26a3bb62edf2b644fce5b245a6e6402e6678b3d0 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Wed, 8 Sep 2021 11:53:44 +0100 Subject: [PATCH 097/139] Added a semi-working fill replace tool UI The back end code still needs writing. Added character limits to the buttons. Redesigned the button layout for better usability --- .../programs/edit/api/ui/tool_manager.py | 2 + .../programs/edit/plugins/tools/__init__.py | 1 + .../plugins/tools/fill_replace/__init__.py | 1 + .../plugins/tools/fill_replace/__main__.py | 25 ++- .../fill_replace/block_container/base.py | 27 +++- .../block_container/block_entry/fill.py | 4 +- .../block_container/block_entry/find.py | 3 +- .../fill_replace/block_container/fill.py | 10 +- .../tools/fill_replace/fill_replace_tool.py | 71 +++++++++ .../tools/fill_replace/fill_replace_widget.py | 148 ++++++++++++++---- .../tools/fill_replace/replace_widget.py | 60 ------- 11 files changed, 240 insertions(+), 112 deletions(-) create mode 100644 amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_tool.py delete mode 100644 amulet_map_editor/programs/edit/plugins/tools/fill_replace/replace_widget.py diff --git a/amulet_map_editor/programs/edit/api/ui/tool_manager.py b/amulet_map_editor/programs/edit/api/ui/tool_manager.py index c0df819a..a6da68bf 100644 --- a/amulet_map_editor/programs/edit/api/ui/tool_manager.py +++ b/amulet_map_editor/programs/edit/api/ui/tool_manager.py @@ -16,6 +16,7 @@ ExportTool, OperationTool, SelectTool, + FillReplaceTool, ChunkTool, PasteTool, ) @@ -45,6 +46,7 @@ def __init__(self, canvas: "EditCanvas"): self.Add(tool_select_sizer, 0, wx.EXPAND, 0) self.register_tool(SelectTool) + self.register_tool(FillReplaceTool) self.register_tool(PasteTool) self.register_tool(OperationTool) self.register_tool(ImportTool) diff --git a/amulet_map_editor/programs/edit/plugins/tools/__init__.py b/amulet_map_editor/programs/edit/plugins/tools/__init__.py index 104ae379..b492160e 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/__init__.py +++ b/amulet_map_editor/programs/edit/plugins/tools/__init__.py @@ -1,4 +1,5 @@ from .select import SelectTool +from .fill_replace import FillReplaceTool from .operation import OperationTool from .export_tool import ExportTool from .import_tool import ImportTool diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/__init__.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/__init__.py index e69de29b..62263bb7 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/__init__.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/__init__.py @@ -0,0 +1 @@ +from .fill_replace_tool import FillReplaceTool diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/__main__.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/__main__.py index f7c0cf56..61a382b3 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/__main__.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/__main__.py @@ -3,23 +3,44 @@ from amulet_map_editor.programs.edit.plugins.tools.fill_replace.fill_replace_widget import ( FillReplaceWidget, ) +from amulet_map_editor.api.wx.ui.events import EVT_CHILD_SIZE, ChildSizeEvent if __name__ == "__main__": + class FillReplaceTool(wx.BoxSizer): + def __init__(self, canvas): + wx.BoxSizer.__init__(self, wx.VERTICAL) + self._panel = wx.Panel(canvas) + self.Add(self._panel) + panel_sizer = wx.BoxSizer(wx.VERTICAL) + self._panel.SetSizer(panel_sizer) + self._operations = FillReplaceWidget( + self._panel, + PyMCTranslate.new_translation_manager(), + ) + panel_sizer.Add(self._operations, 1, wx.LEFT | wx.TOP, 5) + self._button = wx.Button(self._panel, label="Run Operation") + panel_sizer.Add(self._button, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.BOTTOM | wx.TOP | wx.FIXED_MINSIZE, 5) + def main(): app = wx.App() dialog = wx.Dialog(None, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER) sizer = wx.BoxSizer() dialog.SetSizer(sizer) + cls = FillReplaceTool(dialog) sizer.Add( - FillReplaceWidget(dialog, PyMCTranslate.new_translation_manager()), + cls, 1, - wx.ALL | wx.EXPAND, + wx.ALL, 5, ) dialog.Show() dialog.Fit() + def do_layout(evt): + dialog.Layout() + dialog.Bind(EVT_CHILD_SIZE, do_layout) dialog.Bind(wx.EVT_CLOSE, lambda evt: dialog.Destroy()) + wx.lib.inspection.InspectionTool().Show() app.MainLoop() main() diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/base.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/base.py index 020f88de..3e63bf9e 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/base.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/base.py @@ -3,6 +3,7 @@ import PyMCTranslate from amulet.api.data_types import VersionNumberTuple +from amulet_map_editor.api.wx.ui.events import ChildSizeEvent from .block_entry import BaseBlockEntry, EVT_BLOCK_CLOSE @@ -24,16 +25,11 @@ def __init__( self._translation_manager = translation_manager self._version = (platform, version_number, force_blockstate) self._expert = False - sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(sizer) top_sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.Add(top_sizer, 0, wx.EXPAND, 5) - find_label = wx.StaticText( - self, label=self.name, style=wx.ALIGN_CENTER_HORIZONTAL - ) - top_sizer.Add(find_label, 1, wx.ALIGN_CENTER_VERTICAL) self._add_button = wx.Button(self, label="➕") self._add_button.Bind(wx.EVT_BUTTON, lambda evt: self._add_block()) @@ -41,12 +37,21 @@ def __init__( self._add_button.Show(self._expert) top_sizer.Add(self._add_button) + label = wx.StaticText( + self, label=self.name, style=wx.ALIGN_CENTER_HORIZONTAL + ) + top_sizer.Add(label, 1, wx.ALIGN_CENTER_VERTICAL) + self._block_sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(self._block_sizer, 0, wx.EXPAND, 0) self._blocks = [] self._add_block() + def _post_change_size(self): + """Call this when the size changes to notify parent elements.""" + wx.PostEvent(self, ChildSizeEvent(0)) + @property def name(self) -> str: raise NotImplementedError @@ -58,12 +63,16 @@ def set_expert(self, expert: bool): block.show_close(expert) if not expert: while len(self._blocks) > 1: - self._destroy_block_entry(self._blocks[-1]) + self._do_destroy_block_entry(self._blocks[-1]) def _create_block(self) -> BaseBlockEntry: raise NotImplementedError def _add_block(self): + self._do_add_block() + self._post_change_size() + + def _do_add_block(self): block = self._create_block() block.Bind(EVT_BLOCK_CLOSE, self._on_block_entry_close) self._block_sizer.Add(block, 0, wx.EXPAND | wx.TOP, 5) @@ -78,7 +87,6 @@ def _add_block(self): # enable the block that was just added self._blocks[-1].enable_close() block.show_close(self._expert) - self.GetTopLevelParent().Layout() def _on_block_entry_close(self, evt): window = evt.GetEventObject() @@ -86,13 +94,16 @@ def _on_block_entry_close(self, evt): self._destroy_block_entry(window) def _destroy_block_entry(self, window: BaseBlockEntry): + self._do_destroy_block_entry(window) + self._post_change_size() + + def _do_destroy_block_entry(self, window: BaseBlockEntry): if window in self._blocks: self._blocks.remove(window) window.Destroy() if len(self._blocks) == 1: block = self._blocks[-1] block.enable_close(False) - self.GetTopLevelParent().Layout() @property def states(self) -> List[Dict[str, Any]]: diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/fill.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/fill.py index 2b973a32..597d33a0 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/fill.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/fill.py @@ -25,7 +25,7 @@ def __init__( **kwargs, ): super().__init__(parent, **kwargs) - + self._init_close_button() self._block_button = BlockDefineButton( self, translation_manager, @@ -35,13 +35,13 @@ def __init__( namespace, base_name, properties, + max_char_length=40 ) self._sizer.Add(self._block_button, 1) self._weight = wx.SpinCtrlDouble(self, initial=1.0, min=0.0, max=1.0, inc=0.1) self._weight.SetDigits(2) self._sizer.Add(self._weight) self.show_weight(False) - self._init_close_button() @property def block_button(self) -> BlockDefineButton: diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/find.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/find.py index aebfe534..4bca2f9f 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/find.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/find.py @@ -29,6 +29,7 @@ def __init__( **kwargs, ): super().__init__(parent, **kwargs) + self._init_close_button() self._block_button = WildcardBlockDefineButton( self, translation_manager, @@ -39,9 +40,9 @@ def __init__( base_name, selected_properties, all_properties, + max_char_length=40 ) self._sizer.Add(self._block_button, 1) - self._init_close_button() @property def block_button(self) -> WildcardBlockDefineButton: diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/fill.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/fill.py index efb4c461..9bbebc31 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/fill.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/fill.py @@ -11,24 +11,22 @@ class FillBlockContainer(BaseBlockContainer): def name(self) -> str: return lang.get("program_3d_edit.fill_tool.fill") - def _add_block(self): - super()._add_block() + def _do_add_block(self): + super()._do_add_block() if len(self._blocks) == 2: for block in self._blocks: block.show_weight() elif len(self._blocks) >= 2: self._blocks[-1].show_weight() - self.GetTopLevelParent().Layout() def _create_block(self) -> FillBlockEntry: return FillBlockEntry(self, self._translation_manager, *self._version) - def _destroy_block_entry(self, window: FillBlockEntry): - super()._destroy_block_entry(window) + def _do_destroy_block_entry(self, window: FillBlockEntry): + super()._do_destroy_block_entry(window) if len(self._blocks) == 1: block = self._blocks[-1] block.show_weight(False) - self.GetTopLevelParent().Layout() @property def weights(self) -> Tuple[float, ...]: diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_tool.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_tool.py new file mode 100644 index 00000000..7bc4d0ac --- /dev/null +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_tool.py @@ -0,0 +1,71 @@ +from typing import TYPE_CHECKING, Optional +import wx +from OpenGL.GL import ( + glClear, + GL_DEPTH_BUFFER_BIT, +) + +from amulet_map_editor.api.opengl.camera import Projection +from amulet_map_editor.programs.edit.api.behaviour.block_selection_behaviour import ( + BlockSelectionBehaviour, +) +from amulet_map_editor.programs.edit.api.ui.tool import DefaultBaseToolUI +from amulet_map_editor.api.wx.ui.events import EVT_CHILD_SIZE +from .fill_replace_widget import FillReplaceWidget + +if TYPE_CHECKING: + from amulet_map_editor.programs.edit.api.canvas import EditCanvas + + +class FillReplaceTool(wx.BoxSizer, DefaultBaseToolUI): + def __init__(self, canvas: "EditCanvas"): + wx.BoxSizer.__init__(self, wx.VERTICAL) + DefaultBaseToolUI.__init__(self, canvas) + self._panel: Optional[wx.Panel] = None + self._operations: Optional[FillReplaceWidget] = None + self._button: Optional[wx.Button] = None + self._selection: Optional[BlockSelectionBehaviour] = None + + def setup(self): + super().setup() + self._panel = wx.Panel(self.canvas) + self.Add(self._panel) + panel_sizer = wx.BoxSizer(wx.VERTICAL) + self._panel.SetSizer(panel_sizer) + self._operations = FillReplaceWidget( + self._panel, + self.canvas.world.translation_manager, + self.canvas.world.level_wrapper.platform, + self.canvas.world.level_wrapper.version, + ) + panel_sizer.Add(self._operations, 1, wx.LEFT | wx.TOP, 5) + + self._button = wx.Button(self._panel, label="Run Operation") + panel_sizer.Add(self._button, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.BOTTOM | wx.TOP, 5) + self._button.Bind(wx.EVT_BUTTON, self._operation) + + self._selection = BlockSelectionBehaviour(self.canvas) + + @property + def name(self) -> str: + return "Fill/Replace" + + def bind_events(self): + super().bind_events() + self._selection.bind_events() + + def enable(self): + super().enable() + self._selection.enable() + + def _on_draw(self, evt): + self.canvas.renderer.start_draw() + if self.canvas.camera.projection_mode == Projection.PERSPECTIVE: + self.canvas.renderer.draw_sky_box() + glClear(GL_DEPTH_BUFFER_BIT) + self.canvas.renderer.draw_level() + self._selection.draw() + self.canvas.renderer.end_draw() + + def _operation(self, evt): + pass diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_widget.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_widget.py index c48a9ded..3c3db209 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_widget.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_widget.py @@ -1,20 +1,15 @@ from enum import Enum -from typing import List, Optional - +from typing import Tuple, List, Dict, Any import wx +from wx.lib.scrolledpanel import ScrolledPanel import PyMCTranslate -from amulet.api.data_types import VersionGroupType +from amulet.api.data_types import VersionNumberTuple from amulet_map_editor import lang -from amulet_map_editor.api.wx.ui.simple import SimpleChoiceAny, SimpleScrollablePanel -from amulet_map_editor.api.wx.ui.mc.version import ( - VersionSelect, - VersionChangeEvent, - EVT_VERSION_CHANGE, -) - -from .replace_widget import ReplaceOperationWidget +from amulet_map_editor.api.wx.ui.simple import SimpleChoiceAny +from amulet_map_editor.api.wx.ui.events import EVT_CHILD_SIZE, ChildSizeEvent +from .block_container import FillBlockContainer, FindBlockContainer class ReplaceMode(Enum): @@ -23,29 +18,112 @@ class ReplaceMode(Enum): Map = 2 +class ReplaceOperationWidget(wx.Panel): + """A widget containing a single Fill and optional Find widget.""" + + def __init__( + self, + parent: wx.Window, + translation_manager: PyMCTranslate.TranslationManager, + platform: str = None, + version_number: VersionNumberTuple = None, + force_blockstate: bool = None, + **kwargs, + ): + kwargs["style"] = kwargs.get("style", 0) | wx.BORDER_SIMPLE + super().__init__(parent, **kwargs) + + sizer = wx.BoxSizer(wx.VERTICAL) + self.SetSizer(sizer) + + self._find = FindBlockContainer( + self, translation_manager, platform, version_number, force_blockstate + ) + self._find.Hide() + sizer.Add(self._find, 0, wx.EXPAND | wx.ALL, 5) + + self._swap_button = wx.Button( + self, wx.ID_ANY, lang.get("program_3d_edit.fill_tool.swap") + ) + self._swap_button.Bind(wx.EVT_BUTTON, self._swap_blocks) + self._swap_button.Hide() + sizer.Add(self._swap_button, 0, wx.EXPAND | wx.ALL, 5) + + self._fill = FillBlockContainer( + self, translation_manager, platform, version_number, force_blockstate + ) + sizer.Add(self._fill, 0, wx.EXPAND | wx.ALL, 5) + + def _post_change_size(self): + """Call this to resize and notify parent elements.""" + wx.PostEvent(self, ChildSizeEvent(0)) + + def _swap_blocks(self, evt): + self._find.states, self._fill.states = self._fill.states, self._find.states + self._post_change_size() + + def set_replace(self, replace: bool): + self._find.Show(replace) + self._swap_button.Show(replace) + + def set_expert(self, expert: bool): + self._find.set_expert(expert) + self._fill.set_expert(expert) + + @property + def operation(self) -> Tuple[List[Dict[str, Any]], List[Dict[str, Any]]]: + return self._find.states, self._fill.states + + +class OperationContainer(ScrolledPanel): + def __init__(self, parent: wx.Window): + super().__init__(parent) + self._operations: List[ReplaceOperationWidget] = [] + self._operation_sizer = wx.BoxSizer(wx.VERTICAL) + self.SetSizer(self._operation_sizer) + self.SetupScrolling(scroll_x=False) + self.FitInside() + + def DoGetBestSize(self): + sizer = self.GetSizer() + if sizer is None: + return -1, -1 + else: + sx, sy = sizer.CalcMin() + return ( + sx + wx.SystemSettings.GetMetric(wx.SYS_VSCROLL_X), + sy, + ) + + def __iter__(self): + yield from self._operations + + def add_operation(self, replace_operation: ReplaceOperationWidget): + self._operation_sizer.Add(replace_operation, 0, wx.TOP, 5) + self._operations.append(replace_operation) + + class FillReplaceWidget(wx.Panel): def __init__( self, parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager, - default_version: Optional[VersionGroupType] = None, + platform: str = None, + version_number: VersionNumberTuple = None, + force_blockstate: bool = None, **kwargs ): super().__init__(parent, **kwargs) self._translation_manager = translation_manager - if default_version is None: - default_version = ( - "java", - max(translation_manager.version_numbers("java")), - False, - ) - self._default_version = default_version + self._platform = platform + self._version_number = version_number + self._force_blockstate = force_blockstate sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(sizer) top_sizer = wx.BoxSizer(wx.HORIZONTAL) - sizer.Add(top_sizer, 0, wx.BOTTOM | wx.EXPAND, 5) + sizer.Add(top_sizer, 0, wx.EXPAND) self._replace = wx.CheckBox( self, wx.ID_ANY, lang.get("program_3d_edit.fill_tool.replace") @@ -80,39 +158,39 @@ def __init__( } ) top_sizer.Add( - self._replace_mode, 1, wx.LEFT | wx.RESERVE_SPACE_EVEN_IF_HIDDEN, 5 + self._replace_mode, 0, wx.LEFT, 5 ) - self._operation_panel = SimpleScrollablePanel(self) - sizer.Add(self._operation_panel, 1, wx.EXPAND) - self._operation_sizer = wx.BoxSizer(wx.VERTICAL) - self._operation_panel.SetSizer(self._operation_sizer) - - self._operations: List[ReplaceOperationWidget] = [] + self._operation_panel = OperationContainer(self) + sizer.Add(self._operation_panel, 0) self._add_operation() - self.Layout() + def _post_change_size(self): + """Call this to resize and notify parent elements.""" + wx.PostEvent(self, ChildSizeEvent(0)) def _add_operation(self): replace_operation = ReplaceOperationWidget( self._operation_panel, self._translation_manager, - *self._default_version, + self._platform, + self._version_number, + self._force_blockstate ) replace_operation.set_expert(self.is_expert) replace_operation.set_replace(self.is_replace) - self._operations.append(replace_operation) - self._operation_sizer.Add(replace_operation, 0, wx.EXPAND | wx.BOTTOM, 5) + self._operation_panel.add_operation(replace_operation) + self._operation_panel.FitInside() def _update_buttons(self): self._pull_source.Show(self.is_expert) # TODO: when multiple support is added uncomment this # self._multiple.Show(self.is_expert) # self._replace_mode.Show(self.is_expert and self._multiple.GetValue()) - for operation in self._operations: + for operation in self._operation_panel: operation.set_replace(self.is_replace) operation.set_expert(self.is_expert) - self.GetTopLevelParent().Layout() + self._post_change_size() def _on_check_change(self, evt): self._update_buttons() @@ -129,3 +207,7 @@ def is_expert(self) -> bool: @property def replace_mode(self) -> ReplaceMode: return self._replace_mode.GetCurrentObject() + + @property + def operations(self) -> List[Tuple[List[Dict[str, Any]], List[Dict[str, Any]]]]: + return [op.operation for op in self._operation_panel] diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/replace_widget.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/replace_widget.py deleted file mode 100644 index 51531b29..00000000 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/replace_widget.py +++ /dev/null @@ -1,60 +0,0 @@ -import wx - -import PyMCTranslate -from amulet.api.data_types import VersionNumberTuple -from amulet_map_editor import lang -from .block_container import FillBlockContainer, FindBlockContainer - - -class ReplaceOperationWidget(wx.Panel): - """A widget containing a single Fill and optional Find widget.""" - - def __init__( - self, - parent: wx.Window, - translation_manager: PyMCTranslate.TranslationManager, - platform: str = None, - version_number: VersionNumberTuple = None, - force_blockstate: bool = None, - **kwargs, - ): - kwargs["style"] = kwargs.get("style", 0) | wx.BORDER_SIMPLE - super().__init__(parent, **kwargs) - self._replace = False - - sizer = wx.BoxSizer(wx.VERTICAL) - self.SetSizer(sizer) - - self._find = FindBlockContainer( - self, translation_manager, platform, version_number, force_blockstate - ) - self._find.Hide() - sizer.Add(self._find, 0, wx.EXPAND | wx.ALL, 5) - - self._swap_button = wx.Button( - self, wx.ID_ANY, lang.get("program_3d_edit.fill_tool.swap") - ) - self._swap_button.Bind(wx.EVT_BUTTON, self._swap_blocks) - self._swap_button.Hide() - sizer.Add(self._swap_button, 0, wx.EXPAND | wx.ALL, 5) - - self._fill = FillBlockContainer( - self, translation_manager, platform, version_number, force_blockstate - ) - sizer.Add(self._fill, 0, wx.EXPAND | wx.ALL, 5) - - self.Layout() - - def _swap_blocks(self, evt): - self.Freeze() - self._find.states, self._fill.states = self._fill.states, self._find.states - self.GetTopLevelParent().Layout() - self.Thaw() - - def set_replace(self, replace: bool): - self._find.Show(replace) - self._swap_button.Show(replace) - - def set_expert(self, expert: bool): - self._find.set_expert(expert) - self._fill.set_expert(expert) From b3b73dfe010ba9d29d68eca5309191846a36d1d6 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Wed, 8 Sep 2021 11:54:05 +0100 Subject: [PATCH 098/139] Reformatted --- .../programs/edit/api/ui/tool/base_tool_ui.py | 1 + .../edit/plugins/tools/fill_replace/__main__.py | 11 +++++++++-- .../tools/fill_replace/block_container/base.py | 4 +--- .../fill_replace/block_container/block_entry/fill.py | 2 +- .../fill_replace/block_container/block_entry/find.py | 2 +- .../plugins/tools/fill_replace/fill_replace_tool.py | 4 +++- .../plugins/tools/fill_replace/fill_replace_widget.py | 8 +++----- 7 files changed, 19 insertions(+), 13 deletions(-) diff --git a/amulet_map_editor/programs/edit/api/ui/tool/base_tool_ui.py b/amulet_map_editor/programs/edit/api/ui/tool/base_tool_ui.py index 474976ff..aa079020 100644 --- a/amulet_map_editor/programs/edit/api/ui/tool/base_tool_ui.py +++ b/amulet_map_editor/programs/edit/api/ui/tool/base_tool_ui.py @@ -14,6 +14,7 @@ class BaseToolUI(EditCanvasContainer, CanvasToggleElement): """The abstract base class for all tools that are to be loaded into the canvas.""" + def __init__(self, canvas: "EditCanvas"): super().__init__(canvas) self._is_setup = False diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/__main__.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/__main__.py index 61a382b3..a01a6c88 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/__main__.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/__main__.py @@ -6,6 +6,7 @@ from amulet_map_editor.api.wx.ui.events import EVT_CHILD_SIZE, ChildSizeEvent if __name__ == "__main__": + class FillReplaceTool(wx.BoxSizer): def __init__(self, canvas): wx.BoxSizer.__init__(self, wx.VERTICAL) @@ -19,8 +20,12 @@ def __init__(self, canvas): ) panel_sizer.Add(self._operations, 1, wx.LEFT | wx.TOP, 5) self._button = wx.Button(self._panel, label="Run Operation") - panel_sizer.Add(self._button, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.BOTTOM | wx.TOP | wx.FIXED_MINSIZE, 5) - + panel_sizer.Add( + self._button, + 0, + wx.ALIGN_CENTER_HORIZONTAL | wx.BOTTOM | wx.TOP | wx.FIXED_MINSIZE, + 5, + ) def main(): app = wx.App() @@ -36,8 +41,10 @@ def main(): ) dialog.Show() dialog.Fit() + def do_layout(evt): dialog.Layout() + dialog.Bind(EVT_CHILD_SIZE, do_layout) dialog.Bind(wx.EVT_CLOSE, lambda evt: dialog.Destroy()) wx.lib.inspection.InspectionTool().Show() diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/base.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/base.py index 3e63bf9e..e1122632 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/base.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/base.py @@ -37,9 +37,7 @@ def __init__( self._add_button.Show(self._expert) top_sizer.Add(self._add_button) - label = wx.StaticText( - self, label=self.name, style=wx.ALIGN_CENTER_HORIZONTAL - ) + label = wx.StaticText(self, label=self.name, style=wx.ALIGN_CENTER_HORIZONTAL) top_sizer.Add(label, 1, wx.ALIGN_CENTER_VERTICAL) self._block_sizer = wx.BoxSizer(wx.VERTICAL) diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/fill.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/fill.py index 597d33a0..7d143d63 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/fill.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/fill.py @@ -35,7 +35,7 @@ def __init__( namespace, base_name, properties, - max_char_length=40 + max_char_length=40, ) self._sizer.Add(self._block_button, 1) self._weight = wx.SpinCtrlDouble(self, initial=1.0, min=0.0, max=1.0, inc=0.1) diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/find.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/find.py index 4bca2f9f..0d462b05 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/find.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/find.py @@ -40,7 +40,7 @@ def __init__( base_name, selected_properties, all_properties, - max_char_length=40 + max_char_length=40, ) self._sizer.Add(self._block_button, 1) diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_tool.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_tool.py index 7bc4d0ac..56e3a636 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_tool.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_tool.py @@ -41,7 +41,9 @@ def setup(self): panel_sizer.Add(self._operations, 1, wx.LEFT | wx.TOP, 5) self._button = wx.Button(self._panel, label="Run Operation") - panel_sizer.Add(self._button, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.BOTTOM | wx.TOP, 5) + panel_sizer.Add( + self._button, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.BOTTOM | wx.TOP, 5 + ) self._button.Bind(wx.EVT_BUTTON, self._operation) self._selection = BlockSelectionBehaviour(self.canvas) diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_widget.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_widget.py index 3c3db209..aa23048d 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_widget.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_widget.py @@ -111,7 +111,7 @@ def __init__( platform: str = None, version_number: VersionNumberTuple = None, force_blockstate: bool = None, - **kwargs + **kwargs, ): super().__init__(parent, **kwargs) self._translation_manager = translation_manager @@ -157,9 +157,7 @@ def __init__( ReplaceMode.Map: lang.get("program_3d_edit.fill_tool.replace_mode.map"), } ) - top_sizer.Add( - self._replace_mode, 0, wx.LEFT, 5 - ) + top_sizer.Add(self._replace_mode, 0, wx.LEFT, 5) self._operation_panel = OperationContainer(self) sizer.Add(self._operation_panel, 0) @@ -175,7 +173,7 @@ def _add_operation(self): self._translation_manager, self._platform, self._version_number, - self._force_blockstate + self._force_blockstate, ) replace_operation.set_expert(self.is_expert) replace_operation.set_replace(self.is_replace) From 48c0f9791d2fe18cb4f89431af5b6b85d849b189 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Sun, 12 Sep 2021 12:15:31 +0100 Subject: [PATCH 099/139] Made it easier to extend the block UI elements --- .../api/wx/ui/mc/base/base_define.py | 2 + .../api/wx/ui/mc/block/define/button/base.py | 2 + .../wx/ui/mc/block/define/button/normal.py | 26 +++++--- .../api/wx/ui/mc/block/define/widget/base.py | 2 + .../wx/ui/mc/block/define/widget/normal.py | 19 +++--- .../mc/block/properties/single/automatic.py | 61 +++++++++++++------ .../wx/ui/mc/block/properties/single/main.py | 17 ++++-- 7 files changed, 88 insertions(+), 41 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_define.py b/amulet_map_editor/api/wx/ui/mc/base/base_define.py index dd571e29..7ff9bd7a 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/base_define.py +++ b/amulet_map_editor/api/wx/ui/mc/base/base_define.py @@ -17,6 +17,8 @@ class BaseDefine(wx.Panel, BaseMCVersion): + _version_picker: VersionSelect + def __init__( self, parent, diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py index 4766da85..4d7a9c6b 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py @@ -9,6 +9,8 @@ class BaseBlockDefineButton(wx.Button, BaseMCBlockIdentifier): + _block_widget: Optional[BaseBlockDefine] + def __init__( self, parent: wx.Window, diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py index c2714612..a872a469 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py @@ -48,9 +48,8 @@ def __init__( def _init_state(self, state: Dict[str, Any]): NormalMCBlock.__init__(self, **state) - def _on_press(self, evt): - dialog = SimpleDialog(self, "Pick a Block") - self._block_widget = BlockDefine( + def _create_block_define(self, dialog: wx.Dialog) -> BlockDefine: + return BlockDefine( dialog, self._translation_manager, wx.HORIZONTAL, @@ -61,15 +60,22 @@ def _on_press(self, evt): self.base_name, self.properties, ) - dialog.sizer.Add(self._block_widget) + + def _update_from_block_define(self, block_define: BlockDefine): + self._set_platform(self._block_widget.platform) + self._set_version_number(self._block_widget.version_number) + self._set_force_blockstate(self._block_widget.force_blockstate) + self._set_namespace(self._block_widget.namespace) + self._set_base_name(self._block_widget.base_name) + self._set_properties(self._block_widget.properties) + + def _on_press(self, evt): + dialog = SimpleDialog(self, "Pick a Block") + self._block_widget = self._create_block_define(dialog) + dialog.sizer.Add(self._block_widget, 1, wx.EXPAND) dialog.Fit() if dialog.ShowModal() == wx.ID_OK: - self._set_platform(self._block_widget.platform) - self._set_version_number(self._block_widget.version_number) - self._set_force_blockstate(self._block_widget.force_blockstate) - self._set_namespace(self._block_widget.namespace) - self._set_base_name(self._block_widget.base_name) - self._set_properties(self._block_widget.properties) + self._update_from_block_define(self._block_widget) self.update_button() self._block_widget = None dialog.Destroy() diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py index 6ff0c0ff..35000d16 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py @@ -21,6 +21,8 @@ class BaseBlockDefine(BaseDefine, BaseMCBlockIdentifier): A UI that merges a version select widget with a block select widget and a property select. """ + _picker: BlockIdentifierSelect + def __init__( self, parent, diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py index 48bf12ad..7d97fecf 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py @@ -26,6 +26,8 @@ class BlockDefine(BaseBlockDefine, NormalMCBlock): A UI that merges a version select widget with a block select widget and a property select. """ + _property_picker: SinglePropertySelect + def __init__( self, parent, @@ -59,9 +61,17 @@ def __init__( right_sizer = wx.BoxSizer(wx.VERTICAL) border = wx.LEFT if orientation == wx.HORIZONTAL else wx.TOP self._sizer.Add(right_sizer, 1, wx.EXPAND | border, 5) - self._property_picker = SinglePropertySelect( + self._property_picker = self._create_properties() + right_sizer.Add(self._property_picker, 1, wx.EXPAND) + self._property_picker.Bind( + EVT_SINGLE_PROPERTIES_CHANGE, self._on_property_change + ) + self.Layout() + + def _create_properties(self) -> SinglePropertySelect: + return SinglePropertySelect( self, - translation_manager, + self._translation_manager, self.platform, self.version_number, self.force_blockstate, @@ -69,11 +79,6 @@ def __init__( self.base_name, self.properties, ) - right_sizer.Add(self._property_picker, 1, wx.EXPAND) - self._property_picker.Bind( - EVT_SINGLE_PROPERTIES_CHANGE, self._on_property_change - ) - self.Layout() def _init_state(self, state: Dict[str, Any]): NormalMCBlock.__init__(self, **state) diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/single/automatic.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/automatic.py index 244bb267..c74263cb 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/single/automatic.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/single/automatic.py @@ -24,16 +24,15 @@ def __init__( ): super().__init__(parent) - header_sizer = wx.BoxSizer(wx.HORIZONTAL) - self._sizer.Add(header_sizer, 0, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, 5) + self._property_sizer = wx.FlexGridSizer(2, 5, 5) label = wx.StaticText(self, label="Property Name", style=wx.ALIGN_CENTER) - header_sizer.Add(label, 1) + self._property_sizer.Add(label, 1, wx.ALIGN_CENTER) label = wx.StaticText( self, label="Property Value (SNBT)", style=wx.ALIGN_CENTER ) - header_sizer.Add(label, 1, wx.LEFT, 5) - self._property_sizer = wx.GridSizer(2, 5, 5) - self._sizer.Add(self._property_sizer, 0, wx.ALL | wx.EXPAND, 5) + self._property_sizer.Add(label, 1, wx.ALIGN_CENTER) + + self._sizer.Add(self._property_sizer, 1, wx.ALL | wx.EXPAND, 5) self._states: StatesType = {} self._properties: Dict[str, wx.Choice] = {} @@ -61,29 +60,51 @@ def states(self, states: StatesType): default if default < len(valid_choices) else 0, ) self.Freeze() - self._properties.clear() - self._property_sizer.Clear(True) + self._tear_down_properties() props = {} for name, (choices, default) in self._states.items(): - label = wx.StaticText(self, label=name) - self._property_sizer.Add(label, 0, wx.ALIGN_CENTER) - choice = wx.Choice(self, choices=[c.to_snbt() for c in choices]) - self._property_sizer.Add(choice, 0, wx.EXPAND) - choice.Bind( - wx.EVT_CHOICE, - lambda evt: wx.PostEvent( - self, - SinglePropertiesChangeEvent(self.properties), - ), - ) - self._properties[name] = choice + self._create_property(name, choices) props[name] = choices[default] self.properties = props self.Fit() self.GetTopLevelParent().Layout() self.Thaw() + def _tear_down_properties(self): + self._properties.clear() + child: wx.SizerItem + for i, child in enumerate( + self._property_sizer.GetChildren() + ): + if i >= self._property_sizer.GetCols(): + if child.IsWindow(): + child.GetWindow().Destroy() + elif child.IsSizer(): + child.GetSizer().Clear(True) + self._property_sizer.Remove(self._property_sizer.GetCols()) + elif child.IsSpacer(): + self._property_sizer.Remove(self._property_sizer.GetCols()) + else: + raise Exception + + def _post_change(self): + wx.PostEvent( + self, + SinglePropertiesChangeEvent(self.properties), + ) + + def _create_property(self, name: str, choices: Tuple[PropertyValueType]): + label = wx.StaticText(self, label=name) + self._property_sizer.Add(label, 0, wx.ALIGN_CENTER) + choice = wx.Choice(self, choices=[c.to_snbt() for c in choices]) + self._property_sizer.Add(choice, 0, wx.EXPAND) + choice.Bind( + wx.EVT_CHOICE, + lambda evt: self._post_change(), + ) + self._properties[name] = choice + @property def properties(self) -> PropertyType: return { diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py index 2f893737..8252e4d8 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py @@ -19,6 +19,9 @@ class SinglePropertySelect(BasePropertySelect, NormalMCBlock): If it is not known the user can populate it themselves. """ + _simple: AutomaticSingleProperty + _manual: ManualSingleProperty + def __init__( self, parent: wx.Window, @@ -46,14 +49,20 @@ def __init__( ) self._manual_enabled = False - self._simple = AutomaticSingleProperty(self) - self._sizer.Add(self._simple, 0, wx.EXPAND) - self._manual = ManualSingleProperty(self) - self._sizer.Add(self._manual, 0, wx.EXPAND) + self._simple = self._create_automatic() + self._sizer.Add(self._simple, 1, wx.EXPAND) + self._manual = self._create_manual() + self._sizer.Add(self._manual, 1, wx.EXPAND) self._simple.Bind(EVT_SINGLE_PROPERTIES_CHANGE, self._on_change) self._manual.Bind(EVT_SINGLE_PROPERTIES_CHANGE, self._on_change) self.push(True) + def _create_automatic(self) -> AutomaticSingleProperty: + return AutomaticSingleProperty(self) + + def _create_manual(self) -> ManualSingleProperty: + return ManualSingleProperty(self) + def _init_state(self, state: Dict[str, Any]): NormalMCBlock.__init__(self, **state) From d11b5af791a940db6c6f1a7bdc7758d0cba6a7fc Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Sun, 12 Sep 2021 12:15:52 +0100 Subject: [PATCH 100/139] Fixed some sizing issues --- amulet_map_editor/api/wx/ui/mc/base/base_define.py | 2 +- amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_define.py b/amulet_map_editor/api/wx/ui/mc/base/base_define.py index 7ff9bd7a..f2e99079 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/base_define.py +++ b/amulet_map_editor/api/wx/ui/mc/base/base_define.py @@ -48,7 +48,7 @@ def __init__( self._top_sizer = wx.BoxSizer(wx.VERTICAL) if orientation == wx.HORIZONTAL: - self._sizer.Add(self._top_sizer, 0, wx.EXPAND) + self._sizer.Add(self._top_sizer, 1, wx.EXPAND) else: self._sizer.Add(self._top_sizer, 0, wx.EXPAND) diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py index 35000d16..a30fef61 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py @@ -59,7 +59,7 @@ def __init__( base_name, show_pick_block, ) - self._top_sizer.Add(self._picker, 0, wx.EXPAND | wx.TOP, 5) + self._top_sizer.Add(self._picker, 1, wx.EXPAND | wx.TOP, 5) self._picker.Bind(EVT_BLOCK_ID_CHANGE, self._on_block_change) self._property_picker: Optional[BasePropertySelect] = None From 5f5a71bc06aa0db194fdd88a0163e7601067ce2e Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Sun, 12 Sep 2021 12:17:03 +0100 Subject: [PATCH 101/139] Added a custom button that adds a from source component This still needs some work --- .../block_entry/custom_fill_button.py | 144 ++++++++++++++++++ .../block_container/block_entry/fill.py | 9 +- .../fill_replace/block_container/fill.py | 4 + .../tools/fill_replace/fill_replace_tool.py | 1 - .../tools/fill_replace/fill_replace_widget.py | 11 +- 5 files changed, 164 insertions(+), 5 deletions(-) create mode 100644 amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/custom_fill_button.py diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/custom_fill_button.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/custom_fill_button.py new file mode 100644 index 00000000..1b0d02bf --- /dev/null +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/custom_fill_button.py @@ -0,0 +1,144 @@ +import wx +from typing import Dict, Any, Tuple, Optional + +import PyMCTranslate +from amulet.api.data_types import VersionNumberTuple +from amulet.api.block import PropertyType, PropertyValueType +from amulet_map_editor.api.wx.ui.mc.block import ( + BlockDefineButton, + BlockDefine, + SinglePropertySelect, +) +from amulet_map_editor.api.wx.ui.mc.block.properties.single.automatic import ( + AutomaticSingleProperty, +) + + +class CustomAutomaticSingleProperty(AutomaticSingleProperty): + def __init__( + self, + parent: wx.Window, + from_source: bool = False, + ): + self._from_source = from_source + super().__init__(parent) + label = wx.StaticText(self, label="src", style=wx.ALIGN_CENTER) + self._property_sizer.SetCols(3) + self._property_sizer.Add(label, 1, wx.ALIGN_CENTER) + label.Show(from_source) + self._property_sizer.AddGrowableCol(0) + self._property_sizer.AddGrowableCol(1) + self._check_boxes: Dict[str, wx.CheckBox] = {} + + def _tear_down_properties(self): + self._check_boxes.clear() + super()._tear_down_properties() + + def _create_property(self, name: str, choices: Tuple[PropertyValueType]): + super()._create_property(name, choices) + check_box = wx.CheckBox(self) + self._property_sizer.Add(check_box, 1, wx.ALIGN_CENTER) + self._check_boxes[name] = check_box + check_box.Bind(wx.EVT_CHECKBOX, lambda evt: self._post_change()) + check_box.Show(self._from_source) + + def set_from_source(self, from_source: bool): + self._from_source = from_source + for i in range(0, self._property_sizer.GetItemCount(), self._property_sizer.GetCols()): + self._property_sizer.Show(i+2, show=from_source) + self.Layout() + + +class CustomSinglePropertySelect(SinglePropertySelect): + _simple: CustomAutomaticSingleProperty + + def __init__( + self, + parent: wx.Window, + translation_manager: PyMCTranslate.TranslationManager, + platform: str, + version_number: VersionNumberTuple, + force_blockstate: bool, + namespace: str = None, + base_name: str = None, + properties: PropertyType = None, + state: Dict[str, Any] = None, + from_source: bool = False, + ): + self._from_source = from_source + super().__init__( + parent, + translation_manager, + platform, + version_number, + force_blockstate, + namespace, + base_name, + properties, + state=state + ) + + def _create_automatic(self) -> CustomAutomaticSingleProperty: + prop = CustomAutomaticSingleProperty(self, from_source=self._from_source) + return prop + + def set_from_source(self, from_source: bool): + self._simple.set_from_source(from_source) + + +class CustomBlockDefine(BlockDefine): + _property_picker: CustomSinglePropertySelect + + def __init__(self, *args, from_source: bool = False, **kwargs): + self._from_source = from_source + super().__init__(*args, **kwargs) + + def _create_properties(self) -> SinglePropertySelect: + return CustomSinglePropertySelect( + self, + self._translation_manager, + self.platform, + self.version_number, + self.force_blockstate, + self.namespace, + self.base_name, + self.properties, + from_source=self._from_source + ) + + def set_from_source(self, from_source: bool): + self._from_source = from_source + self._property_picker.set_from_source(from_source) + + +class CustomBlockDefineButton(BlockDefineButton): + _block_widget: Optional[CustomBlockDefine] + + def __init__(self, *args, from_source: bool = False, **kwargs): + self._from_source = from_source + super().__init__(*args, **kwargs) + + def _create_block_define(self, dialog: wx.Dialog) -> BlockDefine: + return CustomBlockDefine( + dialog, + self._translation_manager, + wx.HORIZONTAL, + self.platform, + self.version_number, + self.force_blockstate, + self.namespace, + self.base_name, + self.properties, + from_source=self._from_source + ) + + def _update_from_block_define(self, block_define: BlockDefine): + super()._update_from_block_define(block_define) + + # @property + # def from_source(self) -> Dict[str, bool]: + + def set_from_source(self, from_source: bool): + self._from_source = from_source + if self._block_widget is not None: + self._block_widget.set_from_source(from_source) diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/fill.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/fill.py index 7d143d63..24b27f40 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/fill.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/fill.py @@ -3,8 +3,8 @@ import PyMCTranslate from amulet.api.data_types import VersionNumberTuple from amulet.api.block import PropertyType -from amulet_map_editor.api.wx.ui.mc.block import BlockDefineButton from .base import BaseBlockEntry +from .custom_fill_button import CustomBlockDefineButton class FillBlockEntry(BaseBlockEntry): @@ -26,7 +26,7 @@ def __init__( ): super().__init__(parent, **kwargs) self._init_close_button() - self._block_button = BlockDefineButton( + self._block_button = CustomBlockDefineButton( self, translation_manager, platform, @@ -44,7 +44,7 @@ def __init__( self.show_weight(False) @property - def block_button(self) -> BlockDefineButton: + def block_button(self) -> CustomBlockDefineButton: return self._block_button @property @@ -60,3 +60,6 @@ def show_weight(self, show: bool = True): """Show or hide the weight entry.""" self._weight.Show(show) self.Layout() + + def set_from_source(self, from_source: bool): + self._block_button.set_from_source(from_source) diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/fill.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/fill.py index 9bbebc31..9e1ccec0 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/fill.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/fill.py @@ -28,6 +28,10 @@ def _do_destroy_block_entry(self, window: FillBlockEntry): block = self._blocks[-1] block.show_weight(False) + def set_from_source(self, from_source: bool): + for block in self._blocks: + block.set_from_source(from_source) + @property def weights(self) -> Tuple[float, ...]: """The weighting values for the blocks contained in this widget. May be unused.""" diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_tool.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_tool.py index 56e3a636..c8ddce50 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_tool.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_tool.py @@ -10,7 +10,6 @@ BlockSelectionBehaviour, ) from amulet_map_editor.programs.edit.api.ui.tool import DefaultBaseToolUI -from amulet_map_editor.api.wx.ui.events import EVT_CHILD_SIZE from .fill_replace_widget import FillReplaceWidget if TYPE_CHECKING: diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_widget.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_widget.py index aa23048d..b0b5cb62 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_widget.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_widget.py @@ -8,7 +8,7 @@ from amulet.api.data_types import VersionNumberTuple from amulet_map_editor import lang from amulet_map_editor.api.wx.ui.simple import SimpleChoiceAny -from amulet_map_editor.api.wx.ui.events import EVT_CHILD_SIZE, ChildSizeEvent +from amulet_map_editor.api.wx.ui.events import ChildSizeEvent from .block_container import FillBlockContainer, FindBlockContainer @@ -70,6 +70,9 @@ def set_expert(self, expert: bool): self._find.set_expert(expert) self._fill.set_expert(expert) + def set_from_source(self, from_source: bool): + self._fill.set_from_source(from_source) + @property def operation(self) -> Tuple[List[Dict[str, Any]], List[Dict[str, Any]]]: return self._find.states, self._fill.states @@ -137,6 +140,7 @@ def __init__( self._pull_source = wx.CheckBox(self, wx.ID_ANY, "Pull From Source") self._pull_source.Hide() + self._pull_source.Bind(wx.EVT_CHECKBOX, self._on_check_change) top_sizer.Add(self._pull_source, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) self._multiple = wx.CheckBox(self, wx.ID_ANY, "Multiple") @@ -188,6 +192,7 @@ def _update_buttons(self): for operation in self._operation_panel: operation.set_replace(self.is_replace) operation.set_expert(self.is_expert) + operation.set_from_source(self.from_source) self._post_change_size() def _on_check_change(self, evt): @@ -202,6 +207,10 @@ def is_replace(self) -> bool: def is_expert(self) -> bool: return self._expert.GetValue() + @property + def from_source(self) -> bool: + return self._pull_source.GetValue() + @property def replace_mode(self) -> ReplaceMode: return self._replace_mode.GetCurrentObject() From f79b16751022194294b441e4fe70d296874ef346 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Tue, 14 Sep 2021 17:27:15 +0100 Subject: [PATCH 102/139] Added the start of a state managing system The old way I was doing this was rather difficult to maintain. Previously each new class contained its own state and had an API to read and write it. Each class had to manually notify child widgets of changes and events were used for children to notify parent widgets of changes. This will replace all of that. All connected classes will share an instance of the state class and will set states through it. The enter exit logic has been used to automatically trigger the update logic once all the desired states have been set. This should make tracking what has changed a lot easier than before. This file may move somewhere but this seems like a good place for now. --- amulet_map_editor/api/wx/ui/mc/test_state.py | 302 +++++++++++++++++++ 1 file changed, 302 insertions(+) create mode 100644 amulet_map_editor/api/wx/ui/mc/test_state.py diff --git a/amulet_map_editor/api/wx/ui/mc/test_state.py b/amulet_map_editor/api/wx/ui/mc/test_state.py new file mode 100644 index 00000000..ba6b90c2 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/mc/test_state.py @@ -0,0 +1,302 @@ +from abc import ABC, abstractmethod +from enum import Enum +from typing import Callable, List, Dict, Any, Union +from PyMCTranslate import TranslationManager, Version +from amulet.api.data_types import PlatformType, VersionNumberTuple, VersionNumberAny +from amulet_map_editor import log + + +OnChangeType = Callable[[int], None] + + +class State(Enum): + Platform = "platform" + VersionNumber = "version_number" + ForceBlockstate = "force_blockstate" + Namespace = "namespace" + BaseName = "base_name" + Properties = "properties" + PropertiesMultiple = "properties_multiple" + + def __str__(self): + return self.value + + def __eq__(self, other): + if isinstance(other, str): + return self.value == other + super().__eq__(other) + + def __hash__(self): + return hash(self.value) + + +class BaseState(ABC): + _translation_manager: TranslationManager + _edit: bool + _state: Dict[State, Any] + _changed_state: Dict[State, Any] + _on_change: List[OnChangeType] + + def __init__(self, translation_manager: TranslationManager): + self._translation_manager = translation_manager + self._edit = False # Is the instance being edited + self._state = {} # The actual state + self._changed_state = {} # Temporary storage that new states are written to + self._on_change = [] # Functions to call to notify of any changes. + + def __enter__(self): + assert not self._edit, "State is already being set. Release the state before editing again." + self._edit = True + self._changed_state = {} + + def __exit__(self, exc_type, exc_val, exc_tb): + self._edit = False + self._fix_new_state() + self._state.update(self._changed_state) + for on_change in self._on_change: + try: + on_change() + except: + log.warning(f"Error calling {on_change}", exc_info=True) + + def _is_edit(self): + if not self._edit: + raise Exception("The state has not been opened for editing.") + + def is_changed(self, state: Union[State, str]): + """Check if the state has changed.""" + return state in self._changed_state + + def _get_state(self, state: State) -> Any: + if state in self._changed_state: + return self._changed_state[state] + else: + return self._state[state] + + @abstractmethod + def _fix_new_state(self): + """ + The new state may have only been partially set. + Update the new state so that when merged with the old state is fully valid. + Called when released in __exit__ + """ + raise NotImplementedError + + def bind_on_change(self, on_change: OnChangeType): + self._on_change.append(on_change) + + def unbind_on_change(self, on_change: OnChangeType): + while on_change in self._on_change: + self._on_change.remove(on_change) + + +class PlatformState(BaseState): + def __init__( + self, + translation_manager: TranslationManager, + platform: str = None, + ): + super().__init__(translation_manager) + self._state[State.Platform] = self._sanitise_platform(platform) + + def _fix_new_state(self): + if self.is_changed(State.Platform): + self._changed_state[State.Platform] = self._sanitise_platform(self._changed_state[State.Platform]) + + def _sanitise_platform(self, platform: str = None) -> PlatformType: + if platform is not None and platform in self.valid_platforms: + return platform + else: + return self.valid_platforms[0] + + @property + def valid_platforms(self) -> List[PlatformType]: + return self._translation_manager.platforms() + + @property + def platform(self) -> PlatformType: + return self._get_state(State.Platform) + + @platform.setter + def platform(self, platform: PlatformType): + self._is_edit() + self._changed_state[State.Platform] = platform + + +class VersionState(PlatformState): + def __init__( + self, + translation_manager: TranslationManager, + platform: str = None, + version_number: VersionNumberAny = None, + force_blockstate: bool = None, + ): + super().__init__(translation_manager, platform) + self._state[State.VersionNumber] = self._sanitise_version(version_number) + self._state[State.ForceBlockstate] = self._sanitise_force_blockstate(force_blockstate) + + def _fix_new_state(self): + super()._fix_new_state() + if self.is_changed(State.Platform) or self.is_changed(State.VersionNumber): + self._changed_state[State.VersionNumber] = self._sanitise_version(self.version_number) + if self.is_changed(State.VersionNumber) or self.is_changed(State.ForceBlockstate): + self._changed_state[State.ForceBlockstate] = self._sanitise_force_blockstate(self.force_blockstate) + self._fix_version_change() + + def _fix_version_change(self): + pass + + def _get_version(self) -> Version: + return self._translation_manager.get_version(self.platform, self.version_number) + + def _sanitise_version(self, version_number: VersionNumberAny = None) -> VersionNumberTuple: + if version_number is not None: + if version_number in self.valid_version_numbers: + return version_number + else: + try: + return self._translation_manager.get_version(self.platform, version_number).version_number + except KeyError: + pass + return self.valid_version_numbers[0] + + @property + def valid_version_numbers(self) -> List[VersionNumberTuple]: + return self._translation_manager.version_numbers( + self.platform + ) + + @property + def version_number(self) -> VersionNumberTuple: + return self._get_state(State.VersionNumber) + + @version_number.setter + def version_number(self, version_number: VersionNumberAny): + self._is_edit() + self._changed_state[State.VersionNumber] = version_number + + def _sanitise_force_blockstate(self, force_blockstate: bool = None) -> bool: + if self.has_abstract_format: + return bool(force_blockstate) + else: + return False + + @property + def has_abstract_format(self) -> bool: + return self._get_version().has_abstract_format + + @property + def force_blockstate(self) -> bool: + return self._get_state(State.ForceBlockstate) + + @force_blockstate.setter + def force_blockstate(self, force_blockstate: bool): + self._is_edit() + self._changed_state[State.ForceBlockstate] = force_blockstate + + +class BaseNamespaceState(VersionState): + def __init__( + self, + translation_manager: TranslationManager, + platform: str = None, + version_number: VersionNumberAny = None, + force_blockstate: bool = None, + namespace: str = None, + ): + super().__init__(translation_manager, platform, version_number, force_blockstate) + self._state[State.Namespace] = self._sanitise_namespace(namespace) + + def _fix_new_state(self): + super()._fix_new_state() + if self.is_changed(State.Namespace): + self._changed_state[State.Namespace] = self._sanitise_namespace(self.namespace) + + def _sanitise_namespace(self, namespace: str = None) -> str: + if isinstance(namespace, str) and namespace: + return namespace + else: + return self.valid_namespaces[0] + + @property + @abstractmethod + def valid_namespaces(self) -> List[str]: + raise NotImplementedError + + @property + def namespace(self) -> str: + return self._get_state(State.Namespace) + + @namespace.setter + def namespace(self, namespace: str): + self._is_edit() + self._changed_state[State.Namespace] = namespace + + +class BaseResourceIDState(BaseNamespaceState): + def __init__( + self, + translation_manager: TranslationManager, + platform: str = None, + version_number: VersionNumberAny = None, + force_blockstate: bool = None, + namespace: str = None, + base_name: str = None, + ): + super().__init__(translation_manager, platform, version_number, force_blockstate, namespace) + self._state[State.BaseName] = self._sanitise_base_name(base_name) + + def _fix_new_state(self): + super()._fix_new_state() + if self.is_changed(State.BaseName): + self._changed_state[State.BaseName] = self._sanitise_base_name(self.base_name) + + def _sanitise_base_name(self, base_name: str = None) -> str: + if isinstance(base_name, str) and base_name: + return base_name + else: + return self.valid_base_names[0] + + @property + @abstractmethod + def valid_base_names(self) -> List[str]: + raise NotImplementedError + + @property + def base_name(self) -> str: + return self._get_state(State.BaseName) + + @base_name.setter + def base_name(self, base_name: str): + self._is_edit() + self._changed_state[State.BaseName] = base_name + + +class BiomeNamespaceState(BaseNamespaceState): + @property + def valid_namespaces(self) -> List[str]: + # TODO: make the biome translator similar to the block translator + return list(set(biome.split(":", 1)[0] for biome in self._get_version().biome.biome_ids)) + + +class BiomeResourceIDState(BiomeNamespaceState, BaseResourceIDState): + @property + def valid_base_names(self) -> List[str]: + biomes = [] + for biome in self._get_version().biome.biome_ids: + namespace, base_name = biome.split(":", 1) + if namespace == self.namespace: + biomes.append(base_name) + return biomes + + +class BlockNamespaceState(BaseNamespaceState): + @property + def valid_namespaces(self) -> List[str]: + return self._get_version().block.namespaces(self.force_blockstate) + + +class BlockResourceIDState(BlockNamespaceState, BaseResourceIDState): + @property + def valid_base_names(self) -> List[str]: + return self._get_version().block.base_names(self.namespace, self.force_blockstate) From 216938034d412d1ba215f4a8562a9b90c031e0e3 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Thu, 16 Sep 2021 09:27:40 +0100 Subject: [PATCH 103/139] Renamed state file and added property state --- .../api/wx/ui/mc/{test_state.py => state.py} | 123 ++++++++++++++++-- 1 file changed, 109 insertions(+), 14 deletions(-) rename amulet_map_editor/api/wx/ui/mc/{test_state.py => state.py} (65%) diff --git a/amulet_map_editor/api/wx/ui/mc/test_state.py b/amulet_map_editor/api/wx/ui/mc/state.py similarity index 65% rename from amulet_map_editor/api/wx/ui/mc/test_state.py rename to amulet_map_editor/api/wx/ui/mc/state.py index ba6b90c2..d5ce1d54 100644 --- a/amulet_map_editor/api/wx/ui/mc/test_state.py +++ b/amulet_map_editor/api/wx/ui/mc/state.py @@ -1,8 +1,11 @@ from abc import ABC, abstractmethod from enum import Enum from typing import Callable, List, Dict, Any, Union + +import amulet_nbt from PyMCTranslate import TranslationManager, Version from amulet.api.data_types import PlatformType, VersionNumberTuple, VersionNumberAny +from amulet.api.block import PropertyType, PropertyTypeMultiple, Block from amulet_map_editor import log @@ -59,10 +62,6 @@ def __exit__(self, exc_type, exc_val, exc_tb): except: log.warning(f"Error calling {on_change}", exc_info=True) - def _is_edit(self): - if not self._edit: - raise Exception("The state has not been opened for editing.") - def is_changed(self, state: Union[State, str]): """Check if the state has changed.""" return state in self._changed_state @@ -73,6 +72,11 @@ def _get_state(self, state: State) -> Any: else: return self._state[state] + def _set_state(self, state: State, value: Any): + if not self._edit: + raise Exception("The state has not been opened for editing.") + self._changed_state[state] = value + @abstractmethod def _fix_new_state(self): """ @@ -119,8 +123,7 @@ def platform(self) -> PlatformType: @platform.setter def platform(self, platform: PlatformType): - self._is_edit() - self._changed_state[State.Platform] = platform + self._set_state(State.Platform, platform) class VersionState(PlatformState): @@ -172,8 +175,7 @@ def version_number(self) -> VersionNumberTuple: @version_number.setter def version_number(self, version_number: VersionNumberAny): - self._is_edit() - self._changed_state[State.VersionNumber] = version_number + self._set_state(State.VersionNumber, version_number) def _sanitise_force_blockstate(self, force_blockstate: bool = None) -> bool: if self.has_abstract_format: @@ -191,8 +193,7 @@ def force_blockstate(self) -> bool: @force_blockstate.setter def force_blockstate(self, force_blockstate: bool): - self._is_edit() - self._changed_state[State.ForceBlockstate] = force_blockstate + self._set_state(State.ForceBlockstate, force_blockstate) class BaseNamespaceState(VersionState): @@ -229,8 +230,7 @@ def namespace(self) -> str: @namespace.setter def namespace(self, namespace: str): - self._is_edit() - self._changed_state[State.Namespace] = namespace + self._set_state(State.Namespace, namespace) class BaseResourceIDState(BaseNamespaceState): @@ -268,8 +268,7 @@ def base_name(self) -> str: @base_name.setter def base_name(self, base_name: str): - self._is_edit() - self._changed_state[State.BaseName] = base_name + self._set_state(State.BaseName, base_name) class BiomeNamespaceState(BaseNamespaceState): @@ -300,3 +299,99 @@ class BlockResourceIDState(BlockNamespaceState, BaseResourceIDState): @property def valid_base_names(self) -> List[str]: return self._get_version().block.base_names(self.namespace, self.force_blockstate) + + +class BlockState(BlockResourceIDState): + def __init__( + self, + translation_manager: TranslationManager, + platform: str = None, + version_number: VersionNumberAny = None, + force_blockstate: bool = None, + namespace: str = None, + base_name: str = None, + properties: PropertyType = None, + properties_multiple: PropertyTypeMultiple = None, + ): + super().__init__(translation_manager, platform, version_number, force_blockstate, namespace, base_name) + self._state[State.Properties] = self._sanitise_properties(properties) + self._state[State.PropertiesMultiple] = self._sanitise_properties_multiple(properties_multiple) + + def _fix_version_change(self): + universal_block, _, _ = self._translation_manager.get_version(self._state[State.Platform], self._state[State.VersionNumber]).block.to_universal( + Block(self.namespace, self.base_name, self.properties), + force_blockstate=self.force_blockstate + ) + version_block, _, _ = self._translation_manager.get_version(self.platform, self.version_number).block.from_universal(universal_block) + if isinstance(version_block, Block): + version_block: Block + if self.is_changed(State.Namespace) or self.is_changed(State.BaseName): + if version_block.namespace == self.namespace and version_block.base_name == self.base_name: + if not self.is_changed(State.Properties): + self._changed_state[State.Properties] = self._sanitise_properties(version_block.properties) + else: + self._changed_state[State.Namespace] = self._sanitise_namespace(version_block.namespace) + self._changed_state[State.BaseName] = self._sanitise_base_name(version_block.base_name) + if not self.is_changed(State.Properties): + self._changed_state[State.Properties] = self._sanitise_properties(version_block.properties) + + def _fix_new_state(self): + super()._fix_new_state() + if self.is_changed(State.Properties) or self.is_changed(State.PropertiesMultiple): + self._changed_state[State.PropertiesMultiple] = self._sanitise_properties_multiple(self.properties_multiple) + self._changed_state[State.Properties] = self._sanitise_properties(self.properties) + + def _get_block_spec(self): + return self._get_version().block.get_specification(self.namespace, self.base_name, self.force_blockstate) + + @property + def default_properties(self) -> PropertyType: + return self._get_block_spec().default_properties + + @property + def valid_properties(self) -> PropertyTypeMultiple: + return self._get_block_spec().valid_properties + + def _sanitise_properties(self, properties: PropertyType = None) -> PropertyType: + valid_properties = self.valid_properties + default_properties = self.default_properties + if isinstance(properties, dict): + return { + name: properties[name] + if name in properties and isinstance(properties[name], amulet_nbt.BaseValueType) and properties[name] in valid_properties[name] + else default_properties[name] + for name in valid_properties + } + else: + return default_properties + + @property + def properties(self) -> PropertyType: + return self._get_state(State.Properties) + + @properties.setter + def properties(self, properties: PropertyType): + self._set_state(State.Properties, properties) + + def _sanitise_properties_multiple(self, properties: PropertyTypeMultiple = None) -> PropertyTypeMultiple: + valid_properties = self.valid_properties + if isinstance(properties, dict): + return { + name: tuple( + val for val in properties[name] + if isinstance(val, amulet_nbt.BaseValueType) and val in valid_properties[name] + ) + if name in properties and isinstance(properties[name], (list, tuple)) + else valid_properties[name] + for name in valid_properties + } + else: + return valid_properties + + @property + def properties_multiple(self) -> PropertyTypeMultiple: + return self._get_state(State.PropertiesMultiple) + + @properties_multiple.setter + def properties_multiple(self, properties_multiple: PropertyTypeMultiple): + self._set_state(State.PropertiesMultiple, properties_multiple) From 5c2de347658f15602862448da95d929f84cdfdac Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Thu, 16 Sep 2021 09:28:13 +0100 Subject: [PATCH 104/139] Reformatted --- .../mc/block/properties/single/automatic.py | 4 +- amulet_map_editor/api/wx/ui/mc/state.py | 134 +++++++++++++----- .../block_entry/custom_fill_button.py | 12 +- 3 files changed, 107 insertions(+), 43 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/single/automatic.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/automatic.py index c74263cb..8f7f72c8 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/single/automatic.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/single/automatic.py @@ -74,9 +74,7 @@ def states(self, states: StatesType): def _tear_down_properties(self): self._properties.clear() child: wx.SizerItem - for i, child in enumerate( - self._property_sizer.GetChildren() - ): + for i, child in enumerate(self._property_sizer.GetChildren()): if i >= self._property_sizer.GetCols(): if child.IsWindow(): child.GetWindow().Destroy() diff --git a/amulet_map_editor/api/wx/ui/mc/state.py b/amulet_map_editor/api/wx/ui/mc/state.py index d5ce1d54..91afa822 100644 --- a/amulet_map_editor/api/wx/ui/mc/state.py +++ b/amulet_map_editor/api/wx/ui/mc/state.py @@ -48,7 +48,9 @@ def __init__(self, translation_manager: TranslationManager): self._on_change = [] # Functions to call to notify of any changes. def __enter__(self): - assert not self._edit, "State is already being set. Release the state before editing again." + assert ( + not self._edit + ), "State is already being set. Release the state before editing again." self._edit = True self._changed_state = {} @@ -105,7 +107,9 @@ def __init__( def _fix_new_state(self): if self.is_changed(State.Platform): - self._changed_state[State.Platform] = self._sanitise_platform(self._changed_state[State.Platform]) + self._changed_state[State.Platform] = self._sanitise_platform( + self._changed_state[State.Platform] + ) def _sanitise_platform(self, platform: str = None) -> PlatformType: if platform is not None and platform in self.valid_platforms: @@ -136,14 +140,22 @@ def __init__( ): super().__init__(translation_manager, platform) self._state[State.VersionNumber] = self._sanitise_version(version_number) - self._state[State.ForceBlockstate] = self._sanitise_force_blockstate(force_blockstate) + self._state[State.ForceBlockstate] = self._sanitise_force_blockstate( + force_blockstate + ) def _fix_new_state(self): super()._fix_new_state() if self.is_changed(State.Platform) or self.is_changed(State.VersionNumber): - self._changed_state[State.VersionNumber] = self._sanitise_version(self.version_number) - if self.is_changed(State.VersionNumber) or self.is_changed(State.ForceBlockstate): - self._changed_state[State.ForceBlockstate] = self._sanitise_force_blockstate(self.force_blockstate) + self._changed_state[State.VersionNumber] = self._sanitise_version( + self.version_number + ) + if self.is_changed(State.VersionNumber) or self.is_changed( + State.ForceBlockstate + ): + self._changed_state[ + State.ForceBlockstate + ] = self._sanitise_force_blockstate(self.force_blockstate) self._fix_version_change() def _fix_version_change(self): @@ -152,22 +164,24 @@ def _fix_version_change(self): def _get_version(self) -> Version: return self._translation_manager.get_version(self.platform, self.version_number) - def _sanitise_version(self, version_number: VersionNumberAny = None) -> VersionNumberTuple: + def _sanitise_version( + self, version_number: VersionNumberAny = None + ) -> VersionNumberTuple: if version_number is not None: if version_number in self.valid_version_numbers: return version_number else: try: - return self._translation_manager.get_version(self.platform, version_number).version_number + return self._translation_manager.get_version( + self.platform, version_number + ).version_number except KeyError: pass return self.valid_version_numbers[0] @property def valid_version_numbers(self) -> List[VersionNumberTuple]: - return self._translation_manager.version_numbers( - self.platform - ) + return self._translation_manager.version_numbers(self.platform) @property def version_number(self) -> VersionNumberTuple: @@ -205,13 +219,17 @@ def __init__( force_blockstate: bool = None, namespace: str = None, ): - super().__init__(translation_manager, platform, version_number, force_blockstate) + super().__init__( + translation_manager, platform, version_number, force_blockstate + ) self._state[State.Namespace] = self._sanitise_namespace(namespace) def _fix_new_state(self): super()._fix_new_state() if self.is_changed(State.Namespace): - self._changed_state[State.Namespace] = self._sanitise_namespace(self.namespace) + self._changed_state[State.Namespace] = self._sanitise_namespace( + self.namespace + ) def _sanitise_namespace(self, namespace: str = None) -> str: if isinstance(namespace, str) and namespace: @@ -243,13 +261,17 @@ def __init__( namespace: str = None, base_name: str = None, ): - super().__init__(translation_manager, platform, version_number, force_blockstate, namespace) + super().__init__( + translation_manager, platform, version_number, force_blockstate, namespace + ) self._state[State.BaseName] = self._sanitise_base_name(base_name) def _fix_new_state(self): super()._fix_new_state() if self.is_changed(State.BaseName): - self._changed_state[State.BaseName] = self._sanitise_base_name(self.base_name) + self._changed_state[State.BaseName] = self._sanitise_base_name( + self.base_name + ) def _sanitise_base_name(self, base_name: str = None) -> str: if isinstance(base_name, str) and base_name: @@ -275,7 +297,9 @@ class BiomeNamespaceState(BaseNamespaceState): @property def valid_namespaces(self) -> List[str]: # TODO: make the biome translator similar to the block translator - return list(set(biome.split(":", 1)[0] for biome in self._get_version().biome.biome_ids)) + return list( + set(biome.split(":", 1)[0] for biome in self._get_version().biome.biome_ids) + ) class BiomeResourceIDState(BiomeNamespaceState, BaseResourceIDState): @@ -298,7 +322,9 @@ def valid_namespaces(self) -> List[str]: class BlockResourceIDState(BlockNamespaceState, BaseResourceIDState): @property def valid_base_names(self) -> List[str]: - return self._get_version().block.base_names(self.namespace, self.force_blockstate) + return self._get_version().block.base_names( + self.namespace, self.force_blockstate + ) class BlockState(BlockResourceIDState): @@ -313,36 +339,68 @@ def __init__( properties: PropertyType = None, properties_multiple: PropertyTypeMultiple = None, ): - super().__init__(translation_manager, platform, version_number, force_blockstate, namespace, base_name) + super().__init__( + translation_manager, + platform, + version_number, + force_blockstate, + namespace, + base_name, + ) self._state[State.Properties] = self._sanitise_properties(properties) - self._state[State.PropertiesMultiple] = self._sanitise_properties_multiple(properties_multiple) + self._state[State.PropertiesMultiple] = self._sanitise_properties_multiple( + properties_multiple + ) def _fix_version_change(self): - universal_block, _, _ = self._translation_manager.get_version(self._state[State.Platform], self._state[State.VersionNumber]).block.to_universal( + universal_block, _, _ = self._translation_manager.get_version( + self._state[State.Platform], self._state[State.VersionNumber] + ).block.to_universal( Block(self.namespace, self.base_name, self.properties), - force_blockstate=self.force_blockstate + force_blockstate=self.force_blockstate, ) - version_block, _, _ = self._translation_manager.get_version(self.platform, self.version_number).block.from_universal(universal_block) + version_block, _, _ = self._translation_manager.get_version( + self.platform, self.version_number + ).block.from_universal(universal_block) if isinstance(version_block, Block): version_block: Block if self.is_changed(State.Namespace) or self.is_changed(State.BaseName): - if version_block.namespace == self.namespace and version_block.base_name == self.base_name: + if ( + version_block.namespace == self.namespace + and version_block.base_name == self.base_name + ): if not self.is_changed(State.Properties): - self._changed_state[State.Properties] = self._sanitise_properties(version_block.properties) + self._changed_state[ + State.Properties + ] = self._sanitise_properties(version_block.properties) else: - self._changed_state[State.Namespace] = self._sanitise_namespace(version_block.namespace) - self._changed_state[State.BaseName] = self._sanitise_base_name(version_block.base_name) + self._changed_state[State.Namespace] = self._sanitise_namespace( + version_block.namespace + ) + self._changed_state[State.BaseName] = self._sanitise_base_name( + version_block.base_name + ) if not self.is_changed(State.Properties): - self._changed_state[State.Properties] = self._sanitise_properties(version_block.properties) + self._changed_state[State.Properties] = self._sanitise_properties( + version_block.properties + ) def _fix_new_state(self): super()._fix_new_state() - if self.is_changed(State.Properties) or self.is_changed(State.PropertiesMultiple): - self._changed_state[State.PropertiesMultiple] = self._sanitise_properties_multiple(self.properties_multiple) - self._changed_state[State.Properties] = self._sanitise_properties(self.properties) + if self.is_changed(State.Properties) or self.is_changed( + State.PropertiesMultiple + ): + self._changed_state[ + State.PropertiesMultiple + ] = self._sanitise_properties_multiple(self.properties_multiple) + self._changed_state[State.Properties] = self._sanitise_properties( + self.properties + ) def _get_block_spec(self): - return self._get_version().block.get_specification(self.namespace, self.base_name, self.force_blockstate) + return self._get_version().block.get_specification( + self.namespace, self.base_name, self.force_blockstate + ) @property def default_properties(self) -> PropertyType: @@ -358,7 +416,9 @@ def _sanitise_properties(self, properties: PropertyType = None) -> PropertyType: if isinstance(properties, dict): return { name: properties[name] - if name in properties and isinstance(properties[name], amulet_nbt.BaseValueType) and properties[name] in valid_properties[name] + if name in properties + and isinstance(properties[name], amulet_nbt.BaseValueType) + and properties[name] in valid_properties[name] else default_properties[name] for name in valid_properties } @@ -373,13 +433,17 @@ def properties(self) -> PropertyType: def properties(self, properties: PropertyType): self._set_state(State.Properties, properties) - def _sanitise_properties_multiple(self, properties: PropertyTypeMultiple = None) -> PropertyTypeMultiple: + def _sanitise_properties_multiple( + self, properties: PropertyTypeMultiple = None + ) -> PropertyTypeMultiple: valid_properties = self.valid_properties if isinstance(properties, dict): return { name: tuple( - val for val in properties[name] - if isinstance(val, amulet_nbt.BaseValueType) and val in valid_properties[name] + val + for val in properties[name] + if isinstance(val, amulet_nbt.BaseValueType) + and val in valid_properties[name] ) if name in properties and isinstance(properties[name], (list, tuple)) else valid_properties[name] diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/custom_fill_button.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/custom_fill_button.py index 1b0d02bf..560f15e2 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/custom_fill_button.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/custom_fill_button.py @@ -44,8 +44,10 @@ def _create_property(self, name: str, choices: Tuple[PropertyValueType]): def set_from_source(self, from_source: bool): self._from_source = from_source - for i in range(0, self._property_sizer.GetItemCount(), self._property_sizer.GetCols()): - self._property_sizer.Show(i+2, show=from_source) + for i in range( + 0, self._property_sizer.GetItemCount(), self._property_sizer.GetCols() + ): + self._property_sizer.Show(i + 2, show=from_source) self.Layout() @@ -75,7 +77,7 @@ def __init__( namespace, base_name, properties, - state=state + state=state, ) def _create_automatic(self) -> CustomAutomaticSingleProperty: @@ -103,7 +105,7 @@ def _create_properties(self) -> SinglePropertySelect: self.namespace, self.base_name, self.properties, - from_source=self._from_source + from_source=self._from_source, ) def set_from_source(self, from_source: bool): @@ -129,7 +131,7 @@ def _create_block_define(self, dialog: wx.Dialog) -> BlockDefine: self.namespace, self.base_name, self.properties, - from_source=self._from_source + from_source=self._from_source, ) def _update_from_block_define(self, block_define: BlockDefine): From 12ac050db3fbf724b3d25f475ca71795aee6ba39 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Thu, 16 Sep 2021 11:45:07 +0100 Subject: [PATCH 105/139] Added a state holder class --- amulet_map_editor/api/wx/ui/mc/state.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/amulet_map_editor/api/wx/ui/mc/state.py b/amulet_map_editor/api/wx/ui/mc/state.py index 91afa822..4ed8c658 100644 --- a/amulet_map_editor/api/wx/ui/mc/state.py +++ b/amulet_map_editor/api/wx/ui/mc/state.py @@ -96,6 +96,28 @@ def unbind_on_change(self, on_change: OnChangeType): self._on_change.remove(on_change) +class StateHolder: + _state: BaseState + + def __init__(self, state: BaseState): + self._state = None + self.state = state + + @property + def state(self): + return self._state + + @state.setter + def state(self, state: BaseState): + if self._state is not None: + self._state.unbind_on_change(self._on_state_change) + self._state = state + self._state.bind_on_change(self._on_state_change) + + def _on_state_change(self): + pass + + class PlatformState(BaseState): def __init__( self, From 094f6b371c9feb0ed5a53d7a54a3854b6171e751 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Thu, 16 Sep 2021 11:45:19 +0100 Subject: [PATCH 106/139] Fixed __enter__ method --- amulet_map_editor/api/wx/ui/mc/state.py | 1 + 1 file changed, 1 insertion(+) diff --git a/amulet_map_editor/api/wx/ui/mc/state.py b/amulet_map_editor/api/wx/ui/mc/state.py index 4ed8c658..66501641 100644 --- a/amulet_map_editor/api/wx/ui/mc/state.py +++ b/amulet_map_editor/api/wx/ui/mc/state.py @@ -53,6 +53,7 @@ def __enter__(self): ), "State is already being set. Release the state before editing again." self._edit = True self._changed_state = {} + return self def __exit__(self, exc_type, exc_val, exc_tb): self._edit = False From c4a52ae92e6f2875232558df3f549f9d986b5d5f Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Thu, 16 Sep 2021 11:45:39 +0100 Subject: [PATCH 107/139] Fixed function using builtin name --- amulet_map_editor/api/wx/ui/mc/biome/biome_define.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py b/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py index cc683904..4cd41ac7 100644 --- a/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py +++ b/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py @@ -182,7 +182,7 @@ def on_biome_change(evt: BiomeIDChangeEvent): dialog.Fit() dialog.Bind(wx.EVT_CLOSE, lambda evt: dialog.Destroy()) - def set( + def set_data( platform: PlatformType, version: VersionNumberTuple, force_blockstate: bool, @@ -205,7 +205,7 @@ def set( interval = 1_000 wx.CallLater( - interval * 1, set, "java", (1, 17, 0), False, "minecraft", "end_highlands" + interval * 1, set_data, "java", (1, 17, 0), False, "minecraft", "end_highlands" ) wx.CallLater( interval * 2, From 374c2c61a3a81a5c067151bffa5883c632986f06 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Thu, 16 Sep 2021 11:46:29 +0100 Subject: [PATCH 108/139] Rewritten the platform and version select widgets --- .../api/wx/ui/mc/version/events.py | 31 +-- .../api/wx/ui/mc/version/platform_select.py | 101 +++++---- .../api/wx/ui/mc/version/version_select.py | 191 ++++++------------ 3 files changed, 109 insertions(+), 214 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/version/events.py b/amulet_map_editor/api/wx/ui/mc/version/events.py index 2902548d..0fc379f1 100644 --- a/amulet_map_editor/api/wx/ui/mc/version/events.py +++ b/amulet_map_editor/api/wx/ui/mc/version/events.py @@ -12,21 +12,15 @@ class PlatformChangeEvent(wx.PyEvent): Is run when the user or code changes the platform. """ - def __init__(self, platform: str, old_platform: str): + def __init__(self, platform: str): wx.PyEvent.__init__(self, eventType=_PlatformEventType) self._platform = platform - self._old_platform = old_platform @property def platform(self) -> str: """The platform that the selection was changed to.""" return self._platform - @property - def old_platform(self) -> str: - """The platform that was selected before it was changed.""" - return self._old_platform - _VersionChangeEventType = wx.NewEventType() EVT_VERSION_CHANGE = wx.PyEventBinder(_VersionChangeEventType) @@ -42,17 +36,11 @@ def __init__( platform: str, version_number: VersionNumberTuple, force_blockstate: bool, - old_platform: str, - old_version_number: VersionNumberTuple, - old_force_blockstate: bool, ): wx.PyEvent.__init__(self, eventType=_VersionChangeEventType) self._platform = platform self._version_number = version_number self._force_blockstate = force_blockstate - self._old_platform = old_platform - self._old_version_number = old_version_number - self._old_force_blockstate = old_force_blockstate @property def platform(self) -> str: @@ -70,20 +58,3 @@ def force_blockstate(self) -> bool: True if the format is force blockstate, False otherwise. """ return self._force_blockstate - - @property - def old_platform(self) -> str: - """The platform that was selected before it was changed.""" - return self._old_platform - - @property - def old_version_number(self) -> VersionNumberTuple: - """The version_number that was selected before it was changed.""" - return self._old_version_number - - @property - def old_force_blockstate(self) -> bool: - """ - True if the format was force blockstate, False otherwise. - """ - return self._old_force_blockstate diff --git a/amulet_map_editor/api/wx/ui/mc/version/platform_select.py b/amulet_map_editor/api/wx/ui/mc/version/platform_select.py index 3c09df3b..00e2a6a1 100644 --- a/amulet_map_editor/api/wx/ui/mc/version/platform_select.py +++ b/amulet_map_editor/api/wx/ui/mc/version/platform_select.py @@ -5,23 +5,24 @@ from amulet.api.data_types import PlatformType from .events import PlatformChangeEvent, EVT_PLATFORM_CHANGE -from amulet_map_editor.api.wx.ui.mc.api.platform import BaseMCPlatform +from amulet_map_editor.api.wx.ui.mc.state import PlatformState, StateHolder, State -class PlatformSelect(wx.Panel, BaseMCPlatform): +class PlatformSelect(wx.Panel, StateHolder): """ A UI element that allows you to pick between the platforms in the translator. """ + state: PlatformState def __init__( self, parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager, + state: PlatformState = None, platform: PlatformType = None, allow_universal: bool = True, allow_vanilla: bool = True, allowed_platforms: Tuple[PlatformType, ...] = None, - state: Dict[str, Any] = None, style: Dict[str, Any] = None, ): """ @@ -29,22 +30,19 @@ def __init__( :param parent: The parent window. :param translation_manager: The translation manager to populate from. - :param platform: The default platform (optional) + :param state: optional PlatformSelect instance holding the state of the platform. + :param platform: The default platform (optional). If state is defined this will not be used. :param allow_universal: If True the universal format will be included. :param allow_vanilla: If True the vanilla formats will be included. :param allowed_platforms: A whitelist of platforms. - :param state: A dictionary containing kwargs passed to the state manager. :param style: Dictionary of keyword args to be given to the Panel. """ - state = state or {} - state.setdefault("translation_manager", translation_manager) - state.setdefault("platform", platform) - # This is the init call to the class that stores the internal state of the data. - # This needs to be at the start to ensure that the internal state is set up before anything else is done. - # It is not a direct call to init so that subclasses of this class can substitute in which state subclass is used. - self._init_state(state) - - # Init the panel + # init the state + if not isinstance(state, PlatformState): + state = PlatformState(translation_manager, platform) + StateHolder.__init__(self, state) + + # init the panel style = style or {} style.setdefault("style", wx.BORDER_SIMPLE) wx.Panel.__init__(self, parent, **style) @@ -56,26 +54,18 @@ def __init__( self._sizer.AddGrowableCol(0) self._sizer.AddGrowableCol(1) - self._allow_universal = allow_universal - self._allow_vanilla = allow_vanilla - self._allowed_platforms = allowed_platforms + # self._allow_universal = allow_universal + # self._allow_vanilla = allow_vanilla + # self._allowed_platforms = allowed_platforms self._platform_choice: SimpleChoice = self._add_ui_element( "Platform:", SimpleChoice ) - self._populate_platform() - self._push_platform() + self._update_platform() self._platform_choice.Bind( wx.EVT_CHOICE, self._on_platform_change, ) - def _init_state(self, state: Dict[str, Any]): - """ - Call the init method of the state manager. - This is here so that nested classes do not have to init the state managers multiple times. - """ - BaseMCPlatform.__init__(self, **state) - def _add_ui_element( self, label: str, obj: Type[wx.Control], shown=True, **kwargs ) -> Any: @@ -88,37 +78,36 @@ def _add_ui_element( wx_obj.Hide() return wx_obj - def _populate_platform(self): - """Update the UI with the valid platforms.""" - platforms = self._translation_manager.platforms() - if self._allowed_platforms is not None: - platforms = [p for p in platforms if p in self._allowed_platforms] - if not self._allow_universal: - platforms = [p for p in platforms if p != "universal"] - if not self._allow_vanilla: - platforms = [p for p in platforms if p == "universal"] - self._platform_choice.SetItems(platforms) - - def _push_platform(self): + # def _populate_platform(self): + # """Update the UI with the valid platforms.""" + # TODO + # platforms = self._translation_manager.platforms() + # if self._allowed_platforms is not None: + # platforms = [p for p in platforms if p in self._allowed_platforms] + # if not self._allow_universal: + # platforms = [p for p in platforms if p != "universal"] + # if not self._allow_vanilla: + # platforms = [p for p in platforms if p == "universal"] + # self._platform_choice.SetItems(self.state.valid_platforms) + + def _update_platform(self): """Push the internal platform state to the UI.""" + self._platform_choice.SetItems(self.state.valid_platforms) self._platform_choice.SetSelection( - self._platform_choice.GetItems().index(self.platform) + self._platform_choice.GetItems().index(self.state.platform) ) + def _on_state_change(self): + if self.state.is_changed(State.Platform): + self._update_platform() + def _on_platform_change(self, evt): """The event run when the platform choice is changed by a user.""" - old_platform = self.platform - new_platform = self._platform_choice.GetCurrentString() - if old_platform != new_platform: - # write the changes back to the internal state - self._set_platform(new_platform) - wx.PostEvent(self, PlatformChangeEvent(new_platform, old_platform)) - - def _on_push(self): - if self.platform != self._platform_choice.GetCurrentString(): - self._push_platform() - return True - return False + platform = self._platform_choice.GetCurrentString() + if platform != self.state.platform: + with self.state as state: + state.platform = platform + wx.PostEvent(self, PlatformChangeEvent(self.state.platform)) def demo(): @@ -134,7 +123,7 @@ def demo(): ) sizer = wx.BoxSizer() dialog.SetSizer(sizer) - obj = PlatformSelect(dialog, translation_manager, "java") + obj = PlatformSelect(dialog, translation_manager, platform="java") sizer.Add( obj, 1, @@ -142,8 +131,14 @@ def demo(): 5, ) + def set_data(platform: str): + with obj.state as state: + state.platform = platform + + wx.CallLater(1000, set_data, "bedrock") + def on_change(evt: PlatformChangeEvent): - print(evt.platform, evt.old_platform) + print(evt.platform) obj.Bind(EVT_PLATFORM_CHANGE, on_change) dialog.Bind(wx.EVT_CLOSE, lambda evt: dialog.Destroy()) diff --git a/amulet_map_editor/api/wx/ui/mc/version/version_select.py b/amulet_map_editor/api/wx/ui/mc/version/version_select.py index de841eea..1162c76a 100644 --- a/amulet_map_editor/api/wx/ui/mc/version/version_select.py +++ b/amulet_map_editor/api/wx/ui/mc/version/version_select.py @@ -6,18 +6,21 @@ from amulet.api.data_types import VersionNumberTuple, PlatformType from .platform_select import PlatformSelect from amulet_map_editor.api.wx.ui.mc.api.version import BaseMCVersion +from amulet_map_editor.api.wx.ui.mc.state import VersionState, State from .events import VersionChangeEvent, EVT_VERSION_CHANGE -class VersionSelect(PlatformSelect, BaseMCVersion): +class VersionSelect(PlatformSelect): """ A UI element that allows you to pick between the platforms and versions in the translator. """ + state: VersionState def __init__( self, parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager, + state: VersionState = None, platform: PlatformType = None, version_number: VersionNumberTuple = None, force_blockstate: bool = None, @@ -27,7 +30,6 @@ def __init__( show_force_blockstate: bool = True, allow_numerical: bool = True, allow_blockstate: bool = True, - state: Dict[str, Any] = None, style: Dict[str, Any] = None, ): """ @@ -35,29 +37,29 @@ def __init__( :param parent: The parent window. :param translation_manager: The translation manager to populate from. - :param platform: The default platform (optional) - :param version_number: The default version number (optional) - :param force_blockstate: If True and the native format is numerical will use the custom blockstate format. Else will use the native format. + :param state: optional VersionSelect instance holding the state of the platform and version. + :param platform: The default platform (optional). If state is defined this will not be used. + :param version_number: The default version number (optional). If state is defined this will not be used. + :param force_blockstate: If True and the native format is numerical will use the custom blockstate format. Else will use the native format. If state is defined this will not be used. :param allow_universal: If True the universal format will be included. :param allow_vanilla: If True the vanilla formats will be included. :param allowed_platforms: A whitelist of platforms. :param show_force_blockstate: Should the format selection be shown to the user. :param allow_numerical: Should the numerical versions be shown to the user. :param allow_blockstate: Should the blockstate versions be shown to the user. - :param state: A dictionary containing kwargs passed to the state manager. :param style: Dictionary of keyword args to be given to the Panel. """ - state = state or {} - state.setdefault("version_number", version_number) - state.setdefault("force_blockstate", force_blockstate) + # init the state + if not isinstance(state, VersionState): + state = VersionState(translation_manager, platform, version_number, force_blockstate) + super().__init__( parent, translation_manager, - platform, + state, allow_universal=allow_universal, allow_vanilla=allow_vanilla, allowed_platforms=allowed_platforms, - state=state, style=style, ) self._allow_numerical = allow_numerical @@ -66,8 +68,7 @@ def __init__( self._version_choice: Optional[SimpleChoiceAny] = self._add_ui_element( "Version:", SimpleChoiceAny, reverse=True ) - self._populate_version() - self._push_version_number() + self._update_version_number() self._version_choice.Bind( wx.EVT_CHOICE, self._on_version_number_change, @@ -77,128 +78,61 @@ def __init__( "Format:", SimpleChoice, shown=show_force_blockstate ) self._blockstate_choice.SetItems(["native", "blockstate"]) - self._blockstate_choice.SetSelection(0) - self._populate_blockstate() - self._push_force_blockstate() + self._update_force_blockstate() self._blockstate_choice.Bind(wx.EVT_CHOICE, self._on_blockstate_change) - def _init_state(self, state: Dict[str, Any]): - """ - Call the init method of the state manager. - This is here so that nested classes do not have to init the state managers multiple times. - """ - BaseMCVersion.__init__(self, **state) - - def _populate_version(self): - """Populate the version UI element""" - versions = self._translation_manager.version_numbers(self.platform) - if not self._allow_blockstate: - versions = [v for v in versions if v < (1, 13, 0)] - if not self._allow_numerical: - versions = [v for v in versions if v >= (1, 13, 0)] - self._version_choice.SetItems(versions) - - def _populate_blockstate(self): - if self._translation_manager.get_version( - self.platform, self.version_number - ).has_abstract_format: - self._blockstate_choice.Enable() - else: - self._blockstate_choice.Disable() - - def _push_version_number(self): + # def _populate_version(self): + # """Populate the version UI element""" + # TODO + # versions = self._translation_manager.version_numbers(self.platform) + # if not self._allow_blockstate: + # versions = [v for v in versions if v < (1, 13, 0)] + # if not self._allow_numerical: + # versions = [v for v in versions if v >= (1, 13, 0)] + # self._version_choice.SetItems(versions) + + def _update_version_number(self): """Push the internal version number state to the UI.""" + self._version_choice.SetItems(self.state.valid_version_numbers) self._version_choice.SetSelection( - self._version_choice.values.index(self.version_number) + self._version_choice.values.index(self.state.version_number) ) - def _push_force_blockstate(self): + def _update_force_blockstate(self): """Push the internal block format state to the UI.""" - self._blockstate_choice.SetSelection(int(self.force_blockstate)) - - def _on_change(self, changed: int): - """ - Handle the UI changes and create an event. - - :param changed: 1=platform, 2=version, 3=blockstate - :return: - """ - old_platform = self.platform - old_version = self.version_number - old_force_blockstate = self.force_blockstate - - if changed <= 1: - # write the changes back to the internal state - new_platform = self._platform_choice.GetCurrentString() - if new_platform == old_platform: - # nothing changed - return - self._set_platform(new_platform) - else: - new_platform = old_platform + self._blockstate_choice.Enable(self.state.has_abstract_format) + self._blockstate_choice.SetSelection(int(self.state.force_blockstate)) - if changed <= 2: - if changed < 2: - self._populate_version() - self._version_choice.SetSelection(0) - new_version = self._version_choice.GetCurrentObject() - if changed == 2 and new_version == old_version: - return - self._set_version_number(new_version) - else: - new_version = old_version - - if changed < 3: - self._populate_blockstate() - self._blockstate_choice.SetSelection(0) - new_force_blockstate = ( - self._blockstate_choice.GetCurrentString() == "blockstate" - ) - if changed == 3 and new_force_blockstate == old_force_blockstate: - return - self._set_force_blockstate(new_force_blockstate) + def _on_state_change(self): + super()._on_state_change() + if self.state.is_changed(State.VersionNumber): + self._update_version_number() + if self.state.is_changed(State.ForceBlockstate): + self._update_force_blockstate() + def _post_version_change(self): wx.PostEvent( self, VersionChangeEvent( - new_platform, - new_version, - new_force_blockstate, - old_platform, - old_version, - old_force_blockstate, - ), + self.state.platform, + self.state.version_number, + self.state.force_blockstate, + ) ) - def _on_platform_change(self, evt): - self._on_change(1) - def _on_version_number_change(self, evt): - self._on_change(2) + version = self._version_choice.GetCurrentObject() + if version != self.state.version_number: + with self.state as state: + state.version_number = version + self._post_version_change() def _on_blockstate_change(self, evt): - self._on_change(3) - - def _on_push(self) -> bool: - update = super()._on_push() - # If the user set these out of order they may be messed up. - # This should fix that. - self._set_version_number(self.version_number) - self._set_force_blockstate(self.force_blockstate) - - if update: - self._populate_version() - if update or self.version_number != self._version_choice.GetCurrentObject(): - self._push_version_number() - update = True - if update: - self._populate_blockstate() - if update or self.force_blockstate != ( - self._blockstate_choice.GetCurrentString() == "blockstate" - ): - self._push_force_blockstate() - update = True - return update + force_blockstate = bool(self._version_choice.GetCurrentSelection()) + if force_blockstate != self.state.force_blockstate: + with self.state as state: + state.force_blockstate = force_blockstate + self._post_version_change() def demo(): @@ -229,9 +163,6 @@ def on_change(evt: VersionChangeEvent): evt.platform, evt.version_number, evt.force_blockstate, - evt.old_platform, - evt.old_version_number, - evt.force_blockstate, ) select.Bind(EVT_VERSION_CHANGE, on_change) @@ -250,11 +181,10 @@ def set_version( version: VersionNumberTuple, force_blockstate: bool, ): - obj.platform, obj.version_number, obj.force_blockstate = ( - platform, - version, - force_blockstate, - ) + with obj.state as state: + state.platform = platform + state.version_number = version + state.force_blockstate = force_blockstate interval = 1_000 @@ -269,11 +199,10 @@ def set_version2( version: VersionNumberTuple, force_blockstate: bool, ): - obj.force_blockstate, obj.version_number, obj.platform = ( - force_blockstate, - version, - platform, - ) + with obj.state as state: + state.force_blockstate = force_blockstate + state.version_number = version + state.platform = platform wx.CallLater(interval * 5, set_version2, select, "java", (1, 15, 0), False) wx.CallLater(interval * 6, set_version2, select, "java", (1, 17, 0), False) From e17d77316b66576c732daf656e6773ff1a6fff6d Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Thu, 16 Sep 2021 17:18:59 +0100 Subject: [PATCH 109/139] Reworked the identifier select UI elements to use the new state system --- .../base_identifier_select.py | 184 ++++++++---------- .../mc/base/base_identifier_select/events.py | 12 -- .../biome_identifier_select.py | 68 +++---- .../ui/mc/biome/identifier_select/events.py | 4 +- .../block_identifier_select.py | 60 +++--- .../ui/mc/block/identifier_select/events.py | 4 +- 6 files changed, 145 insertions(+), 187 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py index 159aaead..46727696 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py +++ b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py @@ -1,17 +1,16 @@ import wx -from typing import List, Dict, Any import PyMCTranslate from amulet.api.data_types import VersionNumberTuple from amulet_map_editor.api.image import COLOUR_PICKER -from amulet_map_editor.api.wx.ui.mc.api.resource_id import BaseMCResourceID +from amulet_map_editor.api.wx.ui.mc.state import BaseResourceIDState, StateHolder, State from .events import ( PickEvent, ) -class BaseIdentifierSelect(wx.Panel, BaseMCResourceID): +class BaseIdentifierSelect(wx.Panel, StateHolder): """ BaseIdentifierSelect is a base class for a UI containing a namespace choice @@ -19,30 +18,30 @@ class BaseIdentifierSelect(wx.Panel, BaseMCResourceID): a list of base names """ + state: BaseResourceIDState + def __init__( self, parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager, - platform: str, - version_number: VersionNumberTuple, + state: BaseResourceIDState = None, + platform: str = None, + version_number: VersionNumberTuple = None, force_blockstate: bool = None, namespace: str = None, base_name: str = None, show_pick: bool = False, - state: Dict[str, Any] = None, ): - state = state or {} - state.setdefault("translation_manager", translation_manager) - state.setdefault("platform", platform) - state.setdefault("version_number", version_number) - state.setdefault("force_blockstate", force_blockstate) - state.setdefault("namespace", namespace) - state.setdefault("base_name", base_name) - # This is the init call to the class that stores the internal state of the data. - # This needs to be at the start to ensure that the internal state is set up before anything else is done. - # It is not a direct call to init so that subclasses of this class can substitute in which state subclass is used. - self._init_state(state) - + if not isinstance(state, BaseResourceIDState): + state = self._create_default_state( + translation_manager, + platform, + version_number, + force_blockstate, + namespace, + base_name, + ) + StateHolder.__init__(self, state) wx.Panel.__init__(self, parent, style=wx.BORDER_SIMPLE) self._sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(self._sizer) @@ -53,8 +52,7 @@ def __init__( sizer.Add(text, 1, wx.ALIGN_CENTER_VERTICAL) self._namespace_combo = wx.ComboBox(self) sizer.Add(self._namespace_combo, 2) - self._populate_namespace() - self._push_namespace() + self._update_namespace() # This was previously done with EVT_TEXT but that is also triggered by the Set method. # This is a workaround so that it is only triggered by user input. @@ -88,50 +86,65 @@ def __init__( ) self._base_name_list_box = wx.ListBox(self, style=wx.LB_SINGLE) sizer.Add(self._base_name_list_box, 1, wx.EXPAND) - - self._base_names: List[str] = [] - self._populate_base_name() - self._push_base_name() + self._update_base_name() self._base_name_list_box.Bind(wx.EVT_LISTBOX, self._on_base_name_change) - def _init_state(self, state: Dict[str, Any]): - """ - Call the init method of the state manager. - This is here so that nested classes do not have to init the state managers multiple times. - """ - BaseMCResourceID.__init__(self, **state) + def _create_default_state( + self, + translation_manager: PyMCTranslate.TranslationManager, + platform: str = None, + version_number: VersionNumberTuple = None, + force_blockstate: bool = None, + namespace: str = None, + base_name: str = None, + ) -> BaseResourceIDState: + raise NotImplementedError @property def type_name(self) -> str: raise NotImplementedError - def _populate_namespace(self): - raise NotImplementedError("This method should be overridden in child classes.") - - def _populate_base_name(self): - raise NotImplementedError("This method should be overridden in child classes.") - - def _push_namespace(self): - """Push the internal namespace to the UI.""" - namespace = self.namespace - if namespace in self._namespace_combo.GetItems(): - self._namespace_combo.SetSelection( - self._namespace_combo.GetItems().index(namespace) - ) - else: - self._namespace_combo.ChangeValue(namespace) + def _post_event( + self, + namespace: str, + base_name: str, + ): + raise NotImplementedError - def _push_base_name(self): - """Push the internal base name to the UI.""" - if self.base_name in self._base_names: - location = self._base_name_list_box.FindString(self.base_name) + def _on_state_change(self): + if self.state.is_changed(State.Namespace) or self.state.is_changed( + State.ForceBlockstate + ): + self._update_namespace() + if self.state.is_changed(State.Namespace) or self.state.is_changed( + State.BaseName + ): + self._update_base_name() + + def _update_namespace(self): + if self.state.is_changed(State.ForceBlockstate): + self._namespace_combo.Set(self.state.valid_namespaces) + namespace = self.state.namespace + if namespace != self._namespace_combo.GetValue(): + index = self._namespace_combo.FindString(namespace) + if index == wx.NOT_FOUND: + self._namespace_combo.ChangeValue(namespace) + else: + self._namespace_combo.SetSelection(index) + + def _update_base_name(self): + base_name = self.state.base_name + if base_name in self.state.valid_base_names: + # The base name is known + location = self._base_name_list_box.FindString(base_name) if location == wx.NOT_FOUND: self._search.ChangeValue("") self._update_from_search() - location = self._base_name_list_box.FindString(self.base_name) + location = self._base_name_list_box.FindString(base_name) self._base_name_list_box.SetSelection(location) else: - self._search.ChangeValue(self.base_name) + # The base name is not known + self._search.ChangeValue(base_name) self._update_from_search() def _update_from_search(self) -> bool: @@ -141,17 +154,11 @@ def _update_from_search(self) -> bool: :return: True if the text in the field changed """ search_str = self._search.GetValue() - base_names = [bn for bn in self._base_names if search_str in bn] + base_names = [bn for bn in self.state.valid_base_names if search_str in bn] exact = search_str in base_names - # I originally wrote this in one line but it was rather difficult to read. - if exact: - # if we have an exact match - pass - elif not search_str and base_names: - # if the search string is blank but there are options - pass - else: + if (search_str and not exact) or (not search_str and not base_names): + # We have a search which is not a match or we don't have a search string or options base_names.insert(0, f'"{search_str}"') # find the previously selected string @@ -177,33 +184,18 @@ def _update_from_search(self) -> bool: self._base_name_list_box.GetSelection() ) - def _post_event( - self, - old_namespace: str, - old_base_name: str, - new_namespace: str, - new_base_name: str, - ): - raise NotImplementedError + def _handle_namespace_change(self): + namespace = self._namespace_combo.GetValue() + if namespace != self.state.namespace: + with self.state as state: + state.namespace = namespace + self._post_event(self.state.namespace, self.state.base_name) def _on_namespace_change(self, evt): self._handle_namespace_change() if isinstance(evt, wx.KeyEvent): evt.Skip() - def _handle_namespace_change(self): - self.Freeze() - old_namespace = self.namespace - new_namespace = self._namespace_combo.GetValue() - if new_namespace != old_namespace: - self._set_namespace(new_namespace) - - self._populate_base_name() - self._update_from_search() - - self._on_change(old_namespace) - self.Thaw() - def _on_namespace_char(self, evt): wx.CallAfter(self._handle_namespace_change) evt.Skip() @@ -215,26 +207,16 @@ def _on_search_change(self, evt): def _on_base_name_change(self, evt): self._on_change() - def _on_change(self, old_namespace=None): - if old_namespace is None: - old_namespace = self.namespace - old_base_name = self.base_name - new_base_name = self._base_name_list_box.GetString( + def _on_change(self): + base_name = self._base_name_list_box.GetString( self._base_name_list_box.GetSelection() ) - if self._base_name_list_box.GetSelection() == 0 and new_base_name.startswith( - '"' + if ( + self._base_name_list_box.GetSelection() == 0 + and base_name not in self.state.valid_base_names ): - new_base_name = new_base_name[1:-1] - if old_namespace != self.namespace or old_base_name != new_base_name: - self._set_base_name(new_base_name) - self._post_event( - old_namespace, old_base_name, self.namespace, new_base_name - ) - - def _on_push(self) -> bool: - self._populate_namespace() - self._push_namespace() - self._populate_base_name() - self._push_base_name() - return True + base_name = base_name[1:-1] + if base_name != self.state.namespace: + with self.state as state: + state.base_name = base_name + self._post_event(self.state.namespace, self.state.base_name) diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/events.py b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/events.py index caf809d7..f111452e 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/events.py +++ b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/events.py @@ -16,14 +16,10 @@ def __init__( self, namespace: str, base_name: str, - old_namespace: str, - old_base_name: str, ): wx.PyEvent.__init__(self) self._namespace = namespace self._base_name = base_name - self._old_namespace = old_namespace - self._old_base_name = old_base_name @property def namespace(self) -> str: @@ -32,11 +28,3 @@ def namespace(self) -> str: @property def base_name(self) -> str: return self._base_name - - @property - def old_namespace(self) -> str: - return self._old_namespace - - @property - def old_base_name(self) -> str: - return self._old_base_name diff --git a/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/biome_identifier_select.py b/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/biome_identifier_select.py index 87d164c4..0dd566d4 100644 --- a/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/biome_identifier_select.py +++ b/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/biome_identifier_select.py @@ -1,17 +1,18 @@ -from typing import Dict, Any import wx +import PyMCTranslate +from amulet.api.data_types import VersionNumberTuple +from amulet_map_editor.api.wx.ui.mc.state import BiomeResourceIDState from amulet_map_editor.api.wx.ui.mc.base.base_identifier_select import ( BaseIdentifierSelect, ) -from amulet_map_editor.api.wx.ui.mc.api.biome import BaseMCBiomeIdentifier from amulet_map_editor.api.wx.ui.mc.biome.identifier_select.events import ( BiomeIDChangeEvent, EVT_BIOME_ID_CHANGE, ) -class BiomeIdentifierSelect(BaseIdentifierSelect, BaseMCBiomeIdentifier): +class BiomeIdentifierSelect(BaseIdentifierSelect): """ A UI consisting of a namespace choice, biome name search box and list of biome names. """ @@ -20,47 +21,34 @@ class BiomeIdentifierSelect(BaseIdentifierSelect, BaseMCBiomeIdentifier): def type_name(self) -> str: return "Biome" - def _init_state(self, state: Dict[str, Any]): - BaseMCBiomeIdentifier.__init__(self, **state) - - def _populate_namespace(self): - version = self._translation_manager.get_version( - self.platform, self.version_number - ) - namespaces = list( - set([biome_id.split(":", 1)[0] for biome_id in version.biome.biome_ids]) - ) - self._namespace_combo.Set(namespaces) - - def _populate_base_name(self): - version = self._translation_manager.get_version( - self.platform, self.version_number + def _create_default_state( + self, + translation_manager: PyMCTranslate.TranslationManager, + platform: str = None, + version_number: VersionNumberTuple = None, + force_blockstate: bool = None, + namespace: str = None, + base_name: str = None, + ) -> BiomeResourceIDState: + return BiomeResourceIDState( + translation_manager, + platform, + version_number, + force_blockstate, + namespace, + base_name, ) - namespace = f"{self.namespace}:" - namespace_size = len(namespace) - - self._base_names = [ - biome_id[namespace_size:] - for biome_id in version.biome.biome_ids - if biome_id.startswith(namespace) - ] - self._base_name_list_box.SetItems(self._base_names) - self._update_from_search() def _post_event( self, - old_namespace: str, - old_base_name: str, - new_namespace: str, - new_base_name: str, + namespace: str, + base_name: str, ): wx.PostEvent( self, BiomeIDChangeEvent( - new_namespace, - new_base_name, - old_namespace, - old_base_name, + namespace, + base_name, ), ) @@ -81,11 +69,15 @@ def demo(): sizer = wx.BoxSizer() dialog.SetSizer(sizer) widget = BiomeIdentifierSelect( - dialog, translation_manager, "java", (1, 16, 0), False + dialog, + translation_manager, + platform="java", + version_number=(1, 16, 0), + force_blockstate=False, ) def on_change(evt: BiomeIDChangeEvent): - print(evt.old_namespace, evt.old_base_name, evt.namespace, evt.base_name) + print(evt.namespace, evt.base_name) widget.Bind(EVT_BIOME_ID_CHANGE, on_change) sizer.Add( diff --git a/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/events.py b/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/events.py index 0d69c4c6..6d3decdd 100644 --- a/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/events.py +++ b/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/events.py @@ -14,8 +14,6 @@ def __init__( self, namespace: str, base_name: str, - old_namespace: str, - old_base_name: str, ): - super().__init__(namespace, base_name, old_namespace, old_base_name) + super().__init__(namespace, base_name) self.SetEventType(_BiomeIDChangeEventType) diff --git a/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py b/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py index 4dfc2d9e..f860b0c5 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py +++ b/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py @@ -1,17 +1,18 @@ -from typing import Dict, Any import wx +import PyMCTranslate +from amulet.api.data_types import VersionNumberTuple from amulet_map_editor.api.wx.ui.mc.base.base_identifier_select import ( BaseIdentifierSelect, ) -from amulet_map_editor.api.wx.ui.mc.api.block import BaseMCBlockIdentifier +from amulet_map_editor.api.wx.ui.mc.state import BlockResourceIDState from amulet_map_editor.api.wx.ui.mc.block.identifier_select.events import ( BlockIDChangeEvent, EVT_BLOCK_ID_CHANGE, ) -class BlockIdentifierSelect(BaseIdentifierSelect, BaseMCBlockIdentifier): +class BlockIdentifierSelect(BaseIdentifierSelect): """ A UI consisting of a namespace choice, block name search box and list of block names. """ @@ -20,39 +21,34 @@ class BlockIdentifierSelect(BaseIdentifierSelect, BaseMCBlockIdentifier): def type_name(self) -> str: return "Block" - def _init_state(self, state: Dict[str, Any]): - BaseMCBlockIdentifier.__init__(self, **state) - - def _populate_namespace(self): - version = self._translation_manager.get_version( - self.platform, self.version_number - ) - self._namespace_combo.Set(version.block.namespaces(self.force_blockstate)) - - def _populate_base_name(self): - version = self._translation_manager.get_version( - self.platform, self.version_number - ) - self._base_names = version.block.base_names( - self.namespace, self.force_blockstate + def _create_default_state( + self, + translation_manager: PyMCTranslate.TranslationManager, + platform: str = None, + version_number: VersionNumberTuple = None, + force_blockstate: bool = None, + namespace: str = None, + base_name: str = None, + ) -> BlockResourceIDState: + return BlockResourceIDState( + translation_manager, + platform, + version_number, + force_blockstate, + namespace, + base_name, ) - self._base_name_list_box.SetItems(self._base_names) - self._update_from_search() def _post_event( self, - old_namespace: str, - old_base_name: str, - new_namespace: str, - new_base_name: str, + namespace: str, + base_name: str, ): wx.PostEvent( self, BlockIDChangeEvent( - new_namespace, - new_base_name, - old_namespace, - old_base_name, + namespace, + base_name, ), ) @@ -73,11 +69,15 @@ def demo(): sizer = wx.BoxSizer() dialog.SetSizer(sizer) widget = BlockIdentifierSelect( - dialog, translation_manager, "java", (1, 16, 0), False + dialog, + translation_manager, + platform="java", + version_number=(1, 16, 0), + force_blockstate=False, ) def on_change(evt: BlockIDChangeEvent): - print(evt.old_namespace, evt.old_base_name, evt.namespace, evt.base_name) + print(evt.namespace, evt.base_name) widget.Bind(EVT_BLOCK_ID_CHANGE, on_change) sizer.Add( diff --git a/amulet_map_editor/api/wx/ui/mc/block/identifier_select/events.py b/amulet_map_editor/api/wx/ui/mc/block/identifier_select/events.py index 42f938b6..d885d06c 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/identifier_select/events.py +++ b/amulet_map_editor/api/wx/ui/mc/block/identifier_select/events.py @@ -14,8 +14,6 @@ def __init__( self, namespace: str, base_name: str, - old_namespace: str, - old_base_name: str, ): - super().__init__(namespace, base_name, old_namespace, old_base_name) + super().__init__(namespace, base_name) self.SetEventType(_BlockIDChangeEventType) From 0d57f03a73d858390d8b3b388a0aece97bf51498 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Thu, 16 Sep 2021 17:19:45 +0100 Subject: [PATCH 110/139] Cleanup and fixed some calls --- amulet_map_editor/api/wx/ui/mc/version/platform_select.py | 1 + amulet_map_editor/api/wx/ui/mc/version/version_select.py | 8 +++++--- .../stock_plugins/export_operations/construction.py | 2 +- .../stock_plugins/export_operations/mcstructure.py | 2 +- .../stock_plugins/export_operations/schematic.py | 2 +- .../stock_plugins/export_operations/sponge_schematic.py | 2 +- 6 files changed, 10 insertions(+), 7 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/version/platform_select.py b/amulet_map_editor/api/wx/ui/mc/version/platform_select.py index 00e2a6a1..eae20c20 100644 --- a/amulet_map_editor/api/wx/ui/mc/version/platform_select.py +++ b/amulet_map_editor/api/wx/ui/mc/version/platform_select.py @@ -12,6 +12,7 @@ class PlatformSelect(wx.Panel, StateHolder): """ A UI element that allows you to pick between the platforms in the translator. """ + state: PlatformState def __init__( diff --git a/amulet_map_editor/api/wx/ui/mc/version/version_select.py b/amulet_map_editor/api/wx/ui/mc/version/version_select.py index 1162c76a..b5ff6c81 100644 --- a/amulet_map_editor/api/wx/ui/mc/version/version_select.py +++ b/amulet_map_editor/api/wx/ui/mc/version/version_select.py @@ -5,7 +5,6 @@ from amulet.api.data_types import VersionNumberTuple, PlatformType from .platform_select import PlatformSelect -from amulet_map_editor.api.wx.ui.mc.api.version import BaseMCVersion from amulet_map_editor.api.wx.ui.mc.state import VersionState, State from .events import VersionChangeEvent, EVT_VERSION_CHANGE @@ -14,6 +13,7 @@ class VersionSelect(PlatformSelect): """ A UI element that allows you to pick between the platforms and versions in the translator. """ + state: VersionState def __init__( @@ -51,7 +51,9 @@ def __init__( """ # init the state if not isinstance(state, VersionState): - state = VersionState(translation_manager, platform, version_number, force_blockstate) + state = VersionState( + translation_manager, platform, version_number, force_blockstate + ) super().__init__( parent, @@ -117,7 +119,7 @@ def _post_version_change(self): self.state.platform, self.state.version_number, self.state.force_blockstate, - ) + ), ) def _on_version_number_change(self, evt): diff --git a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/construction.py b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/construction.py index 04c57ff4..0022d5e1 100644 --- a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/construction.py +++ b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/construction.py @@ -40,7 +40,7 @@ def __init__( self._version_define = VersionSelect( self, world.translation_manager, - options.get("platform", None) or world.level_wrapper.platform, + platform=options.get("platform", None) or world.level_wrapper.platform, allow_universal=False, ) self._sizer.Add(self._version_define, 0, wx.CENTRE, 5) diff --git a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/mcstructure.py b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/mcstructure.py index 045cc91b..467c75d1 100644 --- a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/mcstructure.py +++ b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/mcstructure.py @@ -40,7 +40,7 @@ def __init__( self._version_define = VersionSelect( self, world.translation_manager, - options.get("platform", None) or world.level_wrapper.platform, + platform=options.get("platform", None) or world.level_wrapper.platform, allowed_platforms=("bedrock",), allow_numerical=False, ) diff --git a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/schematic.py b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/schematic.py index b71dfe1e..3c1897ef 100644 --- a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/schematic.py +++ b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/schematic.py @@ -51,7 +51,7 @@ def __init__( self._platform_define = PlatformSelect( self, world.translation_manager, - options.get("platform", None) or world.level_wrapper.platform, + platform=options.get("platform", None) or world.level_wrapper.platform, allow_universal=False, ) self._sizer.Add(self._platform_define, 0, wx.CENTRE, 5) diff --git a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/sponge_schematic.py b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/sponge_schematic.py index fb3db7d1..a1961318 100644 --- a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/sponge_schematic.py +++ b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/sponge_schematic.py @@ -40,7 +40,7 @@ def __init__( self._version_define = VersionSelect( self, world.translation_manager, - options.get("platform", None) or world.level_wrapper.platform, + platform=options.get("platform", None) or world.level_wrapper.platform, allowed_platforms=("java",), allow_numerical=False, ) From ea4d8f0514017b6c0a77426a7f031bfc3e0266be Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Fri, 17 Sep 2021 10:49:09 +0100 Subject: [PATCH 111/139] Forced state inputs to be keyword arguments By adding * in the inputs means that the keyword arguments cannot be given as normal arguments. This should make it a lot easier to add keyword arguments in the future --- .../base_identifier_select/base_identifier_select.py | 12 +++++++----- .../identifier_select/biome_identifier_select.py | 11 ++++++----- .../identifier_select/block_identifier_select.py | 11 ++++++----- amulet_map_editor/api/wx/ui/mc/state.py | 5 +++++ .../api/wx/ui/mc/version/platform_select.py | 3 ++- .../api/wx/ui/mc/version/version_select.py | 6 +++++- 6 files changed, 31 insertions(+), 17 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py index 46727696..2e2f8cba 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py +++ b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py @@ -24,6 +24,7 @@ def __init__( self, parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager, + *, state: BaseResourceIDState = None, platform: str = None, version_number: VersionNumberTuple = None, @@ -35,11 +36,11 @@ def __init__( if not isinstance(state, BaseResourceIDState): state = self._create_default_state( translation_manager, - platform, - version_number, - force_blockstate, - namespace, - base_name, + platform=platform, + version_number=version_number, + force_blockstate=force_blockstate, + namespace=namespace, + base_name=base_name, ) StateHolder.__init__(self, state) wx.Panel.__init__(self, parent, style=wx.BORDER_SIMPLE) @@ -92,6 +93,7 @@ def __init__( def _create_default_state( self, translation_manager: PyMCTranslate.TranslationManager, + *, platform: str = None, version_number: VersionNumberTuple = None, force_blockstate: bool = None, diff --git a/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/biome_identifier_select.py b/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/biome_identifier_select.py index 0dd566d4..a2a8c5ca 100644 --- a/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/biome_identifier_select.py +++ b/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/biome_identifier_select.py @@ -24,6 +24,7 @@ def type_name(self) -> str: def _create_default_state( self, translation_manager: PyMCTranslate.TranslationManager, + *, platform: str = None, version_number: VersionNumberTuple = None, force_blockstate: bool = None, @@ -32,11 +33,11 @@ def _create_default_state( ) -> BiomeResourceIDState: return BiomeResourceIDState( translation_manager, - platform, - version_number, - force_blockstate, - namespace, - base_name, + platform=platform, + version_number=version_number, + force_blockstate=force_blockstate, + namespace=namespace, + base_name=base_name, ) def _post_event( diff --git a/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py b/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py index f860b0c5..aec079c6 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py +++ b/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py @@ -24,6 +24,7 @@ def type_name(self) -> str: def _create_default_state( self, translation_manager: PyMCTranslate.TranslationManager, + *, platform: str = None, version_number: VersionNumberTuple = None, force_blockstate: bool = None, @@ -32,11 +33,11 @@ def _create_default_state( ) -> BlockResourceIDState: return BlockResourceIDState( translation_manager, - platform, - version_number, - force_blockstate, - namespace, - base_name, + platform=platform, + version_number=version_number, + force_blockstate=force_blockstate, + namespace=namespace, + base_name=base_name, ) def _post_event( diff --git a/amulet_map_editor/api/wx/ui/mc/state.py b/amulet_map_editor/api/wx/ui/mc/state.py index 66501641..178a5db8 100644 --- a/amulet_map_editor/api/wx/ui/mc/state.py +++ b/amulet_map_editor/api/wx/ui/mc/state.py @@ -123,6 +123,7 @@ class PlatformState(BaseState): def __init__( self, translation_manager: TranslationManager, + *, platform: str = None, ): super().__init__(translation_manager) @@ -157,6 +158,7 @@ class VersionState(PlatformState): def __init__( self, translation_manager: TranslationManager, + *, platform: str = None, version_number: VersionNumberAny = None, force_blockstate: bool = None, @@ -237,6 +239,7 @@ class BaseNamespaceState(VersionState): def __init__( self, translation_manager: TranslationManager, + *, platform: str = None, version_number: VersionNumberAny = None, force_blockstate: bool = None, @@ -278,6 +281,7 @@ class BaseResourceIDState(BaseNamespaceState): def __init__( self, translation_manager: TranslationManager, + *, platform: str = None, version_number: VersionNumberAny = None, force_blockstate: bool = None, @@ -354,6 +358,7 @@ class BlockState(BlockResourceIDState): def __init__( self, translation_manager: TranslationManager, + *, platform: str = None, version_number: VersionNumberAny = None, force_blockstate: bool = None, diff --git a/amulet_map_editor/api/wx/ui/mc/version/platform_select.py b/amulet_map_editor/api/wx/ui/mc/version/platform_select.py index eae20c20..6ab42d5c 100644 --- a/amulet_map_editor/api/wx/ui/mc/version/platform_select.py +++ b/amulet_map_editor/api/wx/ui/mc/version/platform_select.py @@ -19,6 +19,7 @@ def __init__( self, parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager, + *, state: PlatformState = None, platform: PlatformType = None, allow_universal: bool = True, @@ -40,7 +41,7 @@ def __init__( """ # init the state if not isinstance(state, PlatformState): - state = PlatformState(translation_manager, platform) + state = PlatformState(translation_manager, platform=platform) StateHolder.__init__(self, state) # init the panel diff --git a/amulet_map_editor/api/wx/ui/mc/version/version_select.py b/amulet_map_editor/api/wx/ui/mc/version/version_select.py index b5ff6c81..91eed21f 100644 --- a/amulet_map_editor/api/wx/ui/mc/version/version_select.py +++ b/amulet_map_editor/api/wx/ui/mc/version/version_select.py @@ -20,6 +20,7 @@ def __init__( self, parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager, + *, state: VersionState = None, platform: PlatformType = None, version_number: VersionNumberTuple = None, @@ -52,7 +53,10 @@ def __init__( # init the state if not isinstance(state, VersionState): state = VersionState( - translation_manager, platform, version_number, force_blockstate + translation_manager, + platform=platform, + version_number=version_number, + force_blockstate=force_blockstate, ) super().__init__( From 6d1fb83571f13ef8e98661f17f4d9409e0e4e1c9 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Fri, 17 Sep 2021 10:49:45 +0100 Subject: [PATCH 112/139] Fixed flickering issue during searching --- .../ui/mc/base/base_identifier_select/base_identifier_select.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py index 2e2f8cba..279ddcc6 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py +++ b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py @@ -169,6 +169,7 @@ def _update_from_search(self) -> bool: if selection != wx.NOT_FOUND: previous_string = self._base_name_list_box.GetString(selection) + self.Freeze() self._base_name_list_box.SetItems(base_names) if exact: # if the searched text perfectly matches select that @@ -181,6 +182,7 @@ def _update_from_search(self) -> bool: self._base_name_list_box.SetSelection(1) else: self._base_name_list_box.SetSelection(0) + self.Thaw() return previous_string != self._base_name_list_box.GetString( self._base_name_list_box.GetSelection() From e22f968d3dff83973839aeb3e1ca35f085943eb1 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Mon, 20 Sep 2021 10:46:07 +0100 Subject: [PATCH 113/139] Improved the SimpleChoiceAny UI Renamed to ChoiceRaw which seems like a better name. Kept the old name for backwards compatibility. Added choices input in init. Added option to set from tuple of (obj, str). Dict is a bit limited because each key must be hashable and unique. Added SetObject method --- amulet_map_editor/api/wx/ui/simple.py | 93 ++++++++++++++++++--------- 1 file changed, 63 insertions(+), 30 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/simple.py b/amulet_map_editor/api/wx/ui/simple.py index 93e4a965..c0ff82ce 100644 --- a/amulet_map_editor/api/wx/ui/simple.py +++ b/amulet_map_editor/api/wx/ui/simple.py @@ -71,17 +71,28 @@ def GetCurrentString(self) -> str: StringableType = Any +ChoicesType = Iterable[Union[StringableType, Tuple[Any, str]]] -class SimpleChoiceAny(wx.Choice): - """An extension for wx.Choice that enables showing and returning objects that are not strings.""" +class ChoiceRaw(wx.Choice): + """ + An extension for wx.Choice that enables handling for more than just strings. + The normal wx.Choice class only allows the storage of string objects. + This became an issue when more complex data needed to be displayed in a choice. + This class can be created from an iterable of any object and the result of str(obj) will be displayed to the user. + It can also be given a iterable of tuples containing the object and a string to show to the user. + """ - def __init__(self, parent: wx.Window, sort=True, reverse=False): + def __init__( + self, parent: wx.Window, *, choices: ChoicesType = (), sort=True, reverse=False + ): super().__init__(parent) self._values: List[Any] = [] # the data hidden behind the string self._keys: List[str] = [] # the strings shown to the user self._sorted = sort self._reverse = reverse + if choices: + self.SetItems(choices) @property def keys(self) -> Tuple[str, ...]: @@ -98,41 +109,60 @@ def items(self) -> Tuple[Tuple[str, Any], ...]: """Get the string value and the data hidden behind the value""" return tuple(zip(self._keys, self._values)) + def _set_items(self, items: Iterable[Tuple[Any, str]], default: Any = None): + if items: + if self._sorted: + items = sorted(items, key=lambda x: x[1], reverse=self._reverse) + self._values, self._keys = zip(*items) + super().SetItems(self._keys) + if default in self._values: + self.SetSelection(self._values.index(default)) + else: + self.SetSelection(0) + def SetItems( self, - items: Union[Iterable[StringableType], Dict[StringableType, Any]], + items: Union[ + Iterable[Tuple[Any, str]], + Iterable[StringableType], + Dict[StringableType, Any], + ], default: StringableType = None, ): - """Set items. Does not have to be strings. + """ + Set items. Does not have to be strings. + Can be an iterable of any object and the result of str(obj) will be displayed to the user. + If the object is a tuple + It can also be given a iterable of tuples containing the object and a string to show to the user. + + If items is a dictionary the string of the values are show to the user and the key is returned from GetCurrentObject If it is just an iterable the string of the values are shown and the raw equivalent input is returned.""" - if not items: - return if isinstance(items, dict): - items: List[Tuple[str, Any]] = [ - (str(value), key) for key, value in items.items() - ] - if self._sorted: - items = sorted(items, key=lambda x: x[0], reverse=self._reverse) - self._keys = [key.strip() for key, _ in items] - self._values = [value for _, value in items] - else: - if self._sorted: - self._values = list(sorted(items)) - if self._reverse: - self._values.reverse() - else: - self._values = list(items) - self._keys = [str(v).strip() for v in self._values] - super().SetItems(self._keys) - if default is not None and default in self._values: - self.SetSelection(self._values.index(default)) + items = tuple(items.items()) else: - self.SetSelection(0) - - def SetValue(self, value: Any): - if value in self._keys: - self.SetSelection(self._keys.index(value)) + items_ = [] + for item in items: + if ( + isinstance(item, (tuple, list)) + and len(item) == 2 + and isinstance(item[1], str) + ): + item = (item[0], str(item[1])) + else: + item = (item, str(item)) + items_.append(item) + self._set_items(items, default) + + def SetObject(self, obj: Any): + """Set the selected item from the data hidden behind the text.""" + if obj in self._values: + self.SetSelection(self._values.index(obj)) + + def SetValue(self, key: Any): + """Set the selected item based on the text in the choice.""" + if key in self._keys: + self.SetSelection(self._keys.index(key)) def GetCurrentObject(self) -> Optional[Any]: """Return the value currently selected in the form before it was converted to a string""" @@ -144,6 +174,9 @@ def GetCurrentString(self) -> str: return self.GetString(self.GetSelection()) +SimpleChoiceAny = ChoiceRaw + + class SimpleDialog(wx.Dialog): """A dialog with ok and cancel buttons set up.""" From 126c44f20b4d78624182a7e40643afd308aa7f20 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Mon, 20 Sep 2021 10:48:55 +0100 Subject: [PATCH 114/139] Updated imports to use ChoiceRaw --- amulet_map_editor/api/wx/ui/mc/version/version_select.py | 6 +++--- amulet_map_editor/programs/edit/api/ui/file.py | 4 ++-- .../programs/edit/api/ui/tool/base_operation_choice.py | 6 +++--- .../operations/stock_plugins/operations/set_biome.py | 4 ++-- .../edit/plugins/tools/fill_replace/fill_replace_widget.py | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/version/version_select.py b/amulet_map_editor/api/wx/ui/mc/version/version_select.py index 91eed21f..ccc7ecd6 100644 --- a/amulet_map_editor/api/wx/ui/mc/version/version_select.py +++ b/amulet_map_editor/api/wx/ui/mc/version/version_select.py @@ -1,4 +1,4 @@ -from amulet_map_editor.api.wx.ui.simple import SimpleChoice, SimpleChoiceAny +from amulet_map_editor.api.wx.ui.simple import SimpleChoice, ChoiceRaw import wx import PyMCTranslate from typing import Optional, Dict, Any, Tuple @@ -71,8 +71,8 @@ def __init__( self._allow_numerical = allow_numerical self._allow_blockstate = allow_blockstate - self._version_choice: Optional[SimpleChoiceAny] = self._add_ui_element( - "Version:", SimpleChoiceAny, reverse=True + self._version_choice: Optional[ChoiceRaw] = self._add_ui_element( + "Version:", ChoiceRaw, reverse=True ) self._update_version_number() self._version_choice.Bind( diff --git a/amulet_map_editor/programs/edit/api/ui/file.py b/amulet_map_editor/programs/edit/api/ui/file.py index 3e76cef0..7e6c9bca 100644 --- a/amulet_map_editor/programs/edit/api/ui/file.py +++ b/amulet_map_editor/programs/edit/api/ui/file.py @@ -4,7 +4,7 @@ from amulet_map_editor.programs.edit.api.edit_canvas_container import ( EditCanvasContainer, ) -from amulet_map_editor.api.wx.ui.simple import SimpleChoiceAny +from amulet_map_editor.api.wx.ui.simple import ChoiceRaw from amulet_map_editor.programs.edit.api.events import ( EVT_CAMERA_MOVED, EVT_UNDO, @@ -56,7 +56,7 @@ def __init__(self, canvas: "EditCanvas"): self.Add(self._location_button, 0, wx.TOP | wx.BOTTOM | wx.RIGHT | wx.CENTER, 5) - self._dim_options = SimpleChoiceAny(canvas) + self._dim_options = ChoiceRaw(canvas) self._dim_options.SetToolTip(lang.get("program_3d_edit.file_ui.dim_tooltip")) self._dim_options.SetItems(level.level_wrapper.dimensions) self._dim_options.SetValue(level.level_wrapper.dimensions[0]) diff --git a/amulet_map_editor/programs/edit/api/ui/tool/base_operation_choice.py b/amulet_map_editor/programs/edit/api/ui/tool/base_operation_choice.py index c483e022..04c83e78 100644 --- a/amulet_map_editor/programs/edit/api/ui/tool/base_operation_choice.py +++ b/amulet_map_editor/programs/edit/api/ui/tool/base_operation_choice.py @@ -4,7 +4,7 @@ from amulet_map_editor import log from amulet_map_editor.api.image import REFRESH_ICON -from amulet_map_editor.api.wx.ui.simple import SimpleChoiceAny +from amulet_map_editor.api.wx.ui.simple import ChoiceRaw from amulet_map_editor.api.wx.ui.traceback_dialog import TracebackDialog from amulet_map_editor.programs.edit.api.operations import OperationUIType @@ -25,7 +25,7 @@ def __init__(self, canvas: "EditCanvas"): self._active_operation: Optional[OperationUIType] = None self._last_active_operation_id: Optional[str] = None - self._operation_choice: Optional[SimpleChoiceAny] = None + self._operation_choice: Optional[ChoiceRaw] = None self._reload_operation: Optional[wx.BitmapButton] = None self._operations: Optional[UIOperationManager] = None self._operation_sizer: Optional[wx.BoxSizer] = None @@ -34,7 +34,7 @@ def setup(self): super().setup() horizontal_sizer = wx.BoxSizer(wx.HORIZONTAL) - self._operation_choice = SimpleChoiceAny(self.canvas) + self._operation_choice = ChoiceRaw(self.canvas) self._reload_operation = wx.BitmapButton( self.canvas, bitmap=REFRESH_ICON.bitmap(16, 16) ) diff --git a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/set_biome.py b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/set_biome.py index b56c01e7..b26c4494 100644 --- a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/set_biome.py +++ b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/set_biome.py @@ -8,7 +8,7 @@ from amulet_map_editor.api.wx.ui.mc.base.base_identifier_select import EVT_PICK from amulet_map_editor.api.wx.ui.mc.biome import BiomeDefine from amulet_map_editor.programs.edit.api.operations import SimpleOperationPanel -from amulet_map_editor.api.wx.ui.simple import SimpleChoiceAny +from amulet_map_editor.api.wx.ui.simple import ChoiceRaw if TYPE_CHECKING: from amulet.api.level import BaseLevel @@ -46,7 +46,7 @@ def __init__( self.Freeze() options = self._load_options({}) - self._mode = SimpleChoiceAny(self, sort=False) + self._mode = ChoiceRaw(self, sort=False) self._mode.SetItems({mode: lang[mode] for mode in MODES.keys()}) self._sizer.Add(self._mode, 0, Border, 5) self._mode.Bind(wx.EVT_CHOICE, self._on_mode_change) diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_widget.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_widget.py index b0b5cb62..781a4ac1 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_widget.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_widget.py @@ -7,7 +7,7 @@ from amulet.api.data_types import VersionNumberTuple from amulet_map_editor import lang -from amulet_map_editor.api.wx.ui.simple import SimpleChoiceAny +from amulet_map_editor.api.wx.ui.simple import ChoiceRaw from amulet_map_editor.api.wx.ui.events import ChildSizeEvent from .block_container import FillBlockContainer, FindBlockContainer @@ -148,7 +148,7 @@ def __init__( self._multiple.Bind(wx.EVT_CHECKBOX, self._on_check_change) top_sizer.Add(self._multiple, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) - self._replace_mode = SimpleChoiceAny(self, sort=False) + self._replace_mode = ChoiceRaw(self, sort=False) self._replace_mode.Hide() self._replace_mode.SetItems( { From add65b98c6883ee53d3a0f2bceb7c75e9543bb6f Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Mon, 20 Sep 2021 13:48:35 +0100 Subject: [PATCH 115/139] Fixed some issues in state --- amulet_map_editor/api/wx/ui/mc/state.py | 23 ++++++++++++------- .../api/wx/ui/mc/version/version_select.py | 2 +- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/state.py b/amulet_map_editor/api/wx/ui/mc/state.py index 178a5db8..de0c6f5f 100644 --- a/amulet_map_editor/api/wx/ui/mc/state.py +++ b/amulet_map_editor/api/wx/ui/mc/state.py @@ -163,7 +163,7 @@ def __init__( version_number: VersionNumberAny = None, force_blockstate: bool = None, ): - super().__init__(translation_manager, platform) + super().__init__(translation_manager, platform=platform) self._state[State.VersionNumber] = self._sanitise_version(version_number) self._state[State.ForceBlockstate] = self._sanitise_force_blockstate( force_blockstate @@ -246,7 +246,10 @@ def __init__( namespace: str = None, ): super().__init__( - translation_manager, platform, version_number, force_blockstate + translation_manager, + platform=platform, + version_number=version_number, + force_blockstate=force_blockstate, ) self._state[State.Namespace] = self._sanitise_namespace(namespace) @@ -289,7 +292,11 @@ def __init__( base_name: str = None, ): super().__init__( - translation_manager, platform, version_number, force_blockstate, namespace + translation_manager, + platform=platform, + version_number=version_number, + force_blockstate=force_blockstate, + namespace=namespace, ) self._state[State.BaseName] = self._sanitise_base_name(base_name) @@ -369,11 +376,11 @@ def __init__( ): super().__init__( translation_manager, - platform, - version_number, - force_blockstate, - namespace, - base_name, + platform=platform, + version_number=version_number, + force_blockstate=force_blockstate, + namespace=namespace, + base_name=base_name, ) self._state[State.Properties] = self._sanitise_properties(properties) self._state[State.PropertiesMultiple] = self._sanitise_properties_multiple( diff --git a/amulet_map_editor/api/wx/ui/mc/version/version_select.py b/amulet_map_editor/api/wx/ui/mc/version/version_select.py index ccc7ecd6..8191d71b 100644 --- a/amulet_map_editor/api/wx/ui/mc/version/version_select.py +++ b/amulet_map_editor/api/wx/ui/mc/version/version_select.py @@ -62,7 +62,7 @@ def __init__( super().__init__( parent, translation_manager, - state, + state=state, allow_universal=allow_universal, allow_vanilla=allow_vanilla, allowed_platforms=allowed_platforms, From 000d75a76b4c38d59b07e928c766452b64ea7bda Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Mon, 20 Sep 2021 13:52:47 +0100 Subject: [PATCH 116/139] Renamed automatic and manual to vanilla and modded Also partially rewritten the single properties --- .../api/wx/ui/mc/block/properties/base.py | 43 +++--- .../properties/multiple/automatic/__init__.py | 1 - .../ui/mc/block/properties/multiple/main.py | 44 +++--- .../multiple/{manual.py => modded.py} | 2 +- .../properties/multiple/vanilla/__init__.py | 1 + .../multiple/{automatic => vanilla}/popup.py | 0 .../automatic.py => vanilla/vanilla.py} | 2 +- .../mc/block/properties/single/automatic.py | 127 ---------------- .../wx/ui/mc/block/properties/single/base.py | 23 +-- .../wx/ui/mc/block/properties/single/main.py | 142 +++++++----------- .../single/{manual.py => modded.py} | 8 +- .../ui/mc/block/properties/single/vanilla.py | 95 ++++++++++++ 12 files changed, 202 insertions(+), 286 deletions(-) delete mode 100644 amulet_map_editor/api/wx/ui/mc/block/properties/multiple/automatic/__init__.py rename amulet_map_editor/api/wx/ui/mc/block/properties/multiple/{manual.py => modded.py} (99%) create mode 100644 amulet_map_editor/api/wx/ui/mc/block/properties/multiple/vanilla/__init__.py rename amulet_map_editor/api/wx/ui/mc/block/properties/multiple/{automatic => vanilla}/popup.py (100%) rename amulet_map_editor/api/wx/ui/mc/block/properties/multiple/{automatic/automatic.py => vanilla/vanilla.py} (98%) delete mode 100644 amulet_map_editor/api/wx/ui/mc/block/properties/single/automatic.py rename amulet_map_editor/api/wx/ui/mc/block/properties/single/{manual.py => modded.py} (95%) create mode 100644 amulet_map_editor/api/wx/ui/mc/block/properties/single/vanilla.py diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/base.py b/amulet_map_editor/api/wx/ui/mc/block/properties/base.py index ebddd122..eb6f5f29 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/base.py @@ -1,42 +1,33 @@ import wx -from typing import Tuple, Dict, Any import PyMCTranslate from amulet.api.data_types import VersionNumberTuple -from amulet_map_editor.api.wx.ui.mc.api.block import BaseMCBlockIdentifier +from amulet_map_editor.api.wx.ui.mc.state import StateHolder, BlockState -class BasePropertySelect(wx.Panel, BaseMCBlockIdentifier): +class BasePropertySelect(wx.Panel, StateHolder): def __init__( self, parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager, - platform: str, - version_number: VersionNumberTuple, - force_blockstate: bool, + *, + state: BlockState = None, + platform: str = None, + version_number: VersionNumberTuple = None, + force_blockstate: bool = None, namespace: str = None, base_name: str = None, - state: Dict[str, Any] = None, ): - state = state or {} - state.setdefault("translation_manager", translation_manager) - state.setdefault("platform", platform) - state.setdefault("version_number", version_number) - state.setdefault("force_blockstate", force_blockstate) - state.setdefault("namespace", namespace) - state.setdefault("base_name", base_name) - # This is the init call to the class that stores the internal state of the data. - # This needs to be at the start to ensure that the internal state is set up before anything else is done. - # It is not a direct call to init so that subclasses of this class can substitute in which state subclass is used. - self._init_state(state) - + if not isinstance(state, BlockState): + state = BlockState( + translation_manager, + platform=platform, + version_number=version_number, + force_blockstate=force_blockstate, + namespace=namespace, + base_name=base_name, + ) + StateHolder.__init__(self, state) wx.Panel.__init__(self, parent) self._sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(self._sizer) - - def _init_state(self, state: Dict[str, Any]): - """ - Call the init method of the state manager. - This is here so that nested classes do not have to init the state managers multiple times. - """ - BaseMCBlockIdentifier.__init__(self, **state) diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/automatic/__init__.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/automatic/__init__.py deleted file mode 100644 index f77e8f87..00000000 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/automatic/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .automatic import AutomaticMultipleProperty diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py index 2ac44d01..5be96cf2 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py @@ -1,5 +1,5 @@ import wx -from typing import Tuple, Dict, Any, List +from typing import Dict, Any, List import PyMCTranslate import amulet_nbt @@ -8,8 +8,8 @@ from amulet_map_editor.api.wx.ui.mc.api import WildcardMCBlock from ..base import BasePropertySelect -from .automatic import AutomaticMultipleProperty -from .manual import ManualMultipleProperty +from .vanilla import VanillaMultipleProperty +from .modded import ModdedMultipleProperty from .events import MultiplePropertiesChangeEvent, EVT_MULTIPLE_PROPERTIES_CHANGE @@ -40,21 +40,21 @@ def __init__( self, parent, translation_manager, - platform, - version_number, - force_blockstate, - namespace, - base_name, + platform=platform, + version_number=version_number, + force_blockstate=force_blockstate, + namespace=namespace, + base_name=base_name, state=state, ) self._manual_enabled = False - self._simple = AutomaticMultipleProperty(self) - self._sizer.Add(self._simple, 1, wx.EXPAND) - self._manual = ManualMultipleProperty(self) - self._sizer.Add(self._manual, 1, wx.EXPAND) - self._simple.Bind(EVT_MULTIPLE_PROPERTIES_CHANGE, self._on_change) - self._manual.Bind(EVT_MULTIPLE_PROPERTIES_CHANGE, self._on_change) + self._vanilla = VanillaMultipleProperty(self) + self._sizer.Add(self._vanilla, 1, wx.EXPAND) + self._modded = ModdedMultipleProperty(self) + self._sizer.Add(self._modded, 1, wx.EXPAND) + self._vanilla.Bind(EVT_MULTIPLE_PROPERTIES_CHANGE, self._on_change) + self._modded.Bind(EVT_MULTIPLE_PROPERTIES_CHANGE, self._on_change) self.push(True) def _init_state(self, state: Dict[str, Any]): @@ -70,22 +70,22 @@ def _on_push(self) -> bool: self.namespace, self.force_blockstate ) if self._manual_enabled: - self._simple.Hide() - self._manual.Show() - self._manual.all_properties = self.all_properties - self._manual.selected_properties = self.selected_properties + self._vanilla.Hide() + self._modded.Show() + self._modded.all_properties = self.all_properties + self._modded.selected_properties = self.selected_properties else: - self._manual.Hide() - self._simple.Show() + self._modded.Hide() + self._vanilla.Show() spec = translator.get_specification( self.namespace, self.base_name, self._force_blockstate ) properties: Dict[str, List[str]] = spec.get("properties", {}) - self._simple.all_properties = { + self._vanilla.all_properties = { name: [amulet_nbt.from_snbt(p) for p in properties[name]] for name in properties } - self._simple.selected_properties = self.selected_properties + self._vanilla.selected_properties = self.selected_properties self.Thaw() return True diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/manual.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/modded.py similarity index 99% rename from amulet_map_editor/api/wx/ui/mc/block/properties/multiple/manual.py rename to amulet_map_editor/api/wx/ui/mc/block/properties/multiple/modded.py index 816c7dcd..37a15cb2 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/manual.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/modded.py @@ -9,7 +9,7 @@ from .base import BaseMultipleProperty -class ManualMultipleProperty(BaseMultipleProperty): +class ModdedMultipleProperty(BaseMultipleProperty): def __init__(self, parent: wx.Window): super().__init__(parent) header_sizer = wx.BoxSizer(wx.HORIZONTAL) diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/vanilla/__init__.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/vanilla/__init__.py new file mode 100644 index 00000000..80a87a6b --- /dev/null +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/vanilla/__init__.py @@ -0,0 +1 @@ +from .vanilla import VanillaMultipleProperty diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/automatic/popup.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/vanilla/popup.py similarity index 100% rename from amulet_map_editor/api/wx/ui/mc/block/properties/multiple/automatic/popup.py rename to amulet_map_editor/api/wx/ui/mc/block/properties/multiple/vanilla/popup.py diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/automatic/automatic.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/vanilla/vanilla.py similarity index 98% rename from amulet_map_editor/api/wx/ui/mc/block/properties/multiple/automatic/automatic.py rename to amulet_map_editor/api/wx/ui/mc/block/properties/multiple/vanilla/vanilla.py index 59840d9e..8e01f18b 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/automatic/automatic.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/vanilla/vanilla.py @@ -7,7 +7,7 @@ from .popup import PropertyValueComboPopup -class AutomaticMultipleProperty(BaseMultipleProperty): +class VanillaMultipleProperty(BaseMultipleProperty): def __init__(self, parent: wx.Window): super().__init__(parent) diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/single/automatic.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/automatic.py deleted file mode 100644 index 8f7f72c8..00000000 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/single/automatic.py +++ /dev/null @@ -1,127 +0,0 @@ -import wx -from typing import Dict, Tuple - - -from amulet.api.block import PropertyDataTypes, PropertyType, PropertyValueType -from .events import SinglePropertiesChangeEvent -from .base import BaseSingleProperty - -StatesType = Dict[str, Tuple[Tuple[PropertyValueType, ...], int]] - - -class AutomaticSingleProperty(BaseSingleProperty): - """ - A UI from which a user can choose one value for each property. - - The UI is automatically populated from the given specification. - """ - - _states: StatesType - - def __init__( - self, - parent: wx.Window, - ): - super().__init__(parent) - - self._property_sizer = wx.FlexGridSizer(2, 5, 5) - label = wx.StaticText(self, label="Property Name", style=wx.ALIGN_CENTER) - self._property_sizer.Add(label, 1, wx.ALIGN_CENTER) - label = wx.StaticText( - self, label="Property Value (SNBT)", style=wx.ALIGN_CENTER - ) - self._property_sizer.Add(label, 1, wx.ALIGN_CENTER) - - self._sizer.Add(self._property_sizer, 1, wx.ALL | wx.EXPAND, 5) - - self._states: StatesType = {} - self._properties: Dict[str, wx.Choice] = {} - - @property - def states(self) -> StatesType: - """ - A dictionary mapping the string property names to the valid states and the index of the default state. - """ - return self._states - - @states.setter - def states(self, states: StatesType): - self._states = {} - for key, (choices, default) in states.items(): - if isinstance(key, str): - valid_choices = tuple( - choice - for choice in choices - if isinstance(choice, PropertyDataTypes) - ) - if valid_choices: - self._states[key] = ( - valid_choices, - default if default < len(valid_choices) else 0, - ) - self.Freeze() - self._tear_down_properties() - - props = {} - for name, (choices, default) in self._states.items(): - self._create_property(name, choices) - props[name] = choices[default] - self.properties = props - self.Fit() - self.GetTopLevelParent().Layout() - self.Thaw() - - def _tear_down_properties(self): - self._properties.clear() - child: wx.SizerItem - for i, child in enumerate(self._property_sizer.GetChildren()): - if i >= self._property_sizer.GetCols(): - if child.IsWindow(): - child.GetWindow().Destroy() - elif child.IsSizer(): - child.GetSizer().Clear(True) - self._property_sizer.Remove(self._property_sizer.GetCols()) - elif child.IsSpacer(): - self._property_sizer.Remove(self._property_sizer.GetCols()) - else: - raise Exception - - def _post_change(self): - wx.PostEvent( - self, - SinglePropertiesChangeEvent(self.properties), - ) - - def _create_property(self, name: str, choices: Tuple[PropertyValueType]): - label = wx.StaticText(self, label=name) - self._property_sizer.Add(label, 0, wx.ALIGN_CENTER) - choice = wx.Choice(self, choices=[c.to_snbt() for c in choices]) - self._property_sizer.Add(choice, 0, wx.EXPAND) - choice.Bind( - wx.EVT_CHOICE, - lambda evt: self._post_change(), - ) - self._properties[name] = choice - - @property - def properties(self) -> PropertyType: - return { - name: self._states[name][0][choice.GetSelection()] - for name, choice in self._properties.items() - } - - @properties.setter - def properties(self, properties: PropertyType): - is_frozen = self.IsFrozen() - if not is_frozen: - self.Freeze() - for name, nbt in properties.items(): - if name in self._properties: - if isinstance(nbt, PropertyDataTypes) and nbt in self._states[name][0]: - self._properties[name].SetSelection( - self._states[name][0].index(nbt) - ) - else: - self._properties[name].SetSelection(self._states[name][1]) - if not is_frozen: - self.Thaw() diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/single/base.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/base.py index 0a2112d0..3bfe4aad 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/single/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/single/base.py @@ -1,9 +1,8 @@ import wx +from amulet_map_editor.api.wx.ui.mc.state import StateHolder, BlockState -from amulet.api.block import PropertyType - -class BaseSingleProperty(wx.Panel): +class BaseSingleProperty(wx.Panel, StateHolder): """ A UI from which a user can choose one value for each property. @@ -11,18 +10,10 @@ class BaseSingleProperty(wx.Panel): Subclasses must implement the logic. """ - def __init__(self, parent: wx.Window): - super().__init__(parent) + state: BlockState + + def __init__(self, parent: wx.Window, state: BlockState): + StateHolder.__init__(self, state) + wx.Panel.__init__(self, parent) self._sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(self._sizer) - - @property - def properties(self) -> PropertyType: - """ - A dictionary mapping string property names to the selected NBT values. - """ - raise NotImplementedError - - @properties.setter - def properties(self, properties: PropertyType): - raise NotImplementedError diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py index 8252e4d8..aae55a8b 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py @@ -1,5 +1,4 @@ import wx -from typing import Tuple, Dict, Any, List import PyMCTranslate import amulet_nbt @@ -7,102 +6,64 @@ from amulet.api.block import PropertyType from ..base import BasePropertySelect from .events import SinglePropertiesChangeEvent, EVT_SINGLE_PROPERTIES_CHANGE -from .automatic import AutomaticSingleProperty -from .manual import ManualSingleProperty -from amulet_map_editor.api.wx.ui.mc.api import NormalMCBlock +from .vanilla import VanillaSingleProperty +from .modded import ModdedSingleProperty +from amulet_map_editor.api.wx.ui.mc.state import State, BlockState -class SinglePropertySelect(BasePropertySelect, NormalMCBlock): +class SinglePropertySelect(BasePropertySelect): """ This is a UI which lets the user pick one value for each property for a given block. If the block is known it will be populated from the specification. If it is not known the user can populate it themselves. """ - _simple: AutomaticSingleProperty - _manual: ManualSingleProperty + state: BlockState + + _vanilla: VanillaSingleProperty + _modded: ModdedSingleProperty def __init__( self, parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager, - platform: str, - version_number: VersionNumberTuple, - force_blockstate: bool, + *, + state: BlockState = None, + platform: str = None, + version_number: VersionNumberTuple = None, + force_blockstate: bool = None, namespace: str = None, base_name: str = None, properties: PropertyType = None, - state: Dict[str, Any] = None, ): - state = state or {} - state.setdefault("properties", properties) - BasePropertySelect.__init__( - self, - parent, - translation_manager, - platform, - version_number, - force_blockstate, - namespace, - base_name, - state=state, - ) - - self._manual_enabled = False - self._simple = self._create_automatic() - self._sizer.Add(self._simple, 1, wx.EXPAND) - self._manual = self._create_manual() - self._sizer.Add(self._manual, 1, wx.EXPAND) - self._simple.Bind(EVT_SINGLE_PROPERTIES_CHANGE, self._on_change) - self._manual.Bind(EVT_SINGLE_PROPERTIES_CHANGE, self._on_change) - self.push(True) - - def _create_automatic(self) -> AutomaticSingleProperty: - return AutomaticSingleProperty(self) + if not isinstance(state, BlockState): + state = BlockState( + translation_manager, + platform=platform, + version_number=version_number, + force_blockstate=force_blockstate, + namespace=namespace, + base_name=base_name, + properties=properties, + ) + super().__init__(parent, translation_manager, state=state) - def _create_manual(self) -> ManualSingleProperty: - return ManualSingleProperty(self) + self._vanilla = self._create_automatic() + self._sizer.Add(self._vanilla, 1, wx.EXPAND) + self._modded = self._create_manual() + self._sizer.Add(self._modded, 1, wx.EXPAND) - def _init_state(self, state: Dict[str, Any]): - NormalMCBlock.__init__(self, **state) + def _create_automatic(self) -> VanillaSingleProperty: + return VanillaSingleProperty(self, self.state) - def _on_push(self) -> bool: - translator = self._translation_manager.get_version( - self._platform, self._version_number - ).block + def _create_manual(self) -> ModdedSingleProperty: + return ModdedSingleProperty(self, self.state) - self._manual_enabled = self.base_name not in translator.base_names( - self.namespace, self._force_blockstate - ) - if self._manual_enabled: - self._simple.Hide() - self._manual.Show() - self._manual.properties = self.properties - else: - self._manual.Hide() - self._simple.Show() - spec = translator.get_specification( - self.namespace, self.base_name, self._force_blockstate - ) - properties: Dict[str, List[str]] = spec.get("properties", {}) - defaults = spec.get("defaults", {}) - self._simple.states = { - name: ( - [amulet_nbt.from_snbt(p) for p in properties[name]], - properties[name].index(defaults[name]), - ) - for name in properties - } - self._simple.properties = self.properties - return True - - def _on_change(self, evt: SinglePropertiesChangeEvent): - if evt.properties != self.properties: - self._set_properties(evt.properties) - wx.PostEvent( - self, - SinglePropertiesChangeEvent(self.properties), - ) + def _on_state_change(self): + if self.state.is_changed(State.BaseName): + vanilla = self.state.base_name in self.state.valid_base_names + self._vanilla.Show(vanilla) + self._modded.Show(not vanilla) def demo(): @@ -112,33 +73,38 @@ def demo(): """ translation_manager = PyMCTranslate.new_translation_manager() for block in ( - ( - "minecraft", - "oak_fence", - { + { + "namespace": "minecraft", + "base_name": "oak_fence", + "properties": { "east": amulet_nbt.TAG_String("false"), "north": amulet_nbt.TAG_String("true"), "south": amulet_nbt.TAG_String("false"), "west": amulet_nbt.TAG_String("false"), }, - ), - ( - "modded", - "block", - { + }, + { + "namespace": "modded", + "base_name": "block", + "properties": { "test": amulet_nbt.TAG_String("hello"), }, - ), + }, ): dialog = wx.Dialog( None, - title=f"SinglePropertySelect with block {block[0]}:{block[1]}", + title=f"SinglePropertySelect with block {block['namespace']}:{block['base_name']}", style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT, ) sizer = wx.BoxSizer() dialog.SetSizer(sizer) obj = SinglePropertySelect( - dialog, translation_manager, "java", (1, 16, 0), False, *block + dialog, + translation_manager, + platform="java", + version_number=(1, 16, 0), + force_blockstate=False, + **block, ) sizer.Add( obj, diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/single/manual.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/modded.py similarity index 95% rename from amulet_map_editor/api/wx/ui/mc/block/properties/single/manual.py rename to amulet_map_editor/api/wx/ui/mc/block/properties/single/modded.py index 2e1dacde..eb32714f 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/single/manual.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/single/modded.py @@ -1,24 +1,24 @@ import wx from typing import Tuple, Dict -import PyMCTranslate import amulet_nbt from amulet_nbt import SNBTType from amulet.api.block import PropertyDataTypes, PropertyType from amulet_map_editor.api.image import ADD_ICON, SUBTRACT_ICON +from amulet_map_editor.api.wx.ui.mc.state import BlockState from .events import SinglePropertiesChangeEvent from .base import BaseSingleProperty -class ManualSingleProperty(BaseSingleProperty): +class ModdedSingleProperty(BaseSingleProperty): """ A UI from which a user can choose one value for each property. This is used when the block is not know so the user can define the properties themselves. """ - def __init__(self, parent: wx.Window): - super().__init__(parent) + def __init__(self, parent: wx.Window, state: BlockState): + super().__init__(parent, state) header_sizer = wx.BoxSizer(wx.HORIZONTAL) add_button = wx.BitmapButton( self, bitmap=ADD_ICON.bitmap(30, 30), size=(30, 30) diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/single/vanilla.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/vanilla.py new file mode 100644 index 00000000..5eb61433 --- /dev/null +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/single/vanilla.py @@ -0,0 +1,95 @@ +import wx +from typing import Dict, Tuple + +from amulet.api.block import PropertyType, PropertyValueType +from amulet_map_editor.api.wx.ui.mc.state import BlockState, State +from amulet_map_editor.api.wx.ui.simple import ChoiceRaw +from .base import BaseSingleProperty + + +class BaseVanillaSingleProperty(BaseSingleProperty): + """ + A UI from which a user can choose one value for each property. + + The UI is automatically populated from the given specification. + """ + + state: BlockState + + def __init__(self, parent: wx.Window, state: BlockState): + super().__init__(parent, state) + + self._property_sizer = wx.FlexGridSizer(2, 5, 5) + label = wx.StaticText(self, label="Property Name", style=wx.ALIGN_CENTER) + self._property_sizer.Add(label, 1, wx.ALIGN_CENTER) + label = wx.StaticText( + self, label="Property Value (SNBT)", style=wx.ALIGN_CENTER + ) + self._property_sizer.Add(label, 1, wx.ALIGN_CENTER) + + self._sizer.Add(self._property_sizer, 1, wx.ALL | wx.EXPAND, 5) + + self._properties: Dict[str, ChoiceRaw] = {} + + def _rebuild_properties(self): + self.Freeze() + self._tear_down_properties() + for name, choices in self.state.valid_properties.items(): + self._create_property(name, choices) + self.Fit() + self.Thaw() + + def _tear_down_properties(self): + self._properties.clear() + child: wx.SizerItem + for i, child in enumerate(self._property_sizer.GetChildren()): + if i >= self._property_sizer.GetCols(): + if child.IsWindow(): + child.GetWindow().Destroy() + elif child.IsSizer(): + child.GetSizer().Clear(True) + self._property_sizer.Remove(self._property_sizer.GetCols()) + elif child.IsSpacer(): + self._property_sizer.Remove(self._property_sizer.GetCols()) + else: + raise Exception + + def _update_properties(self): + for name, nbt in self.state.properties.items(): + property_ui = self._properties[name] + property_ui.SetSelection(property_ui.SetObject(nbt)) + + def _on_state_change(self): + if self.state.base_name in self.state.valid_base_names: + if self.state.is_changed(State.BaseName): + self._rebuild_properties() + elif self.state.is_changed(State.Properties): + self._update_properties() + + def _on_property_change(self, evt): + properties = self._get_ui_properties() + if properties != self.state.properties: + with self.state as state: + state.properties = properties + + def _create_property(self, name: str, choices: Tuple[PropertyValueType]): + label = wx.StaticText(self, label=name) + self._property_sizer.Add(label, 0, wx.ALIGN_CENTER) + choice = ChoiceRaw(self, choices=[c.to_snbt() for c in choices]) + self._property_sizer.Add(choice, 0, wx.EXPAND) + choice.Bind( + wx.EVT_CHOICE, + self._on_property_change, + ) + self._properties[name] = choice + + def _get_ui_properties(self) -> PropertyType: + return { + name: choice.GetCurrentObject() for name, choice in self._properties.items() + } + + +class VanillaSingleProperty(BaseVanillaSingleProperty): + def __init__(self, parent: wx.Window, state: BlockState): + super().__init__(parent, state) + self._rebuild_properties() From 24b6be576ed63fa83564d7aab29784515b8dd44b Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Tue, 21 Sep 2021 14:50:38 +0100 Subject: [PATCH 117/139] Fixed some issues in the ChoiceRaw and added optional default If the default is not found it will auto to the first option --- amulet_map_editor/api/wx/ui/simple.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/amulet_map_editor/api/wx/ui/simple.py b/amulet_map_editor/api/wx/ui/simple.py index c0ff82ce..280b0375 100644 --- a/amulet_map_editor/api/wx/ui/simple.py +++ b/amulet_map_editor/api/wx/ui/simple.py @@ -84,7 +84,7 @@ class ChoiceRaw(wx.Choice): """ def __init__( - self, parent: wx.Window, *, choices: ChoicesType = (), sort=True, reverse=False + self, parent: wx.Window, *, choices: ChoicesType = (), default: Any = None, sort=True, reverse=False ): super().__init__(parent) self._values: List[Any] = [] # the data hidden behind the string @@ -93,6 +93,7 @@ def __init__( self._reverse = reverse if choices: self.SetItems(choices) + self.SetObject(default) @property def keys(self) -> Tuple[str, ...]: @@ -152,17 +153,22 @@ def SetItems( else: item = (item, str(item)) items_.append(item) + items = items_ self._set_items(items, default) def SetObject(self, obj: Any): """Set the selected item from the data hidden behind the text.""" if obj in self._values: self.SetSelection(self._values.index(obj)) + elif self.GetCurrentSelection() == wx.NOT_FOUND and self._values: + self.SetSelection(0) def SetValue(self, key: Any): """Set the selected item based on the text in the choice.""" if key in self._keys: self.SetSelection(self._keys.index(key)) + elif self.GetCurrentSelection() == wx.NOT_FOUND and self._values: + self.SetSelection(0) def GetCurrentObject(self) -> Optional[Any]: """Return the value currently selected in the form before it was converted to a string""" From 78f4c248a9061d0ef018ff6b3397f70962462f50 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Tue, 21 Sep 2021 14:53:03 +0100 Subject: [PATCH 118/139] Fixed some issues setting properties in the state If the data was not known it would throw an error. Once that was fixed it just set a blank dictionary. Added is_supported property to check if the data has a specification file. --- amulet_map_editor/api/wx/ui/mc/state.py | 90 ++++++++++++++++--------- 1 file changed, 60 insertions(+), 30 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/state.py b/amulet_map_editor/api/wx/ui/mc/state.py index de0c6f5f..8c29f7d3 100644 --- a/amulet_map_editor/api/wx/ui/mc/state.py +++ b/amulet_map_editor/api/wx/ui/mc/state.py @@ -4,8 +4,9 @@ import amulet_nbt from PyMCTranslate import TranslationManager, Version +from PyMCTranslate.py3.api.version.translators.block import BlockSpecification from amulet.api.data_types import PlatformType, VersionNumberTuple, VersionNumberAny -from amulet.api.block import PropertyType, PropertyTypeMultiple, Block +from amulet.api.block import PropertyType, PropertyTypeMultiple, Block, PropertyDataTypes from amulet_map_editor import log @@ -326,6 +327,10 @@ def base_name(self) -> str: def base_name(self, base_name: str): self._set_state(State.BaseName, base_name) + @property + def is_supported(self): + return self.base_name in self.valid_base_names + class BiomeNamespaceState(BaseNamespaceState): @property @@ -433,9 +438,12 @@ def _fix_new_state(self): ) def _get_block_spec(self): - return self._get_version().block.get_specification( - self.namespace, self.base_name, self.force_blockstate - ) + if self.is_supported: + return self._get_version().block.get_specification( + self.namespace, self.base_name, self.force_blockstate + ) + else: + return BlockSpecification({}) @property def default_properties(self) -> PropertyType: @@ -446,19 +454,28 @@ def valid_properties(self) -> PropertyTypeMultiple: return self._get_block_spec().valid_properties def _sanitise_properties(self, properties: PropertyType = None) -> PropertyType: - valid_properties = self.valid_properties - default_properties = self.default_properties - if isinstance(properties, dict): - return { - name: properties[name] - if name in properties - and isinstance(properties[name], amulet_nbt.BaseValueType) - and properties[name] in valid_properties[name] - else default_properties[name] - for name in valid_properties - } + if self.is_supported: + valid_properties = self.valid_properties + default_properties = self.default_properties + if isinstance(properties, dict): + return { + name: properties[name] + if name in properties + and isinstance(properties[name], PropertyDataTypes) + and properties[name] in valid_properties[name] + else default_properties[name] + for name in valid_properties + } + else: + return default_properties else: - return default_properties + if isinstance(properties, dict): + return { + key: val for key, val in properties.items() + if isinstance(val, PropertyDataTypes) + } + else: + return {} @property def properties(self) -> PropertyType: @@ -471,21 +488,34 @@ def properties(self, properties: PropertyType): def _sanitise_properties_multiple( self, properties: PropertyTypeMultiple = None ) -> PropertyTypeMultiple: - valid_properties = self.valid_properties - if isinstance(properties, dict): - return { - name: tuple( - val - for val in properties[name] - if isinstance(val, amulet_nbt.BaseValueType) - and val in valid_properties[name] - ) - if name in properties and isinstance(properties[name], (list, tuple)) - else valid_properties[name] - for name in valid_properties - } + if self.is_supported: + valid_properties = self.valid_properties + if isinstance(properties, dict): + return { + name: tuple( + val + for val in properties[name] + if isinstance(val, PropertyDataTypes) + and val in valid_properties[name] + ) + if name in properties and isinstance(properties[name], (list, tuple)) + else valid_properties[name] + for name in valid_properties + } + else: + return valid_properties else: - return valid_properties + if isinstance(properties, dict): + return { + name: tuple( + val + for val in properties[name] + if isinstance(val, PropertyDataTypes) + ) + for name in properties + } + else: + return {} @property def properties_multiple(self) -> PropertyTypeMultiple: From ec01204be7dc8f6a367c0cdf31fee3590dac8eff Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Tue, 21 Sep 2021 14:53:54 +0100 Subject: [PATCH 119/139] Reworked the single property select UI to use the state system --- .../wx/ui/mc/block/properties/single/base.py | 33 ++++- .../wx/ui/mc/block/properties/single/main.py | 71 ++++++---- .../ui/mc/block/properties/single/modded.py | 130 +++++++++++------- .../ui/mc/block/properties/single/vanilla.py | 38 ++--- 4 files changed, 168 insertions(+), 104 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/single/base.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/base.py index 3bfe4aad..97f68e51 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/single/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/single/base.py @@ -1,5 +1,7 @@ import wx -from amulet_map_editor.api.wx.ui.mc.state import StateHolder, BlockState + +from amulet.api.block import PropertyType, PropertyValueType +from amulet_map_editor.api.wx.ui.mc.state import StateHolder, BlockState, State class BaseSingleProperty(wx.Panel, StateHolder): @@ -17,3 +19,32 @@ def __init__(self, parent: wx.Window, state: BlockState): wx.Panel.__init__(self, parent) self._sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(self._sizer) + + def _rebuild_properties(self): + raise NotImplementedError + + def _tear_down_properties(self): + raise NotImplementedError + + def _update_properties(self): + raise NotImplementedError + + def _get_ui_properties(self) -> PropertyType: + raise NotImplementedError + + def _if_do_state_change(self) -> bool: + raise NotImplementedError + + def _on_state_change(self): + if self._if_do_state_change(): + if self.state.is_changed(State.BaseName): + self._rebuild_properties() + elif self.state.is_changed(State.Properties): + if self.state.properties != self._get_ui_properties(): + self._update_properties() + + def _on_property_change(self): + properties = self._get_ui_properties() + if properties != self.state.properties: + with self.state as state: + state.properties = properties diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py index aae55a8b..f759aa59 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py @@ -9,6 +9,7 @@ from .vanilla import VanillaSingleProperty from .modded import ModdedSingleProperty from amulet_map_editor.api.wx.ui.mc.state import State, BlockState +from amulet_map_editor.api.wx.ui.events import EVT_CHILD_SIZE class SinglePropertySelect(BasePropertySelect): @@ -49,9 +50,10 @@ def __init__( super().__init__(parent, translation_manager, state=state) self._vanilla = self._create_automatic() - self._sizer.Add(self._vanilla, 1, wx.EXPAND) + self._sizer.Add(self._vanilla, 0, wx.EXPAND) self._modded = self._create_manual() - self._sizer.Add(self._modded, 1, wx.EXPAND) + self._sizer.Add(self._modded, 0, wx.EXPAND) + self._do_show() def _create_automatic(self) -> VanillaSingleProperty: return VanillaSingleProperty(self, self.state) @@ -59,11 +61,16 @@ def _create_automatic(self) -> VanillaSingleProperty: def _create_manual(self) -> ModdedSingleProperty: return ModdedSingleProperty(self, self.state) + def _do_show(self): + vanilla = self.state.is_supported + self._vanilla.Show(vanilla) + self._modded.Show(not vanilla) + def _on_state_change(self): if self.state.is_changed(State.BaseName): - vanilla = self.state.base_name in self.state.valid_base_names - self._vanilla.Show(vanilla) - self._modded.Show(not vanilla) + self._do_show() + if self.state.is_changed(State.Properties): + wx.PostEvent(self, SinglePropertiesChangeEvent(self.state.properties)) def demo(): @@ -72,25 +79,8 @@ def demo(): An app instance must be created first. """ translation_manager = PyMCTranslate.new_translation_manager() - for block in ( - { - "namespace": "minecraft", - "base_name": "oak_fence", - "properties": { - "east": amulet_nbt.TAG_String("false"), - "north": amulet_nbt.TAG_String("true"), - "south": amulet_nbt.TAG_String("false"), - "west": amulet_nbt.TAG_String("false"), - }, - }, - { - "namespace": "modded", - "base_name": "block", - "properties": { - "test": amulet_nbt.TAG_String("hello"), - }, - }, - ): + + def create_dialog(block): dialog = wx.Dialog( None, title=f"SinglePropertySelect with block {block['namespace']}:{block['base_name']}", @@ -118,12 +108,35 @@ def on_change(evt: SinglePropertiesChangeEvent): obj.Bind(EVT_SINGLE_PROPERTIES_CHANGE, on_change) - def get_on_close(dialog_): - def on_close(evt): - dialog_.Destroy() + def on_close(evt): + dialog.Destroy() - return on_close + def on_child_size(evt): + dialog.Layout() + evt.Skip() - dialog.Bind(wx.EVT_CLOSE, get_on_close(dialog)) + dialog.Bind(wx.EVT_CLOSE, on_close) + dialog.Bind(EVT_CHILD_SIZE, on_child_size) dialog.Show() dialog.Fit() + + for block_ in ( + { + "namespace": "minecraft", + "base_name": "oak_fence", + "properties": { + "east": amulet_nbt.TAG_String("false"), + "north": amulet_nbt.TAG_String("true"), + "south": amulet_nbt.TAG_String("false"), + "west": amulet_nbt.TAG_String("false"), + }, + }, + { + "namespace": "modded", + "base_name": "block", + "properties": { + "test": amulet_nbt.TAG_String("hello"), + }, + }, + ): + create_dialog(block_) diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/single/modded.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/modded.py index eb32714f..d9b3b8ce 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/single/modded.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/single/modded.py @@ -1,22 +1,28 @@ import wx -from typing import Tuple, Dict +from typing import Dict +from collections import namedtuple import amulet_nbt from amulet_nbt import SNBTType from amulet.api.block import PropertyDataTypes, PropertyType from amulet_map_editor.api.image import ADD_ICON, SUBTRACT_ICON from amulet_map_editor.api.wx.ui.mc.state import BlockState -from .events import SinglePropertiesChangeEvent +from amulet_map_editor.api.wx.ui.events import ChildSizeEvent from .base import BaseSingleProperty +PropertyStorage = namedtuple("PropertyStorage", ("sizer", "key_entry", "value_entry", "snbt_text")) -class ModdedSingleProperty(BaseSingleProperty): + +class BaseModdedSingleProperty(BaseSingleProperty): """ A UI from which a user can choose one value for each property. This is used when the block is not know so the user can define the properties themselves. """ + state: BlockState + _properties: Dict[int, PropertyStorage] + def __init__(self, parent: wx.Window, state: BlockState): super().__init__(parent, state) header_sizer = wx.BoxSizer(wx.HORIZONTAL) @@ -38,15 +44,28 @@ def __init__(self, parent: wx.Window, state: BlockState): self._property_sizer, 0, wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, 5 ) - add_button.Bind(wx.EVT_BUTTON, lambda evt: self._add_property()) + add_button.Bind(wx.EVT_BUTTON, self._on_add_property) + + self._property_id = 0 + self._properties = {} - self._property_index = 0 - self._properties: Dict[int, Tuple[wx.TextCtrl, wx.TextCtrl]] = {} + def _resize(self): + wx.PostEvent(self, ChildSizeEvent(0)) + + def _rebuild_properties(self): + self.Freeze() + self._tear_down_properties() + for name, prop in self.state.properties.items(): + self._create_property(name, prop.to_snbt()) + self.Fit() + self.Thaw() - def _post_property_change(self): - wx.PostEvent(self, SinglePropertiesChangeEvent(self.properties)) + def _tear_down_properties(self): + self._property_sizer.Clear(True) + self._properties.clear() + self._property_id = 0 - def _add_property(self, name: str = "", value: SNBTType = ""): + def _create_property(self, name: str = "", value: SNBTType = ""): """ Add a property to the UI with the given data. @@ -54,39 +73,60 @@ def _add_property(self, name: str = "", value: SNBTType = ""): :param value: The SNBT text of the value for that property. :return: """ - self.Freeze() sizer = wx.BoxSizer(wx.HORIZONTAL) - self._property_index += 1 + self._property_id += 1 subtract_button = wx.BitmapButton( self, bitmap=SUBTRACT_ICON.bitmap(30, 30), size=(30, 30) ) sizer.Add(subtract_button, 0, wx.ALIGN_CENTER_VERTICAL) - index = self._property_index + property_id = self._property_id subtract_button.Bind( - wx.EVT_BUTTON, lambda evt: self._on_remove_property(sizer, index) + wx.EVT_BUTTON, lambda evt: self._on_remove_property(property_id) ) name_entry = wx.TextCtrl(self, value=name, style=wx.TE_CENTER) sizer.Add(name_entry, 1, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) - name_entry.Bind(wx.EVT_TEXT, lambda evt: self._post_property_change()) + name_entry.Bind(wx.EVT_TEXT, self._on_key_change) value_entry = wx.TextCtrl(self, value=value, style=wx.TE_CENTER) sizer.Add(value_entry, 1, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) snbt_text = wx.StaticText(self, style=wx.ALIGN_CENTER) sizer.Add(snbt_text, 1, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) - self._change_value(value, snbt_text) - value_entry.Bind(wx.EVT_TEXT, lambda evt: self._on_value_change(evt, snbt_text)) + self._change_snbt_text(value, snbt_text) + value_entry.Bind(wx.EVT_TEXT, lambda evt: self._on_value_change(evt, property_id)) self._property_sizer.Add(sizer, 1, wx.TOP | wx.EXPAND, 5) - self._properties[self._property_index] = (name_entry, value_entry) - self.Fit() - self.TopLevelParent.Layout() - self.Thaw() + self._properties[self._property_id] = PropertyStorage(sizer, name_entry, value_entry, snbt_text) + self.Layout() + self._resize() + + def _update_properties(self): + if self.state.properties != self._get_ui_properties(): + self._rebuild_properties() + + def _get_ui_properties(self) -> PropertyType: + properties = {} + for prop in self._properties.values(): + try: + nbt = amulet_nbt.from_snbt(prop.value_entry.GetValue()) + except: + continue + name: str = prop.key_entry.GetValue() + if name and isinstance(nbt, PropertyDataTypes): + properties[name] = nbt + return properties + + def _if_do_state_change(self) -> bool: + return not self.state.is_supported + + def _on_key_change(self, evt): + wx.CallAfter(self._on_property_change) + evt.Skip() - def _on_value_change(self, evt, snbt_text: wx.StaticText): - self._change_value(evt.GetString(), snbt_text) - self._post_property_change() + def _on_value_change(self, evt, property_id: int): + self._change_snbt_text(evt.GetString(), self._properties[property_id].snbt_text) + wx.CallAfter(self._on_property_change) evt.Skip() - def _change_value(self, snbt: SNBTType, snbt_text: wx.StaticText): + def _change_snbt_text(self, snbt: SNBTType, snbt_text: wx.StaticText): try: nbt = amulet_nbt.from_snbt(snbt) except: @@ -101,32 +141,20 @@ def _change_value(self, snbt: SNBTType, snbt_text: wx.StaticText): snbt_text.SetBackgroundColour((255, 200, 200)) self.Layout() - def _on_remove_property(self, sizer: wx.Sizer, key: int): - self.Freeze() - self._property_sizer.Detach(sizer) - sizer.Clear(True) - del self._properties[key] - self.TopLevelParent.Layout() - self.Thaw() - self._post_property_change() + def _on_add_property(self, evt): + self._create_property() + self._on_property_change() - @property - def properties(self) -> PropertyType: - properties = {} - for name_ui, value_ui in self._properties.values(): - try: - nbt = amulet_nbt.from_snbt(value_ui.GetValue()) - except: - continue - name: str = name_ui.GetValue() - if name and isinstance(nbt, PropertyDataTypes): - properties[name] = nbt - return properties + def _on_remove_property(self, property_id: int): + property_state = self._properties.pop(property_id) + self._property_sizer.Detach(property_state.sizer) + property_state.sizer.Clear(True) + self.Layout() + self._resize() + self._on_property_change() - @properties.setter - def properties(self, properties: PropertyType): - self._property_sizer.Clear(True) - self._properties.clear() - self._property_index = 0 - for name, value in properties.items(): - self._add_property(name, value.to_snbt()) + +class ModdedSingleProperty(BaseModdedSingleProperty): + def __init__(self, parent: wx.Window, state: BlockState): + super().__init__(parent, state) + self._rebuild_properties() diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/single/vanilla.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/vanilla.py index 5eb61433..c9fb847c 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/single/vanilla.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/single/vanilla.py @@ -34,8 +34,10 @@ def __init__(self, parent: wx.Window, state: BlockState): def _rebuild_properties(self): self.Freeze() self._tear_down_properties() - for name, choices in self.state.valid_properties.items(): - self._create_property(name, choices) + valid_properties = self.state.valid_properties + current_properties = self.state.properties + for name in valid_properties: + self._create_property(name, valid_properties[name], current_properties[name]) self.Fit() self.Thaw() @@ -54,40 +56,30 @@ def _tear_down_properties(self): else: raise Exception - def _update_properties(self): - for name, nbt in self.state.properties.items(): - property_ui = self._properties[name] - property_ui.SetSelection(property_ui.SetObject(nbt)) - - def _on_state_change(self): - if self.state.base_name in self.state.valid_base_names: - if self.state.is_changed(State.BaseName): - self._rebuild_properties() - elif self.state.is_changed(State.Properties): - self._update_properties() - - def _on_property_change(self, evt): - properties = self._get_ui_properties() - if properties != self.state.properties: - with self.state as state: - state.properties = properties - - def _create_property(self, name: str, choices: Tuple[PropertyValueType]): + def _create_property(self, name: str, choices: Tuple[PropertyValueType, ...], default: PropertyValueType = None): label = wx.StaticText(self, label=name) self._property_sizer.Add(label, 0, wx.ALIGN_CENTER) - choice = ChoiceRaw(self, choices=[c.to_snbt() for c in choices]) + choice = ChoiceRaw(self, choices=choices, default=default) self._property_sizer.Add(choice, 0, wx.EXPAND) choice.Bind( wx.EVT_CHOICE, - self._on_property_change, + lambda evt: self._on_property_change(), ) self._properties[name] = choice + def _update_properties(self): + for name, nbt in self.state.properties.items(): + property_ui = self._properties[name] + property_ui.SetObject(nbt) + def _get_ui_properties(self) -> PropertyType: return { name: choice.GetCurrentObject() for name, choice in self._properties.items() } + def _if_do_state_change(self) -> bool: + return self.state.is_supported + class VanillaSingleProperty(BaseVanillaSingleProperty): def __init__(self, parent: wx.Window, state: BlockState): From e4383f724b398f459b9d6b87a54b0f5cf944358a Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Tue, 21 Sep 2021 14:54:13 +0100 Subject: [PATCH 120/139] Reformatted --- .../api/wx/ui/mc/block/properties/single/modded.py | 12 +++++++++--- .../api/wx/ui/mc/block/properties/single/vanilla.py | 11 +++++++++-- amulet_map_editor/api/wx/ui/mc/state.py | 13 ++++++++++--- amulet_map_editor/api/wx/ui/simple.py | 8 +++++++- 4 files changed, 35 insertions(+), 9 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/single/modded.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/modded.py index d9b3b8ce..118cc10d 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/single/modded.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/single/modded.py @@ -10,7 +10,9 @@ from amulet_map_editor.api.wx.ui.events import ChildSizeEvent from .base import BaseSingleProperty -PropertyStorage = namedtuple("PropertyStorage", ("sizer", "key_entry", "value_entry", "snbt_text")) +PropertyStorage = namedtuple( + "PropertyStorage", ("sizer", "key_entry", "value_entry", "snbt_text") +) class BaseModdedSingleProperty(BaseSingleProperty): @@ -91,10 +93,14 @@ def _create_property(self, name: str = "", value: SNBTType = ""): snbt_text = wx.StaticText(self, style=wx.ALIGN_CENTER) sizer.Add(snbt_text, 1, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) self._change_snbt_text(value, snbt_text) - value_entry.Bind(wx.EVT_TEXT, lambda evt: self._on_value_change(evt, property_id)) + value_entry.Bind( + wx.EVT_TEXT, lambda evt: self._on_value_change(evt, property_id) + ) self._property_sizer.Add(sizer, 1, wx.TOP | wx.EXPAND, 5) - self._properties[self._property_id] = PropertyStorage(sizer, name_entry, value_entry, snbt_text) + self._properties[self._property_id] = PropertyStorage( + sizer, name_entry, value_entry, snbt_text + ) self.Layout() self._resize() diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/single/vanilla.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/vanilla.py index c9fb847c..7b81379b 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/single/vanilla.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/single/vanilla.py @@ -37,7 +37,9 @@ def _rebuild_properties(self): valid_properties = self.state.valid_properties current_properties = self.state.properties for name in valid_properties: - self._create_property(name, valid_properties[name], current_properties[name]) + self._create_property( + name, valid_properties[name], current_properties[name] + ) self.Fit() self.Thaw() @@ -56,7 +58,12 @@ def _tear_down_properties(self): else: raise Exception - def _create_property(self, name: str, choices: Tuple[PropertyValueType, ...], default: PropertyValueType = None): + def _create_property( + self, + name: str, + choices: Tuple[PropertyValueType, ...], + default: PropertyValueType = None, + ): label = wx.StaticText(self, label=name) self._property_sizer.Add(label, 0, wx.ALIGN_CENTER) choice = ChoiceRaw(self, choices=choices, default=default) diff --git a/amulet_map_editor/api/wx/ui/mc/state.py b/amulet_map_editor/api/wx/ui/mc/state.py index 8c29f7d3..9c8e113c 100644 --- a/amulet_map_editor/api/wx/ui/mc/state.py +++ b/amulet_map_editor/api/wx/ui/mc/state.py @@ -6,7 +6,12 @@ from PyMCTranslate import TranslationManager, Version from PyMCTranslate.py3.api.version.translators.block import BlockSpecification from amulet.api.data_types import PlatformType, VersionNumberTuple, VersionNumberAny -from amulet.api.block import PropertyType, PropertyTypeMultiple, Block, PropertyDataTypes +from amulet.api.block import ( + PropertyType, + PropertyTypeMultiple, + Block, + PropertyDataTypes, +) from amulet_map_editor import log @@ -471,7 +476,8 @@ def _sanitise_properties(self, properties: PropertyType = None) -> PropertyType: else: if isinstance(properties, dict): return { - key: val for key, val in properties.items() + key: val + for key, val in properties.items() if isinstance(val, PropertyDataTypes) } else: @@ -498,7 +504,8 @@ def _sanitise_properties_multiple( if isinstance(val, PropertyDataTypes) and val in valid_properties[name] ) - if name in properties and isinstance(properties[name], (list, tuple)) + if name in properties + and isinstance(properties[name], (list, tuple)) else valid_properties[name] for name in valid_properties } diff --git a/amulet_map_editor/api/wx/ui/simple.py b/amulet_map_editor/api/wx/ui/simple.py index 280b0375..2184b008 100644 --- a/amulet_map_editor/api/wx/ui/simple.py +++ b/amulet_map_editor/api/wx/ui/simple.py @@ -84,7 +84,13 @@ class ChoiceRaw(wx.Choice): """ def __init__( - self, parent: wx.Window, *, choices: ChoicesType = (), default: Any = None, sort=True, reverse=False + self, + parent: wx.Window, + *, + choices: ChoicesType = (), + default: Any = None, + sort=True, + reverse=False ): super().__init__(parent) self._values: List[Any] = [] # the data hidden behind the string From 74bda05e42afb2398d27db8aa5323e28092113b7 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Sun, 3 Oct 2021 14:00:36 +0100 Subject: [PATCH 121/139] Rewritten the property select UI elements to use the new state system Simplified the multi select popup to handle only strings. Added a property to define valid states in the state manager. --- .../ui/mc/block/properties/multiple/base.py | 54 +++-- .../ui/mc/block/properties/multiple/main.py | 176 ++++++++--------- .../ui/mc/block/properties/multiple/modded.py | 158 +++------------ .../properties/multiple/vanilla/popup.py | 78 +++----- .../properties/multiple/vanilla/vanilla.py | 136 +++++++------ .../wx/ui/mc/block/properties/single/main.py | 10 +- amulet_map_editor/api/wx/ui/mc/state.py | 186 ++++++++++++------ 7 files changed, 372 insertions(+), 426 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/base.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/base.py index f0dcd63c..f94ab762 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/base.py @@ -1,34 +1,50 @@ import wx from amulet.api.block import PropertyTypeMultiple +from amulet_map_editor.api.wx.ui.mc.state import StateHolder, BlockState, State -class BaseMultipleProperty(wx.Panel): - def __init__(self, parent: wx.Window): - super().__init__(parent) +class BaseMultipleProperty(wx.Panel, StateHolder): + """ + A UI from which a user can choose zero or more values for each property. + + This is base class for both flavours of multiple property selection UIs. + Subclasses must implement the logic. + """ + + state: BlockState + + def __init__(self, parent: wx.Window, state: BlockState): + StateHolder.__init__(self, state) + wx.Panel.__init__(self, parent) self._sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(self._sizer) - @property - def all_properties(self) -> PropertyTypeMultiple: - """ - All the states defined for every property. - This contains all values regardless of if they are selected or not. - """ + def _rebuild_properties(self): raise NotImplementedError - @all_properties.setter - def all_properties(self, all_properties: PropertyTypeMultiple): + def _tear_down_properties(self): raise NotImplementedError - @property - def selected_properties(self) -> PropertyTypeMultiple: - """ - The values that are checked for each property. - This UI can have more than one property value checked (ticked). - """ + def _update_properties(self): raise NotImplementedError - @selected_properties.setter - def selected_properties(self, selected_properties: PropertyTypeMultiple): + def _get_ui_properties_multiple(self) -> PropertyTypeMultiple: raise NotImplementedError + + def _if_do_state_change(self) -> bool: + raise NotImplementedError + + def _on_state_change(self): + if self._if_do_state_change(): + if self.state.is_changed(State.BaseName): + self._rebuild_properties() + elif self.state.is_changed(State.PropertiesMultiple): + if self.state.properties_multiple != self._get_ui_properties_multiple(): + self._update_properties() + + def _on_property_change(self): + properties_multiple = self._get_ui_properties_multiple() + if properties_multiple != self.state.properties_multiple: + with self.state as state: + state.properties_multiple = properties_multiple diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py index 5be96cf2..2c793277 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py @@ -1,100 +1,85 @@ import wx -from typing import Dict, Any, List import PyMCTranslate import amulet_nbt from amulet.api.data_types import VersionNumberTuple from amulet.api.block import PropertyTypeMultiple -from amulet_map_editor.api.wx.ui.mc.api import WildcardMCBlock -from ..base import BasePropertySelect +from amulet_map_editor.api.wx.ui.mc.state import State, BlockState +from amulet_map_editor.api.wx.ui.events import EVT_CHILD_SIZE +from ..base import BasePropertySelect from .vanilla import VanillaMultipleProperty from .modded import ModdedMultipleProperty from .events import MultiplePropertiesChangeEvent, EVT_MULTIPLE_PROPERTIES_CHANGE -class MultiplePropertySelect(BasePropertySelect, WildcardMCBlock): +class MultiplePropertySelect(BasePropertySelect): """ This is a UI which lets the user pick zero or more values for each property. If the block is known it will be populated from the specification. If it is not known the user can populate it themselves. """ + state: BlockState + + _vanilla: VanillaMultipleProperty + _modded: ModdedMultipleProperty + def __init__( self, parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager, + *, + state: BlockState = None, platform: str, version_number: VersionNumberTuple, force_blockstate: bool, namespace: str, base_name: str, - selected_properties: PropertyTypeMultiple = None, all_properties: PropertyTypeMultiple = None, - state: Dict[str, Any] = None, + selected_properties: PropertyTypeMultiple = None, ): - state = state or {} - state.setdefault("selected_properties", selected_properties or {}) - state.setdefault("all_properties", all_properties) + if not isinstance(state, BlockState): + state = BlockState( + translation_manager, + platform=platform, + version_number=version_number, + force_blockstate=force_blockstate, + namespace=namespace, + base_name=base_name, + properties_multiple=selected_properties, + valid_properties=all_properties, + ) BasePropertySelect.__init__( self, parent, translation_manager, - platform=platform, - version_number=version_number, - force_blockstate=force_blockstate, - namespace=namespace, - base_name=base_name, state=state, ) - self._manual_enabled = False - self._vanilla = VanillaMultipleProperty(self) + self._vanilla = self._create_automatic() self._sizer.Add(self._vanilla, 1, wx.EXPAND) - self._modded = ModdedMultipleProperty(self) + self._modded = self._create_manual() self._sizer.Add(self._modded, 1, wx.EXPAND) - self._vanilla.Bind(EVT_MULTIPLE_PROPERTIES_CHANGE, self._on_change) - self._modded.Bind(EVT_MULTIPLE_PROPERTIES_CHANGE, self._on_change) - self.push(True) + self._do_show() - def _init_state(self, state: Dict[str, Any]): - WildcardMCBlock.__init__(self, **state) + def _create_automatic(self) -> VanillaMultipleProperty: + return VanillaMultipleProperty(self, self.state) - def _on_push(self) -> bool: - self.Freeze() - translator = self._translation_manager.get_version( - self._platform, self._version_number - ).block + def _create_manual(self) -> ModdedMultipleProperty: + return ModdedMultipleProperty(self, self.state) - self._manual_enabled = self.base_name not in translator.base_names( - self.namespace, self.force_blockstate - ) - if self._manual_enabled: - self._vanilla.Hide() - self._modded.Show() - self._modded.all_properties = self.all_properties - self._modded.selected_properties = self.selected_properties - else: - self._modded.Hide() - self._vanilla.Show() - spec = translator.get_specification( - self.namespace, self.base_name, self._force_blockstate - ) - properties: Dict[str, List[str]] = spec.get("properties", {}) - self._vanilla.all_properties = { - name: [amulet_nbt.from_snbt(p) for p in properties[name]] - for name in properties - } - self._vanilla.selected_properties = self.selected_properties - self.Thaw() - return True - - def _on_change(self, evt: MultiplePropertiesChangeEvent): - if evt.selected_properties != self.selected_properties: - self._set_selected_properties(evt.selected_properties) + def _do_show(self): + vanilla = self.state.is_supported + self._vanilla.Show(vanilla) + self._modded.Show(not vanilla) + + def _on_state_change(self): + if self.state.is_changed(State.BaseName): + self._do_show() + if self.state.is_changed(State.PropertiesMultiple): wx.PostEvent( - self, - MultiplePropertiesChangeEvent(self.selected_properties), + self, MultiplePropertiesChangeEvent(self.state.properties_multiple) ) @@ -104,43 +89,22 @@ def demo(): An app instance must be created first. """ translation_manager = PyMCTranslate.new_translation_manager() - for block in ( - ( - "minecraft", - "oak_fence", - { - "east": ( - amulet_nbt.TAG_String("true"), - amulet_nbt.TAG_String("false"), - ), - "north": ( - amulet_nbt.TAG_String("true"), - amulet_nbt.TAG_String("false"), - ), - "south": (amulet_nbt.TAG_String("false"),), - "west": (amulet_nbt.TAG_String("true"),), - }, - ), - ( - "modded", - "block", - { - "test": ( - amulet_nbt.TAG_String("hello"), - amulet_nbt.TAG_String("hello2"), - ), - }, - ), - ): + + def create_dialog(block): dialog = wx.Dialog( None, - title=f"MultiplePropertySelect with block {block[0]}:{block[1]}", + title=f"MultiplePropertySelect with block {block['namespace']}:{block['base_name']}", style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT, ) sizer = wx.BoxSizer() dialog.SetSizer(sizer) obj = MultiplePropertySelect( - dialog, translation_manager, "java", (1, 16, 0), False, *block + dialog, + translation_manager, + platform="java", + version_number=(1, 16, 0), + force_blockstate=False, + **block, ) sizer.Add( @@ -153,13 +117,45 @@ def demo(): def on_change(evt: MultiplePropertiesChangeEvent): print(evt.selected_properties) - def get_on_close(dialog_): - def on_close(evt): - dialog_.Destroy() + def on_close(evt): + dialog.Destroy() - return on_close + def on_child_size(evt): + dialog.Layout() + evt.Skip() obj.Bind(EVT_MULTIPLE_PROPERTIES_CHANGE, on_change) - dialog.Bind(wx.EVT_CLOSE, get_on_close(dialog)) + dialog.Bind(wx.EVT_CLOSE, on_close) + dialog.Bind(EVT_CHILD_SIZE, on_child_size) dialog.Show() dialog.Fit() + + for block_ in ( + { + "namespace": "minecraft", + "base_name": "oak_fence", + "selected_properties": { + "east": ( + amulet_nbt.TAG_String("true"), + amulet_nbt.TAG_String("false"), + ), + "north": ( + amulet_nbt.TAG_String("true"), + amulet_nbt.TAG_String("false"), + ), + "south": (amulet_nbt.TAG_String("false"),), + "west": (amulet_nbt.TAG_String("true"),), + }, + }, + { + "namespace": "modded", + "base_name": "block", + "selected_properties": { + "test": ( + amulet_nbt.TAG_String("hello"), + amulet_nbt.TAG_String("hello2"), + ), + }, + }, + ): + create_dialog(block_) diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/modded.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/modded.py index 37a15cb2..ff1dd7e3 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/modded.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/modded.py @@ -1,143 +1,35 @@ import wx -from typing import Tuple, Dict +from collections import namedtuple -import amulet_nbt -from amulet_nbt import SNBTType -from amulet.api.block import PropertyDataTypes, PropertyType, PropertyTypeMultiple -from amulet_map_editor.api.image import ADD_ICON, SUBTRACT_ICON -from .events import MultiplePropertiesChangeEvent -from .base import BaseMultipleProperty +from amulet_map_editor.api.wx.ui.mc.state import BlockState +from ..single.modded import BaseModdedSingleProperty +PropertyStorage = namedtuple( + "PropertyStorage", ("sizer", "key_entry", "value_entry", "snbt_text") +) -class ModdedMultipleProperty(BaseMultipleProperty): - def __init__(self, parent: wx.Window): - super().__init__(parent) - header_sizer = wx.BoxSizer(wx.HORIZONTAL) - add_button = wx.BitmapButton( - self, bitmap=ADD_ICON.bitmap(30, 30), size=(30, 30) - ) - header_sizer.Add(add_button) - self._sizer.Add(header_sizer, 0, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, 5) - label = wx.StaticText(self, label="Property Name", style=wx.ALIGN_CENTER) - header_sizer.Add(label, 1, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5) - label = wx.StaticText( - self, label="Property Value (SNBT)", style=wx.ALIGN_CENTER - ) - header_sizer.Add(label, 1, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5) - header_sizer.AddStretchSpacer(1) - self._property_sizer = wx.BoxSizer(wx.VERTICAL) - self._sizer.Add( - self._property_sizer, 0, wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, 5 - ) +class BaseModdedMultipleProperty(BaseModdedSingleProperty): + """ + A UI from which a user can choose zero or more values for each property. - add_button.Bind(wx.EVT_BUTTON, lambda evt: self._add_property()) + This is used when the block is not know so the user can define the properties themselves. + """ - self._property_index = 0 - self._properties: Dict[int, Tuple[wx.TextCtrl, wx.TextCtrl]] = {} + # TODO: This currently only allows on value to be defined. + state: BlockState - def _post_property_change(self): - wx.PostEvent(self, MultiplePropertiesChangeEvent(self.selected_properties)) + def _on_property_change(self): + properties = self._get_ui_properties() + if properties != self.state.properties: + with self.state as state: + state.properties = properties + state.properties_multiple = { + key: (val,) for key, val in properties.items() + } - def _add_property(self, name: str = "", value: SNBTType = ""): - self.Freeze() - sizer = wx.BoxSizer(wx.HORIZONTAL) - self._property_index += 1 - subtract_button = wx.BitmapButton( - self, bitmap=SUBTRACT_ICON.bitmap(30, 30), size=(30, 30) - ) - sizer.Add(subtract_button, 0, wx.ALIGN_CENTER_VERTICAL) - index = self._property_index - subtract_button.Bind( - wx.EVT_BUTTON, lambda evt: self._on_remove_property(sizer, index) - ) - name_entry = wx.TextCtrl(self, value=name, style=wx.TE_CENTER) - sizer.Add(name_entry, 1, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) - name_entry.Bind(wx.EVT_TEXT, lambda evt: self._post_property_change()) - value_entry = wx.TextCtrl(self, value=value, style=wx.TE_CENTER) - sizer.Add(value_entry, 1, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) - snbt_text = wx.StaticText(self, style=wx.ALIGN_CENTER) - sizer.Add(snbt_text, 1, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) - self._change_value(value, snbt_text) - value_entry.Bind(wx.EVT_TEXT, lambda evt: self._on_value_change(evt, snbt_text)) - self._property_sizer.Add(sizer, 1, wx.TOP | wx.EXPAND, 5) - self._properties[self._property_index] = (name_entry, value_entry) - self.Fit() - self.TopLevelParent.Layout() - self.Thaw() - - def _on_value_change(self, evt, snbt_text: wx.StaticText): - self._change_value(evt.GetString(), snbt_text) - self._post_property_change() - evt.Skip() - - def _change_value(self, snbt: SNBTType, snbt_text: wx.StaticText): - try: - nbt = amulet_nbt.from_snbt(snbt) - except: - snbt_text.SetLabel("Invalid SNBT") - snbt_text.SetBackgroundColour((255, 200, 200)) - else: - if isinstance(nbt, PropertyDataTypes): - snbt_text.SetLabel(nbt.to_snbt()) - snbt_text.SetBackgroundColour(wx.NullColour) - else: - snbt_text.SetLabel(f"{nbt.__class__.__name__} not valid") - snbt_text.SetBackgroundColour((255, 200, 200)) - self.Layout() - - def _on_remove_property(self, sizer: wx.Sizer, key: int): - self.Freeze() - self._property_sizer.Detach(sizer) - sizer.Clear(True) - del self._properties[key] - self.TopLevelParent.Layout() - self.Thaw() - self._post_property_change() - - @property - def properties(self) -> PropertyType: - properties = {} - for name_ui, value_ui in self._properties.values(): - try: - nbt = amulet_nbt.from_snbt(value_ui.GetValue()) - except: - continue - name: str = name_ui.GetValue() - if name and isinstance(nbt, PropertyDataTypes): - properties[name] = nbt - return properties - - @properties.setter - def properties(self, properties: PropertyType): - self._property_sizer.Clear(True) - self._properties.clear() - self._property_index = 0 - for name, value in properties.items(): - self._add_property(name, value.to_snbt()) - - @property - def all_properties(self) -> PropertyTypeMultiple: - return {prop: (val,) for prop, val in self.properties.items()} - - @all_properties.setter - def all_properties(self, all_properties: PropertyTypeMultiple): - props = {} - for prop, val in all_properties.items(): - if val: - props[prop] = val[0] - self.properties = props - - # TODO: implement this properly - @property - def selected_properties(self) -> PropertyTypeMultiple: - return {prop: (val,) for prop, val in self.properties.items()} - - @selected_properties.setter - def selected_properties(self, properties: PropertyTypeMultiple): - props = {} - for prop, val in properties.items(): - if val: - props[prop] = val[0] - self.properties = props +class ModdedMultipleProperty(BaseModdedMultipleProperty): + def __init__(self, parent: wx.Window, state: BlockState): + super().__init__(parent, state) + self._rebuild_properties() diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/vanilla/popup.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/vanilla/popup.py index dcb57815..4d4248f0 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/vanilla/popup.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/vanilla/popup.py @@ -1,12 +1,11 @@ import wx -from typing import Tuple, Iterable, Optional - -from amulet.api.block import PropertyValueType -import amulet_nbt +from typing import Tuple, Iterable, Optional, Sequence class PropertyValueCheckList(wx.Panel): - def __init__(self, parent: wx.Window, values: Iterable[str]): + def __init__( + self, parent: wx.Window, values: Iterable[str], selected: Iterable[bool] + ): super().__init__(parent) sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(sizer) @@ -14,43 +13,17 @@ def __init__(self, parent: wx.Window, values: Iterable[str]): self._toggle_checkbox.Set3StateValue(wx.CHK_CHECKED) sizer.Add(self._toggle_checkbox, 0, wx.ALL, 3) self._check_list_box = wx.CheckListBox(self, choices=values) - self._check_list_box.SetCheckedStrings(values) + self._check_list_box.SetCheckedStrings( + [value for value, select in zip(values, selected) if select] + ) sizer.Add(self._check_list_box, 0) self._toggle_checkbox.Bind(wx.EVT_CHECKBOX, self._on_toggle) self._check_list_box.Bind(wx.EVT_LEFT_DOWN, self._on_left_down) @property - def checked_snbt(self) -> Tuple[str, ...]: - return self._check_list_box.GetCheckedStrings() - - @checked_snbt.setter - def checked_snbt(self, checked_snbt: Iterable[str]): - self._check_list_box.SetCheckedStrings(checked_snbt) - - @property - def checked_nbt(self) -> Tuple[PropertyValueType, ...]: - nbt = [] - for entry in self._check_list_box.GetCheckedStrings(): - try: - nbt.append(amulet_nbt.from_snbt(entry)) - except: - pass - return tuple(nbt) - - @checked_nbt.setter - def checked_nbt(self, checked_nbt: Iterable[PropertyValueType]): - self._check_list_box.SetCheckedStrings([v.to_snbt() for v in checked_nbt]) - - @property - def all_nbt(self) -> Tuple[PropertyValueType, ...]: - nbt = [] - for entry in self._check_list_box.GetStrings(): - try: - nbt.append(amulet_nbt.from_snbt(entry)) - except: - pass - return tuple(nbt) + def check_list_box(self) -> wx.CheckListBox: + return self._check_list_box def _on_toggle(self, evt): if self._toggle_checkbox.GetValue(): @@ -60,7 +33,7 @@ def _on_toggle(self, evt): else: self._check_list_box.SetCheckedItems([]) - def _update_check(self): + def update_check(self): items = self._check_list_box.GetCheckedItems() if len(items) == self._check_list_box.GetCount(): self._toggle_checkbox.Set3StateValue(wx.CHK_CHECKED) @@ -76,36 +49,41 @@ def _on_left_down(self, evt): self._check_list_box.Check( item, check=not self._check_list_box.IsChecked(item) ) - self._update_check() + self.update_check() class PropertyValueComboPopup(wx.ComboPopup): - def __init__(self, values: Iterable[str]): + def __init__(self, values: Iterable[str], selected: Iterable[bool]): super().__init__() self._check_list: Optional[PropertyValueCheckList] = None - self._values = values + self._state = values, selected @property - def all_nbt(self) -> Tuple[PropertyValueType, ...]: - return self._check_list.all_nbt + def _check_list_box(self) -> wx.CheckListBox: + return self._check_list.check_list_box - @property - def checked_nbt(self) -> Tuple[PropertyValueType, ...]: - return self._check_list.checked_nbt + def GetCheckedStrings(self) -> Tuple[str]: + return self._check_list_box.GetCheckedStrings() + + def SetCheckedStrings(self, strings: Sequence[str]): + self._check_list_box.SetCheckedStrings(strings) + self._check_list.update_check() + + def GetItems(self) -> Tuple[str]: + return self._check_list_box.GetItems() - @checked_nbt.setter - def checked_nbt(self, checked_nbt: Iterable[PropertyValueType]): - self._check_list.checked_nbt = checked_nbt + def SetItems(self, strings: Sequence[str]): + self._check_list_box.SetItems(strings) def Create(self, parent): - self._check_list = PropertyValueCheckList(parent, self._values) + self._check_list = PropertyValueCheckList(parent, *self._state) return True def GetControl(self): return self._check_list def GetStringValue(self): - return "|".join(self._check_list.checked_snbt) + return "|".join(self.GetCheckedStrings()) def GetAdjustedSize(self, minWidth, prefHeight, maxHeight): self.GetControl().Fit() diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/vanilla/vanilla.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/vanilla/vanilla.py index 8e01f18b..29faa487 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/vanilla/vanilla.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/vanilla/vanilla.py @@ -1,15 +1,24 @@ import wx from typing import Dict, Tuple -from amulet.api.block import PropertyTypeMultiple, PropertyDataTypes -from ..events import MultiplePropertiesChangeEvent +import amulet_nbt +from amulet.api.block import PropertyTypeMultiple, PropertyValueType +from amulet_map_editor.api.wx.ui.mc.state import BlockState from ..base import BaseMultipleProperty from .popup import PropertyValueComboPopup -class VanillaMultipleProperty(BaseMultipleProperty): - def __init__(self, parent: wx.Window): - super().__init__(parent) +class BaseVanillaMultipleProperty(BaseMultipleProperty): + """ + A UI from which a user can choose zero or more values for each property. + + The UI is automatically populated from the given specification. + """ + + state: BlockState + + def __init__(self, parent: wx.Window, state: BlockState): + super().__init__(parent, state) header_sizer = wx.BoxSizer(wx.HORIZONTAL) self._sizer.Add(header_sizer, 0, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, 5) @@ -25,75 +34,64 @@ def __init__(self, parent: wx.Window): self._states: PropertyTypeMultiple = {} self._properties: Dict[str, Tuple[wx.ComboCtrl, PropertyValueComboPopup]] = {} - @property - def all_properties(self) -> PropertyTypeMultiple: - return self._states - - @all_properties.setter - def all_properties(self, all_properties: PropertyTypeMultiple): - self._states = {} - for key, choices in all_properties.items(): - if isinstance(key, str): - valid_choices = tuple( - choice - for choice in choices - if isinstance(choice, PropertyDataTypes) - ) - if valid_choices: - self._states[key] = valid_choices + def _rebuild_properties(self): self.Freeze() + self._tear_down_properties() + valid_properties = self.state.valid_properties + current_properties = self.state.properties_multiple + for name in valid_properties: + self._create_property( + name, valid_properties[name], current_properties[name] + ) + self.Fit() + self.Thaw() + + def _tear_down_properties(self): self._properties.clear() self._property_sizer.Clear(True) - props = {} - for name, choices in self._states.items(): - label = wx.StaticText(self, label=name) - self._property_sizer.Add(label, 0, wx.ALIGN_CENTER) - - def create_choice(): - choice = wx.ComboCtrl(self, style=wx.CB_READONLY) - popup = PropertyValueComboPopup([c.to_snbt() for c in choices]) - choice.SetPopupControl(popup) - choice.SetValue(popup.GetStringValue()) - - self._property_sizer.Add(choice, 0, wx.EXPAND) - - def on_close(evt): - choice.SetValue(popup.GetStringValue()) - wx.PostEvent( - self, - MultiplePropertiesChangeEvent(self.selected_properties), - ) - - choice.Bind( - wx.EVT_COMBOBOX_CLOSEUP, - on_close, - ) - self._properties[name] = (choice, popup) - - create_choice() - props[name] = choices - self.selected_properties = props - self.Fit() - self.GetTopLevelParent().Layout() - self.Thaw() + def _create_property( + self, + name: str, + choices: Tuple[PropertyValueType, ...], + selected: Tuple[PropertyValueType, ...] = None, + ): + label = wx.StaticText(self, label=name) + self._property_sizer.Add(label, 0, wx.ALIGN_CENTER) - @property - def selected_properties(self) -> PropertyTypeMultiple: - """ - The values that are checked for each property. - This UI can have more than one property value checked (ticked). - """ + choice = wx.ComboCtrl(self, style=wx.CB_READONLY) + if selected is None: + selected = [bool] * len(choices) + else: + selected = [c in choices for c in selected] + popup = PropertyValueComboPopup([c.to_snbt() for c in choices], selected) + choice.SetPopupControl(popup) + choice.SetValue(popup.GetStringValue()) + self._property_sizer.Add(choice, 0, wx.EXPAND) + + def on_closeup(evt): + choice.SetValue(popup.GetStringValue()) + self._on_property_change() + + choice.Bind(wx.EVT_COMBOBOX_CLOSEUP, on_closeup) + self._properties[name] = (choice, popup) + + def _update_properties(self): + for name, choices in self.state.properties_multiple.items(): + property_ui = self._properties[name][1] + property_ui.SetCheckedStrings([c.to_snbt() for c in choices]) + + def _get_ui_properties_multiple(self) -> PropertyTypeMultiple: return { - prop: popup.checked_nbt for prop, (_, popup) in self._properties.items() + prop: tuple(amulet_nbt.from_snbt(v) for v in popup.GetCheckedStrings()) + for prop, (_, popup) in self._properties.items() } - @selected_properties.setter - def selected_properties(self, properties: PropertyTypeMultiple): - self.Freeze() - for name, nbt_tuple in properties.items(): - if name in self._properties: - choice, popup = self._properties[name] - popup.checked_nbt = nbt_tuple - choice.SetValue(popup.GetStringValue()) - self.Thaw() + def _if_do_state_change(self) -> bool: + return self.state.is_supported + + +class VanillaMultipleProperty(BaseVanillaMultipleProperty): + def __init__(self, parent: wx.Window, state: BlockState): + super().__init__(parent, state) + self._rebuild_properties() diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py index f759aa59..082853fa 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py @@ -4,12 +4,13 @@ import amulet_nbt from amulet.api.data_types import VersionNumberTuple from amulet.api.block import PropertyType +from amulet_map_editor.api.wx.ui.mc.state import State, BlockState +from amulet_map_editor.api.wx.ui.events import EVT_CHILD_SIZE + from ..base import BasePropertySelect -from .events import SinglePropertiesChangeEvent, EVT_SINGLE_PROPERTIES_CHANGE from .vanilla import VanillaSingleProperty from .modded import ModdedSingleProperty -from amulet_map_editor.api.wx.ui.mc.state import State, BlockState -from amulet_map_editor.api.wx.ui.events import EVT_CHILD_SIZE +from .events import SinglePropertiesChangeEvent, EVT_SINGLE_PROPERTIES_CHANGE class SinglePropertySelect(BasePropertySelect): @@ -106,8 +107,6 @@ def create_dialog(block): def on_change(evt: SinglePropertiesChangeEvent): print(evt.properties) - obj.Bind(EVT_SINGLE_PROPERTIES_CHANGE, on_change) - def on_close(evt): dialog.Destroy() @@ -115,6 +114,7 @@ def on_child_size(evt): dialog.Layout() evt.Skip() + obj.Bind(EVT_SINGLE_PROPERTIES_CHANGE, on_change) dialog.Bind(wx.EVT_CLOSE, on_close) dialog.Bind(EVT_CHILD_SIZE, on_child_size) dialog.Show() diff --git a/amulet_map_editor/api/wx/ui/mc/state.py b/amulet_map_editor/api/wx/ui/mc/state.py index 9c8e113c..e6501399 100644 --- a/amulet_map_editor/api/wx/ui/mc/state.py +++ b/amulet_map_editor/api/wx/ui/mc/state.py @@ -1,6 +1,6 @@ from abc import ABC, abstractmethod from enum import Enum -from typing import Callable, List, Dict, Any, Union +from typing import Callable, List, Dict, Any, Union, Tuple, Optional import amulet_nbt from PyMCTranslate import TranslationManager, Version @@ -26,6 +26,7 @@ class State(Enum): BaseName = "base_name" Properties = "properties" PropertiesMultiple = "properties_multiple" + ValidProperties = "valid_properties" def __str__(self): return self.value @@ -372,6 +373,11 @@ def valid_base_names(self) -> List[str]: class BlockState(BlockResourceIDState): + """ + A class to store the state of a block id and properties. + Supports a single value per property and a sequence of values. + """ + def __init__( self, translation_manager: TranslationManager, @@ -383,6 +389,7 @@ def __init__( base_name: str = None, properties: PropertyType = None, properties_multiple: PropertyTypeMultiple = None, + valid_properties: PropertyTypeMultiple = None, ): super().__init__( translation_manager, @@ -392,9 +399,14 @@ def __init__( namespace=namespace, base_name=base_name, ) - self._state[State.Properties] = self._sanitise_properties(properties) - self._state[State.PropertiesMultiple] = self._sanitise_properties_multiple( - properties_multiple + ( + self._state[State.Properties], + self._state[State.PropertiesMultiple], + self._state[State.ValidProperties], + ) = self._sync_properties( + self._sanitise_properties(properties), + self._sanitise_properties_multiple(properties_multiple), + self._sanitise_properties_multiple(valid_properties), ) def _fix_version_change(self): @@ -432,15 +444,24 @@ def _fix_version_change(self): def _fix_new_state(self): super()._fix_new_state() - if self.is_changed(State.Properties) or self.is_changed( - State.PropertiesMultiple - ): - self._changed_state[ - State.PropertiesMultiple - ] = self._sanitise_properties_multiple(self.properties_multiple) + validate = False + if self.is_changed(State.Properties): self._changed_state[State.Properties] = self._sanitise_properties( self.properties ) + validate = True + if self.is_changed(State.PropertiesMultiple): + self._changed_state[ + State.PropertiesMultiple + ] = self._sanitise_properties_multiple(self.properties_multiple) + validate = True + if self.is_changed(State.ValidProperties) and self.is_supported: + self._changed_state[ + State.ValidProperties + ] = self._sanitise_properties_multiple(self.valid_properties) + + if validate: + self._sync_properties() def _get_block_spec(self): if self.is_supported: @@ -452,39 +473,101 @@ def _get_block_spec(self): @property def default_properties(self) -> PropertyType: - return self._get_block_spec().default_properties + """The default properties for this block.""" + if self.is_supported: + return self._get_block_spec().default_properties + else: + return @property def valid_properties(self) -> PropertyTypeMultiple: - return self._get_block_spec().valid_properties + """The properties that are valid for this block.""" + if self.is_supported: + return self._get_block_spec().valid_properties + else: + return self._get_state(State.ValidProperties) + + @valid_properties.setter + def valid_properties(self, valid_properties: PropertyTypeMultiple): + self._set_state(State.ValidProperties, valid_properties) def _sanitise_properties(self, properties: PropertyType = None) -> PropertyType: - if self.is_supported: + if isinstance(properties, dict): + return { + key: val + for key, val in properties.items() + if isinstance(val, PropertyDataTypes) + } + else: + return {} + + def _sync_properties( + self, + properties: PropertyType = None, + properties_multiple: PropertyTypeMultiple = None, + valid_properties: PropertyTypeMultiple = None, + ) -> Tuple[PropertyType, PropertyTypeMultiple, Optional[PropertyTypeMultiple]]: + """Make sure that all the properties states are in sync.""" + if properties is None: + properties = self.properties + if properties_multiple is None: + properties_multiple = self.properties_multiple + if valid_properties is None or self.is_supported: valid_properties = self.valid_properties + if not self.is_supported: + # Nothing is known. Populate/extend valid from the filled out. + valid_properties = { + prop: tuple( + set( + valid_properties.get(prop, ()) + + properties_multiple.get(prop, ()) + + ((properties[prop],) if prop in properties else ()) + ) + ) + for prop in set( + list(valid_properties) + + list(properties) + + list(properties_multiple) + ) + } + # Make sure there are valid properties. + valid_properties = { + key: val for key, val in valid_properties.items() if val + } + default_properties = {key: val[0] for key, val in valid_properties.items()} + else: default_properties = self.default_properties - if isinstance(properties, dict): - return { - name: properties[name] - if name in properties - and isinstance(properties[name], PropertyDataTypes) - and properties[name] in valid_properties[name] - else default_properties[name] - for name in valid_properties - } - else: - return default_properties + + # Make sure all the properties are defined and are a subset of valid properties + properties = { + name: properties[name] + if name in properties + and isinstance(properties[name], PropertyDataTypes) + and properties[name] in valid_properties[name] + else default_properties[name] + for name in valid_properties + } + + # Make sure all the multiple properties are defined and are a subset of valid properties + properties_multiple = { + name: tuple( + val + for val in properties_multiple[name] + if isinstance(val, PropertyDataTypes) and val in valid_properties[name] + ) + if name in properties_multiple + and isinstance(properties_multiple[name], (list, tuple)) + else valid_properties[name] + for name in valid_properties + } + if self.is_supported: + return properties, properties_multiple, None else: - if isinstance(properties, dict): - return { - key: val - for key, val in properties.items() - if isinstance(val, PropertyDataTypes) - } - else: - return {} + return properties, properties_multiple, valid_properties @property def properties(self) -> PropertyType: + """The value for each property that is currently active for this block.""" return self._get_state(State.Properties) @properties.setter @@ -494,38 +577,21 @@ def properties(self, properties: PropertyType): def _sanitise_properties_multiple( self, properties: PropertyTypeMultiple = None ) -> PropertyTypeMultiple: - if self.is_supported: - valid_properties = self.valid_properties - if isinstance(properties, dict): - return { - name: tuple( - val - for val in properties[name] - if isinstance(val, PropertyDataTypes) - and val in valid_properties[name] - ) - if name in properties - and isinstance(properties[name], (list, tuple)) - else valid_properties[name] - for name in valid_properties - } - else: - return valid_properties + if isinstance(properties, dict): + return { + name: tuple( + val + for val in properties[name] + if isinstance(val, PropertyDataTypes) + ) + for name in properties + } else: - if isinstance(properties, dict): - return { - name: tuple( - val - for val in properties[name] - if isinstance(val, PropertyDataTypes) - ) - for name in properties - } - else: - return {} + return {} @property def properties_multiple(self) -> PropertyTypeMultiple: + """The values for each property that are currently active for this block.""" return self._get_state(State.PropertiesMultiple) @properties_multiple.setter From b9fad64338d949c281b54d0384449580d5458583 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Tue, 5 Oct 2021 13:12:15 +0100 Subject: [PATCH 122/139] Modified default sort behaviour of ChoiceRaw --- amulet_map_editor/api/wx/ui/mc/version/version_select.py | 2 +- amulet_map_editor/api/wx/ui/simple.py | 2 +- .../plugins/operations/stock_plugins/operations/set_biome.py | 2 +- .../edit/plugins/tools/fill_replace/fill_replace_widget.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/version/version_select.py b/amulet_map_editor/api/wx/ui/mc/version/version_select.py index 8191d71b..4cb6da36 100644 --- a/amulet_map_editor/api/wx/ui/mc/version/version_select.py +++ b/amulet_map_editor/api/wx/ui/mc/version/version_select.py @@ -72,7 +72,7 @@ def __init__( self._allow_blockstate = allow_blockstate self._version_choice: Optional[ChoiceRaw] = self._add_ui_element( - "Version:", ChoiceRaw, reverse=True + "Version:", ChoiceRaw, reverse=True, sort=True ) self._update_version_number() self._version_choice.Bind( diff --git a/amulet_map_editor/api/wx/ui/simple.py b/amulet_map_editor/api/wx/ui/simple.py index 2184b008..1c3d2ae5 100644 --- a/amulet_map_editor/api/wx/ui/simple.py +++ b/amulet_map_editor/api/wx/ui/simple.py @@ -89,7 +89,7 @@ def __init__( *, choices: ChoicesType = (), default: Any = None, - sort=True, + sort=False, reverse=False ): super().__init__(parent) diff --git a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/set_biome.py b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/set_biome.py index b26c4494..0f62e1ea 100644 --- a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/set_biome.py +++ b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/set_biome.py @@ -46,7 +46,7 @@ def __init__( self.Freeze() options = self._load_options({}) - self._mode = ChoiceRaw(self, sort=False) + self._mode = ChoiceRaw(self) self._mode.SetItems({mode: lang[mode] for mode in MODES.keys()}) self._sizer.Add(self._mode, 0, Border, 5) self._mode.Bind(wx.EVT_CHOICE, self._on_mode_change) diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_widget.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_widget.py index 781a4ac1..4744313c 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_widget.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_widget.py @@ -148,7 +148,7 @@ def __init__( self._multiple.Bind(wx.EVT_CHECKBOX, self._on_check_change) top_sizer.Add(self._multiple, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) - self._replace_mode = ChoiceRaw(self, sort=False) + self._replace_mode = ChoiceRaw(self) self._replace_mode.Hide() self._replace_mode.SetItems( { From 11b0288056445e485b2b1d65ec847db0802453d2 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Tue, 5 Oct 2021 13:12:47 +0100 Subject: [PATCH 123/139] Fixed some issues in the state manager --- amulet_map_editor/api/wx/ui/mc/state.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/state.py b/amulet_map_editor/api/wx/ui/mc/state.py index e6501399..3fe134f7 100644 --- a/amulet_map_editor/api/wx/ui/mc/state.py +++ b/amulet_map_editor/api/wx/ui/mc/state.py @@ -262,7 +262,7 @@ def __init__( def _fix_new_state(self): super()._fix_new_state() - if self.is_changed(State.Namespace): + if self.is_changed(State.ForceBlockstate) or self.is_changed(State.Namespace): self._changed_state[State.Namespace] = self._sanitise_namespace( self.namespace ) @@ -309,7 +309,7 @@ def __init__( def _fix_new_state(self): super()._fix_new_state() - if self.is_changed(State.BaseName): + if self.is_changed(State.Namespace) or self.is_changed(State.BaseName): self._changed_state[State.BaseName] = self._sanitise_base_name( self.base_name ) @@ -444,7 +444,7 @@ def _fix_version_change(self): def _fix_new_state(self): super()._fix_new_state() - validate = False + validate = self.is_changed(State.BaseName) if self.is_changed(State.Properties): self._changed_state[State.Properties] = self._sanitise_properties( self.properties @@ -461,7 +461,11 @@ def _fix_new_state(self): ] = self._sanitise_properties_multiple(self.valid_properties) if validate: - self._sync_properties() + ( + self._changed_state[State.Properties], + self._changed_state[State.PropertiesMultiple], + self._changed_state[State.ValidProperties], + ) = self._sync_properties() def _get_block_spec(self): if self.is_supported: @@ -477,7 +481,7 @@ def default_properties(self) -> PropertyType: if self.is_supported: return self._get_block_spec().default_properties else: - return + return {} @property def valid_properties(self) -> PropertyTypeMultiple: @@ -561,7 +565,7 @@ def _sync_properties( for name in valid_properties } if self.is_supported: - return properties, properties_multiple, None + return properties, properties_multiple, {} else: return properties, properties_multiple, valid_properties From 8b9177ce7848d89324995ad2f369fff1d7b9b8e3 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Tue, 5 Oct 2021 13:38:33 +0100 Subject: [PATCH 124/139] Reworked the block define widgets --- .../api/wx/ui/mc/base/base_define.py | 65 +--- .../base_identifier_select.py | 3 +- .../api/wx/ui/mc/block/define/events.py | 62 ---- .../api/wx/ui/mc/block/define/widget/base.py | 67 +--- .../wx/ui/mc/block/define/widget/normal.py | 288 +++++------------ .../wx/ui/mc/block/define/widget/wildcard.py | 302 +++++------------- .../ui/mc/block/properties/multiple/main.py | 10 +- .../properties/multiple/vanilla/vanilla.py | 3 +- .../ui/mc/block/properties/single/vanilla.py | 3 +- 9 files changed, 184 insertions(+), 619 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_define.py b/amulet_map_editor/api/wx/ui/mc/base/base_define.py index f2e99079..1571bae0 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/base_define.py +++ b/amulet_map_editor/api/wx/ui/mc/base/base_define.py @@ -1,45 +1,32 @@ import wx import wx.lib.scrolledpanel -from typing import Optional, Dict, Any import PyMCTranslate -from amulet.api.data_types import VersionNumberTuple, PlatformType from amulet_map_editor.api.wx.ui.mc.base.base_identifier_select import ( BaseIdentifierSelect, ) from amulet_map_editor.api.wx.ui.mc.version import ( VersionSelect, - EVT_VERSION_CHANGE, - VersionChangeEvent, ) -from amulet_map_editor.api.wx.ui.mc.api import BaseMCVersion +from amulet_map_editor.api.wx.ui.mc.state import BaseResourceIDState, StateHolder -class BaseDefine(wx.Panel, BaseMCVersion): +class BaseDefine(wx.Panel, StateHolder): _version_picker: VersionSelect + _identifier_select: BaseIdentifierSelect + state: BaseResourceIDState def __init__( self, parent, translation_manager: PyMCTranslate.TranslationManager, + *, + state: BaseResourceIDState = None, orientation=wx.VERTICAL, - platform: PlatformType = None, - version_number: VersionNumberTuple = None, - force_blockstate: bool = False, - state: Dict[str, Any] = None, - style: Dict[str, Any] = None, - **kwargs, ): - state = state or {} - state.setdefault("translation_manager", translation_manager) - state.setdefault("platform", platform) - state.setdefault("version_number", version_number) - state.setdefault("force_blockstate", force_blockstate) - # This is the init call to the class that stores the internal state of the data. - # This needs to be at the start to ensure that the internal state is set up before anything else is done. - # It is not a direct call to init so that subclasses of this class can substitute in which state subclass is used. - self._init_state(state) + assert isinstance(state, BaseResourceIDState) + StateHolder.__init__(self, state) wx.Panel.__init__(self, parent) self._orientation = orientation @@ -55,42 +42,8 @@ def __init__( self._version_picker = VersionSelect( self, translation_manager, - platform, - version_number, - force_blockstate, - style=style, - **kwargs, + state=self.state ) self._top_sizer.Add(self._version_picker, 0, wx.EXPAND) - self._version_picker.Bind(EVT_VERSION_CHANGE, self._on_version_change) - - self._picker: Optional[BaseIdentifierSelect] = None self.Layout() - - def _init_state(self, state: Dict[str, Any]): - """ - Call the init method of the state manager. - This is here so that nested classes do not have to init the state managers multiple times. - """ - BaseMCVersion.__init__(self, **state) - - def _on_version_change(self, evt: VersionChangeEvent): - raise NotImplementedError - - def _on_push(self) -> bool: - self._set_platform(self.platform) - self._set_version_number(self.version_number) - self._set_force_blockstate(self.force_blockstate) - if ( - self.platform != self._version_picker.platform - or self.version_number != self._version_picker.version_number - or self.force_blockstate != self._version_picker.force_blockstate - ): - ( - self._version_picker.platform, - self._version_picker.version_number, - self._version_picker.force_blockstate, - ) = (self.platform, self.version_number, self.force_blockstate) - return True - return False diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py index 279ddcc6..47c2dc69 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py +++ b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py @@ -124,8 +124,7 @@ def _on_state_change(self): self._update_base_name() def _update_namespace(self): - if self.state.is_changed(State.ForceBlockstate): - self._namespace_combo.Set(self.state.valid_namespaces) + self._namespace_combo.Set(self.state.valid_namespaces) namespace = self.state.namespace if namespace != self._namespace_combo.GetValue(): index = self._namespace_combo.FindString(namespace) diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/events.py b/amulet_map_editor/api/wx/ui/mc/block/define/events.py index 8a6bf062..70082392 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/events.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/events.py @@ -11,22 +11,12 @@ def __init__( force_blockstate: bool, namespace: str, base_name: str, - old_platform: str, - old_version_number: VersionNumberTuple, - old_force_blockstate: bool, - old_namespace: str, - old_base_name: str, ): self._platform = platform self._version_number = version_number self._force_blockstate = force_blockstate self._namespace = namespace self._base_name = base_name - self._old_platform = old_platform - self._old_version_number = old_version_number - self._old_force_blockstate = old_force_blockstate - self._old_namespace = old_namespace - self._old_base_name = old_base_name @property def platform(self) -> PlatformType: @@ -48,26 +38,6 @@ def namespace(self) -> str: def base_name(self) -> str: return self._base_name - @property - def old_platform(self) -> str: - return self._old_platform - - @property - def old_version_number(self) -> VersionNumberTuple: - return self._old_version_number - - @property - def old_force_blockstate(self) -> bool: - return self._old_force_blockstate - - @property - def old_namespace(self) -> str: - return self._old_namespace - - @property - def old_base_name(self) -> str: - return self._old_base_name - _BlockChangeEventType = wx.NewEventType() EVT_BLOCK_CHANGE = wx.PyEventBinder(_BlockChangeEventType) @@ -86,12 +56,6 @@ def __init__( namespace: str, base_name: str, properties: PropertyType, - old_platform: str, - old_version_number: VersionNumberTuple, - old_force_blockstate: bool, - old_namespace: str, - old_base_name: str, - old_properties: PropertyType, ): wx.PyEvent.__init__(self, eventType=_BlockChangeEventType) BaseBlockChangeEvent.__init__( @@ -101,23 +65,13 @@ def __init__( force_blockstate, namespace, base_name, - old_platform, - old_version_number, - old_force_blockstate, - old_namespace, - old_base_name, ) self._properties = properties - self._old_properties = old_properties @property def properties(self) -> PropertyType: return self._properties - @property - def old_properties(self) -> PropertyType: - return self._old_properties - _WildcardBlockChangeEventType = wx.NewEventType() EVT_WILDCARD_BLOCK_CHANGE = wx.PyEventBinder(_WildcardBlockChangeEventType) @@ -136,12 +90,6 @@ def __init__( namespace: str, base_name: str, selected_properties: PropertyTypeMultiple, - old_platform: str, - old_version_number: VersionNumberTuple, - old_force_blockstate: bool, - old_namespace: str, - old_base_name: str, - old_selected_properties: PropertyTypeMultiple, ): wx.PyEvent.__init__(self, eventType=_WildcardBlockChangeEventType) BaseBlockChangeEvent.__init__( @@ -151,19 +99,9 @@ def __init__( force_blockstate, namespace, base_name, - old_platform, - old_version_number, - old_force_blockstate, - old_namespace, - old_base_name, ) self._selected_properties = selected_properties - self._old_selected_properties = old_selected_properties @property def selected_properties(self) -> PropertyTypeMultiple: return self._selected_properties - - @property - def old_selected_properties(self) -> PropertyTypeMultiple: - return self._old_selected_properties diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py index a30fef61..4594290e 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py @@ -1,84 +1,45 @@ -import wx.lib.scrolledpanel -from typing import Tuple, Optional, Dict, Any - import PyMCTranslate -from amulet.api.data_types import VersionNumberTuple +import wx from amulet_map_editor.api.wx.ui.mc.base.base_define import BaseDefine from amulet_map_editor.api.wx.ui.mc.block import BlockIdentifierSelect from amulet_map_editor.api.wx.ui.mc.block import ( BasePropertySelect, - BlockIDChangeEvent, - EVT_BLOCK_ID_CHANGE, ) -from amulet_map_editor.api.wx.ui.mc.api import BaseMCBlockIdentifier -from amulet_map_editor.api.wx.ui.mc.version import VersionChangeEvent +from amulet_map_editor.api.wx.ui.mc.state import BlockState -class BaseBlockDefine(BaseDefine, BaseMCBlockIdentifier): +class BaseBlockDefine(BaseDefine): """ A UI that merges a version select widget with a block select widget and a property select. """ - _picker: BlockIdentifierSelect + state: BlockState + _identifier_select: BlockIdentifierSelect + _property_picker: BasePropertySelect def __init__( self, parent, translation_manager: PyMCTranslate.TranslationManager, + *, + state: BlockState = None, orientation=wx.VERTICAL, - platform: str = None, - version_number: VersionNumberTuple = None, - force_blockstate: bool = None, - namespace: str = None, - base_name: str = None, show_pick_block: bool = False, - state: Dict[str, Any] = None, ): - state = state or {} - state.setdefault("namespace", namespace) - state.setdefault("base_name", base_name) + assert isinstance(state, BlockState) BaseDefine.__init__( self, parent, translation_manager, - orientation, - platform, - version_number, - force_blockstate, state=state, + orientation=orientation, ) - self._picker = BlockIdentifierSelect( + self._identifier_select = BlockIdentifierSelect( self, translation_manager, - self.platform, - self.version_number, - self.force_blockstate, - namespace, - base_name, - show_pick_block, + state=self.state, + show_pick=show_pick_block, ) - self._top_sizer.Add(self._picker, 1, wx.EXPAND | wx.TOP, 5) - self._picker.Bind(EVT_BLOCK_ID_CHANGE, self._on_block_change) - self._property_picker: Optional[BasePropertySelect] = None - - def _init_state(self, state: Dict[str, Any]): - raise NotImplementedError - - def _on_version_change(self, evt: VersionChangeEvent): - raise NotImplementedError - - def _on_block_change(self, evt: BlockIDChangeEvent): - self._set_namespace(evt.namespace) - self._set_base_name(evt.base_name) - self._property_picker.namespace = self.namespace - self._property_picker.base_name = self.base_name - - def _on_push(self) -> bool: - update = super()._on_push() - if update: - self._property_picker.platform = self.platform - self._property_picker.version_number = self.version_number - self._property_picker.force_blockstate = self.force_blockstate - return update + self._top_sizer.Add(self._identifier_select, 1, wx.EXPAND | wx.TOP, 5) diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py index 7d97fecf..55543334 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py @@ -1,38 +1,39 @@ import wx import wx.lib.scrolledpanel -from typing import Tuple, Dict, Any import PyMCTranslate +import amulet_nbt from amulet.api.data_types import VersionNumberTuple -from amulet.api.block import PropertyType, Block +from amulet.api.block import PropertyType +from amulet_map_editor.api.wx.ui.events import EVT_CHILD_SIZE from amulet_map_editor.api.wx.ui.mc.block import ( - BlockIDChangeEvent, SinglePropertySelect, EVT_SINGLE_PROPERTIES_CHANGE, - SinglePropertiesChangeEvent, + SinglePropertiesChangeEvent ) from amulet_map_editor.api.wx.ui.mc.block.define.widget.base import BaseBlockDefine from amulet_map_editor.api.wx.ui.mc.block.define.events import ( BlockChangeEvent, EVT_BLOCK_CHANGE, ) -from amulet_map_editor.api.wx.ui.mc.api import NormalMCBlock -from amulet_map_editor.api.wx.ui.mc.version import VersionChangeEvent +from amulet_map_editor.api.wx.ui.mc.state import BlockState -class BlockDefine(BaseBlockDefine, NormalMCBlock): +class BlockDefine(BaseBlockDefine): """ A UI that merges a version select widget with a block select widget and a property select. """ + state: BlockState _property_picker: SinglePropertySelect def __init__( self, parent, translation_manager: PyMCTranslate.TranslationManager, - orientation=wx.VERTICAL, + *, + state: BlockState = None, platform: str = None, version_number: VersionNumberTuple = None, force_blockstate: bool = None, @@ -40,187 +41,51 @@ def __init__( base_name: str = None, properties: PropertyType = None, show_pick_block: bool = False, - state: Dict[str, Any] = None, + orientation=wx.VERTICAL, ): - state = state or {} - state.setdefault("properties", properties) + if not isinstance(state, BlockState): + state = BlockState( + translation_manager, + platform=platform, + version_number=version_number, + force_blockstate=force_blockstate, + namespace=namespace, + base_name=base_name, + properties=properties, + ) BaseBlockDefine.__init__( self, parent, translation_manager, - orientation, - platform, - version_number, - force_blockstate, - namespace, - base_name, - show_pick_block=show_pick_block, state=state, + orientation=orientation, + show_pick_block=show_pick_block, ) right_sizer = wx.BoxSizer(wx.VERTICAL) border = wx.LEFT if orientation == wx.HORIZONTAL else wx.TOP self._sizer.Add(right_sizer, 1, wx.EXPAND | border, 5) - self._property_picker = self._create_properties() + self._property_picker = self._create_property_picker(translation_manager) + self._property_picker.Bind(EVT_SINGLE_PROPERTIES_CHANGE, self._post_block_change) right_sizer.Add(self._property_picker, 1, wx.EXPAND) - self._property_picker.Bind( - EVT_SINGLE_PROPERTIES_CHANGE, self._on_property_change - ) self.Layout() - def _create_properties(self) -> SinglePropertySelect: + def _create_property_picker(self, translation_manager: PyMCTranslate.TranslationManager) -> SinglePropertySelect: return SinglePropertySelect( self, - self._translation_manager, - self.platform, - self.version_number, - self.force_blockstate, - self.namespace, - self.base_name, - self.properties, - ) - - def _init_state(self, state: Dict[str, Any]): - NormalMCBlock.__init__(self, **state) - - def _on_version_change(self, evt: VersionChangeEvent): - self.Freeze() - old_platform, old_version, old_force_blockstate = ( - self.platform, - self.version_number, - self.force_blockstate, - ) - old_namespace, old_base_name, old_properties = ( - self.namespace, - self.base_name, - self.properties, - ) - - self._set_platform(evt.platform) - self._set_version_number(evt.version_number) - self._set_force_blockstate(evt.force_blockstate) - - ( - universal_block, - universal_block_entity, - _, - ) = self._translation_manager.get_version( - old_platform, old_version - ).block.to_universal( - Block(old_namespace, old_base_name, old_properties), - force_blockstate=self.force_blockstate, - ) - new_block, _, _ = self._translation_manager.get_version( - self.platform, self.version_number - ).block.from_universal( - universal_block, universal_block_entity, self.force_blockstate - ) - - if isinstance(new_block, Block): - self._set_namespace(new_block.namespace) - self._set_base_name(new_block.base_name) - self._set_properties(new_block.properties) - else: - self._set_namespace(None) - self._set_base_name(None) - self._set_properties(None) - - ( - self._picker.platform, - self._picker.version_number, - self._picker.force_blockstate, - self._picker.namespace, - self._picker.base_name, - ) = ( - self._property_picker.platform, - self._property_picker.version_number, - self._property_picker.force_blockstate, - self._property_picker.namespace, - self._property_picker.base_name, - ) = ( - self.platform, - self.version_number, - self.force_blockstate, - self.namespace, - self.base_name, - ) - self._property_picker.properties = self.properties - - wx.PostEvent( - self, - BlockChangeEvent( - self.platform, - self.version_number, - self.force_blockstate, - self.namespace, - self.base_name, - self.properties, - old_platform, - old_version, - old_force_blockstate, - old_namespace, - old_base_name, - old_properties, - ), - ) - self.Thaw() - - def _on_block_change(self, evt: BlockIDChangeEvent): - old_namespace, old_base_name, old_properties = ( - self.namespace, - self.base_name, - self.properties, - ) - super()._on_block_change(evt) - self._set_properties(None) - self._property_picker.properties = self.properties - wx.PostEvent( - self, - BlockChangeEvent( - self.platform, - self.version_number, - self.force_blockstate, - self.namespace, - self.base_name, - self.properties, - self.platform, - self.version_number, - self.force_blockstate, - old_namespace, - old_base_name, - old_properties, - ), - ) - - def _on_property_change(self, evt: SinglePropertiesChangeEvent): - self.Layout() - old_properties = self.properties - self._set_properties(evt.properties) - wx.PostEvent( - self, - BlockChangeEvent( - self.platform, - self.version_number, - self.force_blockstate, - self.namespace, - self.base_name, - self.properties, - self.platform, - self.version_number, - self.force_blockstate, - self.namespace, - self.base_name, - old_properties, - ), + translation_manager, + state=self.state ) - def _on_push(self) -> bool: - update = super()._on_push() - self._set_properties(self.properties) - if update or self.properties != self._property_picker.properties: - update = True - self._property_picker.properties = self.properties - return update + def _post_block_change(self, evt: SinglePropertiesChangeEvent): + wx.PostEvent(self, BlockChangeEvent( + self.state.platform, + self.state.version_number, + self.state.force_blockstate, + self.state.namespace, + self.state.base_name, + self.state.properties + )) def demo(): @@ -228,32 +93,12 @@ def demo(): Show a demo version of the UI. An app instance must be created first. """ - import amulet_nbt - translation_manager = PyMCTranslate.new_translation_manager() - for block in ( - ( - "minecraft", - "oak_fence", - { - "east": amulet_nbt.TAG_String("false"), - "north": amulet_nbt.TAG_String("true"), - "south": amulet_nbt.TAG_String("false"), - "west": amulet_nbt.TAG_String("false"), - }, - ), - ( - "modded", - "block", - { - "test": amulet_nbt.TAG_String("hello"), - }, - ), - ): + def create_dialog(block): dialog = wx.Dialog( None, - title=f"BlockDefine with block {block[0]}:{block[1]}", + title=f"BlockDefine with block {block['namespace']}:{block['base_name']}", style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT, ) sizer = wx.BoxSizer() @@ -261,16 +106,17 @@ def demo(): obj = BlockDefine( dialog, translation_manager, - wx.HORIZONTAL, - "java", - (1, 16, 0), - False, - *block, + platform="java", + version_number=(1, 16, 0), + force_blockstate=False, + **block, + orientation=wx.HORIZONTAL ) + sizer.Add( obj, 1, - wx.ALL | wx.EXPAND, + wx.ALL, 5, ) @@ -281,27 +127,43 @@ def on_change(evt: BlockChangeEvent): evt.force_blockstate, evt.namespace, evt.base_name, - evt.properties, - evt.old_platform, - evt.old_version_number, - evt.old_force_blockstate, - evt.old_namespace, - evt.old_base_name, - evt.old_properties, + evt.properties ) - obj.Bind(EVT_BLOCK_CHANGE, on_change) - - def get_on_close(dialog_): - def on_close(evt): - dialog_.Destroy() + def on_close(evt): + dialog.Destroy() - return on_close + def on_child_size(evt): + dialog.Layout() + evt.Skip() - dialog.Bind(wx.EVT_CLOSE, get_on_close(dialog)) + obj.Bind(EVT_BLOCK_CHANGE, on_change) + dialog.Bind(wx.EVT_CLOSE, on_close) + dialog.Bind(EVT_CHILD_SIZE, on_child_size) dialog.Show() dialog.Fit() + for block_ in ( + { + "namespace": "minecraft", + "base_name": "oak_fence", + "properties": { + "east": amulet_nbt.TAG_String("false"), + "north": amulet_nbt.TAG_String("true"), + "south": amulet_nbt.TAG_String("false"), + "west": amulet_nbt.TAG_String("false"), + }, + }, + { + "namespace": "modded", + "base_name": "block", + "properties": { + "test": amulet_nbt.TAG_String("hello"), + }, + }, + ): + create_dialog(block_) + if __name__ == "__main__": diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py index 9a8a1cf4..9b496fbe 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py @@ -1,13 +1,13 @@ import wx import wx.lib.scrolledpanel -from typing import Tuple, Dict, Any import PyMCTranslate +import amulet_nbt from amulet.api.data_types import VersionNumberTuple -from amulet.api.block import PropertyTypeMultiple, Block +from amulet.api.block import PropertyTypeMultiple +from amulet_map_editor.api.wx.ui.events import EVT_CHILD_SIZE from amulet_map_editor.api.wx.ui.mc.block import ( - BlockIDChangeEvent, MultiplePropertySelect, EVT_MULTIPLE_PROPERTIES_CHANGE, MultiplePropertiesChangeEvent, @@ -17,20 +17,23 @@ WildcardBlockChangeEvent, EVT_WILDCARD_BLOCK_CHANGE, ) -from amulet_map_editor.api.wx.ui.mc.api import WildcardMCBlock -from amulet_map_editor.api.wx.ui.mc.version import VersionChangeEvent +from amulet_map_editor.api.wx.ui.mc.state import BlockState -class WildcardBlockDefine(BaseBlockDefine, WildcardMCBlock): +class WildcardBlockDefine(BaseBlockDefine): """ A UI that merges a version select widget with a block select widget and a multi property select. """ + state: BlockState + _property_picker: MultiplePropertySelect + def __init__( self, parent, translation_manager: PyMCTranslate.TranslationManager, - orientation=wx.VERTICAL, + *, + state: BlockState = None, platform: str = None, version_number: VersionNumberTuple = None, force_blockstate: bool = None, @@ -39,23 +42,26 @@ def __init__( selected_properties: PropertyTypeMultiple = None, all_properties: PropertyTypeMultiple = None, show_pick_block: bool = False, - state: Dict[str, Any] = None, + orientation=wx.VERTICAL, ): - state = state or {} - state.setdefault("selected_properties", selected_properties) - state.setdefault("all_properties", all_properties) + if not isinstance(state, BlockState): + state = BlockState( + translation_manager, + platform=platform, + version_number=version_number, + force_blockstate=force_blockstate, + namespace=namespace, + base_name=base_name, + properties_multiple=selected_properties, + valid_properties=all_properties + ) BaseBlockDefine.__init__( self, parent, translation_manager, - orientation, - platform, - version_number, - force_blockstate, - namespace, - base_name, - show_pick_block=show_pick_block, state=state, + orientation=orientation, + show_pick_block=show_pick_block, ) right_sizer = wx.BoxSizer(wx.VERTICAL) @@ -64,177 +70,27 @@ def __init__( self._property_picker = MultiplePropertySelect( self, translation_manager, - self.platform, - self.version_number, - self.force_blockstate, - self.namespace, - self.base_name, - self.selected_properties, - self.all_properties, + state=state ) right_sizer.Add(self._property_picker, 1, wx.EXPAND) self._property_picker.Bind( - EVT_MULTIPLE_PROPERTIES_CHANGE, self._on_property_change + EVT_MULTIPLE_PROPERTIES_CHANGE, self._post_block_change ) self.Layout() - def _init_state(self, state: Dict[str, Any]): - WildcardMCBlock.__init__(self, **state) - - def _on_version_change(self, evt: VersionChangeEvent): - self.Freeze() - old_platform, old_version, old_force_blockstate = ( - self.platform, - self.version_number, - self.force_blockstate, - ) - old_namespace, old_base_name, old_all_properties, old_selected_properties = ( - self.namespace, - self.base_name, - self.all_properties, - self.selected_properties, - ) - - self._set_platform(evt.platform) - self._set_version_number(evt.version_number) - self._set_force_blockstate(evt.force_blockstate) - - old_properties = { - name: values[0] for name, values in old_all_properties.items() if values - } - - ( - universal_block, - universal_block_entity, - _, - ) = self._translation_manager.get_version( - old_platform, old_version - ).block.to_universal( - Block(old_namespace, old_base_name, old_properties), - force_blockstate=self.force_blockstate, - ) - new_block, _, _ = self._translation_manager.get_version( - self.platform, self.version_number - ).block.from_universal( - universal_block, universal_block_entity, self.force_blockstate - ) - - if isinstance(new_block, Block): - self._set_namespace(new_block.namespace) - self._set_base_name(new_block.base_name) - else: - self._set_namespace(None) - self._set_base_name(None) - self._set_all_properties(None) - self._set_selected_properties(None) - - ( - self._picker.platform, - self._picker.version_number, - self._picker.force_blockstate, - self._picker.namespace, - self._picker.base_name, - ) = ( - self._property_picker.platform, - self._property_picker.version_number, - self._property_picker.force_blockstate, - self._property_picker.namespace, - self._property_picker.base_name, - ) = ( - self.platform, - self.version_number, - self.force_blockstate, - self.namespace, - self.base_name, - ) - self._property_picker.all_properties = self.all_properties - self._property_picker.selected_properties = self.selected_properties - + def _post_block_change(self, evt: MultiplePropertiesChangeEvent): wx.PostEvent( self, WildcardBlockChangeEvent( - self.platform, - self.version_number, - self.force_blockstate, - self.namespace, - self.base_name, - self.selected_properties, - old_platform, - old_version, - old_force_blockstate, - old_namespace, - old_base_name, - old_selected_properties, + self.state.platform, + self.state.version_number, + self.state.force_blockstate, + self.state.namespace, + self.state.base_name, + self.state.properties_multiple ), ) - self.Thaw() - - def _on_block_change(self, evt: BlockIDChangeEvent): - old_namespace, old_base_name, old_selected_properties = ( - self.namespace, - self.base_name, - self.selected_properties, - ) - super()._on_block_change(evt) - self._set_all_properties(None) - self._set_selected_properties(None) - self._property_picker.all_properties = self.all_properties - self._property_picker.selected_properties = self.selected_properties - wx.PostEvent( - self, - WildcardBlockChangeEvent( - self.platform, - self.version_number, - self.force_blockstate, - self.namespace, - self.base_name, - self.selected_properties, - self.platform, - self.version_number, - self.force_blockstate, - old_namespace, - old_base_name, - old_selected_properties, - ), - ) - - def _on_property_change(self, evt: MultiplePropertiesChangeEvent): - self.Layout() - old_selected_properties = self.selected_properties - self._set_all_properties(self._property_picker.all_properties) - self._set_selected_properties(evt.selected_properties) - wx.PostEvent( - self, - WildcardBlockChangeEvent( - self.platform, - self.version_number, - self.force_blockstate, - self.namespace, - self.base_name, - self.selected_properties, - self.platform, - self.version_number, - self.force_blockstate, - self.namespace, - self.base_name, - old_selected_properties, - ), - ) - - def _on_push(self) -> bool: - update = super()._on_push() - self._set_all_properties(self.all_properties) - self._set_selected_properties(self.selected_properties) - if ( - update - or self.all_properties != self._property_picker.all_properties - or self.selected_properties != self._property_picker.selected_properties - ): - update = True - self._property_picker.all_properties = self.all_properties - self._property_picker.selected_properties = self.selected_properties - return update def demo(): @@ -242,40 +98,12 @@ def demo(): Show a demo version of the UI. An app instance must be created first. """ - import amulet_nbt - translation_manager = PyMCTranslate.new_translation_manager() - for block in ( - ( - "minecraft", - "oak_fence", - { - "east": ( - amulet_nbt.TAG_String("true"), - amulet_nbt.TAG_String("false"), - ), - "north": ( - amulet_nbt.TAG_String("true"), - amulet_nbt.TAG_String("false"), - ), - "south": (amulet_nbt.TAG_String("false"),), - "west": (amulet_nbt.TAG_String("true"),), - }, - ), - ( - "modded", - "block", - { - "test": ( - amulet_nbt.TAG_String("hello"), - amulet_nbt.TAG_String("hello2"), - ), - }, - ), - ): + + def create_dialog(block): dialog = wx.Dialog( None, - title=f"WildcardBlockDefine with block {block[0]}:{block[1]}", + title=f"WildcardBlockDefine with block {block['namespace']}:{block['base_name']}", style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.DIALOG_NO_PARENT, ) sizer = wx.BoxSizer() @@ -283,11 +111,11 @@ def demo(): obj = WildcardBlockDefine( dialog, translation_manager, - wx.HORIZONTAL, - "java", - (1, 16, 0), - False, - *block, + platform="java", + version_number=(1, 16, 0), + force_blockstate=False, + **block, + orientation=wx.HORIZONTAL ) sizer.Add( @@ -305,25 +133,51 @@ def on_change(evt: WildcardBlockChangeEvent): evt.namespace, evt.base_name, evt.selected_properties, - evt.old_platform, - evt.old_version_number, - evt.old_force_blockstate, - evt.old_namespace, - evt.old_base_name, - evt.old_selected_properties, ) - def get_on_close(dialog_): - def on_close(evt): - dialog_.Destroy() + def on_close(evt): + dialog.Destroy() - return on_close + def on_child_size(evt): + dialog.Layout() + evt.Skip() obj.Bind(EVT_WILDCARD_BLOCK_CHANGE, on_change) - dialog.Bind(wx.EVT_CLOSE, get_on_close(dialog)) + dialog.Bind(wx.EVT_CLOSE, on_close) + dialog.Bind(EVT_CHILD_SIZE, on_child_size) dialog.Show() dialog.Fit() + for block_ in ( + { + "namespace": "minecraft", + "base_name": "oak_fence", + "selected_properties": { + "east": ( + amulet_nbt.TAG_String("true"), + amulet_nbt.TAG_String("false"), + ), + "north": ( + amulet_nbt.TAG_String("true"), + amulet_nbt.TAG_String("false"), + ), + "south": (amulet_nbt.TAG_String("false"),), + "west": (amulet_nbt.TAG_String("true"),), + }, + }, + { + "namespace": "modded", + "base_name": "block", + "selected_properties": { + "test": ( + amulet_nbt.TAG_String("hello"), + amulet_nbt.TAG_String("hello2"), + ), + }, + }, + ): + create_dialog(block_) + if __name__ == "__main__": diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py index 2c793277..c224c35b 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py @@ -31,11 +31,11 @@ def __init__( translation_manager: PyMCTranslate.TranslationManager, *, state: BlockState = None, - platform: str, - version_number: VersionNumberTuple, - force_blockstate: bool, - namespace: str, - base_name: str, + platform: str = None, + version_number: VersionNumberTuple = None, + force_blockstate: bool = None, + namespace: str = None, + base_name: str = None, all_properties: PropertyTypeMultiple = None, selected_properties: PropertyTypeMultiple = None, ): diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/vanilla/vanilla.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/vanilla/vanilla.py index 29faa487..9d89909f 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/vanilla/vanilla.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/vanilla/vanilla.py @@ -35,7 +35,6 @@ def __init__(self, parent: wx.Window, state: BlockState): self._properties: Dict[str, Tuple[wx.ComboCtrl, PropertyValueComboPopup]] = {} def _rebuild_properties(self): - self.Freeze() self._tear_down_properties() valid_properties = self.state.valid_properties current_properties = self.state.properties_multiple @@ -44,7 +43,7 @@ def _rebuild_properties(self): name, valid_properties[name], current_properties[name] ) self.Fit() - self.Thaw() + self.Layout() def _tear_down_properties(self): self._properties.clear() diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/single/vanilla.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/vanilla.py index 7b81379b..6fdae229 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/single/vanilla.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/single/vanilla.py @@ -32,7 +32,6 @@ def __init__(self, parent: wx.Window, state: BlockState): self._properties: Dict[str, ChoiceRaw] = {} def _rebuild_properties(self): - self.Freeze() self._tear_down_properties() valid_properties = self.state.valid_properties current_properties = self.state.properties @@ -41,7 +40,7 @@ def _rebuild_properties(self): name, valid_properties[name], current_properties[name] ) self.Fit() - self.Thaw() + self.Layout() def _tear_down_properties(self): self._properties.clear() From 31102e2f6ebf7650c40457028f482884837de09f Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Tue, 5 Oct 2021 13:38:53 +0100 Subject: [PATCH 125/139] Reformatted --- .../api/wx/ui/mc/base/base_define.py | 4 +- .../wx/ui/mc/block/define/widget/normal.py | 39 ++++++++++--------- .../wx/ui/mc/block/define/widget/wildcard.py | 10 ++--- 3 files changed, 26 insertions(+), 27 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_define.py b/amulet_map_editor/api/wx/ui/mc/base/base_define.py index 1571bae0..6a37bb07 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/base_define.py +++ b/amulet_map_editor/api/wx/ui/mc/base/base_define.py @@ -40,9 +40,7 @@ def __init__( self._sizer.Add(self._top_sizer, 0, wx.EXPAND) self._version_picker = VersionSelect( - self, - translation_manager, - state=self.state + self, translation_manager, state=self.state ) self._top_sizer.Add(self._version_picker, 0, wx.EXPAND) diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py index 55543334..2208eced 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py @@ -10,7 +10,7 @@ from amulet_map_editor.api.wx.ui.mc.block import ( SinglePropertySelect, EVT_SINGLE_PROPERTIES_CHANGE, - SinglePropertiesChangeEvent + SinglePropertiesChangeEvent, ) from amulet_map_editor.api.wx.ui.mc.block.define.widget.base import BaseBlockDefine from amulet_map_editor.api.wx.ui.mc.block.define.events import ( @@ -66,26 +66,29 @@ def __init__( border = wx.LEFT if orientation == wx.HORIZONTAL else wx.TOP self._sizer.Add(right_sizer, 1, wx.EXPAND | border, 5) self._property_picker = self._create_property_picker(translation_manager) - self._property_picker.Bind(EVT_SINGLE_PROPERTIES_CHANGE, self._post_block_change) + self._property_picker.Bind( + EVT_SINGLE_PROPERTIES_CHANGE, self._post_block_change + ) right_sizer.Add(self._property_picker, 1, wx.EXPAND) self.Layout() - def _create_property_picker(self, translation_manager: PyMCTranslate.TranslationManager) -> SinglePropertySelect: - return SinglePropertySelect( - self, - translation_manager, - state=self.state - ) + def _create_property_picker( + self, translation_manager: PyMCTranslate.TranslationManager + ) -> SinglePropertySelect: + return SinglePropertySelect(self, translation_manager, state=self.state) def _post_block_change(self, evt: SinglePropertiesChangeEvent): - wx.PostEvent(self, BlockChangeEvent( - self.state.platform, - self.state.version_number, - self.state.force_blockstate, - self.state.namespace, - self.state.base_name, - self.state.properties - )) + wx.PostEvent( + self, + BlockChangeEvent( + self.state.platform, + self.state.version_number, + self.state.force_blockstate, + self.state.namespace, + self.state.base_name, + self.state.properties, + ), + ) def demo(): @@ -110,7 +113,7 @@ def create_dialog(block): version_number=(1, 16, 0), force_blockstate=False, **block, - orientation=wx.HORIZONTAL + orientation=wx.HORIZONTAL, ) sizer.Add( @@ -127,7 +130,7 @@ def on_change(evt: BlockChangeEvent): evt.force_blockstate, evt.namespace, evt.base_name, - evt.properties + evt.properties, ) def on_close(evt): diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py index 9b496fbe..3c61d72a 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py @@ -53,7 +53,7 @@ def __init__( namespace=namespace, base_name=base_name, properties_multiple=selected_properties, - valid_properties=all_properties + valid_properties=all_properties, ) BaseBlockDefine.__init__( self, @@ -68,9 +68,7 @@ def __init__( border = wx.LEFT if orientation == wx.HORIZONTAL else wx.TOP self._sizer.Add(right_sizer, 1, wx.EXPAND | border, 5) self._property_picker = MultiplePropertySelect( - self, - translation_manager, - state=state + self, translation_manager, state=state ) right_sizer.Add(self._property_picker, 1, wx.EXPAND) self._property_picker.Bind( @@ -88,7 +86,7 @@ def _post_block_change(self, evt: MultiplePropertiesChangeEvent): self.state.force_blockstate, self.state.namespace, self.state.base_name, - self.state.properties_multiple + self.state.properties_multiple, ), ) @@ -115,7 +113,7 @@ def create_dialog(block): version_number=(1, 16, 0), force_blockstate=False, **block, - orientation=wx.HORIZONTAL + orientation=wx.HORIZONTAL, ) sizer.Add( From 87ea5b44a882501d87c869a778a19295db34042e Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Tue, 5 Oct 2021 17:58:05 +0100 Subject: [PATCH 126/139] Fixed some issues in the updated define UI elements Events were being fired twice because they were being run in the set logic rather than the on change logic. Moved the firing logic to the UI elements that the user changes. Fixed some issues in the state manager. --- .../api/wx/ui/mc/base/base_define.py | 6 +- .../base_identifier_select.py | 9 +- .../api/wx/ui/mc/biome/biome_define.py | 221 ++++++++---------- .../api/wx/ui/mc/block/define/widget/base.py | 5 +- .../wx/ui/mc/block/define/widget/normal.py | 7 +- .../wx/ui/mc/block/define/widget/wildcard.py | 7 +- .../ui/mc/block/properties/multiple/base.py | 4 + .../ui/mc/block/properties/multiple/events.py | 4 +- .../ui/mc/block/properties/multiple/main.py | 4 - .../ui/mc/block/properties/multiple/modded.py | 4 + .../wx/ui/mc/block/properties/single/base.py | 2 + .../ui/mc/block/properties/single/events.py | 4 +- .../wx/ui/mc/block/properties/single/main.py | 2 - amulet_map_editor/api/wx/ui/mc/state.py | 20 +- .../api/wx/ui/mc/version/platform_select.py | 5 +- .../api/wx/ui/mc/version/version_select.py | 6 +- amulet_map_editor/api/wx/ui/simple.py | 7 +- 17 files changed, 154 insertions(+), 163 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_define.py b/amulet_map_editor/api/wx/ui/mc/base/base_define.py index 6a37bb07..bc47d886 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/base_define.py +++ b/amulet_map_editor/api/wx/ui/mc/base/base_define.py @@ -8,6 +8,7 @@ ) from amulet_map_editor.api.wx.ui.mc.version import ( VersionSelect, + EVT_VERSION_CHANGE, ) from amulet_map_editor.api.wx.ui.mc.state import BaseResourceIDState, StateHolder @@ -42,6 +43,9 @@ def __init__( self._version_picker = VersionSelect( self, translation_manager, state=self.state ) + self._version_picker.Bind(EVT_VERSION_CHANGE, self._post_change) self._top_sizer.Add(self._version_picker, 0, wx.EXPAND) - self.Layout() + + def _post_change(self, evt): + raise NotImplementedError diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py index 47c2dc69..f09abcca 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py +++ b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py @@ -124,7 +124,8 @@ def _on_state_change(self): self._update_base_name() def _update_namespace(self): - self._namespace_combo.Set(self.state.valid_namespaces) + if self._namespace_combo.GetItems() != self.state.valid_namespaces: + self._namespace_combo.Set(self.state.valid_namespaces) namespace = self.state.namespace if namespace != self._namespace_combo.GetValue(): index = self._namespace_combo.FindString(namespace) @@ -137,6 +138,8 @@ def _update_base_name(self): base_name = self.state.base_name if base_name in self.state.valid_base_names: # The base name is known + if self.state.is_changed(State.ForceBlockstate): + self._update_from_search() location = self._base_name_list_box.FindString(base_name) if location == wx.NOT_FOUND: self._search.ChangeValue("") @@ -155,7 +158,9 @@ def _update_from_search(self) -> bool: :return: True if the text in the field changed """ search_str = self._search.GetValue() - base_names = [bn for bn in self.state.valid_base_names if search_str in bn] + base_names = sorted( + [bn for bn in self.state.valid_base_names if search_str in bn] + ) exact = search_str in base_names if (search_str and not exact) or (not search_str and not base_names): diff --git a/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py b/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py index 4cd41ac7..2ecd929f 100644 --- a/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py +++ b/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py @@ -1,149 +1,114 @@ -from typing import Tuple, Dict, Any - import PyMCTranslate import wx from amulet.api.data_types import VersionNumberTuple, PlatformType from amulet_map_editor.api.wx.ui.mc.base.base_define import BaseDefine -from amulet_map_editor.api.wx.ui.mc.api.biome import BaseMCBiomeIdentifier from amulet_map_editor.api.wx.ui.mc.biome.identifier_select.biome_identifier_select import ( BiomeIdentifierSelect, ) from amulet_map_editor.api.wx.ui.mc.biome.identifier_select.events import ( - BiomeIDChangeEvent, EVT_BIOME_ID_CHANGE, ) -from amulet_map_editor.api.wx.ui.mc.version.events import VersionChangeEvent +from amulet_map_editor.api.wx.ui.mc.state import BiomeResourceIDState + +_BiomeChangeEventType = wx.NewEventType() +EVT_BIOME_CHANGE = wx.PyEventBinder(_BiomeChangeEventType) -class BiomeDefine(BaseDefine, BaseMCBiomeIdentifier): +class BiomeChangeEvent(wx.PyEvent): + def __init__( + self, + platform: PlatformType, + version_number: VersionNumberTuple, + force_blockstate: bool, + namespace: str, + base_name: str, + ): + wx.PyEvent.__init__(self, eventType=_BiomeChangeEventType) + self._platform = platform + self._version_number = version_number + self._force_blockstate = force_blockstate + self._namespace = namespace + self._base_name = base_name + + @property + def platform(self) -> PlatformType: + return self._platform + + @property + def version_number(self) -> VersionNumberTuple: + return self._version_number + + @property + def force_blockstate(self) -> bool: + return self._force_blockstate + + @property + def namespace(self) -> str: + return self._namespace + + @property + def base_name(self) -> str: + return self._base_name + + +class BiomeDefine(BaseDefine): """ A UI that merges a version select widget with a biome select widget. """ + state: BiomeResourceIDState + _identifier_select: BiomeIdentifierSelect + def __init__( self, parent, translation_manager: PyMCTranslate.TranslationManager, - orientation=wx.VERTICAL, + *, + state: BiomeResourceIDState = None, platform: str = None, version_number: VersionNumberTuple = None, namespace: str = None, base_name: str = None, show_pick_biome: bool = False, - state: Dict[str, Any] = None, + orientation=wx.VERTICAL, ): - state = state or {} - state.setdefault("namespace", namespace) - state.setdefault("base_name", base_name) + if not isinstance(state, BiomeResourceIDState): + state = BiomeResourceIDState( + translation_manager, + platform=platform, + version_number=version_number, + force_blockstate=False, + namespace=namespace, + base_name=base_name, + ) super().__init__( parent, translation_manager, - orientation, - platform, - version_number, - False, state=state, - show_force_blockstate=False, + orientation=orientation, ) - self._picker = BiomeIdentifierSelect( + self._identifier_select = BiomeIdentifierSelect( self, translation_manager, - self.platform, - self.version_number, - self.force_blockstate, - namespace, - base_name, - show_pick_biome, - ) - self._picker.Bind(EVT_BIOME_ID_CHANGE, self._on_biome_id_change) - self._top_sizer.Add(self._picker, 1, wx.EXPAND | wx.TOP, 5) - - def _init_state(self, state: Dict[str, Any]): - BaseMCBiomeIdentifier.__init__(self, **state) - - def _on_version_change(self, evt: VersionChangeEvent): - self.Freeze() - old_platform, old_version = self.platform, self.version_number - old_namespace, old_base_name = self.namespace, self.base_name - - self._set_platform(evt.platform) - self._set_version_number(evt.version_number) - self._set_force_blockstate(evt.force_blockstate) - - universal_biome = self._translation_manager.get_version( - old_platform, old_version - ).biome.to_universal(f"{self.namespace}:{self.base_name}") - new_biome = self._translation_manager.get_version( - self.platform, self.version_number - ).biome.from_universal(universal_biome) - namespace, base_name = new_biome.split(":", 1) - - self._set_namespace(namespace) - self._set_base_name(base_name) - - ( - self._picker.platform, - self._picker.version_number, - self._picker.force_blockstate, - self._picker.namespace, - self._picker.base_name, - ) = ( - self.platform, - self.version_number, - self.force_blockstate, - self.namespace, - self.base_name, - ) - - wx.PostEvent( - self, - BiomeIDChangeEvent( - self.namespace, - self.base_name, - old_namespace, - old_base_name, - ), + state=state, + show_pick=show_pick_biome, ) - self.Thaw() + self._identifier_select.Bind(EVT_BIOME_ID_CHANGE, self._post_change) + self._top_sizer.Add(self._identifier_select, 1, wx.EXPAND | wx.TOP, 5) - def _on_biome_id_change(self, evt: BiomeIDChangeEvent): - self._set_namespace(evt.namespace) - self._set_base_name(evt.base_name) + def _post_change(self, evt): wx.PostEvent( self, - BiomeIDChangeEvent( - evt.namespace, - evt.base_name, - evt.old_namespace, - evt.old_base_name, + BiomeChangeEvent( + self.state.platform, + self.state.version_number, + self.state.force_blockstate, + self.state.namespace, + self.state.base_name, ), ) - def _on_push(self) -> bool: - self.Freeze() - update = super()._on_push() - self._set_namespace(self.namespace) - self._set_base_name(self.base_name) - if update: - # The version changed - ( - self._picker.platform, - self._picker.version_number, - self._picker.force_blockstate, - ) = (self.platform, self.version_number, self.force_blockstate) - if ( - self.namespace != self._picker.namespace - or self.base_name != self._picker.base_name - ): - update = True - self._picker.namespace, self._picker.base_name = ( - self.namespace, - self.base_name, - ) - self.Thaw() - return update - def demo(): """ @@ -158,19 +123,18 @@ def demo(): ) sizer = wx.BoxSizer() dialog.SetSizer(sizer) - obj = BiomeDefine(dialog, translation_manager, wx.HORIZONTAL) + obj = BiomeDefine(dialog, translation_manager, orientation=wx.HORIZONTAL) - def on_biome_change(evt: BiomeIDChangeEvent): + def on_biome_change(evt: BiomeChangeEvent): print( - obj.platform, - obj.version_number, - obj.force_blockstate, - obj.namespace, - obj.base_name, + evt.platform, + evt.version_number, + evt.force_blockstate, + evt.namespace, + evt.base_name, ) - print(evt.namespace, evt.base_name, evt.old_namespace, evt.old_base_name) - obj.Bind(EVT_BIOME_ID_CHANGE, on_biome_change) + obj.Bind(EVT_BIOME_CHANGE, on_biome_change) sizer.Add( obj, @@ -189,19 +153,12 @@ def set_data( namespace: str, base_name: str, ): - ( - obj.platform, - obj.version_number, - obj.force_blockstate, - obj.namespace, - obj.base_name, - ) = ( - platform, - version, - force_blockstate, - namespace, - base_name, - ) + with obj.state as state: + state.platform = platform + state.version_number = version + state.force_blockstate = force_blockstate + state.namespace = namespace + state.base_name = base_name interval = 1_000 wx.CallLater( @@ -209,15 +166,19 @@ def set_data( ) wx.CallLater( interval * 2, - set, + set_data, "bedrock", (1, 17, 0), False, "minecraft", "birch_forest_hills_mutated", ) - wx.CallLater(interval * 3, set, "java", (1, 17, 0), False, "minecraft", "random") - wx.CallLater(interval * 4, set, "bedrock", (1, 17, 0), False, "minecraft", "random") + wx.CallLater( + interval * 3, set_data, "java", (1, 17, 0), False, "minecraft", "random" + ) + wx.CallLater( + interval * 4, set_data, "bedrock", (1, 17, 0), False, "minecraft", "random" + ) if __name__ == "__main__": diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py index 4594290e..73908333 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py @@ -4,9 +4,7 @@ from amulet_map_editor.api.wx.ui.mc.base.base_define import BaseDefine from amulet_map_editor.api.wx.ui.mc.block import BlockIdentifierSelect -from amulet_map_editor.api.wx.ui.mc.block import ( - BasePropertySelect, -) +from amulet_map_editor.api.wx.ui.mc.block import BasePropertySelect, EVT_BLOCK_ID_CHANGE from amulet_map_editor.api.wx.ui.mc.state import BlockState @@ -42,4 +40,5 @@ def __init__( state=self.state, show_pick=show_pick_block, ) + self._identifier_select.Bind(EVT_BLOCK_ID_CHANGE, self._post_change) self._top_sizer.Add(self._identifier_select, 1, wx.EXPAND | wx.TOP, 5) diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py index 2208eced..85139c25 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py @@ -10,7 +10,6 @@ from amulet_map_editor.api.wx.ui.mc.block import ( SinglePropertySelect, EVT_SINGLE_PROPERTIES_CHANGE, - SinglePropertiesChangeEvent, ) from amulet_map_editor.api.wx.ui.mc.block.define.widget.base import BaseBlockDefine from amulet_map_editor.api.wx.ui.mc.block.define.events import ( @@ -66,9 +65,7 @@ def __init__( border = wx.LEFT if orientation == wx.HORIZONTAL else wx.TOP self._sizer.Add(right_sizer, 1, wx.EXPAND | border, 5) self._property_picker = self._create_property_picker(translation_manager) - self._property_picker.Bind( - EVT_SINGLE_PROPERTIES_CHANGE, self._post_block_change - ) + self._property_picker.Bind(EVT_SINGLE_PROPERTIES_CHANGE, self._post_change) right_sizer.Add(self._property_picker, 1, wx.EXPAND) self.Layout() @@ -77,7 +74,7 @@ def _create_property_picker( ) -> SinglePropertySelect: return SinglePropertySelect(self, translation_manager, state=self.state) - def _post_block_change(self, evt: SinglePropertiesChangeEvent): + def _post_change(self, evt): wx.PostEvent( self, BlockChangeEvent( diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py index 3c61d72a..1fd00c4e 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py @@ -10,7 +10,6 @@ from amulet_map_editor.api.wx.ui.mc.block import ( MultiplePropertySelect, EVT_MULTIPLE_PROPERTIES_CHANGE, - MultiplePropertiesChangeEvent, ) from amulet_map_editor.api.wx.ui.mc.block.define.widget.base import BaseBlockDefine from amulet_map_editor.api.wx.ui.mc.block.define.events import ( @@ -71,13 +70,11 @@ def __init__( self, translation_manager, state=state ) right_sizer.Add(self._property_picker, 1, wx.EXPAND) - self._property_picker.Bind( - EVT_MULTIPLE_PROPERTIES_CHANGE, self._post_block_change - ) + self._property_picker.Bind(EVT_MULTIPLE_PROPERTIES_CHANGE, self._post_change) self.Layout() - def _post_block_change(self, evt: MultiplePropertiesChangeEvent): + def _post_change(self, evt): wx.PostEvent( self, WildcardBlockChangeEvent( diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/base.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/base.py index f94ab762..a0447601 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/base.py @@ -2,6 +2,7 @@ from amulet.api.block import PropertyTypeMultiple from amulet_map_editor.api.wx.ui.mc.state import StateHolder, BlockState, State +from .events import MultiplePropertiesChangeEvent class BaseMultipleProperty(wx.Panel, StateHolder): @@ -48,3 +49,6 @@ def _on_property_change(self): if properties_multiple != self.state.properties_multiple: with self.state as state: state.properties_multiple = properties_multiple + wx.PostEvent( + self, MultiplePropertiesChangeEvent(self.state.properties_multiple) + ) diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/events.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/events.py index f4f45269..93dce040 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/events.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/events.py @@ -5,13 +5,13 @@ EVT_MULTIPLE_PROPERTIES_CHANGE = wx.PyEventBinder(_MultiplePropertiesChangeEventType) -class MultiplePropertiesChangeEvent(wx.PyEvent): +class MultiplePropertiesChangeEvent(wx.PyCommandEvent): """ Run when the properties UI changes. """ def __init__(self, selected_properties: PropertyTypeMultiple): - wx.PyEvent.__init__(self, eventType=_MultiplePropertiesChangeEventType) + wx.PyCommandEvent.__init__(self, eventType=_MultiplePropertiesChangeEventType) self._selected_properties = selected_properties @property diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py index c224c35b..e5997abf 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py @@ -77,10 +77,6 @@ def _do_show(self): def _on_state_change(self): if self.state.is_changed(State.BaseName): self._do_show() - if self.state.is_changed(State.PropertiesMultiple): - wx.PostEvent( - self, MultiplePropertiesChangeEvent(self.state.properties_multiple) - ) def demo(): diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/modded.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/modded.py index ff1dd7e3..bc929153 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/modded.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/modded.py @@ -3,6 +3,7 @@ from amulet_map_editor.api.wx.ui.mc.state import BlockState from ..single.modded import BaseModdedSingleProperty +from .events import MultiplePropertiesChangeEvent PropertyStorage = namedtuple( "PropertyStorage", ("sizer", "key_entry", "value_entry", "snbt_text") @@ -27,6 +28,9 @@ def _on_property_change(self): state.properties_multiple = { key: (val,) for key, val in properties.items() } + wx.PostEvent( + self, MultiplePropertiesChangeEvent(self.state.properties_multiple) + ) class ModdedMultipleProperty(BaseModdedMultipleProperty): diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/single/base.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/base.py index 97f68e51..b1485b84 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/single/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/single/base.py @@ -2,6 +2,7 @@ from amulet.api.block import PropertyType, PropertyValueType from amulet_map_editor.api.wx.ui.mc.state import StateHolder, BlockState, State +from .events import SinglePropertiesChangeEvent class BaseSingleProperty(wx.Panel, StateHolder): @@ -48,3 +49,4 @@ def _on_property_change(self): if properties != self.state.properties: with self.state as state: state.properties = properties + wx.PostEvent(self, SinglePropertiesChangeEvent(self.state.properties)) diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/single/events.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/events.py index 07688b04..9760efc8 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/single/events.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/single/events.py @@ -5,13 +5,13 @@ EVT_SINGLE_PROPERTIES_CHANGE = wx.PyEventBinder(_SinglePropertiesChangeEventType) -class SinglePropertiesChangeEvent(wx.PyEvent): +class SinglePropertiesChangeEvent(wx.PyCommandEvent): """ Run when the properties UI changes. """ def __init__(self, properties: PropertyType): - wx.PyEvent.__init__(self, eventType=_SinglePropertiesChangeEventType) + wx.PyCommandEvent.__init__(self, eventType=_SinglePropertiesChangeEventType) self._properties = properties @property diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py index 082853fa..352af767 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py @@ -70,8 +70,6 @@ def _do_show(self): def _on_state_change(self): if self.state.is_changed(State.BaseName): self._do_show() - if self.state.is_changed(State.Properties): - wx.PostEvent(self, SinglePropertiesChangeEvent(self.state.properties)) def demo(): diff --git a/amulet_map_editor/api/wx/ui/mc/state.py b/amulet_map_editor/api/wx/ui/mc/state.py index 3fe134f7..1b511e29 100644 --- a/amulet_map_editor/api/wx/ui/mc/state.py +++ b/amulet_map_editor/api/wx/ui/mc/state.py @@ -209,7 +209,7 @@ def _sanitise_version( ).version_number except KeyError: pass - return self.valid_version_numbers[0] + return self.valid_version_numbers[-1] @property def valid_version_numbers(self) -> List[VersionNumberTuple]: @@ -318,7 +318,11 @@ def _sanitise_base_name(self, base_name: str = None) -> str: if isinstance(base_name, str) and base_name: return base_name else: - return self.valid_base_names[0] + valid = self.valid_base_names + if valid: + return valid[0] + else: + return "" @property @abstractmethod @@ -357,6 +361,18 @@ def valid_base_names(self) -> List[str]: biomes.append(base_name) return biomes + def _fix_version_change(self): + if not self.is_changed(State.Namespace) or self.is_changed(State.BaseName): + universal_biome = self._translation_manager.get_version( + self._state[State.Platform], self._state[State.VersionNumber] + ).biome.to_universal(f"{self.namespace}:{self.base_name}") + version_biome = self._translation_manager.get_version( + self.platform, self.version_number + ).biome.from_universal(universal_biome) + namespace, base_name = version_biome.split(":") + self._changed_state[State.Namespace] = self._sanitise_namespace(namespace) + self._changed_state[State.BaseName] = self._sanitise_base_name(base_name) + class BlockNamespaceState(BaseNamespaceState): @property diff --git a/amulet_map_editor/api/wx/ui/mc/version/platform_select.py b/amulet_map_editor/api/wx/ui/mc/version/platform_select.py index 6ab42d5c..c3bfe8b0 100644 --- a/amulet_map_editor/api/wx/ui/mc/version/platform_select.py +++ b/amulet_map_editor/api/wx/ui/mc/version/platform_select.py @@ -103,13 +103,16 @@ def _on_state_change(self): if self.state.is_changed(State.Platform): self._update_platform() + def _post_change(self): + wx.PostEvent(self, PlatformChangeEvent(self.state.platform)) + def _on_platform_change(self, evt): """The event run when the platform choice is changed by a user.""" platform = self._platform_choice.GetCurrentString() if platform != self.state.platform: with self.state as state: state.platform = platform - wx.PostEvent(self, PlatformChangeEvent(self.state.platform)) + self._post_change() def demo(): diff --git a/amulet_map_editor/api/wx/ui/mc/version/version_select.py b/amulet_map_editor/api/wx/ui/mc/version/version_select.py index 4cb6da36..669c2179 100644 --- a/amulet_map_editor/api/wx/ui/mc/version/version_select.py +++ b/amulet_map_editor/api/wx/ui/mc/version/version_select.py @@ -116,7 +116,7 @@ def _on_state_change(self): if self.state.is_changed(State.ForceBlockstate): self._update_force_blockstate() - def _post_version_change(self): + def _post_change(self): wx.PostEvent( self, VersionChangeEvent( @@ -131,14 +131,14 @@ def _on_version_number_change(self, evt): if version != self.state.version_number: with self.state as state: state.version_number = version - self._post_version_change() + self._post_change() def _on_blockstate_change(self, evt): force_blockstate = bool(self._version_choice.GetCurrentSelection()) if force_blockstate != self.state.force_blockstate: with self.state as state: state.force_blockstate = force_blockstate - self._post_version_change() + self._post_change() def demo(): diff --git a/amulet_map_editor/api/wx/ui/simple.py b/amulet_map_editor/api/wx/ui/simple.py index 1c3d2ae5..29726139 100644 --- a/amulet_map_editor/api/wx/ui/simple.py +++ b/amulet_map_editor/api/wx/ui/simple.py @@ -119,7 +119,12 @@ def items(self) -> Tuple[Tuple[str, Any], ...]: def _set_items(self, items: Iterable[Tuple[Any, str]], default: Any = None): if items: if self._sorted: - items = sorted(items, key=lambda x: x[1], reverse=self._reverse) + try: + items = sorted(items, key=lambda x: x[0]) + except TypeError: + items = sorted(items, key=lambda x: x[1]) + if self._reverse: + items = reversed(items) self._values, self._keys = zip(*items) super().SetItems(self._keys) if default in self._values: From 90baaa2eaeb87e5d28090c71d40b7e44bf9b7742 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Wed, 6 Oct 2021 16:09:39 +0100 Subject: [PATCH 127/139] Exposed the translation manager held by the state This will make it so that each class does not need to store the translation manager as well. --- amulet_map_editor/api/wx/ui/mc/state.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/amulet_map_editor/api/wx/ui/mc/state.py b/amulet_map_editor/api/wx/ui/mc/state.py index 1b511e29..53a6fe0d 100644 --- a/amulet_map_editor/api/wx/ui/mc/state.py +++ b/amulet_map_editor/api/wx/ui/mc/state.py @@ -103,6 +103,10 @@ def unbind_on_change(self, on_change: OnChangeType): while on_change in self._on_change: self._on_change.remove(on_change) + @property + def translation_manager(self) -> TranslationManager: + return self._translation_manager + class StateHolder: _state: BaseState From 4616096cd22ccba384cbfd6652a69b9b7e31160f Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Wed, 6 Oct 2021 16:10:28 +0100 Subject: [PATCH 128/139] Implemented the block define buttons --- .../api/wx/ui/mc/block/define/button/base.py | 68 ++++------- .../wx/ui/mc/block/define/button/normal.py | 90 ++++----------- .../wx/ui/mc/block/define/button/wildcard.py | 107 +++++------------- 3 files changed, 76 insertions(+), 189 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py index 4d7a9c6b..2ab1d9ca 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py @@ -1,74 +1,48 @@ import wx -from typing import Tuple, Optional, Dict, Any +from typing import Optional -import PyMCTranslate -from amulet.api.data_types import VersionNumberTuple - -from amulet_map_editor.api.wx.ui.mc.api import BaseMCBlockIdentifier from amulet_map_editor.api.wx.ui.mc.block.define import BaseBlockDefine +from amulet_map_editor.api.wx.ui.mc.state import StateHolder, BlockState +from amulet_map_editor.api.wx.ui.simple import SimpleDialog -class BaseBlockDefineButton(wx.Button, BaseMCBlockIdentifier): +class BaseBlockDefineButton(wx.Button, StateHolder): _block_widget: Optional[BaseBlockDefine] def __init__( self, parent: wx.Window, - translation_manager: PyMCTranslate.TranslationManager, - platform: str = None, - version_number: VersionNumberTuple = None, - force_blockstate: bool = None, - namespace: str = None, - base_name: str = None, + *, + state: BlockState = None, show_pick_block: bool = False, max_char_length: int = 99999, - state: Dict[str, Any] = None, ): - state = state or {} - state.setdefault("translation_manager", translation_manager) - state.setdefault("platform", platform) - state.setdefault("version_number", version_number) - state.setdefault("force_blockstate", force_blockstate) - state.setdefault("namespace", namespace) - state.setdefault("base_name", base_name) - # This is the init call to the class that stores the internal state of the data. - # This needs to be at the start to ensure that the internal state is set up before anything else is done. - # It is not a direct call to init so that subclasses of this class can substitute in which state subclass is used. - self._init_state(state) + assert isinstance(state, BlockState) + StateHolder.__init__(self, state) wx.Button.__init__(self, parent, style=wx.BU_LEFT) self._block_widget: Optional[BaseBlockDefine] = None self.Bind(wx.EVT_BUTTON, self._on_press) self._show_pick_block = show_pick_block self._max_char_length = max(3, max_char_length) - def _init_state(self, state: Dict[str, Any]): - """ - Call the init method of the state manager. - This is here so that nested classes do not have to init the state managers multiple times. - """ - BaseMCBlockIdentifier.__init__(self, **state) - def SetLabel(self, label: str): if len(label) > self._max_char_length: label = f"{label[:self._max_char_length]}..." super().SetLabel(label) def _on_press(self, evt): + dialog = SimpleDialog(self, "Pick a Block") + self._block_widget = self._create_block_define(dialog) + dialog.sizer.Add(self._block_widget, 1, wx.EXPAND) + dialog.Fit() + if dialog.ShowModal() == wx.ID_OK: + self.state = self._block_widget.state + self.update_button() + self._block_widget = None + dialog.Destroy() + + def _create_block_define(self, dialog: wx.Dialog) -> BaseBlockDefine: raise NotImplementedError - def _on_push(self) -> bool: - self._set_platform(self.platform) - self._set_version_number(self.version_number) - self._set_force_blockstate(self.force_blockstate) - if self._block_widget is not None and ( - self.platform != self._block_widget.platform - or self.version_number != self._block_widget.version_number - or self.force_blockstate != self._block_widget.force_blockstate - ): - ( - self._block_widget.platform, - self._block_widget.version_number, - self._block_widget.force_blockstate, - ) = (self.platform, self.version_number, self.force_blockstate) - return True - return False + def update_button(self): + raise NotImplementedError diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py index a872a469..869efa0e 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py @@ -1,23 +1,26 @@ -from typing import Tuple, Dict, Any import wx +import copy import PyMCTranslate from amulet.api.data_types import VersionNumberTuple -from amulet_map_editor.api.wx.ui.simple import SimpleDialog -from amulet_map_editor.api.wx.ui.mc.api import NormalMCBlock from amulet_map_editor.api.wx.ui.mc.block.define.widget import BlockDefine from amulet_map_editor.api.wx.ui.mc.block.define.button.base import ( BaseBlockDefineButton, ) from amulet.api.block import PropertyType +from amulet_map_editor.api.wx.ui.mc.state import BlockState -class BlockDefineButton(BaseBlockDefineButton, NormalMCBlock): +class BlockDefineButton(BaseBlockDefineButton): + state: BlockState + def __init__( self, parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager, + *, + state: BlockState = None, platform: str = None, version_number: VersionNumberTuple = None, force_blockstate: bool = None, @@ -26,86 +29,43 @@ def __init__( properties: PropertyType = None, show_pick_block: bool = False, max_char_length: int = 99999, - state: Dict[str, Any] = None, ): - state = state or {} - state.setdefault("properties", properties) + if not isinstance(state, BlockState): + state = BlockState( + translation_manager, + platform=platform, + version_number=version_number, + force_blockstate=force_blockstate, + namespace=namespace, + base_name=base_name, + properties=properties, + ) BaseBlockDefineButton.__init__( self, parent, - translation_manager, - platform, - version_number, - force_blockstate, - namespace, - base_name, + state=state, show_pick_block=show_pick_block, max_char_length=max_char_length, - state=state, ) self.update_button() - def _init_state(self, state: Dict[str, Any]): - NormalMCBlock.__init__(self, **state) - def _create_block_define(self, dialog: wx.Dialog) -> BlockDefine: return BlockDefine( dialog, - self._translation_manager, - wx.HORIZONTAL, - self.platform, - self.version_number, - self.force_blockstate, - self.namespace, - self.base_name, - self.properties, + self.state.translation_manager, + state=copy.deepcopy(self.state), + orientation=wx.HORIZONTAL, ) - def _update_from_block_define(self, block_define: BlockDefine): - self._set_platform(self._block_widget.platform) - self._set_version_number(self._block_widget.version_number) - self._set_force_blockstate(self._block_widget.force_blockstate) - self._set_namespace(self._block_widget.namespace) - self._set_base_name(self._block_widget.base_name) - self._set_properties(self._block_widget.properties) - - def _on_press(self, evt): - dialog = SimpleDialog(self, "Pick a Block") - self._block_widget = self._create_block_define(dialog) - dialog.sizer.Add(self._block_widget, 1, wx.EXPAND) - dialog.Fit() - if dialog.ShowModal() == wx.ID_OK: - self._update_from_block_define(self._block_widget) - self.update_button() - self._block_widget = None - dialog.Destroy() - def update_button(self): """Update the text on the button from the internal state.""" - blockstate = self.block.full_blockstate + blockstate = f"{self.state.namespace}:{self.state.base_name}" + if self.state.properties: + props = ",".join(f"{key}={val}" for key, val in self.state.properties.items()) + blockstate = f"{blockstate}[{props}]" self.SetLabel(f" {blockstate}") self.SetToolTip(blockstate) - def _on_push(self) -> bool: - update = super()._on_push() - self._set_namespace(self.namespace) - self._set_base_name(self.base_name) - self._set_properties(self.properties) - update = self._block_widget is not None and ( - update - or self.namespace != self._block_widget.namespace - or self.base_name != self._block_widget.base_name - or self.properties != self._block_widget.properties - ) - if update: - ( - self._block_widget.namespace, - self._block_widget.base_name, - self._block_widget.properties, - ) = (self.namespace, self.base_name, self.properties) - self.update_button() - return False - def demo(): """ diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py index c048c511..ebc632e9 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py @@ -1,23 +1,26 @@ -from typing import Tuple, Dict, Any import wx +import copy import PyMCTranslate from amulet.api.data_types import VersionNumberTuple -from amulet_map_editor.api.wx.ui.simple import SimpleDialog -from amulet_map_editor.api.wx.ui.mc.api import WildcardMCBlock from amulet_map_editor.api.wx.ui.mc.block.define.widget import WildcardBlockDefine from amulet_map_editor.api.wx.ui.mc.block.define.button.base import ( BaseBlockDefineButton, ) from amulet.api.block import PropertyTypeMultiple +from amulet_map_editor.api.wx.ui.mc.state import BlockState -class WildcardBlockDefineButton(BaseBlockDefineButton, WildcardMCBlock): +class WildcardBlockDefineButton(BaseBlockDefineButton): + state: BlockState + def __init__( self, parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager, + *, + state: BlockState = None, platform: str = None, version_number: VersionNumberTuple = None, force_blockstate: bool = None, @@ -27,98 +30,48 @@ def __init__( all_properties: PropertyTypeMultiple = None, show_pick_block: bool = False, max_char_length: int = 99999, - state: Dict[str, Any] = None, ): - state = state or {} - state.setdefault("selected_properties", selected_properties) - state.setdefault("all_properties", all_properties) + if not isinstance(state, BlockState): + state = BlockState( + translation_manager, + platform=platform, + version_number=version_number, + force_blockstate=force_blockstate, + namespace=namespace, + base_name=base_name, + properties_multiple=selected_properties, + valid_properties=all_properties, + ) BaseBlockDefineButton.__init__( self, parent, - translation_manager, - platform, - version_number, - force_blockstate, - namespace, - base_name, + state=state, show_pick_block=show_pick_block, max_char_length=max_char_length, - state=state, ) self.update_button() - def _init_state(self, state: Dict[str, Any]): - WildcardMCBlock.__init__(self, **state) - - def _on_press(self, evt): - dialog = SimpleDialog(self, "Pick a Block") - self._block_widget = WildcardBlockDefine( + def _create_block_define(self, dialog: wx.Dialog) -> WildcardBlockDefine: + return WildcardBlockDefine( dialog, - self._translation_manager, - wx.HORIZONTAL, - self.platform, - self.version_number, - self.force_blockstate, - self.namespace, - self.base_name, - self.selected_properties, - self.all_properties, + self.state.translation_manager, + state=copy.deepcopy(self.state), + orientation=wx.HORIZONTAL, ) - dialog.sizer.Add(self._block_widget) - dialog.Fit() - if dialog.ShowModal() == wx.ID_OK: - self._set_platform(self._block_widget.platform) - self._set_version_number(self._block_widget.version_number) - self._set_force_blockstate(self._block_widget.force_blockstate) - self._set_namespace(self._block_widget.namespace) - self._set_base_name(self._block_widget.base_name) - self._set_all_properties(self._block_widget.all_properties) - self._set_selected_properties(self._block_widget.selected_properties) - self.update_button() - self._block_widget = None - dialog.Destroy() def update_button(self): """Update the text on the button from the internal state.""" - if self.selected_properties: + if self.state.properties_multiple: properties = [ f"{key}:({'|'.join([v.to_snbt() for v in val])})" - for key, val in self.selected_properties.items() + for key, val in self.state.properties_multiple.items() ] - self.SetLabel(f" {self.namespace}:{self.base_name}[{','.join(properties)}]") + self.SetLabel(f" {self.state.namespace}:{self.state.base_name}[{','.join(properties)}]") properties_str = ",\n".join(properties) - self.SetToolTip(f"{self.namespace}:{self.base_name}[\n{properties_str}\n]") + self.SetToolTip(f"{self.state.namespace}:{self.state.base_name}[\n{properties_str}\n]") else: - self.SetLabel(f" {self.namespace}:{self.base_name}") - self.SetToolTip(f"{self.namespace}:{self.base_name}") - - def _on_push(self) -> bool: - update = super()._on_push() - self._set_namespace(self.namespace) - self._set_base_name(self.base_name) - self._set_all_properties(self.all_properties) - self._set_selected_properties(self.selected_properties) - update = self._block_widget is not None and ( - update - or self.namespace != self._block_widget.namespace - or self.base_name != self._block_widget.base_name - or self.all_properties != self._block_widget.all_properties - or self.selected_properties != self._block_widget.selected_properties - ) - if update: - ( - self._block_widget.namespace, - self._block_widget.base_name, - self._block_widget.all_properties, - self._block_widget.selected_properties, - ) = ( - self.namespace, - self.base_name, - self.all_properties, - self.selected_properties, - ) - self.update_button() - return update + self.SetLabel(f" {self.state.namespace}:{self.state.base_name}") + self.SetToolTip(f"{self.state.namespace}:{self.state.base_name}") def demo(): From ddc419e4f5112adb205e4b1f9e40023cce3e7b3c Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Wed, 6 Oct 2021 16:11:01 +0100 Subject: [PATCH 129/139] Reformatted and cleanup --- .../api/wx/ui/mc/block/define/button/normal.py | 4 +++- .../api/wx/ui/mc/block/define/button/wildcard.py | 8 ++++++-- .../api/wx/ui/mc/block/properties/single/base.py | 2 +- .../api/wx/ui/mc/block/properties/single/vanilla.py | 2 +- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py index 869efa0e..bd7eafd8 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py @@ -61,7 +61,9 @@ def update_button(self): """Update the text on the button from the internal state.""" blockstate = f"{self.state.namespace}:{self.state.base_name}" if self.state.properties: - props = ",".join(f"{key}={val}" for key, val in self.state.properties.items()) + props = ",".join( + f"{key}={val}" for key, val in self.state.properties.items() + ) blockstate = f"{blockstate}[{props}]" self.SetLabel(f" {blockstate}") self.SetToolTip(blockstate) diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py index ebc632e9..82445fb4 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py @@ -66,9 +66,13 @@ def update_button(self): f"{key}:({'|'.join([v.to_snbt() for v in val])})" for key, val in self.state.properties_multiple.items() ] - self.SetLabel(f" {self.state.namespace}:{self.state.base_name}[{','.join(properties)}]") + self.SetLabel( + f" {self.state.namespace}:{self.state.base_name}[{','.join(properties)}]" + ) properties_str = ",\n".join(properties) - self.SetToolTip(f"{self.state.namespace}:{self.state.base_name}[\n{properties_str}\n]") + self.SetToolTip( + f"{self.state.namespace}:{self.state.base_name}[\n{properties_str}\n]" + ) else: self.SetLabel(f" {self.state.namespace}:{self.state.base_name}") self.SetToolTip(f"{self.state.namespace}:{self.state.base_name}") diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/single/base.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/base.py index b1485b84..81b1d9c1 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/single/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/single/base.py @@ -1,6 +1,6 @@ import wx -from amulet.api.block import PropertyType, PropertyValueType +from amulet.api.block import PropertyType from amulet_map_editor.api.wx.ui.mc.state import StateHolder, BlockState, State from .events import SinglePropertiesChangeEvent diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/single/vanilla.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/vanilla.py index 6fdae229..322ee137 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/single/vanilla.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/single/vanilla.py @@ -2,7 +2,7 @@ from typing import Dict, Tuple from amulet.api.block import PropertyType, PropertyValueType -from amulet_map_editor.api.wx.ui.mc.state import BlockState, State +from amulet_map_editor.api.wx.ui.mc.state import BlockState from amulet_map_editor.api.wx.ui.simple import ChoiceRaw from .base import BaseSingleProperty From dfebe1e3ac5e6e93f7831c9abd20513d0c001800 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Wed, 6 Oct 2021 16:11:14 +0100 Subject: [PATCH 130/139] Removed the old state system --- .../api/wx/ui/mc/api/__init__.py | 3 - amulet_map_editor/api/wx/ui/mc/api/biome.py | 33 ---- .../api/wx/ui/mc/api/block/__init__.py | 3 - .../api/wx/ui/mc/api/block/identifier.py | 29 --- .../api/wx/ui/mc/api/block/normal.py | 168 ------------------ .../api/wx/ui/mc/api/block/wildcard.py | 148 --------------- .../api/wx/ui/mc/api/platform.py | 109 ------------ .../api/wx/ui/mc/api/resource_id.py | 96 ---------- amulet_map_editor/api/wx/ui/mc/api/version.py | 112 ------------ 9 files changed, 701 deletions(-) delete mode 100644 amulet_map_editor/api/wx/ui/mc/api/__init__.py delete mode 100644 amulet_map_editor/api/wx/ui/mc/api/biome.py delete mode 100644 amulet_map_editor/api/wx/ui/mc/api/block/__init__.py delete mode 100644 amulet_map_editor/api/wx/ui/mc/api/block/identifier.py delete mode 100644 amulet_map_editor/api/wx/ui/mc/api/block/normal.py delete mode 100644 amulet_map_editor/api/wx/ui/mc/api/block/wildcard.py delete mode 100644 amulet_map_editor/api/wx/ui/mc/api/platform.py delete mode 100644 amulet_map_editor/api/wx/ui/mc/api/resource_id.py delete mode 100644 amulet_map_editor/api/wx/ui/mc/api/version.py diff --git a/amulet_map_editor/api/wx/ui/mc/api/__init__.py b/amulet_map_editor/api/wx/ui/mc/api/__init__.py deleted file mode 100644 index ef9bea1c..00000000 --- a/amulet_map_editor/api/wx/ui/mc/api/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .platform import BaseMCPlatformAPI, BaseMC, BaseMCPlatform -from .version import BaseMCVersionAPI, BaseMCVersion -from .block import * diff --git a/amulet_map_editor/api/wx/ui/mc/api/biome.py b/amulet_map_editor/api/wx/ui/mc/api/biome.py deleted file mode 100644 index 8e71f922..00000000 --- a/amulet_map_editor/api/wx/ui/mc/api/biome.py +++ /dev/null @@ -1,33 +0,0 @@ -from typing import Optional - -from .resource_id import BaseMCResourceIDAPI, BaseMCResourceID - - -class BaseMCBiomeIdentifierAPI(BaseMCResourceIDAPI): - pass - - -class BaseMCBiomeIdentifier(BaseMCResourceID, BaseMCBiomeIdentifierAPI): - def _set_namespace(self, namespace: Optional[str]): - if namespace is None: - self._namespace = ( - self._translation_manager.get_version( - self.platform, self.version_number - ) - .biome.biome_ids[0] - .split(":", 1)[0] - ) - else: - self._namespace = str(namespace) - - def _set_base_name(self, base_name: Optional[str]): - if base_name is None: - self._base_name = ( - self._translation_manager.get_version( - self.platform, self.version_number - ) - .biome.biome_ids[0] - .split(":", 1)[-1] - ) - else: - self._base_name = str(base_name) diff --git a/amulet_map_editor/api/wx/ui/mc/api/block/__init__.py b/amulet_map_editor/api/wx/ui/mc/api/block/__init__.py deleted file mode 100644 index 59d3c708..00000000 --- a/amulet_map_editor/api/wx/ui/mc/api/block/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .identifier import BaseMCBlockIdentifierAPI, BaseMCBlockIdentifier -from .normal import NormalMCBlockAPI, NormalMCBlock -from .wildcard import WildcardMCBlockAPI, WildcardMCBlock diff --git a/amulet_map_editor/api/wx/ui/mc/api/block/identifier.py b/amulet_map_editor/api/wx/ui/mc/api/block/identifier.py deleted file mode 100644 index 4bcfd9d7..00000000 --- a/amulet_map_editor/api/wx/ui/mc/api/block/identifier.py +++ /dev/null @@ -1,29 +0,0 @@ -from typing import Optional - -from ..resource_id import BaseMCResourceIDAPI, BaseMCResourceID - - -class BaseMCBlockIdentifierAPI(BaseMCResourceIDAPI): - pass - - -class BaseMCBlockIdentifier(BaseMCResourceID, BaseMCBlockIdentifierAPI): - def _set_namespace(self, namespace: Optional[str]): - if namespace is None: - self._namespace = self._translation_manager.get_version( - self.platform, self.version_number - ).block.namespaces(self.force_blockstate)[0] - else: - self._namespace = str(namespace) - - def _set_base_name(self, base_name: Optional[str]): - if base_name is None: - blocks = self._translation_manager.get_version( - self.platform, self.version_number - ).block.base_names(self.namespace, self.force_blockstate) - if blocks: - self._base_name = blocks[0] - else: - self._base_name = "" - else: - self._base_name = str(base_name) diff --git a/amulet_map_editor/api/wx/ui/mc/api/block/normal.py b/amulet_map_editor/api/wx/ui/mc/api/block/normal.py deleted file mode 100644 index e470463a..00000000 --- a/amulet_map_editor/api/wx/ui/mc/api/block/normal.py +++ /dev/null @@ -1,168 +0,0 @@ -from typing import Optional - -import PyMCTranslate -import amulet_nbt -from amulet.api.data_types import VersionNumberTuple -from amulet.api.block import PropertyType, Block, PropertyDataTypes - -# from amulet.api.block_entity import BlockEntity - -from .identifier import BaseMCBlockIdentifierAPI, BaseMCBlockIdentifier - - -class NormalMCBlockAPI(BaseMCBlockIdentifierAPI): - @property - def properties(self) -> PropertyType: - """The active properties.""" - raise NotImplementedError - - @properties.setter - def properties(self, properties: PropertyType): - """ - Set the active properties. - Changes will propagate to the end of this UI. - No events will be created. - - :param properties: The properties to set. - """ - raise NotImplementedError - - @property - def block(self) -> Block: - """The active block.""" - raise NotImplementedError - - @block.setter - def block(self, block: Block): - """ - Set the active block. - Namespace, base name and properties will be pulled from this. - Changes will propagate to the end of this UI. - No events will be created. - - :param block: The block to set. - """ - raise NotImplementedError - - # @property - # def block_entity(self) -> Optional[BlockEntity]: - # raise NotImplementedError - # - # @block_entity.setter - # def block_entity(self, block_entity: Optional[BlockEntity]): - # raise NotImplementedError - - # @property - # def universal_block(self) -> Tuple[Block, Optional[BlockEntity]]: - # raise NotImplementedError - # - # @universal_block.setter - # def universal_block(self, universal_block: Tuple[Block, Optional[BlockEntity]]): - # raise NotImplementedError - - -class NormalMCBlock(BaseMCBlockIdentifier, NormalMCBlockAPI): - def __init__( - self, - translation_manager: PyMCTranslate.TranslationManager, - platform: str = None, - version_number: VersionNumberTuple = None, - force_blockstate: bool = False, - namespace: str = None, - base_name: str = None, - properties: PropertyType = None, - ): - super().__init__( - translation_manager, - platform, - version_number, - force_blockstate, - namespace, - base_name, - ) - self._properties = None - self._set_properties(properties) - - @property - def properties(self) -> PropertyType: - return self._properties - - @properties.setter - def properties(self, properties: PropertyType): - self._set_properties(properties) - self._schedule_push() - - def _set_properties(self, properties: Optional[PropertyType]): - """ - Set the active properties. - Changes will not propagate. - :meth:`push` must be called once all desired states are set. - - :param properties: A dictionary mapping the property name to the property value. - """ - self._properties = {} - block_manager = self._translation_manager.get_version( - self.platform, self.version_number - ).block - if self.namespace in block_manager.namespaces( - self.force_blockstate - ) and self.base_name in block_manager.base_names( - self.namespace, self.force_blockstate - ): - # Vanilla block. Make sure the property is valid - block_spec = block_manager.get_specification( - self.namespace, self.base_name, self.force_blockstate - ) - props = block_spec.get("properties", {}) - defaults = block_spec.get("defaults", {}) - if isinstance(properties, dict): - for name, ps in props.items(): - if ( - name in properties - and isinstance(properties[name], PropertyDataTypes) - and properties[name].to_snbt() in ps - ): - self._properties[name] = properties[name] - else: - self._properties[name] = amulet_nbt.from_snbt(defaults[name]) - else: - for name, snbt in defaults.items(): - self._properties[name] = amulet_nbt.from_snbt(snbt) - elif isinstance(properties, dict): - for name, nbt in properties.items(): - if isinstance(nbt, PropertyDataTypes): - self._properties[name] = nbt - - @property - def block(self) -> Block: - return Block(self.namespace, self.base_name, self.properties) - - @block.setter - def block(self, block: Block): - self._set_block(block) - self._schedule_push() - - def _set_block(self, block: Optional[Block]): - """ - Set the active block. - Namespace, base name and properties will be pulled from this. - Changes will not propagate. - :meth:`push` must be called once all desired states are set. - - :param block: The block to set. - """ - self._set_namespace(block.namespace) - self._set_base_name(block.base_name) - self._set_properties(block.properties) - - # @property - # def block_entity(self) -> Optional[BlockEntity]: - # return self._block_entity - # - # @block_entity.setter - # def block_entity(self, block_entity: Optional[BlockEntity]): - # self._set_block_entity(block_entity) - # self._schedule_push() - # - # def _set_block_entity(self, block_entity: Optional[BlockEntity]): - # raise NotImplementedError diff --git a/amulet_map_editor/api/wx/ui/mc/api/block/wildcard.py b/amulet_map_editor/api/wx/ui/mc/api/block/wildcard.py deleted file mode 100644 index ea087db4..00000000 --- a/amulet_map_editor/api/wx/ui/mc/api/block/wildcard.py +++ /dev/null @@ -1,148 +0,0 @@ -from typing import Optional -import PyMCTranslate -import amulet_nbt -from amulet.api.data_types import VersionNumberTuple -from amulet.api.block import PropertyTypeMultiple, PropertyDataTypes - -from .identifier import BaseMCBlockIdentifierAPI, BaseMCBlockIdentifier - - -class WildcardMCBlockAPI(BaseMCBlockIdentifierAPI): - @property - def all_properties(self) -> PropertyTypeMultiple: - """The values that exist for every property.""" - raise NotImplementedError - - @all_properties.setter - def all_properties(self, properties: PropertyTypeMultiple): - raise NotImplementedError - - @property - def selected_properties(self) -> PropertyTypeMultiple: - """The values that are selected for every property.""" - raise NotImplementedError - - @selected_properties.setter - def selected_properties(self, properties: PropertyTypeMultiple): - raise NotImplementedError - - -class WildcardMCBlock(BaseMCBlockIdentifier, WildcardMCBlockAPI): - def __init__( - self, - translation_manager: PyMCTranslate.TranslationManager, - platform: str = None, - version_number: VersionNumberTuple = None, - force_blockstate: bool = False, - namespace: str = None, - base_name: str = None, - selected_properties: PropertyTypeMultiple = None, - all_properties: PropertyTypeMultiple = None, - ): - super().__init__( - translation_manager, - platform, - version_number, - force_blockstate, - namespace, - base_name, - ) - self._all_properties = None - self._selected_properties = None - self._set_all_properties(all_properties) - self._set_selected_properties(selected_properties) - - def _block_manager(self): - return self._translation_manager.get_version( - self.platform, self.version_number - ).block - - @property - def is_vanilla(self): - block_manager = self._block_manager() - return self.namespace in block_manager.namespaces( - self.force_blockstate - ) and self.base_name in block_manager.base_names( - self.namespace, self.force_blockstate - ) - - @property - def all_properties(self) -> PropertyTypeMultiple: - return self._all_properties - - @all_properties.setter - def all_properties(self, all_properties: PropertyTypeMultiple): - self._set_all_properties(all_properties) - self._schedule_push() - - def _clean_properties( - self, properties: Optional[PropertyTypeMultiple] - ) -> PropertyTypeMultiple: - out_properties: PropertyTypeMultiple = {} - if self.is_vanilla: - # Vanilla block. Make sure the property is valid - block_spec = self._block_manager().get_specification( - self.namespace, self.base_name, self.force_blockstate - ) - props = block_spec.get("properties", {}) - if isinstance(properties, dict): - for name, snbts in props.items(): - if name in properties: - out_properties[name] = tuple( - nbt - for nbt in properties[name] - if isinstance(nbt, PropertyDataTypes) - and nbt.to_snbt() in snbts - ) - else: - out_properties[name] = tuple( - amulet_nbt.from_snbt(snbt) for snbt in snbts - ) - else: - for name, snbts in props.items(): - out_properties[name] = tuple( - amulet_nbt.from_snbt(snbt) for snbt in snbts - ) - elif isinstance(properties, dict): - for name, nbts in properties.items(): - out_properties[name] = tuple( - nbt for nbt in nbts if isinstance(nbt, PropertyDataTypes) - ) - return out_properties - - def _set_all_properties(self, all_properties: Optional[PropertyTypeMultiple]): - self._all_properties: PropertyTypeMultiple = self._clean_properties( - all_properties - ) - - @property - def selected_properties(self) -> PropertyTypeMultiple: - return self._selected_properties - - @selected_properties.setter - def selected_properties(self, selected_properties: PropertyTypeMultiple): - self._set_selected_properties(selected_properties) - self._schedule_push() - - def _set_selected_properties( - self, selected_properties: Optional[PropertyTypeMultiple] - ): - if self.is_vanilla: - self._selected_properties = { - name: tuple(nbt for nbt in nbts if nbt in self.all_properties[name]) - for name, nbts in self._clean_properties(selected_properties).items() - if name in self.all_properties - } - else: - selected_properties = self._clean_properties(selected_properties) - all_properties = self.all_properties - for name, nbts in selected_properties.items(): - if name in all_properties: - ap = all_properties[name] - all_properties[name] = ap + tuple( - nbt for nbt in nbts if nbt not in ap - ) - else: - all_properties[name] = nbts - self._all_properties = all_properties - self._selected_properties = selected_properties diff --git a/amulet_map_editor/api/wx/ui/mc/api/platform.py b/amulet_map_editor/api/wx/ui/mc/api/platform.py deleted file mode 100644 index f1dec330..00000000 --- a/amulet_map_editor/api/wx/ui/mc/api/platform.py +++ /dev/null @@ -1,109 +0,0 @@ -from typing import Optional -import wx -import PyMCTranslate -from amulet.api.data_types import PlatformType - - -class BaseMCPlatformAPI: - @property - def platform(self) -> PlatformType: - """The active platform.""" - raise NotImplementedError - - @platform.setter - def platform(self, platform: PlatformType): - """ - Set the active platform. - Changes will propagate to the end of this UI. - No events will be created. - - :param platform: The platform string to set. - """ - raise NotImplementedError - - -""" -update method should compare the internal state to the UI state and update as required. This should not create events. -Setter methods must change the set_x method and call the update logic to propagate the changes. -set_x methods should just change the internal state. Other code must call the update method -user changing the UI should propagate the changes and create one event at the end. -""" - - -class BaseMC: - """A class to store the translation manager.""" - - def __init__( - self, - translation_manager: PyMCTranslate.TranslationManager, - ): - self._translation_manager = translation_manager - self._changed = False - - -class BaseMCPlatform(BaseMC, BaseMCPlatformAPI): - """A class to store the state of the platform.""" - - def __init__( - self, - translation_manager: PyMCTranslate.TranslationManager, - platform: str = None, - ): - super().__init__(translation_manager) - self._platform = None - self._set_platform(platform) - - def _schedule_push(self): - """Schedule the pushing of the internal data to the UI.""" - self._changed = True - push = self.push - if isinstance(self, wx.Window): - wx.CallAfter(push) - - def push(self, force=False) -> bool: - """ - Push the internal state to the UI. - No events should be created when calling this method. - - :return: True if data was changed - """ - ret = False - if self._changed or force: - if isinstance(self, wx.Window): - self.Freeze() - ret = self._on_push() - if isinstance(self, wx.Window): - self.Thaw() - self._changed = False - return ret - - def _on_push(self) -> bool: - """ - Push the internal state to the UI. - No events should be created when calling this method. - - :return: True if data was changed - """ - raise NotImplementedError - - @property - def platform(self) -> PlatformType: - return self._platform - - @platform.setter - def platform(self, platform: PlatformType): - self._set_platform(platform) - self._schedule_push() - - def _set_platform(self, platform: Optional[PlatformType]): - """ - Set the active platform. - Changes will not propagate. - :meth:`push` must be called once all desired states are set. - - :param platform: The platform string to set. - """ - if platform is not None and platform in self._translation_manager.platforms(): - self._platform = platform - else: - self._platform = self._translation_manager.platforms()[0] diff --git a/amulet_map_editor/api/wx/ui/mc/api/resource_id.py b/amulet_map_editor/api/wx/ui/mc/api/resource_id.py deleted file mode 100644 index c2e1d8c5..00000000 --- a/amulet_map_editor/api/wx/ui/mc/api/resource_id.py +++ /dev/null @@ -1,96 +0,0 @@ -from typing import Optional - -import PyMCTranslate -from amulet.api.data_types import VersionNumberTuple -from .version import BaseMCVersionAPI, BaseMCVersion - - -class BaseMCResourceIDAPI(BaseMCVersionAPI): - @property - def namespace(self) -> str: - """The active namespace.""" - raise NotImplementedError - - @namespace.setter - def namespace(self, namespace: str): - """ - Set the active namespace. - Changes will propagate to the end of this UI. - No events will be created. - - :param namespace: The namespace to set. - """ - raise NotImplementedError - - @property - def base_name(self) -> str: - """The active base name.""" - raise NotImplementedError - - @base_name.setter - def base_name(self, base_name: str): - """ - Set the active base name. - Changes will propagate to the end of this UI. - No events will be created. - - :param base_name: The base name to set. - """ - raise NotImplementedError - - -class BaseMCResourceID(BaseMCVersion, BaseMCResourceIDAPI): - def __init__( - self, - translation_manager: PyMCTranslate.TranslationManager, - platform: str = None, - version_number: VersionNumberTuple = None, - force_blockstate: bool = False, - namespace: str = None, - base_name: str = None, - ): - super().__init__( - translation_manager, platform, version_number, force_blockstate - ) - self._namespace = None - self._set_namespace(namespace) - self._base_name = None - self._set_base_name(base_name) - - @property - def namespace(self) -> str: - return self._namespace - - @namespace.setter - def namespace(self, namespace: str): - self._set_namespace(namespace) - self._schedule_push() - - def _set_namespace(self, namespace: Optional[str]): - """ - Set the active namespace. - Changes will not propagate. - :meth:`push` must be called once all desired states are set. - - :param namespace: The namespace to set. - """ - raise NotImplementedError - - @property - def base_name(self) -> str: - return self._base_name - - @base_name.setter - def base_name(self, base_name: str): - self._set_base_name(base_name) - self._schedule_push() - - def _set_base_name(self, base_name: Optional[str]): - """ - Set the active base name. - Changes will not propagate. - :meth:`push` must be called once all desired states are set. - - :param base_name: The base name to set. - """ - raise NotImplementedError diff --git a/amulet_map_editor/api/wx/ui/mc/api/version.py b/amulet_map_editor/api/wx/ui/mc/api/version.py deleted file mode 100644 index 1c488478..00000000 --- a/amulet_map_editor/api/wx/ui/mc/api/version.py +++ /dev/null @@ -1,112 +0,0 @@ -from typing import Optional -import PyMCTranslate -from amulet.api.data_types import VersionNumberTuple -from .platform import BaseMCPlatformAPI, BaseMCPlatform - - -class BaseMCVersionAPI(BaseMCPlatformAPI): - @property - def version_number(self) -> VersionNumberTuple: - """The active version tuple.""" - raise NotImplementedError - - @version_number.setter - def version_number(self, version_number: VersionNumberTuple): - """ - Set the active version tuple. - Changes will propagate to the end of this UI. - No events will be created. - - :param version_number: The version number to set. - """ - raise NotImplementedError - - @property - def force_blockstate(self) -> bool: - """ - Is the block format native (False) or blockstate (True) - Note in cases where the native is blockstate this option does nothing. - """ - raise NotImplementedError - - @force_blockstate.setter - def force_blockstate(self, force_blockstate: bool): - """ - Set if blockstate is forced. - Changes will propagate to the end of this UI. - No events will be created. - - :param force_blockstate: False for the native format, True for the blockstate format. - """ - raise NotImplementedError - - -class BaseMCVersion(BaseMCPlatform, BaseMCVersionAPI): - """A class to store the state of the platform, version tuple and force blockstate.""" - - def __init__( - self, - translation_manager: PyMCTranslate.TranslationManager, - platform: str = None, - version_number: VersionNumberTuple = None, - force_blockstate: bool = False, - ): - super().__init__(translation_manager, platform) - self._version_number = None - self._set_version_number(version_number) - self._force_blockstate = None - self._set_force_blockstate(force_blockstate) - - @property - def version_number(self) -> VersionNumberTuple: - return self._version_number - - @version_number.setter - def version_number(self, version_number: VersionNumberTuple): - self._set_version_number(version_number) - self._schedule_push() - - def _set_version_number(self, version_number: Optional[VersionNumberTuple]): - """ - Set the active version tuple. - Changes will not propagate. - :meth:`push` must be called once all desired states are set. - - :param version_number: The version number to set. - """ - v = None - if version_number is not None: - if version_number in self._translation_manager.version_numbers( - self.platform - ): - self._version_number = v = version_number - else: - try: - self._version_number = v = self._translation_manager.get_version( - self.platform, version_number - ).version_number - except KeyError: - pass - if v is None: - self._version_number = max( - self._translation_manager.version_numbers(self.platform) - ) - - @property - def force_blockstate(self) -> bool: - return self._force_blockstate - - @force_blockstate.setter - def force_blockstate(self, force_blockstate: bool): - self._set_force_blockstate(force_blockstate) - self._schedule_push() - - def _set_force_blockstate(self, force_blockstate: Optional[bool]): - """ - Set if blockstate is forced. - Changes will not propagate. - :meth:`push` must be called once all desired states are set. - - :param force_blockstate: False for the native format, True for the blockstate format. - """ - self._force_blockstate = bool(force_blockstate) From a8b99e692ba00a9ab098f5f7ecd7d42c0cebcaef Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Wed, 6 Oct 2021 16:22:37 +0100 Subject: [PATCH 131/139] Fixed error deepcopying the state class --- amulet_map_editor/api/wx/ui/mc/state.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/amulet_map_editor/api/wx/ui/mc/state.py b/amulet_map_editor/api/wx/ui/mc/state.py index 53a6fe0d..91a6ea6c 100644 --- a/amulet_map_editor/api/wx/ui/mc/state.py +++ b/amulet_map_editor/api/wx/ui/mc/state.py @@ -1,8 +1,8 @@ from abc import ABC, abstractmethod from enum import Enum from typing import Callable, List, Dict, Any, Union, Tuple, Optional +import copy -import amulet_nbt from PyMCTranslate import TranslationManager, Version from PyMCTranslate.py3.api.version.translators.block import BlockSpecification from amulet.api.data_types import PlatformType, VersionNumberTuple, VersionNumberAny @@ -107,6 +107,12 @@ def unbind_on_change(self, on_change: OnChangeType): def translation_manager(self) -> TranslationManager: return self._translation_manager + def __deepcopy__(self, memodict=None): + new_state = self.__class__(self.translation_manager) + new_state._state = copy.deepcopy(self._state) + new_state._changed_state = copy.deepcopy(self._state) + return new_state + class StateHolder: _state: BaseState From 75b5d37aa830f85d713c04c93a66f9129b0eaa67 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Thu, 7 Oct 2021 17:20:45 +0100 Subject: [PATCH 132/139] Compacted the state manager Parent arguments are passed through as kwargs rather than redeclaring each argument. Added support to limit platforms and block formats within the state. This was previously done in the UI. --- amulet_map_editor/api/wx/ui/mc/state.py | 75 ++++++++++++------------- 1 file changed, 36 insertions(+), 39 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/state.py b/amulet_map_editor/api/wx/ui/mc/state.py index 91a6ea6c..62fcb75c 100644 --- a/amulet_map_editor/api/wx/ui/mc/state.py +++ b/amulet_map_editor/api/wx/ui/mc/state.py @@ -47,7 +47,7 @@ class BaseState(ABC): _changed_state: Dict[State, Any] _on_change: List[OnChangeType] - def __init__(self, translation_manager: TranslationManager): + def __init__(self, translation_manager: TranslationManager, **kwargs): self._translation_manager = translation_manager self._edit = False # Is the instance being edited self._state = {} # The actual state @@ -142,9 +142,16 @@ def __init__( translation_manager: TranslationManager, *, platform: str = None, + allow_universal: bool = False, + allow_vanilla: bool = True, + platform_key: Callable[[str], bool] = None, + **kwargs, ): - super().__init__(translation_manager) + super().__init__(translation_manager, **kwargs) self._state[State.Platform] = self._sanitise_platform(platform) + self._allow_universal = allow_universal + self._allow_vanilla = allow_vanilla + self._platform_key = platform_key def _fix_new_state(self): if self.is_changed(State.Platform): @@ -160,7 +167,15 @@ def _sanitise_platform(self, platform: str = None) -> PlatformType: @property def valid_platforms(self) -> List[PlatformType]: - return self._translation_manager.platforms() + return [ + p + for p in self._translation_manager.platforms() + if ( + self._allow_universal + if p == "universal" + else (self._platform_key is None or self._platform_key(p)) + ) + ] @property def platform(self) -> PlatformType: @@ -176,15 +191,19 @@ def __init__( self, translation_manager: TranslationManager, *, - platform: str = None, version_number: VersionNumberAny = None, + allow_numerical: bool = True, + allow_blockstate: bool = True, force_blockstate: bool = None, + **kwargs, ): - super().__init__(translation_manager, platform=platform) + super().__init__(translation_manager, **kwargs) self._state[State.VersionNumber] = self._sanitise_version(version_number) self._state[State.ForceBlockstate] = self._sanitise_force_blockstate( force_blockstate ) + self._allow_numerical = allow_numerical + self._allow_blockstate = allow_blockstate def _fix_new_state(self): super()._fix_new_state() @@ -223,7 +242,12 @@ def _sanitise_version( @property def valid_version_numbers(self) -> List[VersionNumberTuple]: - return self._translation_manager.version_numbers(self.platform) + return [ + v + for v in self._translation_manager.version_numbers(self.platform) + if (v >= (1, 13, 0) and self._allow_blockstate) + or (v < (1, 13, 0) and self._allow_numerical) + ] @property def version_number(self) -> VersionNumberTuple: @@ -257,17 +281,10 @@ def __init__( self, translation_manager: TranslationManager, *, - platform: str = None, - version_number: VersionNumberAny = None, - force_blockstate: bool = None, namespace: str = None, + **kwargs, ): - super().__init__( - translation_manager, - platform=platform, - version_number=version_number, - force_blockstate=force_blockstate, - ) + super().__init__(translation_manager, **kwargs) self._state[State.Namespace] = self._sanitise_namespace(namespace) def _fix_new_state(self): @@ -302,19 +319,10 @@ def __init__( self, translation_manager: TranslationManager, *, - platform: str = None, - version_number: VersionNumberAny = None, - force_blockstate: bool = None, - namespace: str = None, base_name: str = None, + **kwargs, ): - super().__init__( - translation_manager, - platform=platform, - version_number=version_number, - force_blockstate=force_blockstate, - namespace=namespace, - ) + super().__init__(translation_manager, **kwargs) self._state[State.BaseName] = self._sanitise_base_name(base_name) def _fix_new_state(self): @@ -408,23 +416,12 @@ def __init__( self, translation_manager: TranslationManager, *, - platform: str = None, - version_number: VersionNumberAny = None, - force_blockstate: bool = None, - namespace: str = None, - base_name: str = None, properties: PropertyType = None, properties_multiple: PropertyTypeMultiple = None, valid_properties: PropertyTypeMultiple = None, + **kwargs, ): - super().__init__( - translation_manager, - platform=platform, - version_number=version_number, - force_blockstate=force_blockstate, - namespace=namespace, - base_name=base_name, - ) + super().__init__(translation_manager, **kwargs) ( self._state[State.Properties], self._state[State.PropertiesMultiple], From 4654b1a8f0a3ef0501f7f1de60a854e1a9c7c9ac Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Mon, 11 Oct 2021 16:37:07 +0100 Subject: [PATCH 133/139] Simplified the widget init method The previous init method could be given either a state class or the translation manager and optional data from which the state would be constructed. This was needlessly complicated. The new init method is given just the state. That can be constructed and given or the classmethod from_data will create it and pass it to the init emulating the old behaviour. --- .../api/wx/ui/mc/base/base_define.py | 19 +++-- .../base_identifier_select.py | 35 +++----- .../api/wx/ui/mc/biome/biome_define.py | 42 +++++----- .../biome_identifier_select.py | 29 +++---- .../api/wx/ui/mc/block/define/button/base.py | 21 ++++- .../wx/ui/mc/block/define/button/normal.py | 41 +--------- .../wx/ui/mc/block/define/button/wildcard.py | 43 +--------- .../api/wx/ui/mc/block/define/widget/base.py | 29 +++++-- .../wx/ui/mc/block/define/widget/normal.py | 38 ++------- .../wx/ui/mc/block/define/widget/wildcard.py | 37 ++------- .../block_identifier_select.py | 29 +++---- .../api/wx/ui/mc/block/properties/base.py | 32 ++++---- .../ui/mc/block/properties/multiple/main.py | 33 ++------ .../wx/ui/mc/block/properties/single/main.py | 28 +------ amulet_map_editor/api/wx/ui/mc/state.py | 24 ++++-- .../api/wx/ui/mc/version/platform_select.py | 61 ++++++-------- .../api/wx/ui/mc/version/version_select.py | 79 ++++++------------- .../export_operations/construction.py | 2 +- .../export_operations/mcstructure.py | 2 +- .../export_operations/schematic.py | 2 +- .../export_operations/sponge_schematic.py | 2 +- .../stock_plugins/operations/fill.py | 4 +- .../stock_plugins/operations/replace.py | 8 +- .../stock_plugins/operations/waterlog.py | 4 +- 24 files changed, 232 insertions(+), 412 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_define.py b/amulet_map_editor/api/wx/ui/mc/base/base_define.py index bc47d886..d9375dc4 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/base_define.py +++ b/amulet_map_editor/api/wx/ui/mc/base/base_define.py @@ -2,7 +2,6 @@ import wx.lib.scrolledpanel import PyMCTranslate - from amulet_map_editor.api.wx.ui.mc.base.base_identifier_select import ( BaseIdentifierSelect, ) @@ -21,9 +20,8 @@ class BaseDefine(wx.Panel, StateHolder): def __init__( self, parent, - translation_manager: PyMCTranslate.TranslationManager, + state: BaseResourceIDState, *, - state: BaseResourceIDState = None, orientation=wx.VERTICAL, ): assert isinstance(state, BaseResourceIDState) @@ -40,12 +38,21 @@ def __init__( else: self._sizer.Add(self._top_sizer, 0, wx.EXPAND) - self._version_picker = VersionSelect( - self, translation_manager, state=self.state - ) + self._version_picker = VersionSelect(self, state) self._version_picker.Bind(EVT_VERSION_CHANGE, self._post_change) self._top_sizer.Add(self._version_picker, 0, wx.EXPAND) self.Layout() + @classmethod + def from_data( + cls, + parent: wx.Window, + translation_manager: PyMCTranslate.TranslationManager, + *, + orientation=wx.VERTICAL, + **kwargs, + ): + raise NotImplementedError + def _post_change(self, evt): raise NotImplementedError diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py index f09abcca..664bd5e7 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py +++ b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py @@ -1,8 +1,6 @@ import wx - import PyMCTranslate -from amulet.api.data_types import VersionNumberTuple from amulet_map_editor.api.image import COLOUR_PICKER from amulet_map_editor.api.wx.ui.mc.state import BaseResourceIDState, StateHolder, State from .events import ( @@ -23,25 +21,11 @@ class BaseIdentifierSelect(wx.Panel, StateHolder): def __init__( self, parent: wx.Window, - translation_manager: PyMCTranslate.TranslationManager, + state: BaseResourceIDState, *, - state: BaseResourceIDState = None, - platform: str = None, - version_number: VersionNumberTuple = None, - force_blockstate: bool = None, - namespace: str = None, - base_name: str = None, show_pick: bool = False, ): - if not isinstance(state, BaseResourceIDState): - state = self._create_default_state( - translation_manager, - platform=platform, - version_number=version_number, - force_blockstate=force_blockstate, - namespace=namespace, - base_name=base_name, - ) + assert isinstance(state, BaseResourceIDState) StateHolder.__init__(self, state) wx.Panel.__init__(self, parent, style=wx.BORDER_SIMPLE) self._sizer = wx.BoxSizer(wx.VERTICAL) @@ -90,16 +74,15 @@ def __init__( self._update_base_name() self._base_name_list_box.Bind(wx.EVT_LISTBOX, self._on_base_name_change) - def _create_default_state( - self, + @classmethod + def from_data( + cls, + parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager, *, - platform: str = None, - version_number: VersionNumberTuple = None, - force_blockstate: bool = None, - namespace: str = None, - base_name: str = None, - ) -> BaseResourceIDState: + show_pick: bool = False, + **kwargs, + ): raise NotImplementedError @property diff --git a/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py b/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py index 2ecd929f..2cf215b3 100644 --- a/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py +++ b/amulet_map_editor/api/wx/ui/mc/biome/biome_define.py @@ -63,40 +63,42 @@ class BiomeDefine(BaseDefine): def __init__( self, parent, - translation_manager: PyMCTranslate.TranslationManager, + state: BiomeResourceIDState, *, - state: BiomeResourceIDState = None, - platform: str = None, - version_number: VersionNumberTuple = None, - namespace: str = None, - base_name: str = None, show_pick_biome: bool = False, orientation=wx.VERTICAL, ): - if not isinstance(state, BiomeResourceIDState): - state = BiomeResourceIDState( - translation_manager, - platform=platform, - version_number=version_number, - force_blockstate=False, - namespace=namespace, - base_name=base_name, - ) + assert isinstance(state, BiomeResourceIDState) super().__init__( parent, - translation_manager, - state=state, + state, orientation=orientation, ) self._identifier_select = BiomeIdentifierSelect( self, - translation_manager, - state=state, + state, show_pick=show_pick_biome, ) self._identifier_select.Bind(EVT_BIOME_ID_CHANGE, self._post_change) self._top_sizer.Add(self._identifier_select, 1, wx.EXPAND | wx.TOP, 5) + @classmethod + def from_data( + cls, + parent: wx.Window, + translation_manager: PyMCTranslate.TranslationManager, + *, + show_pick_biome: bool = False, + orientation=wx.VERTICAL, + **kwargs, + ): + return cls( + parent, + BiomeResourceIDState(translation_manager, **kwargs), + show_pick_biome=show_pick_biome, + orientation=orientation, + ) + def _post_change(self, evt): wx.PostEvent( self, @@ -123,7 +125,7 @@ def demo(): ) sizer = wx.BoxSizer() dialog.SetSizer(sizer) - obj = BiomeDefine(dialog, translation_manager, orientation=wx.HORIZONTAL) + obj = BiomeDefine.from_data(dialog, translation_manager, orientation=wx.HORIZONTAL) def on_biome_change(evt: BiomeChangeEvent): print( diff --git a/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/biome_identifier_select.py b/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/biome_identifier_select.py index a2a8c5ca..a6001ff8 100644 --- a/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/biome_identifier_select.py +++ b/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/biome_identifier_select.py @@ -1,7 +1,6 @@ import wx import PyMCTranslate -from amulet.api.data_types import VersionNumberTuple from amulet_map_editor.api.wx.ui.mc.state import BiomeResourceIDState from amulet_map_editor.api.wx.ui.mc.base.base_identifier_select import ( BaseIdentifierSelect, @@ -21,23 +20,19 @@ class BiomeIdentifierSelect(BaseIdentifierSelect): def type_name(self) -> str: return "Biome" - def _create_default_state( - self, + @classmethod + def from_data( + cls, + parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager, *, - platform: str = None, - version_number: VersionNumberTuple = None, - force_blockstate: bool = None, - namespace: str = None, - base_name: str = None, - ) -> BiomeResourceIDState: - return BiomeResourceIDState( - translation_manager, - platform=platform, - version_number=version_number, - force_blockstate=force_blockstate, - namespace=namespace, - base_name=base_name, + show_pick: bool = False, + **kwargs + ): + return cls( + parent, + BiomeResourceIDState(translation_manager, **kwargs), + show_pick=show_pick, ) def _post_event( @@ -69,7 +64,7 @@ def demo(): ) sizer = wx.BoxSizer() dialog.SetSizer(sizer) - widget = BiomeIdentifierSelect( + widget = BiomeIdentifierSelect.from_data( dialog, translation_manager, platform="java", diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py index 2ab1d9ca..000e751c 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py @@ -1,6 +1,7 @@ import wx from typing import Optional +import PyMCTranslate from amulet_map_editor.api.wx.ui.mc.block.define import BaseBlockDefine from amulet_map_editor.api.wx.ui.mc.state import StateHolder, BlockState from amulet_map_editor.api.wx.ui.simple import SimpleDialog @@ -12,8 +13,8 @@ class BaseBlockDefineButton(wx.Button, StateHolder): def __init__( self, parent: wx.Window, + state: BlockState, *, - state: BlockState = None, show_pick_block: bool = False, max_char_length: int = 99999, ): @@ -24,6 +25,24 @@ def __init__( self.Bind(wx.EVT_BUTTON, self._on_press) self._show_pick_block = show_pick_block self._max_char_length = max(3, max_char_length) + self.update_button() + + @classmethod + def from_data( + cls, + parent: wx.Window, + translation_manager: PyMCTranslate.TranslationManager, + *, + show_pick_block: bool = False, + max_char_length: int = 99999, + **kwargs, + ): + return cls( + parent, + BlockState(translation_manager, **kwargs), + show_pick_block=show_pick_block, + max_char_length=max_char_length, + ) def SetLabel(self, label: str): if len(label) > self._max_char_length: diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py index bd7eafd8..bf850c42 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py @@ -2,58 +2,21 @@ import copy import PyMCTranslate -from amulet.api.data_types import VersionNumberTuple from amulet_map_editor.api.wx.ui.mc.block.define.widget import BlockDefine from amulet_map_editor.api.wx.ui.mc.block.define.button.base import ( BaseBlockDefineButton, ) -from amulet.api.block import PropertyType from amulet_map_editor.api.wx.ui.mc.state import BlockState class BlockDefineButton(BaseBlockDefineButton): state: BlockState - def __init__( - self, - parent: wx.Window, - translation_manager: PyMCTranslate.TranslationManager, - *, - state: BlockState = None, - platform: str = None, - version_number: VersionNumberTuple = None, - force_blockstate: bool = None, - namespace: str = None, - base_name: str = None, - properties: PropertyType = None, - show_pick_block: bool = False, - max_char_length: int = 99999, - ): - if not isinstance(state, BlockState): - state = BlockState( - translation_manager, - platform=platform, - version_number=version_number, - force_blockstate=force_blockstate, - namespace=namespace, - base_name=base_name, - properties=properties, - ) - BaseBlockDefineButton.__init__( - self, - parent, - state=state, - show_pick_block=show_pick_block, - max_char_length=max_char_length, - ) - self.update_button() - def _create_block_define(self, dialog: wx.Dialog) -> BlockDefine: return BlockDefine( dialog, - self.state.translation_manager, - state=copy.deepcopy(self.state), + copy.deepcopy(self.state), orientation=wx.HORIZONTAL, ) @@ -83,7 +46,7 @@ def demo(): sizer = wx.BoxSizer() dialog.SetSizer(sizer) sizer.Add( - BlockDefineButton(dialog, translation_manager), + BlockDefineButton.from_data(dialog, translation_manager), 0, wx.ALL, 5, diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py index 82445fb4..8d82815f 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py @@ -2,60 +2,21 @@ import copy import PyMCTranslate -from amulet.api.data_types import VersionNumberTuple from amulet_map_editor.api.wx.ui.mc.block.define.widget import WildcardBlockDefine from amulet_map_editor.api.wx.ui.mc.block.define.button.base import ( BaseBlockDefineButton, ) -from amulet.api.block import PropertyTypeMultiple from amulet_map_editor.api.wx.ui.mc.state import BlockState class WildcardBlockDefineButton(BaseBlockDefineButton): state: BlockState - def __init__( - self, - parent: wx.Window, - translation_manager: PyMCTranslate.TranslationManager, - *, - state: BlockState = None, - platform: str = None, - version_number: VersionNumberTuple = None, - force_blockstate: bool = None, - namespace: str = None, - base_name: str = None, - selected_properties: PropertyTypeMultiple = None, - all_properties: PropertyTypeMultiple = None, - show_pick_block: bool = False, - max_char_length: int = 99999, - ): - if not isinstance(state, BlockState): - state = BlockState( - translation_manager, - platform=platform, - version_number=version_number, - force_blockstate=force_blockstate, - namespace=namespace, - base_name=base_name, - properties_multiple=selected_properties, - valid_properties=all_properties, - ) - BaseBlockDefineButton.__init__( - self, - parent, - state=state, - show_pick_block=show_pick_block, - max_char_length=max_char_length, - ) - self.update_button() - def _create_block_define(self, dialog: wx.Dialog) -> WildcardBlockDefine: return WildcardBlockDefine( dialog, - self.state.translation_manager, - state=copy.deepcopy(self.state), + copy.deepcopy(self.state), orientation=wx.HORIZONTAL, ) @@ -92,7 +53,7 @@ def demo(): sizer = wx.BoxSizer() dialog.SetSizer(sizer) sizer.Add( - WildcardBlockDefineButton(dialog, translation_manager), + WildcardBlockDefineButton.from_data(dialog, translation_manager), 0, wx.ALL, 5, diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py index 73908333..8a8c9fc6 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py @@ -20,9 +20,8 @@ class BaseBlockDefine(BaseDefine): def __init__( self, parent, - translation_manager: PyMCTranslate.TranslationManager, + state: BlockState, *, - state: BlockState = None, orientation=wx.VERTICAL, show_pick_block: bool = False, ): @@ -30,15 +29,33 @@ def __init__( BaseDefine.__init__( self, parent, - translation_manager, - state=state, + state, orientation=orientation, ) self._identifier_select = BlockIdentifierSelect( self, - translation_manager, - state=self.state, + state, show_pick=show_pick_block, ) self._identifier_select.Bind(EVT_BLOCK_ID_CHANGE, self._post_change) self._top_sizer.Add(self._identifier_select, 1, wx.EXPAND | wx.TOP, 5) + + @classmethod + def from_data( + cls, + parent: wx.Window, + translation_manager: PyMCTranslate.TranslationManager, + *, + orientation=wx.VERTICAL, + show_pick_block: bool = False, + **kwargs, + ): + return cls( + parent, + BlockState( + translation_manager, + **kwargs, + ), + orientation=orientation, + show_pick_block=show_pick_block, + ) diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py index 85139c25..1e43f727 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py @@ -3,8 +3,6 @@ import PyMCTranslate import amulet_nbt -from amulet.api.data_types import VersionNumberTuple -from amulet.api.block import PropertyType from amulet_map_editor.api.wx.ui.events import EVT_CHILD_SIZE from amulet_map_editor.api.wx.ui.mc.block import ( @@ -30,33 +28,15 @@ class BlockDefine(BaseBlockDefine): def __init__( self, parent, - translation_manager: PyMCTranslate.TranslationManager, + state: BlockState, *, - state: BlockState = None, - platform: str = None, - version_number: VersionNumberTuple = None, - force_blockstate: bool = None, - namespace: str = None, - base_name: str = None, - properties: PropertyType = None, show_pick_block: bool = False, orientation=wx.VERTICAL, ): - if not isinstance(state, BlockState): - state = BlockState( - translation_manager, - platform=platform, - version_number=version_number, - force_blockstate=force_blockstate, - namespace=namespace, - base_name=base_name, - properties=properties, - ) - BaseBlockDefine.__init__( - self, + assert isinstance(state, BlockState) + super().__init__( parent, - translation_manager, - state=state, + state, orientation=orientation, show_pick_block=show_pick_block, ) @@ -64,15 +44,13 @@ def __init__( right_sizer = wx.BoxSizer(wx.VERTICAL) border = wx.LEFT if orientation == wx.HORIZONTAL else wx.TOP self._sizer.Add(right_sizer, 1, wx.EXPAND | border, 5) - self._property_picker = self._create_property_picker(translation_manager) + self._property_picker = self._create_property_picker() self._property_picker.Bind(EVT_SINGLE_PROPERTIES_CHANGE, self._post_change) right_sizer.Add(self._property_picker, 1, wx.EXPAND) self.Layout() - def _create_property_picker( - self, translation_manager: PyMCTranslate.TranslationManager - ) -> SinglePropertySelect: - return SinglePropertySelect(self, translation_manager, state=self.state) + def _create_property_picker(self) -> SinglePropertySelect: + return SinglePropertySelect(self, self.state) def _post_change(self, evt): wx.PostEvent( @@ -103,7 +81,7 @@ def create_dialog(block): ) sizer = wx.BoxSizer() dialog.SetSizer(sizer) - obj = BlockDefine( + obj = BlockDefine.from_data( dialog, translation_manager, platform="java", diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py index 1fd00c4e..3e485e5e 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py @@ -3,8 +3,6 @@ import PyMCTranslate import amulet_nbt -from amulet.api.data_types import VersionNumberTuple -from amulet.api.block import PropertyTypeMultiple from amulet_map_editor.api.wx.ui.events import EVT_CHILD_SIZE from amulet_map_editor.api.wx.ui.mc.block import ( @@ -30,35 +28,16 @@ class WildcardBlockDefine(BaseBlockDefine): def __init__( self, parent, - translation_manager: PyMCTranslate.TranslationManager, + state: BlockState, *, - state: BlockState = None, - platform: str = None, - version_number: VersionNumberTuple = None, - force_blockstate: bool = None, - namespace: str = None, - base_name: str = None, - selected_properties: PropertyTypeMultiple = None, - all_properties: PropertyTypeMultiple = None, show_pick_block: bool = False, orientation=wx.VERTICAL, ): - if not isinstance(state, BlockState): - state = BlockState( - translation_manager, - platform=platform, - version_number=version_number, - force_blockstate=force_blockstate, - namespace=namespace, - base_name=base_name, - properties_multiple=selected_properties, - valid_properties=all_properties, - ) + assert isinstance(state, BlockState) BaseBlockDefine.__init__( self, parent, - translation_manager, - state=state, + state, orientation=orientation, show_pick_block=show_pick_block, ) @@ -66,9 +45,7 @@ def __init__( right_sizer = wx.BoxSizer(wx.VERTICAL) border = wx.LEFT if orientation == wx.HORIZONTAL else wx.TOP self._sizer.Add(right_sizer, 1, wx.EXPAND | border, 5) - self._property_picker = MultiplePropertySelect( - self, translation_manager, state=state - ) + self._property_picker = MultiplePropertySelect(self, state) right_sizer.Add(self._property_picker, 1, wx.EXPAND) self._property_picker.Bind(EVT_MULTIPLE_PROPERTIES_CHANGE, self._post_change) @@ -103,7 +80,7 @@ def create_dialog(block): ) sizer = wx.BoxSizer() dialog.SetSizer(sizer) - obj = WildcardBlockDefine( + obj = WildcardBlockDefine.from_data( dialog, translation_manager, platform="java", @@ -147,7 +124,7 @@ def on_child_size(evt): { "namespace": "minecraft", "base_name": "oak_fence", - "selected_properties": { + "properties_multiple": { "east": ( amulet_nbt.TAG_String("true"), amulet_nbt.TAG_String("false"), @@ -163,7 +140,7 @@ def on_child_size(evt): { "namespace": "modded", "base_name": "block", - "selected_properties": { + "properties_multiple": { "test": ( amulet_nbt.TAG_String("hello"), amulet_nbt.TAG_String("hello2"), diff --git a/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py b/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py index aec079c6..d4f823ff 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py +++ b/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py @@ -1,7 +1,6 @@ import wx import PyMCTranslate -from amulet.api.data_types import VersionNumberTuple from amulet_map_editor.api.wx.ui.mc.base.base_identifier_select import ( BaseIdentifierSelect, ) @@ -21,23 +20,19 @@ class BlockIdentifierSelect(BaseIdentifierSelect): def type_name(self) -> str: return "Block" - def _create_default_state( - self, + @classmethod + def from_data( + cls, + parent: wx.Window, translation_manager: PyMCTranslate.TranslationManager, *, - platform: str = None, - version_number: VersionNumberTuple = None, - force_blockstate: bool = None, - namespace: str = None, - base_name: str = None, - ) -> BlockResourceIDState: - return BlockResourceIDState( - translation_manager, - platform=platform, - version_number=version_number, - force_blockstate=force_blockstate, - namespace=namespace, - base_name=base_name, + show_pick: bool = False, + **kwargs + ): + return cls( + parent, + BlockResourceIDState(translation_manager, **kwargs), + show_pick=show_pick, ) def _post_event( @@ -69,7 +64,7 @@ def demo(): ) sizer = wx.BoxSizer() dialog.SetSizer(sizer) - widget = BlockIdentifierSelect( + widget = BlockIdentifierSelect.from_data( dialog, translation_manager, platform="java", diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/base.py b/amulet_map_editor/api/wx/ui/mc/block/properties/base.py index eb6f5f29..319732de 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/base.py @@ -1,7 +1,6 @@ import wx import PyMCTranslate -from amulet.api.data_types import VersionNumberTuple from amulet_map_editor.api.wx.ui.mc.state import StateHolder, BlockState @@ -9,25 +8,22 @@ class BasePropertySelect(wx.Panel, StateHolder): def __init__( self, parent: wx.Window, - translation_manager: PyMCTranslate.TranslationManager, - *, - state: BlockState = None, - platform: str = None, - version_number: VersionNumberTuple = None, - force_blockstate: bool = None, - namespace: str = None, - base_name: str = None, + state: BlockState, ): - if not isinstance(state, BlockState): - state = BlockState( - translation_manager, - platform=platform, - version_number=version_number, - force_blockstate=force_blockstate, - namespace=namespace, - base_name=base_name, - ) + assert isinstance(state, BlockState) StateHolder.__init__(self, state) wx.Panel.__init__(self, parent) self._sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(self._sizer) + + @classmethod + def from_data( + cls, + parent: wx.Window, + translation_manager: PyMCTranslate.TranslationManager, + **kwargs + ): + return cls( + parent, + BlockState(translation_manager, **kwargs), + ) diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py index e5997abf..50221113 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py @@ -2,8 +2,6 @@ import PyMCTranslate import amulet_nbt -from amulet.api.data_types import VersionNumberTuple -from amulet.api.block import PropertyTypeMultiple from amulet_map_editor.api.wx.ui.mc.state import State, BlockState from amulet_map_editor.api.wx.ui.events import EVT_CHILD_SIZE @@ -28,33 +26,12 @@ class MultiplePropertySelect(BasePropertySelect): def __init__( self, parent: wx.Window, - translation_manager: PyMCTranslate.TranslationManager, - *, - state: BlockState = None, - platform: str = None, - version_number: VersionNumberTuple = None, - force_blockstate: bool = None, - namespace: str = None, - base_name: str = None, - all_properties: PropertyTypeMultiple = None, - selected_properties: PropertyTypeMultiple = None, + state: BlockState, ): - if not isinstance(state, BlockState): - state = BlockState( - translation_manager, - platform=platform, - version_number=version_number, - force_blockstate=force_blockstate, - namespace=namespace, - base_name=base_name, - properties_multiple=selected_properties, - valid_properties=all_properties, - ) BasePropertySelect.__init__( self, parent, - translation_manager, - state=state, + state, ) self._vanilla = self._create_automatic() @@ -94,7 +71,7 @@ def create_dialog(block): ) sizer = wx.BoxSizer() dialog.SetSizer(sizer) - obj = MultiplePropertySelect( + obj = MultiplePropertySelect.from_data( dialog, translation_manager, platform="java", @@ -130,7 +107,7 @@ def on_child_size(evt): { "namespace": "minecraft", "base_name": "oak_fence", - "selected_properties": { + "properties_multiple": { "east": ( amulet_nbt.TAG_String("true"), amulet_nbt.TAG_String("false"), @@ -146,7 +123,7 @@ def on_child_size(evt): { "namespace": "modded", "base_name": "block", - "selected_properties": { + "properties_multiple": { "test": ( amulet_nbt.TAG_String("hello"), amulet_nbt.TAG_String("hello2"), diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py index 352af767..60da48e8 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py @@ -25,30 +25,8 @@ class SinglePropertySelect(BasePropertySelect): _vanilla: VanillaSingleProperty _modded: ModdedSingleProperty - def __init__( - self, - parent: wx.Window, - translation_manager: PyMCTranslate.TranslationManager, - *, - state: BlockState = None, - platform: str = None, - version_number: VersionNumberTuple = None, - force_blockstate: bool = None, - namespace: str = None, - base_name: str = None, - properties: PropertyType = None, - ): - if not isinstance(state, BlockState): - state = BlockState( - translation_manager, - platform=platform, - version_number=version_number, - force_blockstate=force_blockstate, - namespace=namespace, - base_name=base_name, - properties=properties, - ) - super().__init__(parent, translation_manager, state=state) + def __init__(self, parent: wx.Window, state: BlockState): + super().__init__(parent, state) self._vanilla = self._create_automatic() self._sizer.Add(self._vanilla, 0, wx.EXPAND) @@ -87,7 +65,7 @@ def create_dialog(block): ) sizer = wx.BoxSizer() dialog.SetSizer(sizer) - obj = SinglePropertySelect( + obj = SinglePropertySelect.from_data( dialog, translation_manager, platform="java", diff --git a/amulet_map_editor/api/wx/ui/mc/state.py b/amulet_map_editor/api/wx/ui/mc/state.py index 62fcb75c..6434ae5b 100644 --- a/amulet_map_editor/api/wx/ui/mc/state.py +++ b/amulet_map_editor/api/wx/ui/mc/state.py @@ -118,8 +118,9 @@ class StateHolder: _state: BaseState def __init__(self, state: BaseState): - self._state = None - self.state = state + assert isinstance(state, BaseState) + self._state = state + self._state.bind_on_change(self._on_state_change) @property def state(self): @@ -148,10 +149,10 @@ def __init__( **kwargs, ): super().__init__(translation_manager, **kwargs) - self._state[State.Platform] = self._sanitise_platform(platform) self._allow_universal = allow_universal self._allow_vanilla = allow_vanilla self._platform_key = platform_key + self._state[State.Platform] = self._sanitise_platform(platform) def _fix_new_state(self): if self.is_changed(State.Platform): @@ -185,6 +186,13 @@ def platform(self) -> PlatformType: def platform(self, platform: PlatformType): self._set_state(State.Platform, platform) + def __deepcopy__(self, memodict=None): + new_state = super().__deepcopy__(memodict=memodict) + new_state._allow_universal = self._allow_universal + new_state._allow_vanilla = self._allow_vanilla + new_state._platform_key = self._platform_key + return new_state + class VersionState(PlatformState): def __init__( @@ -198,12 +206,12 @@ def __init__( **kwargs, ): super().__init__(translation_manager, **kwargs) + self._allow_numerical = allow_numerical + self._allow_blockstate = allow_blockstate self._state[State.VersionNumber] = self._sanitise_version(version_number) self._state[State.ForceBlockstate] = self._sanitise_force_blockstate( force_blockstate ) - self._allow_numerical = allow_numerical - self._allow_blockstate = allow_blockstate def _fix_new_state(self): super()._fix_new_state() @@ -275,6 +283,12 @@ def force_blockstate(self) -> bool: def force_blockstate(self, force_blockstate: bool): self._set_state(State.ForceBlockstate, force_blockstate) + def __deepcopy__(self, memodict=None): + new_state = super().__deepcopy__(memodict=memodict) + new_state._allow_numerical = self._allow_numerical + new_state._allow_blockstate = self._allow_blockstate + return new_state + class BaseNamespaceState(VersionState): def __init__( diff --git a/amulet_map_editor/api/wx/ui/mc/version/platform_select.py b/amulet_map_editor/api/wx/ui/mc/version/platform_select.py index c3bfe8b0..29ed0d59 100644 --- a/amulet_map_editor/api/wx/ui/mc/version/platform_select.py +++ b/amulet_map_editor/api/wx/ui/mc/version/platform_select.py @@ -1,9 +1,8 @@ from amulet_map_editor.api.wx.ui.simple import SimpleChoice import wx import PyMCTranslate -from typing import Tuple, Type, Any, Dict +from typing import Type, Any -from amulet.api.data_types import PlatformType from .events import PlatformChangeEvent, EVT_PLATFORM_CHANGE from amulet_map_editor.api.wx.ui.mc.state import PlatformState, StateHolder, State @@ -18,36 +17,20 @@ class PlatformSelect(wx.Panel, StateHolder): def __init__( self, parent: wx.Window, - translation_manager: PyMCTranslate.TranslationManager, - *, - state: PlatformState = None, - platform: PlatformType = None, - allow_universal: bool = True, - allow_vanilla: bool = True, - allowed_platforms: Tuple[PlatformType, ...] = None, - style: Dict[str, Any] = None, + state: PlatformState, + **kwargs, ): """ Construct a :class:`PlatformSelect` UI. :param parent: The parent window. - :param translation_manager: The translation manager to populate from. :param state: optional PlatformSelect instance holding the state of the platform. - :param platform: The default platform (optional). If state is defined this will not be used. - :param allow_universal: If True the universal format will be included. - :param allow_vanilla: If True the vanilla formats will be included. - :param allowed_platforms: A whitelist of platforms. - :param style: Dictionary of keyword args to be given to the Panel. """ # init the state - if not isinstance(state, PlatformState): - state = PlatformState(translation_manager, platform=platform) StateHolder.__init__(self, state) # init the panel - style = style or {} - style.setdefault("style", wx.BORDER_SIMPLE) - wx.Panel.__init__(self, parent, **style) + wx.Panel.__init__(self, parent, style=wx.BORDER_SIMPLE) sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(sizer) self._sizer = wx.FlexGridSizer(2, 5, 5) @@ -56,9 +39,6 @@ def __init__( self._sizer.AddGrowableCol(0) self._sizer.AddGrowableCol(1) - # self._allow_universal = allow_universal - # self._allow_vanilla = allow_vanilla - # self._allowed_platforms = allowed_platforms self._platform_choice: SimpleChoice = self._add_ui_element( "Platform:", SimpleChoice ) @@ -68,6 +48,25 @@ def __init__( self._on_platform_change, ) + @classmethod + def from_data( + cls, + parent: wx.Window, + translation_manager: PyMCTranslate.TranslationManager, + **kwargs, + ): + """ + :param parent: The parent window. + :param translation_manager: The translation manager to populate from. + """ + return cls( + parent, + PlatformState( + translation_manager, + **kwargs, + ), + ) + def _add_ui_element( self, label: str, obj: Type[wx.Control], shown=True, **kwargs ) -> Any: @@ -80,18 +79,6 @@ def _add_ui_element( wx_obj.Hide() return wx_obj - # def _populate_platform(self): - # """Update the UI with the valid platforms.""" - # TODO - # platforms = self._translation_manager.platforms() - # if self._allowed_platforms is not None: - # platforms = [p for p in platforms if p in self._allowed_platforms] - # if not self._allow_universal: - # platforms = [p for p in platforms if p != "universal"] - # if not self._allow_vanilla: - # platforms = [p for p in platforms if p == "universal"] - # self._platform_choice.SetItems(self.state.valid_platforms) - def _update_platform(self): """Push the internal platform state to the UI.""" self._platform_choice.SetItems(self.state.valid_platforms) @@ -128,7 +115,7 @@ def demo(): ) sizer = wx.BoxSizer() dialog.SetSizer(sizer) - obj = PlatformSelect(dialog, translation_manager, platform="java") + obj = PlatformSelect.from_data(dialog, translation_manager, platform="java") sizer.Add( obj, 1, diff --git a/amulet_map_editor/api/wx/ui/mc/version/version_select.py b/amulet_map_editor/api/wx/ui/mc/version/version_select.py index 669c2179..0f7e9eff 100644 --- a/amulet_map_editor/api/wx/ui/mc/version/version_select.py +++ b/amulet_map_editor/api/wx/ui/mc/version/version_select.py @@ -1,7 +1,7 @@ from amulet_map_editor.api.wx.ui.simple import SimpleChoice, ChoiceRaw import wx import PyMCTranslate -from typing import Optional, Dict, Any, Tuple +from typing import Optional from amulet.api.data_types import VersionNumberTuple, PlatformType from .platform_select import PlatformSelect @@ -19,57 +19,18 @@ class VersionSelect(PlatformSelect): def __init__( self, parent: wx.Window, - translation_manager: PyMCTranslate.TranslationManager, + state: VersionState, *, - state: VersionState = None, - platform: PlatformType = None, - version_number: VersionNumberTuple = None, - force_blockstate: bool = None, - allow_universal: bool = True, - allow_vanilla: bool = True, - allowed_platforms: Tuple[PlatformType, ...] = None, show_force_blockstate: bool = True, - allow_numerical: bool = True, - allow_blockstate: bool = True, - style: Dict[str, Any] = None, + **kwargs, ): """ Construct a :class:`VersionSelect` UI. :param parent: The parent window. - :param translation_manager: The translation manager to populate from. :param state: optional VersionSelect instance holding the state of the platform and version. - :param platform: The default platform (optional). If state is defined this will not be used. - :param version_number: The default version number (optional). If state is defined this will not be used. - :param force_blockstate: If True and the native format is numerical will use the custom blockstate format. Else will use the native format. If state is defined this will not be used. - :param allow_universal: If True the universal format will be included. - :param allow_vanilla: If True the vanilla formats will be included. - :param allowed_platforms: A whitelist of platforms. - :param show_force_blockstate: Should the format selection be shown to the user. - :param allow_numerical: Should the numerical versions be shown to the user. - :param allow_blockstate: Should the blockstate versions be shown to the user. - :param style: Dictionary of keyword args to be given to the Panel. """ - # init the state - if not isinstance(state, VersionState): - state = VersionState( - translation_manager, - platform=platform, - version_number=version_number, - force_blockstate=force_blockstate, - ) - - super().__init__( - parent, - translation_manager, - state=state, - allow_universal=allow_universal, - allow_vanilla=allow_vanilla, - allowed_platforms=allowed_platforms, - style=style, - ) - self._allow_numerical = allow_numerical - self._allow_blockstate = allow_blockstate + super().__init__(parent, state, **kwargs) self._version_choice: Optional[ChoiceRaw] = self._add_ui_element( "Version:", ChoiceRaw, reverse=True, sort=True @@ -87,15 +48,25 @@ def __init__( self._update_force_blockstate() self._blockstate_choice.Bind(wx.EVT_CHOICE, self._on_blockstate_change) - # def _populate_version(self): - # """Populate the version UI element""" - # TODO - # versions = self._translation_manager.version_numbers(self.platform) - # if not self._allow_blockstate: - # versions = [v for v in versions if v < (1, 13, 0)] - # if not self._allow_numerical: - # versions = [v for v in versions if v >= (1, 13, 0)] - # self._version_choice.SetItems(versions) + @classmethod + def from_data( + cls, + parent: wx.Window, + translation_manager: PyMCTranslate.TranslationManager, + *, + show_force_blockstate: bool = True, + **kwargs, + ): + """ + :param parent: The parent window. + :param translation_manager: The translation manager to populate from. + :param show_force_blockstate: Should the format selection be shown to the user. + """ + return cls( + parent, + VersionState(translation_manager, **kwargs), + show_force_blockstate=show_force_blockstate, + ) def _update_version_number(self): """Push the internal version number state to the UI.""" @@ -149,10 +120,10 @@ def demo(): translation_manager = PyMCTranslate.new_translation_manager() for cls, title in ( ( - lambda *args: VersionSelect(*args, show_force_blockstate=False), + lambda *args: VersionSelect.from_data(*args, show_force_blockstate=False), "VersionSelect without format choice", ), - (VersionSelect, "VersionSelect with format choice"), + (VersionSelect.from_data, "VersionSelect with format choice"), ): dialog = wx.Dialog( None, title=title, style=wx.DEFAULT_DIALOG_STYLE | wx.DIALOG_NO_PARENT diff --git a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/construction.py b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/construction.py index 0022d5e1..38ddb4f7 100644 --- a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/construction.py +++ b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/construction.py @@ -37,7 +37,7 @@ def __init__( style=wx.FLP_USE_TEXTCTRL | wx.FLP_SAVE | wx.FLP_OVERWRITE_PROMPT, ) self._sizer.Add(self._file_picker, 0, wx.ALL | wx.CENTER, 5) - self._version_define = VersionSelect( + self._version_define = VersionSelect.from_data( self, world.translation_manager, platform=options.get("platform", None) or world.level_wrapper.platform, diff --git a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/mcstructure.py b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/mcstructure.py index 467c75d1..96ad62ae 100644 --- a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/mcstructure.py +++ b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/mcstructure.py @@ -37,7 +37,7 @@ def __init__( style=wx.FLP_USE_TEXTCTRL | wx.FLP_SAVE | wx.FLP_OVERWRITE_PROMPT, ) self._sizer.Add(self._file_picker, 0, wx.ALL | wx.CENTER, 5) - self._version_define = VersionSelect( + self._version_define = VersionSelect.from_data( self, world.translation_manager, platform=options.get("platform", None) or world.level_wrapper.platform, diff --git a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/schematic.py b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/schematic.py index 3c1897ef..3b51e832 100644 --- a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/schematic.py +++ b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/schematic.py @@ -48,7 +48,7 @@ def __init__( style=wx.FLP_USE_TEXTCTRL | wx.FLP_SAVE, ) self._sizer.Add(self._file_picker, 0, wx.ALL | wx.CENTER, 5) - self._platform_define = PlatformSelect( + self._platform_define = PlatformSelect.from_data( self, world.translation_manager, platform=options.get("platform", None) or world.level_wrapper.platform, diff --git a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/sponge_schematic.py b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/sponge_schematic.py index a1961318..c71341d5 100644 --- a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/sponge_schematic.py +++ b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/export_operations/sponge_schematic.py @@ -37,7 +37,7 @@ def __init__( style=wx.FLP_USE_TEXTCTRL | wx.FLP_SAVE | wx.FLP_OVERWRITE_PROMPT, ) self._sizer.Add(self._file_picker, 0, wx.ALL | wx.CENTER, 5) - self._version_define = VersionSelect( + self._version_define = VersionSelect.from_data( self, world.translation_manager, platform=options.get("platform", None) or world.level_wrapper.platform, diff --git a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/fill.py b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/fill.py index 82193334..04f3a93a 100644 --- a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/fill.py +++ b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/fill.py @@ -28,10 +28,10 @@ def __init__( options = self._load_options({}) - self._block_define = BlockDefine( + self._block_define = BlockDefine.from_data( self, world.translation_manager, - wx.VERTICAL, + orientation=wx.VERTICAL, *(options.get("fill_block_options", []) or [world.level_wrapper.platform]), show_pick_block=True ) diff --git a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/replace.py b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/replace.py index fe37bb89..7ead4924 100644 --- a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/replace.py +++ b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/replace.py @@ -27,10 +27,10 @@ def __init__( self.Freeze() options = self._load_options({}) - self._original_block = BlockDefine( + self._original_block = BlockDefine.from_data( self, world.level_wrapper.translation_manager, - wx.VERTICAL, + orientation=wx.VERTICAL, *( options.get("original_block_options", []) or [world.level_wrapper.platform] @@ -40,10 +40,10 @@ def __init__( ) self._sizer.Add(self._original_block, 1, wx.ALL | wx.ALIGN_CENTRE_HORIZONTAL, 5) self._original_block.Bind(EVT_PICK, lambda evt: self._on_pick_block_button(1)) - self._replacement_block = BlockDefine( + self._replacement_block = BlockDefine.from_data( self, world.level_wrapper.translation_manager, - wx.VERTICAL, + orientation=wx.VERTICAL, *( options.get("replacement_block_options", []) or [world.level_wrapper.platform] diff --git a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/waterlog.py b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/waterlog.py index c34eadad..248f5fe6 100644 --- a/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/waterlog.py +++ b/amulet_map_editor/programs/edit/plugins/operations/stock_plugins/operations/waterlog.py @@ -80,10 +80,10 @@ def on_button(evt): ) self._mode_description.Fit() - self._block_define = BlockDefine( + self._block_define = BlockDefine.from_data( self, world.level_wrapper.translation_manager, - wx.VERTICAL, + orientation=wx.VERTICAL, *(options.get("fill_block_options", []) or [world.level_wrapper.platform]), show_pick_block=True ) From 262d3ce062b4da793dc12a46592233e141e9c561 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Mon, 11 Oct 2021 16:46:22 +0100 Subject: [PATCH 134/139] Cleanup --- .../api/wx/ui/mc/block/properties/single/main.py | 2 -- amulet_map_editor/api/wx/ui/mc/state.py | 6 ++++-- amulet_map_editor/api/wx/ui/mc/version/events.py | 1 - 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py index 60da48e8..8107a624 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py @@ -2,8 +2,6 @@ import PyMCTranslate import amulet_nbt -from amulet.api.data_types import VersionNumberTuple -from amulet.api.block import PropertyType from amulet_map_editor.api.wx.ui.mc.state import State, BlockState from amulet_map_editor.api.wx.ui.events import EVT_CHILD_SIZE diff --git a/amulet_map_editor/api/wx/ui/mc/state.py b/amulet_map_editor/api/wx/ui/mc/state.py index 6434ae5b..02cb4721 100644 --- a/amulet_map_editor/api/wx/ui/mc/state.py +++ b/amulet_map_editor/api/wx/ui/mc/state.py @@ -532,7 +532,8 @@ def valid_properties(self) -> PropertyTypeMultiple: def valid_properties(self, valid_properties: PropertyTypeMultiple): self._set_state(State.ValidProperties, valid_properties) - def _sanitise_properties(self, properties: PropertyType = None) -> PropertyType: + @staticmethod + def _sanitise_properties(properties: PropertyType = None) -> PropertyType: if isinstance(properties, dict): return { key: val @@ -615,8 +616,9 @@ def properties(self) -> PropertyType: def properties(self, properties: PropertyType): self._set_state(State.Properties, properties) + @staticmethod def _sanitise_properties_multiple( - self, properties: PropertyTypeMultiple = None + properties: PropertyTypeMultiple = None, ) -> PropertyTypeMultiple: if isinstance(properties, dict): return { diff --git a/amulet_map_editor/api/wx/ui/mc/version/events.py b/amulet_map_editor/api/wx/ui/mc/version/events.py index 0fc379f1..8099e78d 100644 --- a/amulet_map_editor/api/wx/ui/mc/version/events.py +++ b/amulet_map_editor/api/wx/ui/mc/version/events.py @@ -1,5 +1,4 @@ import wx -from typing import Tuple from amulet.api.data_types import VersionNumberTuple _PlatformEventType = wx.NewEventType() From 576a8accc0fbf3e563cab0e5b86e107f87c51da4 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Wed, 13 Oct 2021 16:00:35 +0100 Subject: [PATCH 135/139] Fixed some issues with the state Added a copy method which seems better than requiring the copy library. Added a list to the state holder to store child state holders. When the state is set these will be notified of the new state. --- .../api/wx/ui/mc/base/base_define.py | 1 + .../wx/ui/mc/block/define/button/normal.py | 3 +-- .../wx/ui/mc/block/define/button/wildcard.py | 3 +-- .../api/wx/ui/mc/block/define/widget/base.py | 1 + .../wx/ui/mc/block/define/widget/normal.py | 1 + .../wx/ui/mc/block/define/widget/wildcard.py | 1 + .../ui/mc/block/properties/multiple/main.py | 2 ++ .../wx/ui/mc/block/properties/single/main.py | 2 ++ amulet_map_editor/api/wx/ui/mc/state.py | 26 ++++++++++++++----- 9 files changed, 30 insertions(+), 10 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_define.py b/amulet_map_editor/api/wx/ui/mc/base/base_define.py index d9375dc4..d8105fee 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/base_define.py +++ b/amulet_map_editor/api/wx/ui/mc/base/base_define.py @@ -41,6 +41,7 @@ def __init__( self._version_picker = VersionSelect(self, state) self._version_picker.Bind(EVT_VERSION_CHANGE, self._post_change) self._top_sizer.Add(self._version_picker, 0, wx.EXPAND) + self._child_state_holders.append(self._version_picker) self.Layout() @classmethod diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py index bf850c42..4bc96a8a 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/normal.py @@ -1,5 +1,4 @@ import wx -import copy import PyMCTranslate @@ -16,7 +15,7 @@ class BlockDefineButton(BaseBlockDefineButton): def _create_block_define(self, dialog: wx.Dialog) -> BlockDefine: return BlockDefine( dialog, - copy.deepcopy(self.state), + self.state.copy(), orientation=wx.HORIZONTAL, ) diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py index 8d82815f..a2bbe829 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/wildcard.py @@ -1,5 +1,4 @@ import wx -import copy import PyMCTranslate @@ -16,7 +15,7 @@ class WildcardBlockDefineButton(BaseBlockDefineButton): def _create_block_define(self, dialog: wx.Dialog) -> WildcardBlockDefine: return WildcardBlockDefine( dialog, - copy.deepcopy(self.state), + self.state.copy(), orientation=wx.HORIZONTAL, ) diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py index 8a8c9fc6..d051c373 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/base.py @@ -39,6 +39,7 @@ def __init__( ) self._identifier_select.Bind(EVT_BLOCK_ID_CHANGE, self._post_change) self._top_sizer.Add(self._identifier_select, 1, wx.EXPAND | wx.TOP, 5) + self._child_state_holders.append(self._identifier_select) @classmethod def from_data( diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py index 1e43f727..d3f4c1aa 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/normal.py @@ -47,6 +47,7 @@ def __init__( self._property_picker = self._create_property_picker() self._property_picker.Bind(EVT_SINGLE_PROPERTIES_CHANGE, self._post_change) right_sizer.Add(self._property_picker, 1, wx.EXPAND) + self._child_state_holders.append(self._property_picker) self.Layout() def _create_property_picker(self) -> SinglePropertySelect: diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py b/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py index 3e485e5e..95fda80d 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/widget/wildcard.py @@ -48,6 +48,7 @@ def __init__( self._property_picker = MultiplePropertySelect(self, state) right_sizer.Add(self._property_picker, 1, wx.EXPAND) self._property_picker.Bind(EVT_MULTIPLE_PROPERTIES_CHANGE, self._post_change) + self._child_state_holders.append(self._property_picker) self.Layout() diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py index 50221113..66ebf1a5 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/main.py @@ -36,8 +36,10 @@ def __init__( self._vanilla = self._create_automatic() self._sizer.Add(self._vanilla, 1, wx.EXPAND) + self._child_state_holders.append(self._vanilla) self._modded = self._create_manual() self._sizer.Add(self._modded, 1, wx.EXPAND) + self._child_state_holders.append(self._modded) self._do_show() def _create_automatic(self) -> VanillaMultipleProperty: diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py index 8107a624..758e1fcd 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/single/main.py @@ -28,8 +28,10 @@ def __init__(self, parent: wx.Window, state: BlockState): self._vanilla = self._create_automatic() self._sizer.Add(self._vanilla, 0, wx.EXPAND) + self._child_state_holders.append(self._vanilla) self._modded = self._create_manual() self._sizer.Add(self._modded, 0, wx.EXPAND) + self._child_state_holders.append(self._modded) self._do_show() def _create_automatic(self) -> VanillaSingleProperty: diff --git a/amulet_map_editor/api/wx/ui/mc/state.py b/amulet_map_editor/api/wx/ui/mc/state.py index 02cb4721..3bd9e230 100644 --- a/amulet_map_editor/api/wx/ui/mc/state.py +++ b/amulet_map_editor/api/wx/ui/mc/state.py @@ -107,19 +107,29 @@ def unbind_on_change(self, on_change: OnChangeType): def translation_manager(self) -> TranslationManager: return self._translation_manager - def __deepcopy__(self, memodict=None): + def copy(self): new_state = self.__class__(self.translation_manager) new_state._state = copy.deepcopy(self._state) new_state._changed_state = copy.deepcopy(self._state) return new_state + def __copy__(self): + return self.copy() + + def __deepcopy__(self, memodict=None): + return self.copy() + class StateHolder: + # The state stored for this instance. _state: BaseState + # A list of child states. When the state changes these will be updated + _child_state_holders: List["StateHolder"] def __init__(self, state: BaseState): assert isinstance(state, BaseState) self._state = state + self._child_state_holders = [] self._state.bind_on_change(self._on_state_change) @property @@ -131,7 +141,11 @@ def state(self, state: BaseState): if self._state is not None: self._state.unbind_on_change(self._on_state_change) self._state = state - self._state.bind_on_change(self._on_state_change) + if state is not None: + self._state.bind_on_change(self._on_state_change) + self._on_state_change() + for child in self._child_state_holders: + child.state = state def _on_state_change(self): pass @@ -186,8 +200,8 @@ def platform(self) -> PlatformType: def platform(self, platform: PlatformType): self._set_state(State.Platform, platform) - def __deepcopy__(self, memodict=None): - new_state = super().__deepcopy__(memodict=memodict) + def copy(self): + new_state = super().copy() new_state._allow_universal = self._allow_universal new_state._allow_vanilla = self._allow_vanilla new_state._platform_key = self._platform_key @@ -283,8 +297,8 @@ def force_blockstate(self) -> bool: def force_blockstate(self, force_blockstate: bool): self._set_state(State.ForceBlockstate, force_blockstate) - def __deepcopy__(self, memodict=None): - new_state = super().__deepcopy__(memodict=memodict) + def copy(self): + new_state = super().copy() new_state._allow_numerical = self._allow_numerical new_state._allow_blockstate = self._allow_blockstate return new_state From fcc101e5d43ae983b8cd237ea5af2a0aed68fe44 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Wed, 13 Oct 2021 16:02:12 +0100 Subject: [PATCH 136/139] Modified the state enum The methods have been moved to a base class. The stored state must now be an instance of StrEnum to allow other places to define their own enum extensions. --- amulet_map_editor/api/wx/ui/mc/state.py | 32 +++++++++++++------------ 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/state.py b/amulet_map_editor/api/wx/ui/mc/state.py index 3bd9e230..fc005715 100644 --- a/amulet_map_editor/api/wx/ui/mc/state.py +++ b/amulet_map_editor/api/wx/ui/mc/state.py @@ -18,16 +18,7 @@ OnChangeType = Callable[[int], None] -class State(Enum): - Platform = "platform" - VersionNumber = "version_number" - ForceBlockstate = "force_blockstate" - Namespace = "namespace" - BaseName = "base_name" - Properties = "properties" - PropertiesMultiple = "properties_multiple" - ValidProperties = "valid_properties" - +class StrEnum: def __str__(self): return self.value @@ -40,11 +31,22 @@ def __hash__(self): return hash(self.value) +class State(StrEnum, Enum): + Platform = "platform" + VersionNumber = "version_number" + ForceBlockstate = "force_blockstate" + Namespace = "namespace" + BaseName = "base_name" + Properties = "properties" + PropertiesMultiple = "properties_multiple" + ValidProperties = "valid_properties" + + class BaseState(ABC): _translation_manager: TranslationManager _edit: bool - _state: Dict[State, Any] - _changed_state: Dict[State, Any] + _state: Dict[StrEnum, Any] + _changed_state: Dict[StrEnum, Any] _on_change: List[OnChangeType] def __init__(self, translation_manager: TranslationManager, **kwargs): @@ -72,17 +74,17 @@ def __exit__(self, exc_type, exc_val, exc_tb): except: log.warning(f"Error calling {on_change}", exc_info=True) - def is_changed(self, state: Union[State, str]): + def is_changed(self, state: Union[StrEnum, str]): """Check if the state has changed.""" return state in self._changed_state - def _get_state(self, state: State) -> Any: + def _get_state(self, state: StrEnum) -> Any: if state in self._changed_state: return self._changed_state[state] else: return self._state[state] - def _set_state(self, state: State, value: Any): + def _set_state(self, state: StrEnum, value: Any): if not self._edit: raise Exception("The state has not been opened for editing.") self._changed_state[state] = value From 4106b2ac7a1aa26928fafcacf0890c5aefd6fbdf Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Wed, 13 Oct 2021 16:06:52 +0100 Subject: [PATCH 137/139] Fixed some issues with the blocks buttons The text is now updated automatically when the state is changed. The child size event is fired when the state changes. The state of the widget is set to None so that the methods bound to the state get unloaded. --- .../api/wx/ui/mc/block/define/button/base.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py b/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py index 000e751c..9fd2a770 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py +++ b/amulet_map_editor/api/wx/ui/mc/block/define/button/base.py @@ -2,6 +2,7 @@ from typing import Optional import PyMCTranslate +from amulet_map_editor.api.wx.ui.events import ChildSizeEvent from amulet_map_editor.api.wx.ui.mc.block.define import BaseBlockDefine from amulet_map_editor.api.wx.ui.mc.state import StateHolder, BlockState from amulet_map_editor.api.wx.ui.simple import SimpleDialog @@ -52,16 +53,22 @@ def SetLabel(self, label: str): def _on_press(self, evt): dialog = SimpleDialog(self, "Pick a Block") self._block_widget = self._create_block_define(dialog) + self._child_state_holders.append(self._block_widget) dialog.sizer.Add(self._block_widget, 1, wx.EXPAND) dialog.Fit() if dialog.ShowModal() == wx.ID_OK: self.state = self._block_widget.state - self.update_button() + self._block_widget.state = None + self._child_state_holders.remove(self._block_widget) self._block_widget = None dialog.Destroy() def _create_block_define(self, dialog: wx.Dialog) -> BaseBlockDefine: raise NotImplementedError + def _on_state_change(self): + self.update_button() + wx.PostEvent(self, ChildSizeEvent(0)) + def update_button(self): raise NotImplementedError From 0502c305c91a671a26b4e7f66f56ddd00ef20ce5 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Wed, 13 Oct 2021 16:08:03 +0100 Subject: [PATCH 138/139] Modified the fill replace tool to use the new state system --- .../plugins/tools/fill_replace/__main__.py | 28 ++- .../fill_replace/block_container/__init__.py | 1 + .../fill_replace/block_container/base.py | 31 ++- .../block_container/block_entry/__init__.py | 1 + .../block_container/block_entry/base.py | 23 +- .../block_entry/custom_fill_button.py | 206 +++++++++++------- .../block_container/block_entry/fill.py | 33 +-- .../block_container/block_entry/find.py | 30 +-- .../fill_replace/block_container/fill.py | 8 +- .../fill_replace/block_container/find.py | 2 +- .../tools/fill_replace/fill_replace_tool.py | 40 +++- .../tools/fill_replace/fill_replace_widget.py | 59 ++--- 12 files changed, 252 insertions(+), 210 deletions(-) diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/__main__.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/__main__.py index a01a6c88..1f0b19cb 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/__main__.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/__main__.py @@ -3,7 +3,10 @@ from amulet_map_editor.programs.edit.plugins.tools.fill_replace.fill_replace_widget import ( FillReplaceWidget, ) -from amulet_map_editor.api.wx.ui.events import EVT_CHILD_SIZE, ChildSizeEvent +from amulet_map_editor.programs.edit.plugins.tools.fill_replace.block_container import ( + SrcBlockState, +) +from amulet_map_editor.api.wx.ui.events import EVT_CHILD_SIZE if __name__ == "__main__": @@ -14,10 +17,27 @@ def __init__(self, canvas): self.Add(self._panel) panel_sizer = wx.BoxSizer(wx.VERTICAL) self._panel.SetSizer(panel_sizer) - self._operations = FillReplaceWidget( - self._panel, - PyMCTranslate.new_translation_manager(), + translation_manager = PyMCTranslate.new_translation_manager() + find_state = SrcBlockState( + translation_manager, + platform="java", + version_number=(1, 16, 0), + namespace="minecraft", + base_name="air", + ) + fill_state = SrcBlockState( + translation_manager, + platform="java", + version_number=(1, 16, 0), + namespace="minecraft", + base_name="stone", ) + for state_ in (find_state, fill_state): + with state_ as state: + state.platform = "bedrock" + state.version_number = None + + self._operations = FillReplaceWidget(self._panel, find_state, fill_state) panel_sizer.Add(self._operations, 1, wx.LEFT | wx.TOP, 5) self._button = wx.Button(self._panel, label="Run Operation") panel_sizer.Add( diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/__init__.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/__init__.py index abcc4b80..c545b58b 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/__init__.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/__init__.py @@ -1,3 +1,4 @@ from .base import BaseBlockContainer from .find import FindBlockContainer from .fill import FillBlockContainer +from .block_entry import SrcBlockState diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/base.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/base.py index e1122632..ea0870f7 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/base.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/base.py @@ -1,29 +1,21 @@ from typing import List, Dict, Any import wx -import PyMCTranslate -from amulet.api.data_types import VersionNumberTuple from amulet_map_editor.api.wx.ui.events import ChildSizeEvent from .block_entry import BaseBlockEntry, EVT_BLOCK_CLOSE +from .block_entry.custom_fill_button import SrcBlockState +from amulet_map_editor.api.wx.ui.mc.state import StateHolder -class BaseBlockContainer(wx.Panel): +class BaseBlockContainer(wx.Panel, StateHolder): """This is a UI element that contains one or more block buttons.""" _blocks: List[BaseBlockEntry] + state: SrcBlockState - def __init__( - self, - parent: wx.Window, - translation_manager: PyMCTranslate.TranslationManager, - platform: str = None, - version_number: VersionNumberTuple = None, - force_blockstate: bool = None, - **kwargs, - ): - super().__init__(parent, **kwargs) - self._translation_manager = translation_manager - self._version = (platform, version_number, force_blockstate) + def __init__(self, parent: wx.Window, default_state: SrcBlockState): + StateHolder.__init__(self, default_state) + wx.Panel.__init__(self, parent) self._expert = False sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(sizer) @@ -104,10 +96,15 @@ def _do_destroy_block_entry(self, window: BaseBlockEntry): block.enable_close(False) @property - def states(self) -> List[Dict[str, Any]]: + def states(self) -> List[SrcBlockState]: return [block.state for block in self._blocks] @states.setter - def states(self, states: List[Dict[str, Any]]): + def states(self, states: List[SrcBlockState]): + while len(states) > len(self._blocks): + self._do_add_block() + while len(states) < len(self._blocks): + self._do_destroy_block_entry(self._blocks[-1]) for state, block in zip(states, self._blocks): block.state = state + self._post_change_size() diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/__init__.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/__init__.py index c2ee39eb..f3f241e1 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/__init__.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/__init__.py @@ -2,3 +2,4 @@ from .base import BaseBlockEntry from .find import FindBlockEntry from .fill import FillBlockEntry +from .custom_fill_button import SrcBlockState diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/base.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/base.py index ad33b179..034ed81e 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/base.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/base.py @@ -1,27 +1,19 @@ import wx -from typing import Optional, Dict, Any +from typing import Optional from amulet_map_editor.api.wx.ui.mc.block import BaseBlockDefineButton from .events import BlockCloseEvent +from .custom_fill_button import SrcBlockState class BaseBlockEntry(wx.Panel): """A UI element that holds a block button, weight entry and close button""" - _button_props = ( - "platform", - "version_number", - "force_blockstate", - "namespace", - "base_name", - ) - def __init__( self, parent: wx.Window, - **kwargs, ): - super().__init__(parent, **kwargs) + super().__init__(parent) self._sizer = wx.BoxSizer(wx.HORIZONTAL) self.SetSizer(self._sizer) self._block_button: Optional[BaseBlockDefineButton] = None @@ -51,10 +43,9 @@ def enable_close(self, enable: bool = True): self._close_button.Enable(enable) @property - def state(self) -> Dict[str, Any]: - return {prop: getattr(self.block_button, prop) for prop in self._button_props} + def state(self) -> SrcBlockState: + return self.block_button.state @state.setter - def state(self, state: Dict[str, Any]): - for prop in self._button_props: - setattr(self.block_button, prop, state.get(prop, None)) + def state(self, state: SrcBlockState): + self.block_button.state = state diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/custom_fill_button.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/custom_fill_button.py index 560f15e2..c2d5d17c 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/custom_fill_button.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/custom_fill_button.py @@ -1,146 +1,200 @@ import wx from typing import Dict, Any, Tuple, Optional +from enum import Enum -import PyMCTranslate -from amulet.api.data_types import VersionNumberTuple -from amulet.api.block import PropertyType, PropertyValueType +from PyMCTranslate import TranslationManager +from amulet.api.block import PropertyValueType +from amulet_map_editor.api.wx.ui.mc.state import BlockState, StrEnum, State from amulet_map_editor.api.wx.ui.mc.block import ( BlockDefineButton, BlockDefine, SinglePropertySelect, ) -from amulet_map_editor.api.wx.ui.mc.block.properties.single.automatic import ( - AutomaticSingleProperty, +from amulet_map_editor.api.wx.ui.mc.block.properties.single.vanilla import ( + BaseVanillaSingleProperty, ) -class CustomAutomaticSingleProperty(AutomaticSingleProperty): +class StateExtra(StrEnum, Enum): + FromSrc = "from_src" + + +FromSrcType = Dict[str, bool] + + +class SrcBlockState(BlockState): + def __init__( + self, + translation_manager: TranslationManager, + *, + from_source: FromSrcType = None, + **kwargs, + ): + super().__init__(translation_manager, **kwargs) + self._state[StateExtra.FromSrc] = self._sanitise_from_source(from_source) + + def _fix_new_state(self): + super()._fix_new_state() + self._state[StateExtra.FromSrc] = self._sanitise_from_source(self.from_source) + + def _sanitise_from_source(self, from_source: FromSrcType = None) -> FromSrcType: + if not isinstance(from_source, dict): + from_source = {} + return { + key: bool(from_source.get(key, True)) + for key in self.valid_properties.keys() + } + + @property + def from_source(self) -> FromSrcType: + """Should the value for this property be driven by the source block.""" + return self._get_state(StateExtra.FromSrc) + + @from_source.setter + def from_source(self, from_source: FromSrcType): + self._set_state(StateExtra.FromSrc, from_source) + + +class CustomVanillaSingleProperty(BaseVanillaSingleProperty): + """A modification of the normal vanilla property that adds a check box for each property.""" + + state: SrcBlockState + def __init__( self, parent: wx.Window, - from_source: bool = False, + state: SrcBlockState, + *, + show_from_source: bool = False, ): - self._from_source = from_source - super().__init__(parent) + assert isinstance(state, SrcBlockState) + super().__init__(parent, state) + self._show_from_source = show_from_source label = wx.StaticText(self, label="src", style=wx.ALIGN_CENTER) self._property_sizer.SetCols(3) self._property_sizer.Add(label, 1, wx.ALIGN_CENTER) - label.Show(from_source) + label.Show(show_from_source) self._property_sizer.AddGrowableCol(0) self._property_sizer.AddGrowableCol(1) self._check_boxes: Dict[str, wx.CheckBox] = {} + self._rebuild_properties() def _tear_down_properties(self): self._check_boxes.clear() super()._tear_down_properties() - def _create_property(self, name: str, choices: Tuple[PropertyValueType]): - super()._create_property(name, choices) + def _create_property( + self, + name: str, + choices: Tuple[PropertyValueType, ...], + default: PropertyValueType = None, + ): + super()._create_property(name, choices, default) check_box = wx.CheckBox(self) self._property_sizer.Add(check_box, 1, wx.ALIGN_CENTER) self._check_boxes[name] = check_box - check_box.Bind(wx.EVT_CHECKBOX, lambda evt: self._post_change()) - check_box.Show(self._from_source) + check_box.Bind(wx.EVT_CHECKBOX, lambda evt: self._on_src_change()) + check_box.Show(self._show_from_source) - def set_from_source(self, from_source: bool): - self._from_source = from_source + def show_from_source(self, show_from_source: bool): + self._show_from_source = show_from_source for i in range( 0, self._property_sizer.GetItemCount(), self._property_sizer.GetCols() ): - self._property_sizer.Show(i + 2, show=from_source) + self._property_sizer.Show(i + 2, show=show_from_source) self.Layout() + def _get_ui_src(self) -> FromSrcType: + return { + key: check_box.GetValue() for key, check_box in self._check_boxes.items() + } + + def _on_src_change(self): + from_source = self._get_ui_src() + if from_source != self.state.from_source: + with self.state as state: + state.from_source = from_source + # wx.PostEvent(self, SinglePropertiesChangeEvent(self.state.from_source)) + + def _on_state_change(self): + super()._on_state_change() + if self.state.is_changed(State.Properties) or self.state.is_changed( + StateExtra.FromSrc + ): + if self.state.from_source != self._get_ui_src(): + for key, val in self.state.from_source.items(): + self._check_boxes[key].SetValue(val) + class CustomSinglePropertySelect(SinglePropertySelect): - _simple: CustomAutomaticSingleProperty + _vanilla: CustomVanillaSingleProperty + state: SrcBlockState def __init__( self, parent: wx.Window, - translation_manager: PyMCTranslate.TranslationManager, - platform: str, - version_number: VersionNumberTuple, - force_blockstate: bool, - namespace: str = None, - base_name: str = None, - properties: PropertyType = None, - state: Dict[str, Any] = None, - from_source: bool = False, + state: SrcBlockState, + *, + show_from_source: bool = False, ): - self._from_source = from_source + self._show_from_source = show_from_source super().__init__( parent, - translation_manager, - platform, - version_number, - force_blockstate, - namespace, - base_name, - properties, - state=state, + state, ) - def _create_automatic(self) -> CustomAutomaticSingleProperty: - prop = CustomAutomaticSingleProperty(self, from_source=self._from_source) - return prop + def _create_automatic(self) -> CustomVanillaSingleProperty: + return CustomVanillaSingleProperty( + self, self.state, show_from_source=self._show_from_source + ) - def set_from_source(self, from_source: bool): - self._simple.set_from_source(from_source) + def show_from_source(self, show_from_source: bool): + self._vanilla.show_from_source(show_from_source) class CustomBlockDefine(BlockDefine): _property_picker: CustomSinglePropertySelect + state: SrcBlockState - def __init__(self, *args, from_source: bool = False, **kwargs): - self._from_source = from_source + def __init__(self, *args, show_from_source: bool = False, **kwargs): + self._show_from_source = show_from_source super().__init__(*args, **kwargs) - def _create_properties(self) -> SinglePropertySelect: + def _create_property_picker(self) -> SinglePropertySelect: return CustomSinglePropertySelect( self, - self._translation_manager, - self.platform, - self.version_number, - self.force_blockstate, - self.namespace, - self.base_name, - self.properties, - from_source=self._from_source, + self.state, + show_from_source=self._show_from_source, ) - def set_from_source(self, from_source: bool): - self._from_source = from_source - self._property_picker.set_from_source(from_source) + def show_from_source(self, show_from_source: bool): + self._show_from_source = show_from_source + self._property_picker.show_from_source(show_from_source) class CustomBlockDefineButton(BlockDefineButton): _block_widget: Optional[CustomBlockDefine] - def __init__(self, *args, from_source: bool = False, **kwargs): - self._from_source = from_source - super().__init__(*args, **kwargs) + def __init__( + self, + parent: wx.Window, + state: BlockState, + *, + show_from_source: bool = False, + **kwargs, + ): + self._show_from_source = show_from_source + super().__init__(parent, state, **kwargs) def _create_block_define(self, dialog: wx.Dialog) -> BlockDefine: return CustomBlockDefine( dialog, - self._translation_manager, - wx.HORIZONTAL, - self.platform, - self.version_number, - self.force_blockstate, - self.namespace, - self.base_name, - self.properties, - from_source=self._from_source, + self.state.copy(), + orientation=wx.HORIZONTAL, + show_from_source=self._show_from_source, ) - def _update_from_block_define(self, block_define: BlockDefine): - super()._update_from_block_define(block_define) - - # @property - # def from_source(self) -> Dict[str, bool]: - - def set_from_source(self, from_source: bool): - self._from_source = from_source + def show_from_source(self, show_from_source: bool): + self._show_from_source = show_from_source if self._block_widget is not None: - self._block_widget.set_from_source(from_source) + self._block_widget.show_from_source(show_from_source) diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/fill.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/fill.py index 24b27f40..c52007cb 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/fill.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/fill.py @@ -1,45 +1,26 @@ import wx -import PyMCTranslate -from amulet.api.data_types import VersionNumberTuple -from amulet.api.block import PropertyType from .base import BaseBlockEntry -from .custom_fill_button import CustomBlockDefineButton +from .custom_fill_button import CustomBlockDefineButton, SrcBlockState class FillBlockEntry(BaseBlockEntry): """A UI element that holds a block button, weight entry and close button""" - _button_props = BaseBlockEntry._button_props + ("properties",) - def __init__( self, parent: wx.Window, - translation_manager: PyMCTranslate.TranslationManager, - platform: str = None, - version_number: VersionNumberTuple = None, - force_blockstate: bool = None, - namespace: str = None, - base_name: str = None, - properties: PropertyType = None, - **kwargs, + state: SrcBlockState, ): - super().__init__(parent, **kwargs) + super().__init__(parent) self._init_close_button() self._block_button = CustomBlockDefineButton( self, - translation_manager, - platform, - version_number, - force_blockstate, - namespace, - base_name, - properties, + state, max_char_length=40, ) self._sizer.Add(self._block_button, 1) - self._weight = wx.SpinCtrlDouble(self, initial=1.0, min=0.0, max=1.0, inc=0.1) - self._weight.SetDigits(2) + self._weight = wx.SpinCtrl(self, initial=1, min=0, max=100) self._sizer.Add(self._weight) self.show_weight(False) @@ -61,5 +42,5 @@ def show_weight(self, show: bool = True): self._weight.Show(show) self.Layout() - def set_from_source(self, from_source: bool): - self._block_button.set_from_source(from_source) + def show_from_source(self, from_source: bool): + self._block_button.show_from_source(from_source) diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/find.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/find.py index 0d462b05..64d5e53c 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/find.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/block_entry/find.py @@ -1,45 +1,23 @@ import wx -import PyMCTranslate -from amulet.api.data_types import VersionNumberTuple -from amulet.api.block import PropertyTypeMultiple from amulet_map_editor.api.wx.ui.mc.block import WildcardBlockDefineButton from .base import BaseBlockEntry +from .custom_fill_button import SrcBlockState class FindBlockEntry(BaseBlockEntry): """A UI element that holds a wildcard block button and close button""" - _button_props = BaseBlockEntry._button_props + ( - "all_properties", - "selected_properties", - ) - def __init__( self, parent: wx.Window, - translation_manager: PyMCTranslate.TranslationManager, - platform: str = None, - version_number: VersionNumberTuple = None, - force_blockstate: bool = None, - namespace: str = None, - base_name: str = None, - selected_properties: PropertyTypeMultiple = None, - all_properties: PropertyTypeMultiple = None, - **kwargs, + state: SrcBlockState, ): - super().__init__(parent, **kwargs) + super().__init__(parent) self._init_close_button() self._block_button = WildcardBlockDefineButton( self, - translation_manager, - platform, - version_number, - force_blockstate, - namespace, - base_name, - selected_properties, - all_properties, + state, max_char_length=40, ) self._sizer.Add(self._block_button, 1) diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/fill.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/fill.py index 9e1ccec0..bf70aadf 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/fill.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/fill.py @@ -2,10 +2,12 @@ from amulet_map_editor import lang from .base import BaseBlockContainer from .block_entry import FillBlockEntry +from .block_entry.custom_fill_button import SrcBlockState class FillBlockContainer(BaseBlockContainer): _blocks: List[FillBlockEntry] + state: SrcBlockState @property def name(self) -> str: @@ -20,7 +22,7 @@ def _do_add_block(self): self._blocks[-1].show_weight() def _create_block(self) -> FillBlockEntry: - return FillBlockEntry(self, self._translation_manager, *self._version) + return FillBlockEntry(self, self.state.copy()) def _do_destroy_block_entry(self, window: FillBlockEntry): super()._do_destroy_block_entry(window) @@ -28,9 +30,9 @@ def _do_destroy_block_entry(self, window: FillBlockEntry): block = self._blocks[-1] block.show_weight(False) - def set_from_source(self, from_source: bool): + def show_from_source(self, from_source: bool): for block in self._blocks: - block.set_from_source(from_source) + block.show_from_source(from_source) @property def weights(self) -> Tuple[float, ...]: diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/find.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/find.py index ec7c91d4..3aa2f7ec 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/find.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/block_container/find.py @@ -12,4 +12,4 @@ def name(self) -> str: return lang.get("program_3d_edit.fill_tool.find") def _create_block(self) -> FindBlockEntry: - return FindBlockEntry(self, self._translation_manager, *self._version) + return FindBlockEntry(self, self.state) diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_tool.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_tool.py index c8ddce50..839358b9 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_tool.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_tool.py @@ -11,12 +11,29 @@ ) from amulet_map_editor.programs.edit.api.ui.tool import DefaultBaseToolUI from .fill_replace_widget import FillReplaceWidget +from .block_container.block_entry.custom_fill_button import SrcBlockState if TYPE_CHECKING: from amulet_map_editor.programs.edit.api.canvas import EditCanvas +""" +FillReplaceTool(wx.BoxSizer) A sizer containing the FillReplaceWidget. + FillReplaceWidget(wx.Panel) A panel containing mode buttons and one or more fill/replace operations + OperationContainer(wx.lib.scrolledpanel.ScrolledPanel) A ScrolledPanel containing one or more fill/replace operations. + ReplaceOperationWidget + FindBlockContainer + FindBlockEntry + ... + FillBlockContainer + FillBlockEntry + ... + ... +""" + class FillReplaceTool(wx.BoxSizer, DefaultBaseToolUI): + """A sizer containing the FillReplaceWidget.""" + def __init__(self, canvas: "EditCanvas"): wx.BoxSizer.__init__(self, wx.VERTICAL) DefaultBaseToolUI.__init__(self, canvas) @@ -31,12 +48,27 @@ def setup(self): self.Add(self._panel) panel_sizer = wx.BoxSizer(wx.VERTICAL) self._panel.SetSizer(panel_sizer) - self._operations = FillReplaceWidget( - self._panel, + + find_state = SrcBlockState( self.canvas.world.translation_manager, - self.canvas.world.level_wrapper.platform, - self.canvas.world.level_wrapper.version, + platform="java", + version_number=(1, 16, 0), + namespace="minecraft", + base_name="air", ) + fill_state = SrcBlockState( + self.canvas.world.translation_manager, + platform="java", + version_number=(1, 16, 0), + namespace="minecraft", + base_name="stone", + ) + for state_ in (find_state, fill_state): + with state_ as state: + state.platform = self.canvas.world.level_wrapper.platform + state.version_number = self.canvas.world.level_wrapper.version + + self._operations = FillReplaceWidget(self._panel, find_state, fill_state) panel_sizer.Add(self._operations, 1, wx.LEFT | wx.TOP, 5) self._button = wx.Button(self._panel, label="Run Operation") diff --git a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_widget.py b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_widget.py index 4744313c..561aedb8 100644 --- a/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_widget.py +++ b/amulet_map_editor/programs/edit/plugins/tools/fill_replace/fill_replace_widget.py @@ -1,15 +1,13 @@ from enum import Enum -from typing import Tuple, List, Dict, Any +from typing import Tuple, List import wx from wx.lib.scrolledpanel import ScrolledPanel -import PyMCTranslate - -from amulet.api.data_types import VersionNumberTuple from amulet_map_editor import lang from amulet_map_editor.api.wx.ui.simple import ChoiceRaw from amulet_map_editor.api.wx.ui.events import ChildSizeEvent from .block_container import FillBlockContainer, FindBlockContainer +from .block_container.block_entry.custom_fill_button import SrcBlockState class ReplaceMode(Enum): @@ -24,21 +22,15 @@ class ReplaceOperationWidget(wx.Panel): def __init__( self, parent: wx.Window, - translation_manager: PyMCTranslate.TranslationManager, - platform: str = None, - version_number: VersionNumberTuple = None, - force_blockstate: bool = None, - **kwargs, + default_find_state: SrcBlockState, + default_fill_state: SrcBlockState, ): - kwargs["style"] = kwargs.get("style", 0) | wx.BORDER_SIMPLE - super().__init__(parent, **kwargs) + super().__init__(parent, style=wx.BORDER_SIMPLE) sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(sizer) - self._find = FindBlockContainer( - self, translation_manager, platform, version_number, force_blockstate - ) + self._find = FindBlockContainer(self, default_find_state) self._find.Hide() sizer.Add(self._find, 0, wx.EXPAND | wx.ALL, 5) @@ -49,9 +41,7 @@ def __init__( self._swap_button.Hide() sizer.Add(self._swap_button, 0, wx.EXPAND | wx.ALL, 5) - self._fill = FillBlockContainer( - self, translation_manager, platform, version_number, force_blockstate - ) + self._fill = FillBlockContainer(self, default_fill_state) sizer.Add(self._fill, 0, wx.EXPAND | wx.ALL, 5) def _post_change_size(self): @@ -70,15 +60,17 @@ def set_expert(self, expert: bool): self._find.set_expert(expert) self._fill.set_expert(expert) - def set_from_source(self, from_source: bool): - self._fill.set_from_source(from_source) + def show_from_source(self, from_source: bool): + self._fill.show_from_source(from_source) @property - def operation(self) -> Tuple[List[Dict[str, Any]], List[Dict[str, Any]]]: + def operation(self) -> Tuple[List[SrcBlockState], List[SrcBlockState]]: return self._find.states, self._fill.states class OperationContainer(ScrolledPanel): + """A ScrolledPanel containing one or more fill/replace operations.""" + def __init__(self, parent: wx.Window): super().__init__(parent) self._operations: List[ReplaceOperationWidget] = [] @@ -107,20 +99,17 @@ def add_operation(self, replace_operation: ReplaceOperationWidget): class FillReplaceWidget(wx.Panel): + """A panel containing mode buttons and one or more fill/replace operations""" + def __init__( self, parent: wx.Window, - translation_manager: PyMCTranslate.TranslationManager, - platform: str = None, - version_number: VersionNumberTuple = None, - force_blockstate: bool = None, - **kwargs, + default_find_state: SrcBlockState, + default_fill_state: SrcBlockState, ): - super().__init__(parent, **kwargs) - self._translation_manager = translation_manager - self._platform = platform - self._version_number = version_number - self._force_blockstate = force_blockstate + super().__init__(parent) + self._default_find_state = default_find_state + self._default_fill_state = default_fill_state sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(sizer) @@ -173,11 +162,7 @@ def _post_change_size(self): def _add_operation(self): replace_operation = ReplaceOperationWidget( - self._operation_panel, - self._translation_manager, - self._platform, - self._version_number, - self._force_blockstate, + self._operation_panel, self._default_find_state, self._default_fill_state ) replace_operation.set_expert(self.is_expert) replace_operation.set_replace(self.is_replace) @@ -192,7 +177,7 @@ def _update_buttons(self): for operation in self._operation_panel: operation.set_replace(self.is_replace) operation.set_expert(self.is_expert) - operation.set_from_source(self.from_source) + operation.show_from_source(self.from_source) self._post_change_size() def _on_check_change(self, evt): @@ -216,5 +201,5 @@ def replace_mode(self) -> ReplaceMode: return self._replace_mode.GetCurrentObject() @property - def operations(self) -> List[Tuple[List[Dict[str, Any]], List[Dict[str, Any]]]]: + def operations(self) -> List[Tuple[List[SrcBlockState], List[SrcBlockState]]]: return [op.operation for op in self._operation_panel] From 6bf37440e039d18f93b49177c32b89eb95382cd1 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Wed, 13 Oct 2021 17:17:57 +0100 Subject: [PATCH 139/139] Localised the Minecraft object widget labels --- .../base_identifier_select.py | 9 ++++++--- .../biome_identifier_select.py | 5 +++-- .../block_identifier_select.py | 5 +++-- .../properties/multiple/vanilla/vanilla.py | 9 +++++++-- .../wx/ui/mc/block/properties/single/modded.py | 17 +++++++++++++---- .../wx/ui/mc/block/properties/single/vanilla.py | 9 +++++++-- .../api/wx/ui/mc/version/platform_select.py | 5 +++-- .../api/wx/ui/mc/version/version_select.py | 16 ++++++++++++---- amulet_map_editor/lang/en.lang | 16 ++++++++++++++++ 9 files changed, 70 insertions(+), 21 deletions(-) diff --git a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py index 664bd5e7..44289ec2 100644 --- a/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py +++ b/amulet_map_editor/api/wx/ui/mc/base/base_identifier_select/base_identifier_select.py @@ -1,6 +1,7 @@ import wx import PyMCTranslate +from amulet_map_editor import lang from amulet_map_editor.api.image import COLOUR_PICKER from amulet_map_editor.api.wx.ui.mc.state import BaseResourceIDState, StateHolder, State from .events import ( @@ -33,7 +34,9 @@ def __init__( sizer = wx.BoxSizer(wx.HORIZONTAL) self._sizer.Add(sizer, 0, wx.EXPAND | wx.ALL, 5) - text = wx.StaticText(self, label="Namespace:", style=wx.ALIGN_CENTER) + text = wx.StaticText( + self, label=lang.get("widget.mc.namespace"), style=wx.ALIGN_CENTER + ) sizer.Add(text, 1, wx.ALIGN_CENTER_VERTICAL) self._namespace_combo = wx.ComboBox(self) sizer.Add(self._namespace_combo, 2) @@ -51,7 +54,7 @@ def __init__( header_sizer.Add( wx.StaticText( self, - label=f"{self.type_name.capitalize()} name:", + label=self.base_name_label, style=wx.ALIGN_CENTER, ), 1, @@ -86,7 +89,7 @@ def from_data( raise NotImplementedError @property - def type_name(self) -> str: + def base_name_label(self) -> str: raise NotImplementedError def _post_event( diff --git a/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/biome_identifier_select.py b/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/biome_identifier_select.py index a6001ff8..64360189 100644 --- a/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/biome_identifier_select.py +++ b/amulet_map_editor/api/wx/ui/mc/biome/identifier_select/biome_identifier_select.py @@ -1,6 +1,7 @@ import wx import PyMCTranslate +from amulet_map_editor import lang from amulet_map_editor.api.wx.ui.mc.state import BiomeResourceIDState from amulet_map_editor.api.wx.ui.mc.base.base_identifier_select import ( BaseIdentifierSelect, @@ -17,8 +18,8 @@ class BiomeIdentifierSelect(BaseIdentifierSelect): """ @property - def type_name(self) -> str: - return "Biome" + def base_name_label(self) -> str: + return lang.get("widget.mc.biome.base_name") @classmethod def from_data( diff --git a/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py b/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py index d4f823ff..f6a091fb 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py +++ b/amulet_map_editor/api/wx/ui/mc/block/identifier_select/block_identifier_select.py @@ -1,6 +1,7 @@ import wx import PyMCTranslate +from amulet_map_editor import lang from amulet_map_editor.api.wx.ui.mc.base.base_identifier_select import ( BaseIdentifierSelect, ) @@ -17,8 +18,8 @@ class BlockIdentifierSelect(BaseIdentifierSelect): """ @property - def type_name(self) -> str: - return "Block" + def base_name_label(self) -> str: + return lang.get("widget.mc.block.base_name") @classmethod def from_data( diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/vanilla/vanilla.py b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/vanilla/vanilla.py index 9d89909f..056e87be 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/vanilla/vanilla.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/multiple/vanilla/vanilla.py @@ -3,6 +3,7 @@ import amulet_nbt from amulet.api.block import PropertyTypeMultiple, PropertyValueType +from amulet_map_editor import lang from amulet_map_editor.api.wx.ui.mc.state import BlockState from ..base import BaseMultipleProperty from .popup import PropertyValueComboPopup @@ -22,10 +23,14 @@ def __init__(self, parent: wx.Window, state: BlockState): header_sizer = wx.BoxSizer(wx.HORIZONTAL) self._sizer.Add(header_sizer, 0, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, 5) - label = wx.StaticText(self, label="Property Name", style=wx.ALIGN_CENTER) + label = wx.StaticText( + self, label=lang.get("widget.mc.block.property.name"), style=wx.ALIGN_CENTER + ) header_sizer.Add(label, 1) label = wx.StaticText( - self, label="Property Value (SNBT)", style=wx.ALIGN_CENTER + self, + label=lang.get("widget.mc.block.property.value"), + style=wx.ALIGN_CENTER, ) header_sizer.Add(label, 1, wx.LEFT, 5) self._property_sizer = wx.GridSizer(2, 5, 5) diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/single/modded.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/modded.py index 118cc10d..7e3a0b7f 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/single/modded.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/single/modded.py @@ -5,6 +5,7 @@ import amulet_nbt from amulet_nbt import SNBTType from amulet.api.block import PropertyDataTypes, PropertyType +from amulet_map_editor import lang from amulet_map_editor.api.image import ADD_ICON, SUBTRACT_ICON from amulet_map_editor.api.wx.ui.mc.state import BlockState from amulet_map_editor.api.wx.ui.events import ChildSizeEvent @@ -33,10 +34,14 @@ def __init__(self, parent: wx.Window, state: BlockState): ) header_sizer.Add(add_button) self._sizer.Add(header_sizer, 0, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, 5) - label = wx.StaticText(self, label="Property Name", style=wx.ALIGN_CENTER) + label = wx.StaticText( + self, label=lang.get("widget.mc.block.property.name"), style=wx.ALIGN_CENTER + ) header_sizer.Add(label, 1, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5) label = wx.StaticText( - self, label="Property Value (SNBT)", style=wx.ALIGN_CENTER + self, + label=lang.get("widget.mc.block.property.value"), + style=wx.ALIGN_CENTER, ) header_sizer.Add(label, 1, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5) header_sizer.AddStretchSpacer(1) @@ -136,14 +141,18 @@ def _change_snbt_text(self, snbt: SNBTType, snbt_text: wx.StaticText): try: nbt = amulet_nbt.from_snbt(snbt) except: - snbt_text.SetLabel("Invalid SNBT") + snbt_text.SetLabel(lang.get("widget.mc.block.property.invalid_snbt")) snbt_text.SetBackgroundColour((255, 200, 200)) else: if isinstance(nbt, PropertyDataTypes): snbt_text.SetLabel(nbt.to_snbt()) snbt_text.SetBackgroundColour(wx.NullColour) else: - snbt_text.SetLabel(f"{nbt.__class__.__name__} not valid") + snbt_text.SetLabel( + lang.get("widget.mc.block.property.invalid_value_fstring").format( + val=nbt.__class__.__name__ + ) + ) snbt_text.SetBackgroundColour((255, 200, 200)) self.Layout() diff --git a/amulet_map_editor/api/wx/ui/mc/block/properties/single/vanilla.py b/amulet_map_editor/api/wx/ui/mc/block/properties/single/vanilla.py index 322ee137..9bd1f54a 100644 --- a/amulet_map_editor/api/wx/ui/mc/block/properties/single/vanilla.py +++ b/amulet_map_editor/api/wx/ui/mc/block/properties/single/vanilla.py @@ -2,6 +2,7 @@ from typing import Dict, Tuple from amulet.api.block import PropertyType, PropertyValueType +from amulet_map_editor import lang from amulet_map_editor.api.wx.ui.mc.state import BlockState from amulet_map_editor.api.wx.ui.simple import ChoiceRaw from .base import BaseSingleProperty @@ -20,10 +21,14 @@ def __init__(self, parent: wx.Window, state: BlockState): super().__init__(parent, state) self._property_sizer = wx.FlexGridSizer(2, 5, 5) - label = wx.StaticText(self, label="Property Name", style=wx.ALIGN_CENTER) + label = wx.StaticText( + self, label=lang.get("widget.mc.block.property.name"), style=wx.ALIGN_CENTER + ) self._property_sizer.Add(label, 1, wx.ALIGN_CENTER) label = wx.StaticText( - self, label="Property Value (SNBT)", style=wx.ALIGN_CENTER + self, + label=lang.get("widget.mc.block.property.value"), + style=wx.ALIGN_CENTER, ) self._property_sizer.Add(label, 1, wx.ALIGN_CENTER) diff --git a/amulet_map_editor/api/wx/ui/mc/version/platform_select.py b/amulet_map_editor/api/wx/ui/mc/version/platform_select.py index 29ed0d59..e5ba2faa 100644 --- a/amulet_map_editor/api/wx/ui/mc/version/platform_select.py +++ b/amulet_map_editor/api/wx/ui/mc/version/platform_select.py @@ -3,8 +3,9 @@ import PyMCTranslate from typing import Type, Any -from .events import PlatformChangeEvent, EVT_PLATFORM_CHANGE +from amulet_map_editor import lang from amulet_map_editor.api.wx.ui.mc.state import PlatformState, StateHolder, State +from .events import PlatformChangeEvent, EVT_PLATFORM_CHANGE class PlatformSelect(wx.Panel, StateHolder): @@ -40,7 +41,7 @@ def __init__( self._sizer.AddGrowableCol(1) self._platform_choice: SimpleChoice = self._add_ui_element( - "Platform:", SimpleChoice + lang.get("widget.mc.platform"), SimpleChoice ) self._update_platform() self._platform_choice.Bind( diff --git a/amulet_map_editor/api/wx/ui/mc/version/version_select.py b/amulet_map_editor/api/wx/ui/mc/version/version_select.py index 0f7e9eff..5200f71c 100644 --- a/amulet_map_editor/api/wx/ui/mc/version/version_select.py +++ b/amulet_map_editor/api/wx/ui/mc/version/version_select.py @@ -4,8 +4,9 @@ from typing import Optional from amulet.api.data_types import VersionNumberTuple, PlatformType -from .platform_select import PlatformSelect +from amulet_map_editor import lang from amulet_map_editor.api.wx.ui.mc.state import VersionState, State +from .platform_select import PlatformSelect from .events import VersionChangeEvent, EVT_VERSION_CHANGE @@ -33,7 +34,7 @@ def __init__( super().__init__(parent, state, **kwargs) self._version_choice: Optional[ChoiceRaw] = self._add_ui_element( - "Version:", ChoiceRaw, reverse=True, sort=True + lang.get("widget.mc.version"), ChoiceRaw, reverse=True, sort=True ) self._update_version_number() self._version_choice.Bind( @@ -42,9 +43,16 @@ def __init__( ) self._blockstate_choice: Optional[SimpleChoice] = self._add_ui_element( - "Format:", SimpleChoice, shown=show_force_blockstate + lang.get("widget.mc.block_format"), + SimpleChoice, + shown=show_force_blockstate, + ) + self._blockstate_choice.SetItems( + [ + lang.get("widget.mc.block_format.native"), + lang.get("widget.mc.block_format.blockstate"), + ] ) - self._blockstate_choice.SetItems(["native", "blockstate"]) self._update_force_blockstate() self._blockstate_choice.Bind(wx.EVT_CHOICE, self._on_blockstate_change) diff --git a/amulet_map_editor/lang/en.lang b/amulet_map_editor/lang/en.lang index dcdac711..cb550cf9 100644 --- a/amulet_map_editor/lang/en.lang +++ b/amulet_map_editor/lang/en.lang @@ -69,6 +69,22 @@ select_world.recent_worlds=Recently Opened Worlds select_world.no_loader_found=Could not find a loader for this world. select_world.loading_world_failed=Error loading world. + +# Minecraft object widget labels +widget.mc.platform=Platform: +widget.mc.version=Version: +widget.mc.block_format=Format: +widget.mc.block_format.native=native +widget.mc.block_format.blockstate=blockstate +widget.mc.namespace=Namespace: +widget.mc.block.base_name=Block Name: +widget.mc.biome.base_name=Biome Name: +widget.mc.block.property.name=Property Name +widget.mc.block.property.value=Property Value (SNBT) +widget.mc.block.property.invalid_snbt=Invalid SNBT +widget.mc.block.property.invalid_value_fstring={val} not valid + + # About ## The default program when a world is opened program_about.tab_name=About