2
0
mirror of https://github.com/9ParsonsB/Pulsar.git synced 2025-04-05 17:39:39 -04:00

[Core] Settings improvements: Grouping w/labels, support for doubles (#137)

* [Core] Settings improvements: Grouping w/labels, support for doubles

Layout improvements:
* Plugins can set `[SettingSuggestedColumnWidth(123)]` on a settings class to adjust the settings view column width to make it wider or narrower to fit things nicely.
* Plugins can set `[SettingNewGroup("label")]` on any setting property to force a "new paragraph", or group, of settings. If "label" is also provided, a grouping header with that text will also be created.
* A double precision up/down numeric control has been added. In support of this, there is now a precision value on the existing `[SettingNumericBounds]` attribute to specify the number of digits of precision the control displays/allows.

Plugins:
* The above have been applied/demonstrated on the Botanist and Explorer plugin settings.
This commit is contained in:
F K 2024-01-29 15:37:00 -05:00 committed by GitHub
parent a555c86083
commit 351dcdb732
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 227 additions and 28 deletions

View File

@ -7,11 +7,12 @@ using System.Threading.Tasks;
namespace Observatory.Botanist namespace Observatory.Botanist
{ {
[SettingSuggestedColumnWidth(450)]
class BotanistSettings class BotanistSettings
{ {
[SettingDisplayName("Enable Sampler Status Overlay")] [SettingDisplayName("Enable Sampler Status Overlay")]
public bool OverlayEnabled { get; set; } public bool OverlayEnabled { get; set; }
[SettingDisplayName("Sampler Status Overlay is sticky until sampling complete (if enabled)")] [SettingDisplayName("Status Overlay is sticky until sampling is complete")]
public bool OverlayIsSticky { get; set; } public bool OverlayIsSticky { get; set; }
} }
} }

View File

@ -26,6 +26,12 @@ namespace Observatory.UI
_plugin = plugin; _plugin = plugin;
// Filtered to only settings without SettingIgnore attribute // Filtered to only settings without SettingIgnore attribute
var attrib = _plugin.Settings.GetType().GetCustomAttribute<SettingSuggestedColumnWidth>();
if (attrib != null && attrib.Width > 0)
{
int minScreenWidth = Screen.AllScreens.Min(s => s.Bounds.Width);
_colWidth = Math.Min(attrib.Width, minScreenWidth / 2);
}
var settings = PluginManagement.PluginManager.GetSettingDisplayNames(plugin.Settings).Where(s => !Attribute.IsDefined(s.Key, typeof(SettingIgnore))); var settings = PluginManagement.PluginManager.GetSettingDisplayNames(plugin.Settings).Where(s => !Attribute.IsDefined(s.Key, typeof(SettingIgnore)));
CreateControls(settings); CreateControls(settings);
@ -51,12 +57,30 @@ namespace Observatory.UI
foreach (var setting in settings) foreach (var setting in settings)
{ {
// Reset the column tracking for checkboxes if this isn't a checkbox // Reset the column tracking for checkboxes if this isn't a checkbox or explicitly requested
// to start a new grouping of settings.
int addedHeight = 35; int addedHeight = 35;
if (setting.Key.PropertyType.Name != "Boolean") var newGroup = Attribute.GetCustomAttribute(setting.Key, typeof(SettingNewGroup)) as SettingNewGroup;
if (setting.Key.PropertyType.Name != "Boolean" || newGroup != null)
{ {
if (recentHalfCol) _colHeight.Add(addedHeight); if (recentHalfCol) _colHeight.Add(addedHeight);
recentHalfCol = false; recentHalfCol = false;
if (newGroup != null)
{
if (!string.IsNullOrEmpty(newGroup.Label))
{
var label = CreateGroupLabel(newGroup.Label);
label.Location = GetSettingPosition();
Controls.Add(label);
trackBottomEdge(label);
_colHeight.Add(label.Height);
}
else
_colHeight.Add(10);
}
} }
switch (setting.Key.GetValue(_plugin.Settings)) switch (setting.Key.GetValue(_plugin.Settings))
@ -98,7 +122,7 @@ namespace Observatory.UI
break; break;
case int: case int:
// We have two options for integer values: // We have two options for integer values:
// 1) A slider (explicit by way of the SettingIntegerUseSlider attribute and bounded to 0..100 by default) // 1) A slider (explicit by way of the SettingNumericUseSlider attribute and bounded to 0..100 by default)
// 2) A numeric up/down (default otherwise, and is unbounded by default). // 2) A numeric up/down (default otherwise, and is unbounded by default).
// Bounds for both can be set via the SettingNumericBounds attribute, only the up/down uses Increment. // Bounds for both can be set via the SettingNumericBounds attribute, only the up/down uses Increment.
var intLabel = CreateSettingLabel(setting.Value); var intLabel = CreateSettingLabel(setting.Value);
@ -109,7 +133,7 @@ namespace Observatory.UI
} }
else else
{ {
intControl = CreateSettingNumericUpDown(setting.Key); intControl = CreateSettingNumericUpDownForInt(setting.Key);
} }
intLabel.Location = GetSettingPosition(); intLabel.Location = GetSettingPosition();
intControl.Location = GetSettingPosition(true); intControl.Location = GetSettingPosition(true);
@ -122,6 +146,23 @@ namespace Observatory.UI
Controls.Add(intControl); Controls.Add(intControl);
trackBottomEdge(intControl); trackBottomEdge(intControl);
break; break;
case double:
// We have one options for double values:
// 1) A numeric up/down (default otherwise, and is unbounded by default).
// Bounds can be set via the SettingNumericBounds attribute.
var doubleLabel = CreateSettingLabel(setting.Value);
Control doubleControl = CreateSettingNumericUpDownForDouble(setting.Key);
doubleLabel.Location = GetSettingPosition();
doubleControl.Location = GetSettingPosition(true);
addedHeight = doubleControl.Height + 2;
doubleLabel.Height = doubleControl.Height;
doubleLabel.TextAlign = ContentAlignment.MiddleRight;
Controls.Add(doubleLabel);
Controls.Add(doubleControl);
trackBottomEdge(doubleControl);
break;
case Action action: case Action action:
var button = CreateSettingButton(setting.Value, action); var button = CreateSettingButton(setting.Value, action);
@ -152,8 +193,7 @@ namespace Observatory.UI
private Point GetSettingPosition(bool secondCol = false) private Point GetSettingPosition(bool secondCol = false)
{ {
//return new Point(10 + (secondCol ? 200 : 0), -26 + _colHeight.Sum()); return new Point(20 + (secondCol ? _colWidth + 20 : 0), 15 + _colHeight.Sum());
return new Point(10 + (secondCol ? _colWidth : 0), 15 + _colHeight.Sum());
} }
@ -170,6 +210,20 @@ namespace Observatory.UI
return label; return label;
} }
private Label CreateGroupLabel(string groupLabel)
{
Label label = new()
{
Text = groupLabel,
TextAlign = System.Drawing.ContentAlignment.MiddleLeft,
Width = _colWidth * 2,
ForeColor = Color.LightGray,
};
label.Font = new Font(label.Font.FontFamily, label.Font.Size + 1, FontStyle.Bold);
label.Height += 10; // Add spacing.
return label;
}
private ComboBox CreateSettingDropdown(PropertyInfo setting, Dictionary<string, object> dropdownItems) private ComboBox CreateSettingDropdown(PropertyInfo setting, Dictionary<string, object> dropdownItems)
{ {
var backingValueName = (SettingBackingValue?)Attribute.GetCustomAttribute(setting, typeof(SettingBackingValue)); var backingValueName = (SettingBackingValue?)Attribute.GetCustomAttribute(setting, typeof(SettingBackingValue));
@ -225,7 +279,7 @@ namespace Observatory.UI
private TrackBar CreateSettingTrackbar(PropertyInfo setting) private TrackBar CreateSettingTrackbar(PropertyInfo setting)
{ {
SettingNumericBounds? bounds = (SettingNumericBounds?)System.Attribute.GetCustomAttribute(setting, typeof(SettingNumericBounds)); SettingNumericBounds? bounds = (SettingNumericBounds?)Attribute.GetCustomAttribute(setting, typeof(SettingNumericBounds));
var minBound = Convert.ToInt32(bounds?.Minimum ?? 0); var minBound = Convert.ToInt32(bounds?.Minimum ?? 0);
var maxBound = Convert.ToInt32(bounds?.Maximum ?? 100); var maxBound = Convert.ToInt32(bounds?.Maximum ?? 100);
@ -246,16 +300,16 @@ namespace Observatory.UI
trackBar.ValueChanged += (sender, e) => trackBar.ValueChanged += (sender, e) =>
{ {
setting.SetValue(_plugin.Settings, Convert.ToInt32(trackBar.Value)); setting.SetValue(_plugin.Settings, trackBar.Value);
SaveSettings(); SaveSettings();
}; };
return trackBar; return trackBar;
} }
private NumericUpDown CreateSettingNumericUpDown(PropertyInfo setting) private NumericUpDown CreateSettingNumericUpDownForInt(PropertyInfo setting)
{ {
SettingNumericBounds? bounds = (SettingNumericBounds?)System.Attribute.GetCustomAttribute(setting, typeof(SettingNumericBounds)); SettingNumericBounds? bounds = (SettingNumericBounds?)Attribute.GetCustomAttribute(setting, typeof(SettingNumericBounds));
NumericUpDown numericUpDown = new() NumericUpDown numericUpDown = new()
{ {
Width = _colWidth, Width = _colWidth,
@ -265,7 +319,6 @@ namespace Observatory.UI
}; };
numericUpDown.Value = (int?)setting.GetValue(_plugin.Settings) ?? 0; numericUpDown.Value = (int?)setting.GetValue(_plugin.Settings) ?? 0;
numericUpDown.ValueChanged += (sender, e) => numericUpDown.ValueChanged += (sender, e) =>
{ {
setting.SetValue(_plugin.Settings, Convert.ToInt32(numericUpDown.Value)); setting.SetValue(_plugin.Settings, Convert.ToInt32(numericUpDown.Value));
@ -275,6 +328,28 @@ namespace Observatory.UI
return numericUpDown; return numericUpDown;
} }
private NumericUpDown CreateSettingNumericUpDownForDouble(PropertyInfo setting)
{
SettingNumericBounds? bounds = (SettingNumericBounds?)Attribute.GetCustomAttribute(setting, typeof(SettingNumericBounds));
NumericUpDown numericUpDown = new()
{
Width = _colWidth,
Minimum = Convert.ToDecimal(bounds?.Minimum ?? Double.MinValue),
Maximum = Convert.ToDecimal(bounds?.Maximum ?? Double.MaxValue),
Increment = Convert.ToDecimal(bounds?.Increment ?? 1.0),
DecimalPlaces = bounds?.Precision ?? 1,
};
numericUpDown.Value = Convert.ToDecimal(setting.GetValue(_plugin.Settings) ?? 0);
numericUpDown.ValueChanged += (sender, e) =>
{
setting.SetValue(_plugin.Settings, Convert.ToDouble(numericUpDown.Value));
SaveSettings();
};
return numericUpDown;
}
private CheckBox CreateBoolSetting(KeyValuePair<PropertyInfo, string> setting) private CheckBox CreateBoolSetting(KeyValuePair<PropertyInfo, string> setting)
{ {
CheckBox checkBox = new() CheckBox checkBox = new()

View File

@ -1,12 +1,14 @@
using Observatory.Framework; using Observatory.Framework;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Observatory.Explorer namespace Observatory.Explorer
{ {
[SettingSuggestedColumnWidth(300)]
public class ExplorerSettings public class ExplorerSettings
{ {
public ExplorerSettings() public ExplorerSettings()
@ -14,6 +16,12 @@ namespace Observatory.Explorer
CustomCriteriaFile = $"{Environment.GetFolderPath(Environment.SpecialFolder.Personal)}{System.IO.Path.DirectorySeparatorChar}ObservatoryCriteria.lua"; CustomCriteriaFile = $"{Environment.GetFolderPath(Environment.SpecialFolder.Personal)}{System.IO.Path.DirectorySeparatorChar}ObservatoryCriteria.lua";
} }
[SettingNewGroup("Display")]
[SettingDisplayName("Only Show Current System")]
public bool OnlyShowCurrentSystem { get; set; }
[SettingNewGroup("Built-in Criteria")]
[SettingDisplayName("Landable & Terraformable")] [SettingDisplayName("Landable & Terraformable")]
public bool LandableTerraformable { get; set; } public bool LandableTerraformable { get; set; }
@ -83,12 +91,9 @@ namespace Observatory.Explorer
[SettingDisplayName("High-Value Body")] [SettingDisplayName("High-Value Body")]
public bool HighValueMappable { get; set; } public bool HighValueMappable { get; set; }
[SettingNewGroup("Custom Criteria")]
[SettingDisplayName("Enable Custom Criteria")] [SettingDisplayName("Enable Custom Criteria")]
public bool EnableCustomCriteria { get; set; } public bool EnableCustomCriteria { get; set; }
[SettingDisplayName("Only Show Current System")]
public bool OnlyShowCurrentSystem { get; set; }
[SettingDisplayName("Custom Criteria File")] [SettingDisplayName("Custom Criteria File")]
[System.Text.Json.Serialization.JsonIgnore] [System.Text.Json.Serialization.JsonIgnore]
public System.IO.FileInfo CustomCriteria {get => new System.IO.FileInfo(CustomCriteriaFile); set => CustomCriteriaFile = value.FullName;} public System.IO.FileInfo CustomCriteria {get => new System.IO.FileInfo(CustomCriteriaFile); set => CustomCriteriaFile = value.FullName;}

View File

@ -6,6 +6,32 @@ using System.Threading.Tasks;
namespace Observatory.Framework namespace Observatory.Framework
{ {
#region Settings class attributes
/// <summary>
/// Specifies the width of a settings column in the settings view. There are two columns.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class SettingSuggestedColumnWidth : Attribute
{
/// <summary>
/// Specifies the width of a settings column in the settings view. There are two columns.
/// </summary>
/// <param name="width">Provides a hint of the width of a settings column.</param>
public SettingSuggestedColumnWidth(int width)
{
Width = width;
}
/// <summary>
/// Provides a hint of the width of a settings column.
/// </summary>
public int Width { get; }
}
#endregion
#region Setting property attributes
/// <summary> /// <summary>
/// Specifies text to display as the name of the setting in the UI instead of the property name. /// Specifies text to display as the name of the setting in the UI instead of the property name.
/// </summary> /// </summary>
@ -34,17 +60,24 @@ namespace Observatory.Framework
} }
/// <summary> /// <summary>
/// Suggests default column width when building basic UI /// Starts a new visual group of settings beginning with the current setting with an optional label.
/// </summary> /// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class ColumnSuggestedWidth : Attribute public class SettingNewGroup : Attribute
{ {
public ColumnSuggestedWidth(int width) /// <summary>
/// Starts a new visual group of settings beginning with the current setting with an optional label.
/// </summary>
/// <param name="label">An optional label describing the group.</param>
public SettingNewGroup(string label = "")
{ {
Width = width; Label = label;
} }
public int Width { get; } /// <summary>
/// An optional label describing the group.
/// </summary>
public string Label { get; }
} }
/// <summary> /// <summary>
@ -97,6 +130,7 @@ namespace Observatory.Framework
private double minimum; private double minimum;
private double maximum; private double maximum;
private double increment; private double increment;
private int precision;
/// <summary> /// <summary>
/// Specify bounds for numeric inputs. /// Specify bounds for numeric inputs.
@ -104,11 +138,13 @@ namespace Observatory.Framework
/// <param name="minimum">Minimum allowed value.</param> /// <param name="minimum">Minimum allowed value.</param>
/// <param name="maximum">Maximum allowed value.</param> /// <param name="maximum">Maximum allowed value.</param>
/// <param name="increment">Increment between allowed values in slider/roller inputs.</param> /// <param name="increment">Increment between allowed values in slider/roller inputs.</param>
public SettingNumericBounds(double minimum, double maximum, double increment = 1.0) /// <param name="precision">The number of digits to display for non integer values.</param>
public SettingNumericBounds(double minimum, double maximum, double increment = 1.0, int precision = 1)
{ {
this.minimum = minimum; this.minimum = minimum;
this.maximum = maximum; this.maximum = maximum;
this.increment = increment; this.increment = increment;
this.precision = precision;
} }
/// <summary> /// <summary>
@ -121,7 +157,7 @@ namespace Observatory.Framework
} }
/// <summary> /// <summary>
/// Maxunyn allowed value. /// Maximum allowed value.
/// </summary> /// </summary>
public double Maximum public double Maximum
{ {
@ -137,5 +173,38 @@ namespace Observatory.Framework
get => increment; get => increment;
set => increment = value; set => increment = value;
} }
/// <summary>
/// The number of digits to display for non integer values.
/// </summary>
public int Precision
{
get => precision;
set => precision = value;
}
} }
#endregion
#region BasicUI attributes
/// <summary>
/// Suggests default column width when building basic plugin grid UI.
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class ColumnSuggestedWidth : Attribute
{
/// <summary>
/// Suggests default column width when building basic plugin grid UI.
/// </summary>
/// <param name="width">The suggested width of the annotated column.</param>
public ColumnSuggestedWidth(int width)
{
Width = width;
}
/// <summary>
/// The suggested width of the annotated column.
/// </summary>
public int Width { get; }
}
#endregion
} }

View File

@ -4,6 +4,22 @@
<name>ObservatoryFramework</name> <name>ObservatoryFramework</name>
</assembly> </assembly>
<members> <members>
<member name="T:Observatory.Framework.SettingSuggestedColumnWidth">
<summary>
Specifies the width of a settings column in the settings view. There are two columns.
</summary>
</member>
<member name="M:Observatory.Framework.SettingSuggestedColumnWidth.#ctor(System.Int32)">
<summary>
Specifies the width of a settings column in the settings view. There are two columns.
</summary>
<param name="width">Provides a hint of the width of a settings column.</param>
</member>
<member name="P:Observatory.Framework.SettingSuggestedColumnWidth.Width">
<summary>
Provides a hint of the width of a settings column.
</summary>
</member>
<member name="T:Observatory.Framework.SettingDisplayName"> <member name="T:Observatory.Framework.SettingDisplayName">
<summary> <summary>
Specifies text to display as the name of the setting in the UI instead of the property name. Specifies text to display as the name of the setting in the UI instead of the property name.
@ -20,9 +36,20 @@
Accessor to get/set displayed name. Accessor to get/set displayed name.
</summary> </summary>
</member> </member>
<member name="T:Observatory.Framework.ColumnSuggestedWidth"> <member name="T:Observatory.Framework.SettingNewGroup">
<summary> <summary>
Suggests default column width when building basic UI Starts a new visual group of settings beginning with the current setting with an optional label.
</summary>
</member>
<member name="M:Observatory.Framework.SettingNewGroup.#ctor(System.String)">
<summary>
Starts a new visual group of settings beginning with the current setting with an optional label.
</summary>
<param name="label">An optional label describing the group.</param>
</member>
<member name="P:Observatory.Framework.SettingNewGroup.Label">
<summary>
An optional label describing the group.
</summary> </summary>
</member> </member>
<member name="T:Observatory.Framework.SettingIgnore"> <member name="T:Observatory.Framework.SettingIgnore">
@ -56,13 +83,14 @@
Specify bounds for numeric inputs. Specify bounds for numeric inputs.
</summary> </summary>
</member> </member>
<member name="M:Observatory.Framework.SettingNumericBounds.#ctor(System.Double,System.Double,System.Double)"> <member name="M:Observatory.Framework.SettingNumericBounds.#ctor(System.Double,System.Double,System.Double,System.Int32)">
<summary> <summary>
Specify bounds for numeric inputs. Specify bounds for numeric inputs.
</summary> </summary>
<param name="minimum">Minimum allowed value.</param> <param name="minimum">Minimum allowed value.</param>
<param name="maximum">Maximum allowed value.</param> <param name="maximum">Maximum allowed value.</param>
<param name="increment">Increment between allowed values in slider/roller inputs.</param> <param name="increment">Increment between allowed values in slider/roller inputs.</param>
<param name="precision">The number of digits to display for non integer values.</param>
</member> </member>
<member name="P:Observatory.Framework.SettingNumericBounds.Minimum"> <member name="P:Observatory.Framework.SettingNumericBounds.Minimum">
<summary> <summary>
@ -71,7 +99,7 @@
</member> </member>
<member name="P:Observatory.Framework.SettingNumericBounds.Maximum"> <member name="P:Observatory.Framework.SettingNumericBounds.Maximum">
<summary> <summary>
Maxunyn allowed value. Maximum allowed value.
</summary> </summary>
</member> </member>
<member name="P:Observatory.Framework.SettingNumericBounds.Increment"> <member name="P:Observatory.Framework.SettingNumericBounds.Increment">
@ -79,6 +107,27 @@
Increment between allowed values in slider/roller inputs. Increment between allowed values in slider/roller inputs.
</summary> </summary>
</member> </member>
<member name="P:Observatory.Framework.SettingNumericBounds.Precision">
<summary>
The number of digits to display for non integer values.
</summary>
</member>
<member name="T:Observatory.Framework.ColumnSuggestedWidth">
<summary>
Suggests default column width when building basic plugin grid UI.
</summary>
</member>
<member name="M:Observatory.Framework.ColumnSuggestedWidth.#ctor(System.Int32)">
<summary>
Suggests default column width when building basic plugin grid UI.
</summary>
<param name="width">The suggested width of the annotated column.</param>
</member>
<member name="P:Observatory.Framework.ColumnSuggestedWidth.Width">
<summary>
The suggested width of the annotated column.
</summary>
</member>
<member name="T:Observatory.Framework.JournalEventArgs"> <member name="T:Observatory.Framework.JournalEventArgs">
<summary> <summary>
Provides data for Elite Dangerous journal events. Provides data for Elite Dangerous journal events.