using System; using System.Collections.Generic; using KKAPI.Maker.UI; using UniRx; namespace KKAPI.Maker { /// /// A wrapper for custom controls used in accessory window (added by using ). /// It abstracts away switching between accessory slots and provides a simple list of values for each accessory. /// /// Type of the control to be wrapped. The control has to be added by using or results will be undefined. /// Type of the control's value. public class AccessoryControlWrapper where T : BaseEditableGuiEntry { /// /// Create a new wrapper. /// /// Control to be wrapped. The control has to be added by using or results will be undefined. public AccessoryControlWrapper(T control) { if (control == null) throw new ArgumentNullException(nameof(control)); Control = control; _defaultValue = control.Value; control.ValueChanged.Subscribe(val => SetValue(AccessoriesApi.SelectedMakerAccSlot, val)); AccessoriesApi.SelectedMakerAccSlotChanged += OnSelectedMakerAccSlotChanged; AccessoriesApi.AccessoryKindChanged += OnAccessoryKindChanged; #if KK AccessoriesApi.AccessoriesCopied += OnAccessoriesCopied; #endif AccessoriesApi.AccessoryTransferred += OnAccessoryTransferred; } private void OnAccessoryTransferred(object sender, AccessoryTransferEventArgs e) { if (CheckDisposed()) return; AccessoryTransferred?.Invoke(sender, e); } #if KK private void OnAccessoriesCopied(object sender, AccessoryCopyEventArgs e) { if (CheckDisposed()) return; AccessoriesCopied?.Invoke(sender, e); } #endif private void OnAccessoryKindChanged(object sender, AccessorySlotEventArgs accessorySlotEventArgs) { if (CheckDisposed()) return; AccessoryKindChanged?.Invoke(sender, accessorySlotEventArgs); } private void OnSelectedMakerAccSlotChanged(object o, AccessorySlotEventArgs accessorySlotEventArgs) { if (CheckDisposed()) return; Control.SetValue(GetValue(accessorySlotEventArgs.SlotIndex), false); VisibleIndexChanged?.Invoke(o, accessorySlotEventArgs); } /// /// The wrapped control. /// public T Control { get; } /// /// Get value of the control for the specified accessory. /// public TVal GetValue(int accessoryIndex) { CheckDisposedThrow(); CheckIndexRangeThrow(accessoryIndex); if (_values.TryGetValue(accessoryIndex, out var result)) return result; return _defaultValue; } /// /// Get value of the control for the currently selected accessory. /// public TVal GetSelectedValue() { return GetValue(CurrentlySelectedIndex); } /// /// Set value of the control for the specified accessory. /// /// Index of the accessory to set the value for /// Value to set public void SetValue(int accessoryIndex, TVal value) { SetValue(accessoryIndex, value, true); } /// /// Set value of the control for the specified accessory. /// /// Index of the accessory to set the value for /// Value to set /// Fire the event if the value actually changed. public void SetValue(int accessoryIndex, TVal value, bool fireEvents) { CheckDisposedThrow(); CheckIndexRangeThrow(accessoryIndex); _values[accessoryIndex] = value; if (AccessoriesApi.SelectedMakerAccSlot == accessoryIndex) Control.SetValue(value, false); if (fireEvents) ValueChanged?.Invoke(this, new AccessoryWindowControlValueChangedEventArgs(value, CurrentlySelectedIndex)); } /// /// Set value of the control for the currently selected accessory. /// /// Value to set public void SetSelectedValue(TVal value) { SetSelectedValue(value, false); } /// /// Set value of the control for the currently selected accessory. /// /// Value to set /// Fire the event if the value actually changed. public void SetSelectedValue(TVal value, bool fireEvents) { SetValue(CurrentlySelectedIndex, value, fireEvents); } /// /// Index of the currently selected accessory. /// public int CurrentlySelectedIndex => AccessoriesApi.SelectedMakerAccSlot; private readonly TVal _defaultValue; private readonly Dictionary _values = new Dictionary(); private bool _isDisposed; /// /// Fired when the value of this control changes for any of the accessories. /// public event EventHandler> ValueChanged; /// /// Fired when the currently visible accessory was changed by the user clicking on one of the slots. /// public event EventHandler VisibleIndexChanged; /// /// Fires when user selects a different accessory in the accessory window. /// public event EventHandler AccessoryKindChanged; #if KK /// /// Fires after user copies accessories between coordinates by using the Copy window. /// public static event EventHandler AccessoriesCopied; #endif /// /// Fires after user copies an accessory within a single coordinate by using the Transfer window. /// public static event EventHandler AccessoryTransferred; private static void CheckIndexRangeThrow(int accessoryIndex) { if (accessoryIndex < 0 || accessoryIndex >= AccessoriesApi.GetCvsAccessoryCount()) throw new IndexOutOfRangeException("accessoryIndex has to be between 0 and AccessoriesApi.GetCvsAccessoryCount() - 1"); } private void CheckDisposedThrow() { if (CheckDisposed()) throw new ObjectDisposedException("The control has been disposed. Controls only live in Maker, and you need to create a new one every time maker starts"); } private bool CheckDisposed() { if (_isDisposed) return true; if (Control.IsDisposed) { _isDisposed = true; _values.Clear(); ValueChanged = null; VisibleIndexChanged = null; AccessoryKindChanged = null; AccessoryTransferred = null; AccessoriesApi.SelectedMakerAccSlotChanged -= OnSelectedMakerAccSlotChanged; AccessoriesApi.AccessoryKindChanged -= OnAccessoryKindChanged; AccessoriesApi.AccessoryTransferred -= OnAccessoryTransferred; #if KK AccessoriesCopied = null; AccessoriesApi.AccessoriesCopied -= OnAccessoriesCopied; #endif return true; } return false; } /// /// If true, the control has been disposed and can no longer be used, likely because the character maker exited. /// A new control has to be created to be used again. /// public bool IsDisposed => CheckDisposed(); } }