def Configure(self) -> None: toggleEnabledButton = OptionBoxButton("Toggle Enabled") editTriggerButton = OptionBoxButton( "Edit Trigger", "Edits what redemption title triggers this effect. Not case sensitive, but must match exactly otherwise." ) extraConfigurationButton = OptionBoxButton( "Extra Options", "Adjust extra, effect specific options.") def GetCaptionStr(trig: CrowdControl._Trigger) -> str: caption = "" if trig.IsEnabled: caption = f"Enabled: '{trig.Trigger}'" else: caption = "Disabled" caption += "\n" + trig.Effect.Description return caption class _EffectButton(OptionBoxButton): TrigObj: CrowdControl._Trigger def __init__(self, trig: CrowdControl._Trigger) -> None: self.TrigObj = trig @property def Name(self) -> str: # type: ignore return f"{self.TrigObj.Effect.Name} - {GetCaptionStr(self.TrigObj)}" effectButtons = [_EffectButton(trig) for trig in self.Triggers] currentButton = effectButtons[0] selectBox = OptionBox(Title="Configure Effects", Caption="Select the effect to configure.", Buttons=effectButtons, Tooltip=OptionBox.CreateTooltipString() + " " + "[R] Reset All") editBox = OptionBox(Title="Configure <effect>", Caption="Enabled\n<description>", Buttons=(toggleEnabledButton, editTriggerButton, extraConfigurationButton)) renameBox = TextInputBox(Title="Configure <effect>") def UpdateEditBox() -> None: editBox.Title = f"Configure {currentButton.TrigObj.Effect.Name}" if currentButton.TrigObj.IsEnabled: editBox.Caption = f"Enabled: '{currentButton.TrigObj.Trigger}'" else: editBox.Caption = "Disabled" editBox.Caption += "\n" + currentButton.TrigObj.Effect.Description if currentButton.TrigObj.Effect.HasConfigMenu: editBox.Buttons = [ toggleEnabledButton, editTriggerButton, extraConfigurationButton ] else: editBox.Buttons = [toggleEnabledButton, editTriggerButton] editBox.Update() def OnSelect(button: _EffectButton) -> None: nonlocal currentButton currentButton = button UpdateEditBox() editBox.Show() def OnEdit(button: OptionBoxButton) -> None: if button == toggleEnabledButton: currentButton.TrigObj.IsEnabled = not currentButton.TrigObj.IsEnabled for option in currentButton.TrigObj.Effect.Options: option.IsHidden = not currentButton.TrigObj.IsEnabled UpdateEditBox() editBox.Show() elif button == editTriggerButton: renameBox.Title = editBox.Title renameBox.DefaultMessage = currentButton.TrigObj.Trigger renameBox.Show() elif button == extraConfigurationButton: currentButton.TrigObj.Effect.FinishConfiguration = editBox.Show # type: ignore currentButton.TrigObj.Effect.ShowConfiguration() def OnRename(msg: str) -> None: if len(msg) > 0: currentButton.TrigObj.Trigger = msg UpdateEditBox() editBox.Show() oldSelectInput = selectBox.OnInput def OnSelectInput(key: str, event: int) -> None: if key == "R" and event == 1: for trig in self.Triggers: trig.IsEnabled = True trig.Trigger = trig.Effect.Name for option in trig.Effect.Options: option.IsHidden = False UpdateEditBox() selectBox.Hide() selectBox.Show() else: oldSelectInput(key, event) selectBox.OnPress = OnSelect # type: ignore selectBox.OnCancel = self.SaveTriggers # type: ignore selectBox.OnInput = OnSelectInput # type:ignore editBox.OnPress = OnEdit # type: ignore editBox.OnCancel = lambda: selectBox.ShowButton(currentButton ) # type: ignore renameBox.OnSubmit = OnRename # type: ignore selectBox.Show()
class PresetManager: FileName: str PresetList: List[Preset] _NewPreset: ClassVar[OptionBoxButton] = OptionBoxButton( "Create New Preset") _OpenPresetFile: ClassVar[OptionBoxButton] = OptionBoxButton( "Open Preset File", "Useful for reordering or sharing presets.") _EditPreset: ClassVar[OptionBoxButton] = OptionBoxButton("Configure") _RenamePreset: ClassVar[OptionBoxButton] = OptionBoxButton("Rename") _DeletePreset: ClassVar[OptionBoxButton] = OptionBoxButton("Delete") _Yes: ClassVar[OptionBoxButton] = OptionBoxButton("Yes") _No: ClassVar[OptionBoxButton] = OptionBoxButton("No") _ConfigureBox: OptionBox _ButtonPresetMap: Dict[OptionBoxButton, Preset] _CurrentPreset: Optional[Preset] _PresetActionBox: OptionBox _ConfirmDeleteBox: OptionBox _RenameBox: TextInputBox def __init__(self, FileName: str): self.FileName = FileName self._ConfigureBox = OptionBox( Title="Configure Presets", Caption="Select the preset you want to configure", Tooltip=OptionBox.CreateTooltipString(EscMessage="Exit"), Buttons=( self._NewPreset, self._OpenPresetFile, ), ) self._ConfigureBox.OnPress = self._SelectSpecificPreset # type: ignore self.LoadPresets() self._UpdateConfigureBox() self._CurrentPreset = None # These two dialog boxes are mostly the constant, we'll just update their title as needed self._PresetActionBox = OptionBox( Title="Selected 'PRESET NAME'", Caption="Select the action to perform on this preset", Tooltip=OptionBox.CreateTooltipString(EscMessage="Back"), Buttons=(self._EditPreset, self._RenamePreset, self._DeletePreset), ) self._PresetActionBox.OnPress = self._SelectPresetAction # type: ignore self._PresetActionBox.OnCancel = self.StartConfiguring # type: ignore self._ConfirmDeleteBox = OptionBox( Title="Delete 'PRESET NAME'", Caption="Are you sure you want to delete this preset?", Tooltip=OptionBox.CreateTooltipString(EscMessage="Back"), Buttons=(self._No, self._Yes), ) self._ConfirmDeleteBox.OnPress = self._OnConfirmDelete # type: ignore self._ConfirmDeleteBox.OnCancel = lambda: self._OnConfirmDelete( None) # type: ignore self._RenameBox = TextInputBox("Rename 'PRESET NAME'") self._RenameBox.OnSubmit = self._OnPresetRename # type: ignore def LoadPresets(self) -> None: loadedData: List[Dict[str, Union[str, Dict[str, str]]]] = [] try: with open(self.FileName) as file: loadedData = json.load(file) except (FileNotFoundError, json.JSONDecodeError): pass self.PresetList = [] for i in range(len(loadedData)): currentDict = loadedData[i] name: str = f"Preset {i}" settings: Dict[str, str] = {} if "Name" in currentDict: name = html.escape(str(currentDict["Name"])) if "Settings" in currentDict and isinstance( currentDict["Settings"], dict): settings = currentDict["Settings"] # Sanity-check the data for cheat in _AllCheats: if cheat.Name in settings: value = settings[cheat.Name] # We don't really care what this value is for regular cheats, but better to # keep it consistent if not isinstance(cheat, ABCCycleableCheat): value = "Run" settings[cheat.Name] = value currentPreset = Preset(name, settings) currentPreset.OnFinishConfiguring = self._OnFinishConfiguringPreset # type: ignore self.PresetList.append(currentPreset) # If there are no valid presets then still add the first one if len(self.PresetList) == 0: newPreset = Preset("Preset 1", {}) newPreset.OnFinishConfiguring = self._OnFinishConfiguringPreset # type: ignore self.PresetList.append(newPreset) # Save the sanity-checked info self.SavePresets() def SavePresets(self) -> None: data = [] for preset in self.PresetList: data.append({ "Name": html.unescape(preset.Name), "Settings": preset.GetSettings() }) with open(self.FileName, "w") as file: json.dump(data, file, indent=2) def StartConfiguring(self) -> None: self.LoadPresets() self._UpdateConfigureBox() self._ConfigureBox.Show() # The next three functions should be overwritten by the main mod def AddKeybind(self, Name: str) -> None: raise NotImplementedError def RenameKeybind(self, OldName: str, NewName: str) -> None: raise NotImplementedError def RemoveKeybind(self, Name: str) -> None: raise NotImplementedError def _OnFinishConfiguringPreset(self) -> None: self.SavePresets() self.StartConfiguring() def _UpdateConfigureBox(self) -> None: buttonList = [] self._ButtonPresetMap = {} for preset in self.PresetList: button = OptionBoxButton(html.unescape(preset.Name)) buttonList.append(button) self._ButtonPresetMap[button] = preset buttonList += [ self._NewPreset, self._OpenPresetFile, ] self._ConfigureBox.Buttons = buttonList self._ConfigureBox.Update() def _SelectSpecificPreset(self, button: OptionBoxButton) -> None: if button == self._NewPreset: # Get a new default name that's at least the size of the list + 1, or the largest # existing default name + 1 # This makes renaming or deleting cheats still add a somewhat logical name maxVal = len(self.PresetList) for preset in self.PresetList: val: int try: val = int(preset.Name.split(" ")[-1]) except ValueError: continue if val > maxVal: maxVal = val name = f"Preset {maxVal + 1}" newPreset = Preset(name, {}) newPreset.OnFinishConfiguring = self._OnFinishConfiguringPreset # type: ignore self.PresetList.append(newPreset) self.AddKeybind(name) self.SavePresets() elif button == self._OpenPresetFile: startfile(self.FileName) if button in self._ButtonPresetMap: self._CurrentPreset = self._ButtonPresetMap[button] self._PresetActionBox.Title = f"Selected '{self._CurrentPreset.Name}'" self._PresetActionBox.Update() self._PresetActionBox.Show() return self.StartConfiguring() def _SelectPresetAction(self, button: OptionBoxButton) -> None: if self._CurrentPreset is None: raise RuntimeError("Current Preset is None") if button == self._EditPreset: self._CurrentPreset.StartConfiguring() self._CurrentPreset = None elif button == self._RenamePreset: self._RenameBox.Title = f"Rename '{self._CurrentPreset.Name}'" self._RenameBox.DefaultMessage = self._CurrentPreset.Name self._RenameBox.Show() elif button == self._DeletePreset: self._ConfirmDeleteBox.Title = f"Delete '{self._CurrentPreset.Name}'" self._ConfirmDeleteBox.Update() self._ConfirmDeleteBox.Show() def _OnPresetRename(self, msg: str) -> None: if self._CurrentPreset is None: raise RuntimeError("Current Preset is None") if len(msg) > 0: self.RenameKeybind(self._CurrentPreset.Name, msg) self._CurrentPreset.UpdateName(msg) self.SavePresets() self._UpdateConfigureBox() self._PresetActionBox.Title = f"Selected '{self._CurrentPreset.Name}'" self._PresetActionBox.Update() self._PresetActionBox.ShowButton(self._RenamePreset) def _OnConfirmDelete(self, button: Optional[OptionBoxButton]) -> None: if self._CurrentPreset is None: raise RuntimeError("Current Preset is None") if button == self._Yes: self.PresetList.remove(self._CurrentPreset) self.RemoveKeybind(self._CurrentPreset.Name) self._CurrentPreset = None self.SavePresets() self.StartConfiguring() else: self._PresetActionBox.Show()