MouseWheels are pretty great.

This commit is contained in:
Ottermandias 2024-02-05 15:21:29 +01:00
parent 5e37f8d2e8
commit 1fefe7366c
22 changed files with 250 additions and 88 deletions

View file

@ -26,6 +26,7 @@ public sealed class Design : DesignBase, ISavable
{ {
Tags = [.. other.Tags]; Tags = [.. other.Tags];
Description = other.Description; Description = other.Description;
QuickDesign = other.QuickDesign;
AssociatedMods = new SortedList<Mod, ModSettings>(other.AssociatedMods); AssociatedMods = new SortedList<Mod, ModSettings>(other.AssociatedMods);
} }
@ -39,6 +40,7 @@ public sealed class Design : DesignBase, ISavable
public string Description { get; internal set; } = string.Empty; public string Description { get; internal set; } = string.Empty;
public string[] Tags { get; internal set; } = []; public string[] Tags { get; internal set; } = [];
public int Index { get; internal set; } public int Index { get; internal set; }
public bool QuickDesign { get; internal set; } = true;
public string Color { get; internal set; } = string.Empty; public string Color { get; internal set; } = string.Empty;
public SortedList<Mod, ModSettings> AssociatedMods { get; private set; } = []; public SortedList<Mod, ModSettings> AssociatedMods { get; private set; } = [];
public LinkContainer Links { get; private set; } = []; public LinkContainer Links { get; private set; } = [];
@ -64,6 +66,7 @@ public sealed class Design : DesignBase, ISavable
["Name"] = Name.Text, ["Name"] = Name.Text,
["Description"] = Description, ["Description"] = Description,
["Color"] = Color, ["Color"] = Color,
["QuickDesign"] = QuickDesign,
["Tags"] = JArray.FromObject(Tags), ["Tags"] = JArray.FromObject(Tags),
["WriteProtected"] = WriteProtected(), ["WriteProtected"] = WriteProtected(),
["Equipment"] = SerializeEquipment(), ["Equipment"] = SerializeEquipment(),
@ -124,6 +127,7 @@ public sealed class Design : DesignBase, ISavable
Description = json["Description"]?.ToObject<string>() ?? string.Empty, Description = json["Description"]?.ToObject<string>() ?? string.Empty,
Tags = ParseTags(json), Tags = ParseTags(json),
LastEdit = json["LastEdit"]?.ToObject<DateTimeOffset>() ?? creationDate, LastEdit = json["LastEdit"]?.ToObject<DateTimeOffset>() ?? creationDate,
QuickDesign = json["QuickDesign"]?.ToObject<bool>() ?? true,
}; };
if (design.LastEdit < creationDate) if (design.LastEdit < creationDate)
design.LastEdit = creationDate; design.LastEdit = creationDate;

View file

@ -284,6 +284,18 @@ public sealed class DesignManager : DesignEditor
DesignChanged.Invoke(DesignChanged.Type.WriteProtection, design, value); DesignChanged.Invoke(DesignChanged.Type.WriteProtection, design, value);
} }
/// <summary> Set the quick design bar display status of a design. </summary>
public void SetQuickDesign(Design design, bool value)
{
if (value == design.QuickDesign)
return;
design.QuickDesign = value;
SaveService.QueueSave(design);
Glamourer.Log.Debug($"Set design {design.Identifier} to {(!value ? "no longer be " : string.Empty)} displayed in the quick design bar.");
DesignChanged.Invoke(DesignChanged.Type.QuickDesignBar, design, value);
}
#endregion #endregion
#region Edit Application Rules #region Edit Application Rules

View file

@ -92,6 +92,9 @@ public sealed class DesignChanged()
/// <summary> An existing design changed its write protection status. Data is the new value [bool]. </summary> /// <summary> An existing design changed its write protection status. Data is the new value [bool]. </summary>
WriteProtection, WriteProtection,
/// <summary> An existing design changed its display status for the quick design bar. Data is the new value [bool]. </summary>
QuickDesignBar,
/// <summary> An existing design changed one of the meta flags. Data is the flag, whether it was about their applying and the new value [(MetaFlag, bool, bool)]. </summary> /// <summary> An existing design changed one of the meta flags. Data is the flag, whether it was about their applying and the new value [(MetaFlag, bool, bool)]. </summary>
Other, Other,
} }

View file

@ -23,6 +23,11 @@ public partial class CustomizationDrawer
{ {
if (ImGui.ColorButton($"{_customize[index].Value}##color", color, ImGuiColorEditFlags.None, _framedIconSize)) if (ImGui.ColorButton($"{_customize[index].Value}##color", color, ImGuiColorEditFlags.None, _framedIconSize))
ImGui.OpenPopup(ColorPickerPopupName); ImGui.OpenPopup(ColorPickerPopupName);
else if (current >= 0 && CaptureMouseWheel(ref current, 0, _currentCount))
{
var data = _set.Data(_currentIndex, current, _customize.Face);
UpdateValue(data.Value);
}
} }
var npc = false; var npc = false;

View file

@ -18,8 +18,9 @@ public partial class CustomizationDrawer
using var bigGroup = ImRaii.Group(); using var bigGroup = ImRaii.Group();
var label = _currentOption; var label = _currentOption;
var current = _set.DataByValue(index, _currentByte, out var custom, _customize.Face); var current = _set.DataByValue(index, _currentByte, out var custom, _customize.Face);
var npc = false; var originalCurrent = current;
var npc = false;
if (current < 0) if (current < 0)
{ {
label = $"{_currentOption} (NPC)"; label = $"{_currentOption} (NPC)";
@ -32,7 +33,14 @@ public partial class CustomizationDrawer
using (_ = ImRaii.Disabled(_locked || _currentIndex is CustomizeIndex.Face && _lockedRedraw)) using (_ = ImRaii.Disabled(_locked || _currentIndex is CustomizeIndex.Face && _lockedRedraw))
{ {
if (ImGui.ImageButton(icon.ImGuiHandle, _iconSize)) if (ImGui.ImageButton(icon.ImGuiHandle, _iconSize))
{
ImGui.OpenPopup(IconSelectorPopup); ImGui.OpenPopup(IconSelectorPopup);
}
else if (originalCurrent >= 0 && CaptureMouseWheel(ref current, 0, _currentCount))
{
var data = _set.Data(_currentIndex, current, _customize.Face);
UpdateValue(data.Value);
}
} }
ImGuiUtil.HoverIconTooltip(icon, _iconSize); ImGuiUtil.HoverIconTooltip(icon, _iconSize);

View file

@ -1,6 +1,7 @@
using ImGuiNET; using ImGuiNET;
using OtterGui; using OtterGui;
using OtterGui.Raii; using OtterGui.Raii;
using OtterGuiInternal;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
@ -34,7 +35,8 @@ public partial class CustomizationDrawer
{ {
var tmp = (int)_currentByte.Value; var tmp = (int)_currentByte.Value;
ImGui.SetNextItemWidth(_comboSelectorSize); ImGui.SetNextItemWidth(_comboSelectorSize);
if (ImGui.SliderInt("##slider", ref tmp, 0, _currentCount - 1, "%i", ImGuiSliderFlags.AlwaysClamp)) if (ImGui.SliderInt("##slider", ref tmp, 0, _currentCount - 1, "%i", ImGuiSliderFlags.AlwaysClamp)
|| CaptureMouseWheel(ref tmp, 0, _currentCount - 1))
UpdateValue((CustomizeValue)tmp); UpdateValue((CustomizeValue)tmp);
} }
@ -42,11 +44,10 @@ public partial class CustomizationDrawer
{ {
var tmp = (int)_currentByte.Value; var tmp = (int)_currentByte.Value;
ImGui.SetNextItemWidth(_inputIntSize); ImGui.SetNextItemWidth(_inputIntSize);
var cap = ImGui.GetIO().KeyCtrl ? byte.MaxValue : _currentCount - 1;
if (ImGui.InputInt("##text", ref tmp, 1, 1)) if (ImGui.InputInt("##text", ref tmp, 1, 1))
{ {
var newValue = (CustomizeValue)(ImGui.GetIO().KeyCtrl var newValue = (CustomizeValue)Math.Clamp(tmp, 0, cap);
? Math.Clamp(tmp, 0, byte.MaxValue)
: Math.Clamp(tmp, 0, _currentCount - 1));
UpdateValue(newValue); UpdateValue(newValue);
} }
@ -73,6 +74,10 @@ public partial class CustomizationDrawer
else if (ImGui.GetIO().KeyCtrl) else if (ImGui.GetIO().KeyCtrl)
UpdateValue((CustomizeValue)value); UpdateValue((CustomizeValue)value);
} }
else
{
CheckWheel();
}
if (!_withApply) if (!_withApply)
ImGuiUtil.HoverTooltip("Hold Control to force updates with invalid/unknown options at your own risk."); ImGuiUtil.HoverTooltip("Hold Control to force updates with invalid/unknown options at your own risk.");
@ -81,15 +86,29 @@ public partial class CustomizationDrawer
if (ImGuiUtil.DrawDisabledButton("-", new Vector2(ImGui.GetFrameHeight()), "Select the previous available option in order.", if (ImGuiUtil.DrawDisabledButton("-", new Vector2(ImGui.GetFrameHeight()), "Select the previous available option in order.",
currentIndex <= 0)) currentIndex <= 0))
UpdateValue(_set.Data(_currentIndex, currentIndex - 1, _customize.Face).Value); UpdateValue(_set.Data(_currentIndex, currentIndex - 1, _customize.Face).Value);
else
CheckWheel();
ImGui.SameLine(); ImGui.SameLine();
if (ImGuiUtil.DrawDisabledButton("+", new Vector2(ImGui.GetFrameHeight()), "Select the next available option in order.", if (ImGuiUtil.DrawDisabledButton("+", new Vector2(ImGui.GetFrameHeight()), "Select the next available option in order.",
currentIndex >= _currentCount - 1 || npc)) currentIndex >= _currentCount - 1 || npc))
UpdateValue(_set.Data(_currentIndex, currentIndex + 1, _customize.Face).Value); UpdateValue(_set.Data(_currentIndex, currentIndex + 1, _customize.Face).Value);
else
CheckWheel();
return;
void CheckWheel()
{
if (currentIndex < 0 || !CaptureMouseWheel(ref currentIndex, 0, _currentCount))
return;
var data = _set.Data(_currentIndex, currentIndex, _customize.Face);
UpdateValue(data.Value);
}
} }
private void DrawListSelector(CustomizeIndex index, bool indexedBy1) private void DrawListSelector(CustomizeIndex index, bool indexedBy1)
{ {
using var id = SetId(index); using var id = SetId(index);
using var bigGroup = ImRaii.Group(); using var bigGroup = ImRaii.Group();
using (_ = ImRaii.Disabled(_locked)) using (_ = ImRaii.Disabled(_locked))
@ -122,29 +141,31 @@ public partial class CustomizationDrawer
private void ListCombo0() private void ListCombo0()
{ {
ImGui.SetNextItemWidth(_comboSelectorSize * ImGui.GetIO().FontGlobalScale); ImGui.SetNextItemWidth(_comboSelectorSize * ImGui.GetIO().FontGlobalScale);
var current = _currentByte.Value; var current = (int)_currentByte.Value;
using var combo = ImRaii.Combo("##combo", $"{_currentOption} #{current + 1}"); using (var combo = ImRaii.Combo("##combo", $"{_currentOption} #{current + 1}"))
if (!combo)
return;
for (var i = 0; i < _currentCount; ++i)
{ {
if (ImGui.Selectable($"{_currentOption} #{i + 1}##combo", i == current)) if (combo)
UpdateValue((CustomizeValue)i);
for (var i = 0; i < _currentCount; ++i)
{
if (ImGui.Selectable($"{_currentOption} #{i + 1}##combo", i == current))
UpdateValue((CustomizeValue)i);
}
} }
if (CaptureMouseWheel(ref current, 0, _currentCount))
UpdateValue((CustomizeValue)current);
} }
private void ListInputInt0() private void ListInputInt0()
{ {
var tmp = _currentByte.Value + 1; var tmp = _currentByte.Value + 1;
ImGui.SetNextItemWidth(_inputIntSize); ImGui.SetNextItemWidth(_inputIntSize);
var cap = ImGui.GetIO().KeyCtrl ? byte.MaxValue + 1 : _currentCount;
if (ImGui.InputInt("##text", ref tmp, 1, 1)) if (ImGui.InputInt("##text", ref tmp, 1, 1))
{ {
var newValue = (CustomizeValue)(ImGui.GetIO().KeyCtrl var newValue = Math.Clamp(tmp, 1, cap);
? Math.Clamp(tmp, 1, byte.MaxValue + 1) UpdateValue((CustomizeValue)(newValue - 1));
: Math.Clamp(tmp, 1, _currentCount));
UpdateValue(newValue - 1);
} }
ImGuiUtil.HoverTooltip($"Input Range: [1, {_currentCount}]\n" ImGuiUtil.HoverTooltip($"Input Range: [1, {_currentCount}]\n"
@ -154,28 +175,29 @@ public partial class CustomizationDrawer
private void ListCombo1() private void ListCombo1()
{ {
ImGui.SetNextItemWidth(_comboSelectorSize * ImGui.GetIO().FontGlobalScale); ImGui.SetNextItemWidth(_comboSelectorSize * ImGui.GetIO().FontGlobalScale);
var current = _currentByte.Value; var current = (int)_currentByte.Value;
using var combo = ImRaii.Combo("##combo", $"{_currentOption} #{current}"); using (var combo = ImRaii.Combo("##combo", $"{_currentOption} #{current}"))
if (!combo)
return;
for (var i = 1; i <= _currentCount; ++i)
{ {
if (ImGui.Selectable($"{_currentOption} #{i}##combo", i == current)) if (combo)
UpdateValue((CustomizeValue)i); for (var i = 1; i <= _currentCount; ++i)
{
if (ImGui.Selectable($"{_currentOption} #{i}##combo", i == current))
UpdateValue((CustomizeValue)i);
}
} }
if (CaptureMouseWheel(ref current, 1, _currentCount))
UpdateValue((CustomizeValue)current);
} }
private void ListInputInt1() private void ListInputInt1()
{ {
var tmp = (int)_currentByte.Value; var tmp = (int)_currentByte.Value;
ImGui.SetNextItemWidth(_inputIntSize); ImGui.SetNextItemWidth(_inputIntSize);
var (offset, cap) = ImGui.GetIO().KeyCtrl ? (0, byte.MaxValue) : (1, _currentCount);
if (ImGui.InputInt("##text", ref tmp, 1, 1)) if (ImGui.InputInt("##text", ref tmp, 1, 1))
{ {
var newValue = (CustomizeValue)(ImGui.GetIO().KeyCtrl var newValue = (CustomizeValue)Math.Clamp(tmp, offset, cap);
? Math.Clamp(tmp, 0, byte.MaxValue)
: Math.Clamp(tmp, 1, _currentCount));
UpdateValue(newValue); UpdateValue(newValue);
} }
@ -183,6 +205,26 @@ public partial class CustomizationDrawer
+ "Hold Control to force updates with invalid/unknown options at your own risk."); + "Hold Control to force updates with invalid/unknown options at your own risk.");
} }
private static bool CaptureMouseWheel(ref int value, int offset, int cap)
{
if (!ImGui.IsItemHovered())
return false;
ImGuiInternal.ItemSetUsingMouseWheel();
var mw = (int)ImGui.GetIO().MouseWheel;
if (mw == 0)
return false;
value -= offset;
value = mw switch
{
< 0 => offset + (value + cap + mw) % cap,
_ => offset + (value + mw) % cap,
};
return true;
}
// Draw a customize checkbox. // Draw a customize checkbox.
private void DrawCheckbox(CustomizeIndex idx) private void DrawCheckbox(CustomizeIndex idx)
{ {

View file

@ -1,7 +1,6 @@
using Dalamud.Interface.Utility; using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Utility.Raii;
using Glamourer.Automation; using Glamourer.Automation;
using Glamourer.GameData;
using Glamourer.Designs; using Glamourer.Designs;
using Glamourer.Events; using Glamourer.Events;
using Glamourer.Services; using Glamourer.Services;
@ -25,7 +24,7 @@ public abstract class DesignComboBase : FilterComboCache<Tuple<Design, string>>,
protected DesignComboBase(Func<IReadOnlyList<Tuple<Design, string>>> generator, Logger log, DesignChanged designChanged, protected DesignComboBase(Func<IReadOnlyList<Tuple<Design, string>>> generator, Logger log, DesignChanged designChanged,
TabSelected tabSelected, EphemeralConfig config, DesignColors designColors) TabSelected tabSelected, EphemeralConfig config, DesignColors designColors)
: base(generator, log) : base(generator, MouseWheelType.Unmodified, log)
{ {
_designChanged = designChanged; _designChanged = designChanged;
TabSelected = tabSelected; TabSelected = tabSelected;
@ -38,7 +37,10 @@ public abstract class DesignComboBase : FilterComboCache<Tuple<Design, string>>,
=> _config.IncognitoMode; => _config.IncognitoMode;
void IDisposable.Dispose() void IDisposable.Dispose()
=> _designChanged.Unsubscribe(OnDesignChange); {
_designChanged.Unsubscribe(OnDesignChange);
GC.SuppressFinalize(this);
}
protected override bool DrawSelectable(int globalIdx, bool selected) protected override bool DrawSelectable(int globalIdx, bool selected)
{ {
@ -118,63 +120,87 @@ public abstract class DesignComboBase : FilterComboCache<Tuple<Design, string>>,
{ {
case DesignChanged.Type.Created: case DesignChanged.Type.Created:
case DesignChanged.Type.Renamed: case DesignChanged.Type.Renamed:
Cleanup(); case DesignChanged.Type.ChangedColor:
break;
case DesignChanged.Type.Deleted: case DesignChanged.Type.Deleted:
Cleanup(); case DesignChanged.Type.QuickDesignBar:
if (CurrentSelection?.Item1 == design) var priorState = IsInitialized;
if (priorState)
Cleanup();
CurrentSelectionIdx = Items.IndexOf(s => ReferenceEquals(s.Item1, CurrentSelection?.Item1));
if (CurrentSelectionIdx >= 0)
{ {
CurrentSelectionIdx = Items.Count > 0 ? 0 : -1; CurrentSelection = Items[CurrentSelectionIdx];
CurrentSelection = Items[CurrentSelectionIdx]; }
else if (Items.Count > 0)
{
CurrentSelectionIdx = 0;
CurrentSelection = Items[0];
}
else
{
CurrentSelection = null;
} }
if (!priorState)
Cleanup();
break; break;
} }
} }
} }
public sealed class DesignCombo : DesignComboBase public abstract class DesignCombo : DesignComboBase
{ {
private readonly DesignManager _manager; protected DesignCombo(Logger log, DesignChanged designChanged, TabSelected tabSelected,
EphemeralConfig config, DesignColors designColors, Func<IReadOnlyList<Tuple<Design, string>>> generator)
public DesignCombo(DesignManager designs, DesignFileSystem fileSystem, Logger log, DesignChanged designChanged, TabSelected tabSelected, : base(generator, log, designChanged, tabSelected, config, designColors)
EphemeralConfig config, DesignColors designColors)
: base(() => designs.Designs
.Select(d => new Tuple<Design, string>(d, fileSystem.FindLeaf(d, out var l) ? l.FullName() : string.Empty))
.OrderBy(d => d.Item2)
.ToList(), log, designChanged, tabSelected, config, designColors)
{ {
_manager = designs; if (Items.Count == 0)
if (designs.Designs.Count == 0)
return; return;
CurrentSelection = Items[0]; CurrentSelection = Items[0];
CurrentSelectionIdx = 0; CurrentSelectionIdx = 0;
base.Cleanup();
} }
public Design? Design public Design? Design
=> CurrentSelection?.Item1; => CurrentSelection?.Item1;
public void Draw(float width) public void Draw(float width)
{ => Draw(Design, (Incognito ? Design?.Incognito : Design?.Name.Text) ?? string.Empty, width);
Draw(Design, (Incognito ? Design?.Incognito : Design?.Name.Text) ?? string.Empty, width);
if (ImGui.IsItemHovered() && _manager.Designs.Count > 1)
{
var mouseWheel = -(int)ImGui.GetIO().MouseWheel % _manager.Designs.Count;
CurrentSelectionIdx = mouseWheel switch
{
< 0 when CurrentSelectionIdx < 0 => _manager.Designs.Count - 1 + mouseWheel,
< 0 => (CurrentSelectionIdx + _manager.Designs.Count + mouseWheel) % _manager.Designs.Count,
> 0 when CurrentSelectionIdx < 0 => mouseWheel,
> 0 => (CurrentSelectionIdx + mouseWheel) % _manager.Designs.Count,
_ => CurrentSelectionIdx,
};
CurrentSelection = Items[CurrentSelectionIdx];
}
}
} }
public sealed class RevertDesignCombo : DesignComboBase, IDisposable public sealed class QuickDesignCombo(
DesignManager designs,
DesignFileSystem fileSystem,
Logger log,
DesignChanged designChanged,
TabSelected tabSelected,
EphemeralConfig config,
DesignColors designColors)
: DesignCombo(log, designChanged, tabSelected, config, designColors, () =>
[
.. designs.Designs
.Where(d => d.QuickDesign)
.Select(d => new Tuple<Design, string>(d, fileSystem.FindLeaf(d, out var l) ? l.FullName() : string.Empty))
.OrderBy(d => d.Item2),
]);
public sealed class LinkDesignCombo(
DesignManager designs,
DesignFileSystem fileSystem,
Logger log,
DesignChanged designChanged,
TabSelected tabSelected,
EphemeralConfig config,
DesignColors designColors)
: DesignCombo(log, designChanged, tabSelected, config, designColors, () =>
[
.. designs.Designs
.Select(d => new Tuple<Design, string>(d, fileSystem.FindLeaf(d, out var l) ? l.FullName() : string.Empty))
.OrderBy(d => d.Item2),
]);
public sealed class RevertDesignCombo : DesignComboBase
{ {
public const int RevertDesignIndex = -1228; public const int RevertDesignIndex = -1228;
public readonly Design RevertDesign; public readonly Design RevertDesign;

View file

@ -24,7 +24,7 @@ public sealed class DesignQuickBar : Window, IDisposable
: ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoDocking | ImGuiWindowFlags.NoFocusOnAppearing; : ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoDocking | ImGuiWindowFlags.NoFocusOnAppearing;
private readonly Configuration _config; private readonly Configuration _config;
private readonly DesignCombo _designCombo; private readonly QuickDesignCombo _designCombo;
private readonly StateManager _stateManager; private readonly StateManager _stateManager;
private readonly AutoDesignApplier _autoDesignApplier; private readonly AutoDesignApplier _autoDesignApplier;
private readonly ObjectManager _objects; private readonly ObjectManager _objects;
@ -34,7 +34,7 @@ public sealed class DesignQuickBar : Window, IDisposable
private DateTime _keyboardToggle = DateTime.UnixEpoch; private DateTime _keyboardToggle = DateTime.UnixEpoch;
private int _numButtons; private int _numButtons;
public DesignQuickBar(Configuration config, DesignCombo designCombo, StateManager stateManager, IKeyState keyState, public DesignQuickBar(Configuration config, QuickDesignCombo designCombo, StateManager stateManager, IKeyState keyState,
ObjectManager objects, AutoDesignApplier autoDesignApplier) ObjectManager objects, AutoDesignApplier autoDesignApplier)
: base("Glamourer Quick Bar", ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoDocking) : base("Glamourer Quick Bar", ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoDocking)
{ {
@ -299,7 +299,8 @@ public sealed class DesignQuickBar : Window, IDisposable
(true, false) => 3, (true, false) => 3,
(false, false) => 2, (false, false) => 2,
}; };
Size = new Vector2((7 + _numButtons) * ImGui.GetFrameHeight() + _numButtons * ImGui.GetStyle().ItemInnerSpacing.X, ImGui.GetFrameHeight()); Size = new Vector2((7 + _numButtons) * ImGui.GetFrameHeight() + _numButtons * ImGui.GetStyle().ItemInnerSpacing.X,
ImGui.GetFrameHeight());
return Size.Value.X; return Size.Value.X;
} }
} }

View file

@ -10,7 +10,7 @@ using Penumbra.GameData.Structs;
namespace Glamourer.Gui.Equipment; namespace Glamourer.Gui.Equipment;
public sealed class GlamourerColorCombo(float _comboWidth, DictStain _stains, FavoriteManager _favorites) public sealed class GlamourerColorCombo(float _comboWidth, DictStain _stains, FavoriteManager _favorites)
: FilterComboColors(_comboWidth, CreateFunc(_stains, _favorites), Glamourer.Log) : FilterComboColors(_comboWidth, MouseWheelType.Unmodified, CreateFunc(_stains, _favorites), Glamourer.Log)
{ {
protected override bool DrawSelectable(int globalIdx, bool selected) protected override bool DrawSelectable(int globalIdx, bool selected)
{ {
@ -36,6 +36,9 @@ public sealed class GlamourerColorCombo(float _comboWidth, DictStain _stains, Fa
return base.DrawSelectable(globalIdx, selected); return base.DrawSelectable(globalIdx, selected);
} }
public override bool Draw(string label, uint color, string name, bool found, bool gloss, float previewWidth)
=> base.Draw(label, color, name, found, gloss, previewWidth);
private static Func<IReadOnlyList<KeyValuePair<byte, (string Name, uint Color, bool Gloss)>>> CreateFunc(DictStain stains, private static Func<IReadOnlyList<KeyValuePair<byte, (string Name, uint Color, bool Gloss)>>> CreateFunc(DictStain stains,
FavoriteManager favorites) FavoriteManager favorites)
=> () => stains.Select(kvp => (kvp, favorites.Contains(kvp.Key))).OrderBy(p => !p.Item2).Select(p => p.kvp) => () => stains.Select(kvp => (kvp, favorites.Contains(kvp.Key))).OrderBy(p => !p.Item2).Select(p => p.kvp)

View file

@ -24,7 +24,7 @@ public sealed class ItemCombo : FilterComboCache<EquipItem>
public Variant CustomVariant { get; private set; } public Variant CustomVariant { get; private set; }
public ItemCombo(IDataManager gameData, ItemManager items, EquipSlot slot, Logger log, FavoriteManager favorites) public ItemCombo(IDataManager gameData, ItemManager items, EquipSlot slot, Logger log, FavoriteManager favorites)
: base(() => GetItems(favorites, items, slot), log) : base(() => GetItems(favorites, items, slot), MouseWheelType.Unmodified, log)
{ {
_favorites = favorites; _favorites = favorites;
Label = GetLabel(gameData, slot); Label = GetLabel(gameData, slot);

View file

@ -17,7 +17,7 @@ public sealed class WeaponCombo : FilterComboCache<EquipItem>
private float _innerWidth; private float _innerWidth;
public WeaponCombo(ItemManager items, FullEquipType type, Logger log) public WeaponCombo(ItemManager items, FullEquipType type, Logger log)
: base(() => GetWeapons(items, type), log) : base(() => GetWeapons(items, type), MouseWheelType.Unmodified, log)
{ {
Label = GetLabel(type); Label = GetLabel(type);
SearchByParts = true; SearchByParts = true;

View file

@ -16,7 +16,7 @@ public sealed class HumanNpcCombo(
DictBNpc bNpcs, DictBNpc bNpcs,
HumanModelList humans, HumanModelList humans,
Logger log) Logger log)
: FilterComboCache<(string Name, ObjectKind Kind, uint[] Ids)>(() => CreateList(modelCharaDict, bNpcNames, bNpcs, humans), log) : FilterComboCache<(string Name, ObjectKind Kind, uint[] Ids)>(() => CreateList(modelCharaDict, bNpcNames, bNpcs, humans), MouseWheelType.None, log)
{ {
protected override string ToString((string Name, ObjectKind Kind, uint[] Ids) obj) protected override string ToString((string Name, ObjectKind Kind, uint[] Ids) obj)
=> obj.Name; => obj.Name;

View file

@ -429,7 +429,7 @@ public class SetPanel(
} }
private sealed class JobGroupCombo(AutoDesignManager manager, JobService jobs, Logger log) private sealed class JobGroupCombo(AutoDesignManager manager, JobService jobs, Logger log)
: FilterComboCache<JobGroup>(() => jobs.JobGroups.Values.ToList(), log) : FilterComboCache<JobGroup>(() => jobs.JobGroups.Values.ToList(), MouseWheelType.None, log)
{ {
public void Draw(AutoDesignSet set, AutoDesign design, int autoDesignIndex) public void Draw(AutoDesignSet set, AutoDesign design, int autoDesignIndex)
{ {

View file

@ -10,8 +10,15 @@ public sealed class DesignColorCombo(DesignColors _designColors, bool _skipAutom
FilterComboCache<string>(_skipAutomatic FilterComboCache<string>(_skipAutomatic
? _designColors.Keys.OrderBy(k => k) ? _designColors.Keys.OrderBy(k => k)
: _designColors.Keys.OrderBy(k => k).Prepend(DesignColors.AutomaticName), : _designColors.Keys.OrderBy(k => k).Prepend(DesignColors.AutomaticName),
Glamourer.Log) MouseWheelType.Shift, Glamourer.Log)
{ {
protected override void OnMouseWheel(string preview, ref int current, int steps)
{
if (CurrentSelectionIdx < 0)
CurrentSelectionIdx = Items.IndexOf(preview);
base.OnMouseWheel(preview, ref current, steps);
}
protected override bool DrawSelectable(int globalIdx, bool selected) protected override bool DrawSelectable(int globalIdx, bool selected)
{ {
var isAutomatic = !_skipAutomatic && globalIdx == 0; var isAutomatic = !_skipAutomatic && globalIdx == 0;

View file

@ -125,10 +125,23 @@ public class DesignDetailTab
Glamourer.Messager.NotificationMessage(ex, ex.Message, "Could not rename or move design", NotificationType.Error); Glamourer.Messager.NotificationMessage(ex, ex.Message, "Could not rename or move design", NotificationType.Error);
} }
ImGuiUtil.DrawFrameColumn("Quick Design Bar");
ImGui.TableNextColumn();
if (ImGui.RadioButton("Display##qdb", _selector.Selected.QuickDesign))
_manager.SetQuickDesign(_selector.Selected!, true);
var hovered = ImGui.IsItemHovered();
ImGui.SameLine();
if (ImGui.RadioButton("Hide##qdb", !_selector.Selected.QuickDesign))
_manager.SetQuickDesign(_selector.Selected!, false);
if (hovered || ImGui.IsItemHovered())
ImGui.SetTooltip("Display or hide this design in your quick design bar.");
ImGuiUtil.DrawFrameColumn("Color"); ImGuiUtil.DrawFrameColumn("Color");
var colorName = _selector.Selected!.Color.Length == 0 ? DesignColors.AutomaticName : _selector.Selected!.Color; var colorName = _selector.Selected!.Color.Length == 0 ? DesignColors.AutomaticName : _selector.Selected!.Color;
ImGui.TableNextColumn(); ImGui.TableNextColumn();
if (_colorCombo.Draw("##colorCombo", colorName, "Associate a color with this design. Right-Click to revert to automatic coloring.", if (_colorCombo.Draw("##colorCombo", colorName, "Associate a color with this design.\n"
+ "Right-Click to revert to automatic coloring.\n"
+ "Hold Control and scroll the mousewheel to scroll.",
width.X - ImGui.GetStyle().ItemSpacing.X - ImGui.GetFrameHeight(), ImGui.GetTextLineHeight()) width.X - ImGui.GetStyle().ItemSpacing.X - ImGui.GetFrameHeight(), ImGui.GetTextLineHeight())
&& _colorCombo.CurrentSelection != null) && _colorCombo.CurrentSelection != null)
{ {

View file

@ -10,7 +10,7 @@ using OtterGui.Services;
namespace Glamourer.Gui.Tabs.DesignTab; namespace Glamourer.Gui.Tabs.DesignTab;
public class DesignLinkDrawer(DesignLinkManager _linkManager, DesignFileSystemSelector _selector, DesignCombo _combo) : IUiService public class DesignLinkDrawer(DesignLinkManager _linkManager, DesignFileSystemSelector _selector, LinkDesignCombo _combo) : IUiService
{ {
private int _dragDropIndex = -1; private int _dragDropIndex = -1;
private LinkOrder _dragDropOrder = LinkOrder.None; private LinkOrder _dragDropOrder = LinkOrder.None;

View file

@ -11,7 +11,7 @@ namespace Glamourer.Gui.Tabs.DesignTab;
public sealed class ModCombo : FilterComboCache<(Mod Mod, ModSettings Settings)> public sealed class ModCombo : FilterComboCache<(Mod Mod, ModSettings Settings)>
{ {
public ModCombo(PenumbraService penumbra, Logger log) public ModCombo(PenumbraService penumbra, Logger log)
: base(penumbra.GetMods, log) : base(penumbra.GetMods, MouseWheelType.None, log)
{ {
SearchByParts = false; SearchByParts = false;
} }

View file

@ -3,6 +3,7 @@ using Dalamud.Interface.Utility;
using Glamourer.Designs; using Glamourer.Designs;
using ImGuiNET; using ImGuiNET;
using OtterGui; using OtterGui;
using OtterGui.Filesystem;
using OtterGui.Raii; using OtterGui.Raii;
namespace Glamourer.Gui.Tabs.DesignTab; namespace Glamourer.Gui.Tabs.DesignTab;
@ -21,6 +22,7 @@ public class MultiDesignPanel(DesignFileSystemSelector _selector, DesignManager
DrawDesignList(); DrawDesignList();
var offset = DrawMultiTagger(width); var offset = DrawMultiTagger(width);
DrawMultiColor(width, offset); DrawMultiColor(width, offset);
DrawMultiQuickDesignBar(offset);
} }
private void DrawDesignList() private void DrawDesignList()
@ -35,6 +37,8 @@ public class MultiDesignPanel(DesignFileSystemSelector _selector, DesignManager
var sizeMods = availableSizePercent * 35; var sizeMods = availableSizePercent * 35;
var sizeFolders = availableSizePercent * 65; var sizeFolders = availableSizePercent * 65;
_numQuickDesignEnabled = 0;
_numDesigns = 0;
using (var table = ImRaii.Table("mods", 3, ImGuiTableFlags.RowBg)) using (var table = ImRaii.Table("mods", 3, ImGuiTableFlags.RowBg))
{ {
if (!table) if (!table)
@ -61,15 +65,24 @@ public class MultiDesignPanel(DesignFileSystemSelector _selector, DesignManager
ImGui.TableNextColumn(); ImGui.TableNextColumn();
ImGui.AlignTextToFramePadding(); ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted(fullName); ImGui.TextUnformatted(fullName);
if (path is not DesignFileSystem.Leaf l2)
continue;
++_numDesigns;
if (l2.Value.QuickDesign)
++_numQuickDesignEnabled;
} }
} }
ImGui.Separator(); ImGui.Separator();
} }
private string _tag = string.Empty; private string _tag = string.Empty;
private readonly List<Design> _addDesigns = []; private int _numQuickDesignEnabled;
private readonly List<(Design, int)> _removeDesigns = []; private int _numDesigns;
private readonly List<Design> _addDesigns = [];
private readonly List<(Design, int)> _removeDesigns = [];
private float DrawMultiTagger(Vector2 width) private float DrawMultiTagger(Vector2 width)
{ {
@ -110,6 +123,30 @@ public class MultiDesignPanel(DesignFileSystemSelector _selector, DesignManager
return offset; return offset;
} }
private void DrawMultiQuickDesignBar(float offset)
{
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted("Multi QDB:");
ImGui.SameLine(offset, ImGui.GetStyle().ItemSpacing.X);
var buttonWidth = new Vector2((ImGui.GetContentRegionAvail().X - ImGui.GetStyle().ItemSpacing.X) / 2, 0);
var diff = _numDesigns - _numQuickDesignEnabled;
var tt = diff == 0
? $"All {_numDesigns} selected designs are already displayed in the quick design bar."
: $"Display all {_numDesigns} selected designs in the quick design bar. Changes {diff} designs.";
if (ImGuiUtil.DrawDisabledButton("Display Selected Designs in QDB", buttonWidth, tt, diff == 0))
foreach(var design in _selector.SelectedPaths.OfType<DesignFileSystem.Leaf>())
_editor.SetQuickDesign(design.Value, true);
ImGui.SameLine();
tt = _numQuickDesignEnabled == 0
? $"All {_numDesigns} selected designs are already hidden in the quick design bar."
: $"Hide all {_numDesigns} selected designs in the quick design bar. Changes {_numQuickDesignEnabled} designs.";
if (ImGuiUtil.DrawDisabledButton("Hide Selected Designs in QDB", buttonWidth, tt, _numQuickDesignEnabled == 0))
foreach (var design in _selector.SelectedPaths.OfType<DesignFileSystem.Leaf>())
_editor.SetQuickDesign(design.Value, false);
ImGui.Separator();
}
private void DrawMultiColor(Vector2 width, float offset) private void DrawMultiColor(Vector2 width, float offset)
{ {
ImGui.AlignTextToFramePadding(); ImGui.AlignTextToFramePadding();

View file

@ -4,7 +4,7 @@ using OtterGui.Widgets;
namespace Glamourer.Gui.Tabs; namespace Glamourer.Gui.Tabs;
public class NpcCombo(NpcCustomizeSet npcCustomizeSet) public class NpcCombo(NpcCustomizeSet npcCustomizeSet)
: FilterComboCache<NpcData>(npcCustomizeSet, Glamourer.Log) : FilterComboCache<NpcData>(npcCustomizeSet, MouseWheelType.None, Glamourer.Log)
{ {
protected override string ToString(NpcData obj) protected override string ToString(NpcData obj)
=> obj.Name; => obj.Name;

View file

@ -146,7 +146,8 @@ public static class ServiceManagerA
.AddSingleton<MultiDesignPanel>() .AddSingleton<MultiDesignPanel>()
.AddSingleton<DesignPanel>() .AddSingleton<DesignPanel>()
.AddSingleton<DesignTab>() .AddSingleton<DesignTab>()
.AddSingleton<DesignCombo>() .AddSingleton<QuickDesignCombo>()
.AddSingleton<LinkDesignCombo>()
.AddSingleton<RevertDesignCombo>() .AddSingleton<RevertDesignCombo>()
.AddSingleton<ModAssociationsTab>() .AddSingleton<ModAssociationsTab>()
.AddSingleton<DesignDetailTab>() .AddSingleton<DesignDetailTab>()

@ -1 +1 @@
Subproject commit 04eb0b5ed3930e9cb87ad00dffa9c8be90b58bb3 Subproject commit 2d8a03eebd80e19c6936a28ab2e3a8c164cc17f3

@ -1 +1 @@
Subproject commit 260ac69cd6f17050eaf9b7e0b5ce9a8843edfee4 Subproject commit fb18c80551203a1cf6cd01ec2b0850fbc8e44240