private void DrawEntryHoverMenu(Rect entryRect, SettingHandle handle) { var topRight = new Vector2( entryRect.x + entryRect.width / 2f - HandleEntryPadding, entryRect.y + entryRect.height / 2f - ModSettingsWidgets.HoverMenuHeight / 2f ); var includeResetEntry = handle.CanBeReset && !handle.Unsaved; var menuHasExtraOptions = handle.ContextMenuEntries != null; var menuEnabled = includeResetEntry || menuHasExtraOptions; var menuButtonClicked = ModSettingsWidgets.DrawHandleHoverMenu( topRight, handle.Description, menuEnabled, menuHasExtraOptions); if (menuButtonClicked) { OpenHandleContextMenu(); } void OpenHandleContextMenu() { var resetOptionLabel = handle.CanBeReset ? "HugsLib_settings_resetValue".Translate() : null; ModSettingsWidgets.OpenExtensibleContextMenu(resetOptionLabel, () => ResetSettingHandles(handle), delegate {}, handle.ContextMenuEntries); } }
// draws the input control for Enum settings private bool DrawHandleInputEnum(SettingHandle handle, Rect controlRect, HandleControlInfo info) { if (info.enumNames == null) { return(false); } var readableValue = (handle.EnumStringPrefix + info.inputValue).Translate(); if (Widgets.ButtonText(controlRect, readableValue)) { var floatOptions = new List <FloatMenuOption>(); foreach (var valueName in info.enumNames) { var name = valueName; var readableOption = (handle.EnumStringPrefix + name).Translate(); floatOptions.Add(new FloatMenuOption(readableOption, () => { handle.StringValue = info.inputValue = name; info.validationScheduled = true; })); } ModSettingsWidgets.OpenFloatMenu(floatOptions); } if (info.validationScheduled) { info.validationScheduled = false; return(true); } return(false); }
// draws the input control for integer settings private bool DrawHandleInputSpinner(SettingHandle handle, Rect controlRect, HandleControlInfo info) { var buttonSize = controlRect.height; var leftButtonRect = new Rect(controlRect.x, controlRect.y, buttonSize, buttonSize); var rightButtonRect = new Rect(controlRect.x + controlRect.width - buttonSize, controlRect.y, buttonSize, buttonSize); var changed = false; if (Widgets.ButtonText(leftButtonRect, "-")) { if (int.TryParse(info.inputValue, out var parsed)) { info.inputValue = (parsed - handle.SpinnerIncrement).ToString(); } info.validationScheduled = true; changed = true; } if (Widgets.ButtonText(rightButtonRect, "+")) { if (int.TryParse(info.inputValue, out var parsed)) { info.inputValue = (parsed + handle.SpinnerIncrement).ToString(); } info.validationScheduled = true; changed = true; } var textRect = new Rect(controlRect.x + buttonSize + 1, controlRect.y, controlRect.width - buttonSize * 2 - 2f, controlRect.height); if (DrawHandleInputText(handle, textRect, info)) { changed = true; } return(changed); }
private bool DrawDefaultHandleEntry(SettingHandle handle, Rect trimmedEntryRect, bool mouseOverEntry) { var controlRect = new Rect(trimmedEntryRect.x + trimmedEntryRect.width / 2f, trimmedEntryRect.y, trimmedEntryRect.width / 2f, trimmedEntryRect.height); GenUI.SetLabelAlign(TextAnchor.MiddleLeft); var leftHalfRect = new Rect(trimmedEntryRect.x, trimmedEntryRect.y, trimmedEntryRect.width / 2f - HandleEntryPadding, trimmedEntryRect.height); // give full width to the label if custom control drawer is used- this allows handle titles to be used as section titles var labelRect = handle.CustomDrawer == null ? leftHalfRect : trimmedEntryRect; // reduce text size if label is long and wraps over to the second line var controlInfo = handleControlInfo[handle]; var cachedTitle = controlInfo.handleTitle ?? (controlInfo.handleTitle = new CachedLabel(handle.Title)); if (cachedTitle.GetHeight(labelRect.width) > labelRect.height) { Text.Font = GameFont.Tiny; labelRect = new Rect(labelRect.x, labelRect.y - 1f, labelRect.width, labelRect.height + 2f); } else { Text.Font = GameFont.Small; } Widgets.Label(labelRect, cachedTitle.Text); Text.Font = GameFont.Small; GenUI.ResetLabelAlign(); var valueChanged = false; if (handle.CustomDrawer == null) { var handleType = handle.ValueType; if (handleType.IsEnum) { handleType = typeof(Enum); } handleDrawers.TryGetValue(handleType, out var drawer); if (drawer == null) { drawer = defaultHandleDrawer; } valueChanged = drawer(handle, controlRect, controlInfo); } else { try { valueChanged = handle.CustomDrawer(controlRect); } catch (Exception e) { HugsLibController.Logger.ReportException(e, currentlyDrawnEntry, true, $"{nameof(SettingHandle)}.{nameof(SettingHandle.CustomDrawer)}"); } } if (mouseOverEntry) { DrawEntryHoverMenu(trimmedEntryRect, handle); } return(valueChanged); }
private void ResetSetting(SettingHandle handle) { if (!handle.CanBeReset) { return; } handle.ResetToDefault(); handleControlInfo[handle] = new HandleControlInfo(handle); settingsHaveChanged = true; }
/// <summary> /// Retrieves an existing SettingHandle from the pack, or creates a new one. /// Loaded settings will only display in the Mod Settings dialog after they have been claimed using this method. /// </summary> /// <typeparam name="T">The type of setting value you are creating.</typeparam> /// <param name="settingName">Unique identifier for the setting. Must be unique for this specific pack only.</param> /// <param name="title">A display name for the setting that will show up next to it in the Mod Settings dialog. Recommended to keep this short.</param> /// <param name="description">A description for the setting that will appear in a tooltip when the player hovers over the setting in the Mod Settings dialog.</param> /// <param name="defaultValue">The value the setting will assume when newly created and when the player resets the setting to its default.</param> /// <param name="validator">An optional delegate that will be called when a new value is about to be assigned to the handle. Receives a string argument and must return a bool to indicate if the passed value is valid for the setting.</param> /// <param name="enumPrefix">Used only for Enum settings. Enum values are displayed in a readable format by the following method: Translate(prefix+EnumValueName)</param> public SettingHandle <T> GetHandle <T>(string settingName, string title, string description, T defaultValue = default(T), SettingHandle.ValueIsValid validator = null, string enumPrefix = null) { if (!PersistentDataManager.IsValidElementName(settingName)) { throw new Exception("Invalid name for mod setting: " + settingName); } SettingHandle <T> handle = null; for (int i = 0; i < handles.Count; i++) { if (handles[i].Name != settingName) { continue; } if (!(handles[i] is SettingHandle <T>)) { continue; } handle = (SettingHandle <T>)handles[i]; break; } if (handle == null) { handle = new SettingHandle <T>(settingName) { Value = defaultValue }; handle.ParentPack = this; handles.Add(handle); } handle.DefaultValue = defaultValue; handle.Title = title; handle.Description = description; handle.Validator = validator; handle.EnumStringPrefix = enumPrefix; if (typeof(T).IsEnum && (enumPrefix == null || !(enumPrefix + Enum.GetName(typeof(T), defaultValue)).CanTranslate())) { HugsLibController.Logger.Warning("Missing enum setting labels for enum " + typeof(T)); } if (loadedValues.ContainsKey(settingName)) { var loadedValue = loadedValues[settingName]; loadedValues.Remove(settingName); handle.StringValue = loadedValue; if (handle.Validator != null && !handle.Validator(loadedValue)) { handle.ResetToDefault(); } } handle.HasUnsavedChanges = false; return(handle); }
// draws the input control for boolean settings private bool DrawHandleInputCheckbox(SettingHandle handle, Rect controlRect, HandleControlInfo info) { const float defaultCheckboxHeight = 24f; var checkOn = bool.Parse(info.inputValue); Widgets.Checkbox(controlRect.x, controlRect.y + (controlRect.height - defaultCheckboxHeight) / 2, ref checkOn); if (checkOn != bool.Parse(info.inputValue)) { handle.StringValue = info.inputValue = checkOn.ToString(); return(true); } return(false); }
private List <string> TryGetEnumNames(SettingHandle handle) { var valueType = handle.ValueType; if (!valueType.IsEnum) { return(null); } var values = Enum.GetValues(valueType); var result = new List <string>(values.Length); foreach (var value in values) { result.Add(Enum.GetName(valueType, value)); } return(result); }
// draws the label and appropriate input for a single setting private void DrawHandleEntry(SettingHandle handle, Rect parentRect, ref float curY, float scrollViewHeight) { var entryHeight = HandleEntryHeight; var anyCustomDrawer = handle.CustomDrawerFullWidth ?? handle.CustomDrawer; if (anyCustomDrawer != null && handle.CustomDrawerHeight > entryHeight) { entryHeight = handle.CustomDrawerHeight + HandleEntryPadding * 2; } var skipDrawing = curY - scrollPosition.y + entryHeight <0f || curY - scrollPosition.y> scrollViewHeight; if (!skipDrawing) { var entryRect = new Rect(parentRect.x, parentRect.y + curY, parentRect.width, entryHeight); var mouseOverEntry = Mouse.IsOver(entryRect); if (mouseOverEntry) { Widgets.DrawHighlight(entryRect); } var trimmedEntryRect = entryRect.ContractedBy(HandleEntryPadding); bool valueChanged = false; if (handle.CustomDrawerFullWidth != null) { try { valueChanged = handle.CustomDrawerFullWidth(trimmedEntryRect); } catch (Exception e) { HugsLibController.Logger.ReportException(e, currentlyDrawnEntry, true, $"{nameof(SettingHandle)}.{nameof(SettingHandle.CustomDrawerFullWidth)}"); } } else { valueChanged = DrawDefaultHandleEntry(handle, trimmedEntryRect, mouseOverEntry); } if (valueChanged) { if (handle.ValueType.IsClass) { // required for SettingHandleConvertible values to be eligible for saving, // since changes in reference-type values can't be automatically detected handle.HasUnsavedChanges = true; } } } curY += entryHeight; }
// draws the input control for string settings private bool DrawHandleInputText(SettingHandle handle, Rect controlRect, HandleControlInfo info) { var evt = Event.current; GUI.SetNextControlName(info.controlName); info.inputValue = Widgets.TextField(controlRect, info.inputValue); var focused = GUI.GetNameOfFocusedControl() == info.controlName; if (focused) { info.validationScheduled = true; if (evt.type == EventType.KeyUp && (evt.keyCode == KeyCode.Return || evt.keyCode == KeyCode.KeypadEnter)) { focused = false; } } var changed = false; if (info.validationScheduled && !focused) { try { if (handle.Validator != null && !handle.Validator(info.inputValue)) { info.badInput = true; } else { info.badInput = false; handle.StringValue = info.inputValue; changed = true; } } catch (Exception e) { HugsLibController.Logger.ReportException(e, currentlyDrawnEntry, false, "SettingsHandle.Validator"); } info.validationScheduled = false; } if (info.badInput) { DrawBadTextValueOutline(controlRect); } return(changed); }
public HandleControlInfo(SettingHandle handle) { controlName = "control" + handle.GetHashCode(); inputValue = handle.StringValue; enumNames = TryGetEnumNames(handle); }
private void OnHandleValueChanged(SettingHandle handle) { ResetHandleControlInfo(handle); }
private void ResetHandleControlInfo(SettingHandle handle) { handleControlInfo[handle] = new HandleControlInfo(handle); }
private void ResetSetting(SettingHandle handle) { handle.ResetToDefault(); handleControlInfo[handle] = new HandleControlInfo(handle); settingsHaveChanged = true; }
// draws the label and appropriate input for a single setting private void DrawHandleEntry(SettingHandle handle, Rect parentRect, ref float curY, float scrollViewHeight) { var entryHeight = HandleEntryHeight; if (handle.CustomDrawer != null && handle.CustomDrawerHeight > entryHeight) { entryHeight = handle.CustomDrawerHeight + HandleEntryPadding * 2; } var skipDrawing = curY - scrollPosition.y + entryHeight <0f || curY - scrollPosition.y> scrollViewHeight; if (!skipDrawing) { var entryRect = new Rect(parentRect.x, parentRect.y + curY, parentRect.width, entryHeight).ContractedBy(HandleEntryPadding); var mouseOver = Mouse.IsOver(entryRect); if (mouseOver) { Widgets.DrawHighlight(entryRect); } var controlRect = new Rect(entryRect.x + entryRect.width / 2f, entryRect.y, entryRect.width / 2f, entryRect.height); GenUI.SetLabelAlign(TextAnchor.MiddleLeft); var leftHalfRect = new Rect(entryRect.x, entryRect.y, entryRect.width / 2f - HandleEntryPadding, entryRect.height); var labelRect = handle.CustomDrawer == null ? leftHalfRect : entryRect; // give full width to the label just in case Widgets.Label(labelRect, handle.Title); GenUI.ResetLabelAlign(); bool valueChanged = false; if (handle.CustomDrawer == null) { SettingsHandleDrawer drawer; var handleType = handle.ValueType; if (handleType.IsEnum) { handleType = typeof(Enum); } handleDrawers.TryGetValue(handleType, out drawer); if (drawer == null) { drawer = defaultHandleDrawer; } valueChanged = drawer(handle, controlRect, handleControlInfo[handle]); } else { try { valueChanged = handle.CustomDrawer(controlRect); } catch (Exception e) { HugsLibController.Logger.ReportException(e, currentlyDrawnEntry, true, "SettingsHandle.CustomDrawer"); } } if (valueChanged) { settingsHaveChanged = true; } if (mouseOver) { if (!handle.Description.NullOrEmpty()) { TooltipHandler.TipRegion(entryRect, handle.Description); } if (Input.GetMouseButtonUp(1)) { var options = new List <FloatMenuOption>(1); options.Add(new FloatMenuOption("HugsLib_settings_resetValue".Translate(), () => { ResetSetting(handle); })); Find.WindowStack.Add(new FloatMenu(options)); } } } curY += entryHeight; }