|
| 1 | +using System; |
| 2 | +using System.Collections.Generic; |
| 3 | +using System.Diagnostics.CodeAnalysis; |
| 4 | +using System.Linq; |
| 5 | +using Yafc.I18n; |
| 6 | +using Yafc.Model; |
| 7 | +using Yafc.UI; |
| 8 | + |
| 9 | +namespace Yafc; |
| 10 | + |
| 11 | +/// <summary> |
| 12 | +/// Encapsulates the options for drawing an object selection list, which may be a dropdown, a <see cref="SelectObjectPanel{TResult, TDisplay}"/>, |
| 13 | +/// or both with the dropdown displayed first. |
| 14 | +/// </summary> |
| 15 | +/// <typeparam name="T">The type of <see cref="FactorioObject"/> to be selected</typeparam> |
| 16 | +/// <param name="Header">The header text to display, or <see langword="null"/> to display no header text.</param> |
| 17 | +/// <param name="Ordering">The sort order to use when displaying the items. Defaults to <see cref="DataUtils.DefaultOrdering"/>.</param> |
| 18 | +/// <param name="MaxCount">The maximum number of elements in a dropdown list. If there are more, and a dropdown is being displayed, a |
| 19 | +/// <see cref="SelectObjectPanel{TResult, TDisplay}"/> can be opened by the user to show the full list.</param> |
| 20 | +/// <param name="Multiple">If <see langword="true"/>, the user will be allowed to select multiple items.<br/> |
| 21 | +/// Must be <see langword="false"/> when selecting with a 'None' item.</param> |
| 22 | +/// <param name="Checkmark">If not <see langword="null"/>, this will be called to determine if a checkmark should be drawn on the item. |
| 23 | +/// Not used when <paramref name="Multiple"/> is <see langword="false"/>.</param> |
| 24 | +/// <param name="YellowMark">If Checkmark is not set, draw a less distinct checkmark instead.</param> |
| 25 | +/// <param name="ExtraText">If not <see langword="null"/>, this will be called to get extra text to be displayed right-justified in a dropdown |
| 26 | +/// list, after the item's name. Not used when displaying <see cref="SelectObjectPanel{TResult, TDisplay}"/>s.</param> |
| 27 | +public record ObjectSelectOptions<T>(string? Header, [AllowNull] IComparer<T> Ordering = null, int MaxCount = 6, bool Multiple = false, Predicate<T>? Checkmark = null, |
| 28 | + Predicate<T>? YellowMark = null, Func<T, string>? ExtraText = null) where T : class, IFactorioObjectWrapper { |
| 29 | + |
| 30 | + public IComparer<T> Ordering { get; init; } = Ordering ?? DataUtils.DefaultOrdering; |
| 31 | +} |
| 32 | + |
| 33 | +/// <summary> |
| 34 | +/// Encapsulates the options for drawing an object selection list, which may be a dropdown, a <see cref="SelectObjectPanel{TResult, TDisplay}"/>, |
| 35 | +/// or both with the dropdown displayed first, along with options to select the quality.<br/> |
| 36 | +/// Also stores the state of the quality selection, for storing the state across draw cycles, reacting to quality changes, and/or reading the |
| 37 | +/// selected qualities when an item is selected or the selection panel is closed. |
| 38 | +/// </summary> |
| 39 | +/// <typeparam name="T"><inheritdoc/></typeparam> |
| 40 | +/// <param name="Header">The header text to display, or <see langword="null"/> to display no header text.</param> |
| 41 | +/// <param name="Ordering">The sort order to use when displaying the items. Defaults to <see cref="DataUtils.DefaultOrdering"/>.</param> |
| 42 | +/// <param name="MaxCount">The maximum number of elements in a dropdown list. If there are more and a dropdown is being displayed, a |
| 43 | +/// <see cref="SelectObjectPanel{TResult, TDisplay}"/> can be opened by the user to show the full list.</param> |
| 44 | +/// <param name="Multiple">If <see langword="true"/>, the user will be allowed to select multiple items and multiple qualities.<br/> |
| 45 | +/// Must be <see langword="false"/> when selecting with a 'None' item.</param> |
| 46 | +/// <param name="Checkmark">If not <see langword="null"/>, this will be called to determine if a checkmark should be drawn on the item. |
| 47 | +/// Not used when <paramref name="Multiple"/> is <see langword="false"/>.</param> |
| 48 | +/// <param name="YellowMark">If Checkmark is not set, draw a less distinct checkmark instead.</param> |
| 49 | +/// <param name="ExtraText">If not <see langword="null"/>, this will be called to get extra text to be displayed right-justified in a dropdown |
| 50 | +/// list, after the item's name. Not used when displaying <see cref="SelectObjectPanel{TResult, TDisplay}"/>s.</param> |
| 51 | +/// <param name="AllowMultipleWithoutControl">When this and <paramref name="Multiple"/> are both <see langword="true"/>, the quality icons in the |
| 52 | +/// selection list will always behave like checkboxes. When <paramref name="Multiple"/> is <see langword="true"/> and this is |
| 53 | +/// <see langword="false"/>, the quality icons will behave like checkboxes when ctrl-clicking, and like radio buttons when clicking without |
| 54 | +/// control. When <paramref name="Multiple"/> is <see langword="false"/>, the quality icons will always behave like radio buttons.</param> |
| 55 | +/// <param name="SelectedQuality">The initially selected <see cref="Quality"/>. If this parameter is <see langword="null"/> and |
| 56 | +/// <paramref name="Multiple"/> is <see langword="false"/>, <see cref="SelectedQuality"/> will be initialized to <see cref="Quality.Normal"/>. |
| 57 | +/// </param> |
| 58 | +/// <remarks>If the quality selections are not preserved across draw cycles, construct the QSO in the containing or calling method.</remarks> |
| 59 | +public record QualitySelectOptions<T>(string? Header, [AllowNull] IComparer<T> Ordering = null, int MaxCount = 6, bool Multiple = false, |
| 60 | + Predicate<T>? Checkmark = null, Predicate<T>? YellowMark = null, Func<T, string>? ExtraText = null, bool AllowMultipleWithoutControl = false, |
| 61 | + Quality? SelectedQuality = null) |
| 62 | + : ObjectSelectOptions<T>(Header, Ordering, MaxCount, Multiple, Checkmark, YellowMark, ExtraText) where T : class, IFactorioObjectWrapper { |
| 63 | + |
| 64 | + /// <summary> |
| 65 | + /// Creates a new <see cref="QualitySelectOptions{T}"/> using just the header text and selected quality. |
| 66 | + /// </summary> |
| 67 | + /// <param name="header">The header text to display, or <see langword="null"/> to display no header text.</param> |
| 68 | + /// <param name="selectedQuality">The initially selected <see cref="Quality"/>. If this parameter is <see langword="null"/>, |
| 69 | + /// <see cref="SelectedQuality"/> will be initialized to <see cref="Quality.Normal"/>.</param> |
| 70 | + // (Seven of the seventeen constructor calls use only these two parameters.) |
| 71 | + public QualitySelectOptions(string? header, Quality? selectedQuality) : this(header, null, SelectedQuality: selectedQuality) { } |
| 72 | + |
| 73 | + /// <summary> |
| 74 | + /// The translation key for text to draw above the quality icons. By default this is <see cref="LSs.SelectQuality"/>, which will be |
| 75 | + /// pluralized as appropriate based on <see cref="ObjectSelectOptions{T}.Multiple"/>. |
| 76 | + /// </summary> |
| 77 | + public LocalizableString QualityHeader { get; init; } = LSs.SelectQuality; |
| 78 | + |
| 79 | + /// <summary> |
| 80 | + /// Gets the (first) quality selected and sets the only quality selected. Gets or sets <see langword="null"/> if no qualities are (or should |
| 81 | + /// be) selected. If the constructor parameter Multiple is <see langword="false"/> and the constructor parameter SelectedQuality is |
| 82 | + /// <see langword="null"/>, this property's initial value is <see cref="Quality.Normal"/>. Otherwise, its initial value is the same as the |
| 83 | + /// constructor parameter SelectedQuality.<br/> |
| 84 | + /// When <see cref="ObjectSelectOptions{T}.Multiple"/> is <see langword="false"/>, this cannot become <see langword="null"/> as a result of |
| 85 | + /// user action, though it may be programmatically set to <see langword="null"/>.<br/> |
| 86 | + /// </summary> |
| 87 | + public Quality? SelectedQuality { |
| 88 | + get => SelectedQualities.FirstOrDefault(); |
| 89 | + set { |
| 90 | + SelectedQualities.Clear(); |
| 91 | + if (value != null) { |
| 92 | + SelectedQualities.Add(value); |
| 93 | + } |
| 94 | + } |
| 95 | + } |
| 96 | + |
| 97 | + /// <summary> |
| 98 | + /// Called when <see cref="SelectedQualities"/>, and possibly <see cref="SelectedQuality"/> has changed due to user input. The parameter is |
| 99 | + /// the active <see cref="ImGui"/>. |
| 100 | + /// </summary> |
| 101 | + public event Action<ImGui>? SelectedQualitiesChanged; |
| 102 | + internal void OnSelectedQualitiesChanged(ImGui gui) => SelectedQualitiesChanged?.Invoke(gui); |
| 103 | + |
| 104 | + /// <summary> |
| 105 | + /// Gets all qualities selected by the user. If no qualities are selected (equivalently, if <see cref="SelectedQuality"/> is |
| 106 | + /// <see langword="null"/>), this is an empty list. |
| 107 | + /// </summary> |
| 108 | + public List<Quality> SelectedQualities { get; } = SelectedQuality == null ? Multiple ? [] : [Quality.Normal] : [SelectedQuality]; |
| 109 | +} |
| 110 | + |
| 111 | +public static class QualityOptionsExtensions { |
| 112 | + /// <summary> |
| 113 | + /// Applies the <see cref="QualitySelectOptions{T}.SelectedQualities"/> to <paramref name="obj"/>. If no qualities are selected, nothing is |
| 114 | + /// returned. Otherwise, a quality-immune <typeparamref name="T"/> is returned once in normal quality, and a quality-compatible |
| 115 | + /// <typeparamref name="T"/> is returned once for each selected quality. |
| 116 | + /// returned only once, in normal quality, while quality aware</summary> |
| 117 | + /// <param name="obj">The object to combine with the currently selected qualities.</param> |
| 118 | + /// <returns>A sequence of <see cref="IObjectWithQuality{T}"/>s containing each valid combination of <paramref name="obj"/> and a selected |
| 119 | + /// <see cref="Quality"/>.</returns> |
| 120 | + public static IEnumerable<IObjectWithQuality<T>> ApplyQualitiesTo<T>(this QualitySelectOptions<T> options, T obj) where T : FactorioObject { |
| 121 | + bool addedSomething = false; |
| 122 | + foreach (var quality in options.SelectedQualities) { |
| 123 | + if (obj.With(quality).quality == quality) { |
| 124 | + addedSomething = true; |
| 125 | + yield return obj.With(quality); |
| 126 | + } |
| 127 | + } |
| 128 | + |
| 129 | + if (!addedSomething) { |
| 130 | + if (options.SelectedQualities.Count > 0) { |
| 131 | + // If the selected object is normal-only and normal quality wasn't selected, return it anyway. |
| 132 | + yield return obj.With(Quality.Normal); |
| 133 | + } |
| 134 | + } |
| 135 | + } |
| 136 | +} |
0 commit comments