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;
 }
Example #6
0
        /// <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;
        }