From 9f95fb90dd300b372231e9116bf846e30e943b3a Mon Sep 17 00:00:00 2001 From: Knack117 Date: Sun, 1 Feb 2026 11:12:54 -0500 Subject: [PATCH] v1.0.6.0: Tooltip icons for action bars, status effects, party cooldowns; World Object Tooltip quest/NPC icons Co-authored-by: Cursor --- HSUI.csproj | 6 +- HSUI.json | 2 +- Helpers/TooltipsHelper.cs | 63 +++++++++++++++---- Interface/GeneralElements/ActionBarsHud.cs | 4 +- Interface/Party/PartyFramesCooldownListHud.cs | 5 +- Interface/PartyCooldowns/PartyCooldownsHud.cs | 5 +- .../StatusEffects/StatusEffectsListHud.cs | 4 +- changelog.md | 4 ++ pluginmaster.json | 12 ++-- 9 files changed, 79 insertions(+), 26 deletions(-) diff --git a/HSUI.csproj b/HSUI.csproj index 78c3f3f..34ef2b1 100644 --- a/HSUI.csproj +++ b/HSUI.csproj @@ -9,9 +9,9 @@ HSUI - 1.0.5.0 - 1.0.5.0 - 1.0.5.0 + 1.0.6.0 + 1.0.6.0 + 1.0.6.0 diff --git a/HSUI.json b/HSUI.json index ef04207..08deabf 100644 --- a/HSUI.json +++ b/HSUI.json @@ -2,7 +2,7 @@ "Author": "Knack117", "Name": "HSUI", "InternalName": "HSUI", - "AssemblyVersion": "1.0.5.0", + "AssemblyVersion": "1.0.6.0", "Description": "HSUI provides a highly configurable HUD replacement for FFXIV, recreated from DelvUI using KamiToolKit, FFXIVClientStructs, and Dalamud. Features unit frames, castbars, job gauges, nameplates, party frames, status effects, enemy list, configurable hotbars with drag-and-drop, and profiles.", "ApplicableVersion": "any", "RepoUrl": "https://github.com/Knack117/HSUI", diff --git a/Helpers/TooltipsHelper.cs b/Helpers/TooltipsHelper.cs index 63199f7..d8ee566 100644 --- a/Helpers/TooltipsHelper.cs +++ b/Helpers/TooltipsHelper.cs @@ -6,6 +6,7 @@ using HSUI.Config; using HSUI.Config.Attributes; using HSUI.Interface.GeneralElements; using Dalamud.Bindings.ImGui; +using FFXIVClientStructs.FFXIV.Client.Game.Object; using System; using System.Numerics; using System.Text; @@ -55,18 +56,21 @@ namespace HSUI.Helpers private string? _currentTooltipTitle = null; private Vector2 _titleSize; private string? _previousRawText = null; + private uint? _currentIconId = null; private Vector2 _position; private Vector2 _size; private bool _dataIsValid = false; - public void ShowTooltipOnCursor(string text, string? title = null, uint id = 0, string name = "") + private const float IconSize = 24f; + + public void ShowTooltipOnCursor(string text, string? title = null, uint id = 0, string name = "", uint? iconId = null) { - ShowTooltip(text, ImGui.GetMousePos(), title, id, name); + ShowTooltip(text, ImGui.GetMousePos(), title, id, name, iconId); } - public void ShowTooltip(string text, Vector2 position, string? title = null, uint id = 0, string name = "") + public void ShowTooltip(string text, Vector2 position, string? title = null, uint id = 0, string name = "", uint? iconId = null) { if (text == null) { @@ -82,6 +86,8 @@ namespace HSUI.Helpers _previousRawText = text; } + _currentIconId = iconId; + // calcualte title size _titleSize = Vector2.Zero; if (title != null) @@ -113,7 +119,13 @@ namespace HSUI.Helpers _textSize = ImGui.CalcTextSize(_currentTooltipText, false, MaxWidth); } - _size = new Vector2(Math.Max(_titleSize.X, _textSize.X) + Margin * 2, _titleSize.Y + _textSize.Y + Margin * 2); + float contentWidth = Math.Max(_titleSize.X, _textSize.X); + float contentHeight = _titleSize.Y + _textSize.Y; + float iconSizeScaled = IconSize * ImGuiHelpers.GlobalScale * _config.TooltipScale; + float iconGap = _currentIconId.HasValue && _currentIconId.Value > 0 ? iconSizeScaled + Margin : 0; + _size = new Vector2( + contentWidth + iconGap + Margin * 2, + Math.Max(contentHeight, _currentIconId.HasValue && _currentIconId.Value > 0 ? iconSizeScaled : 0) + Margin * 2); // position tooltip using the given coordinates as bottom center position.X = position.X - _size.X / 2f; @@ -220,16 +232,29 @@ namespace HSUI.Helpers if (string.IsNullOrEmpty(body)) body = "(No info)"; + uint? iconId = _worldTooltipConfig.ShowIcon ? GetWorldObjectIconId(mouseOverTarget) : null; + if (_worldTooltipConfig.DetachFromCursor) { - ShowTooltip(body, _worldTooltipConfig.Position, name); + ShowTooltip(body, _worldTooltipConfig.Position, name, 0, "", iconId); } else { - ShowTooltipOnCursor(body, name); + ShowTooltipOnCursor(body, name, 0, "", iconId); } } + private static unsafe uint? GetWorldObjectIconId(IGameObject gameObject) + { + if (gameObject == null || gameObject.Address == IntPtr.Zero) + return null; + var obj = (GameObject*)gameObject.Address; + if (obj == null) + return null; + uint iconId = obj->NamePlateIconId; + return iconId > 0 ? iconId : null; + } + public void RemoveTooltip() { _dataIsValid = false; @@ -279,14 +304,26 @@ namespace HSUI.Helpers float globalScaleCorrection = -15 + 15 * ImGuiHelpers.GlobalScale; string fontId = _config.FontID ?? FontsConfig.DefaultSmallFontKey; - float wrapWidth = Math.Max(_titleSize.X, _textSize.X) + Margin; + float iconSizeScaled = IconSize * ImGuiHelpers.GlobalScale * _config.TooltipScale; + float iconGap = (_currentIconId.HasValue && _currentIconId.Value > 0) ? iconSizeScaled + Margin : 0; + float wrapWidth = (_currentIconId.HasValue && _currentIconId.Value > 0) + ? _size.X - iconGap - Margin * 2 + : Math.Max(_titleSize.X, _textSize.X) + Margin; + float textBlockX = windowMargin.X + Margin + iconGap; + + if (_currentIconId.HasValue && _currentIconId.Value > 0) + { + var iconPos = _position + new Vector2(Margin, Margin); + DrawHelper.DrawIcon(_currentIconId.Value, iconPos, new Vector2(iconSizeScaled, iconSizeScaled), false, drawList); + } + if (_currentTooltipTitle != null) { // title Vector2 cursorPos; using (FontsManager.Instance.PushFont(fontId)) { - cursorPos = new Vector2(windowMargin.X + _size.X / 2f - _titleSize.X / 2f, Margin); + cursorPos = new Vector2(textBlockX, Margin); ImGui.SetCursorPos(cursorPos); ImGui.PushTextWrapPos(cursorPos.X + wrapWidth + globalScaleCorrection); ImGui.TextColored(_config.TitleColor.Vector, _currentTooltipTitle); @@ -296,7 +333,7 @@ namespace HSUI.Helpers // text using (FontsManager.Instance.PushFont(fontId)) { - cursorPos = new Vector2(windowMargin.X + _size.X / 2f - _textSize.X / 2f, Margin + _titleSize.Y); + cursorPos = new Vector2(textBlockX, Margin + _titleSize.Y); ImGui.SetCursorPos(cursorPos); ImGui.PushTextWrapPos(cursorPos.X + wrapWidth + globalScaleCorrection); ImGui.TextColored(_config.TextColor.Vector, _currentTooltipText); @@ -308,8 +345,8 @@ namespace HSUI.Helpers // text using (FontsManager.Instance.PushFont(fontId)) { - var cursorPos = windowMargin + new Vector2(Margin, Margin); - var textWidth = _size.X - Margin * 2; + var cursorPos = new Vector2(textBlockX, Margin); + var textWidth = _size.X - iconGap - Margin * 2; ImGui.SetCursorPos(cursorPos); ImGui.PushTextWrapPos(cursorPos.X + textWidth + globalScaleCorrection); @@ -426,6 +463,10 @@ namespace HSUI.Helpers [Order(2, collapseWith = nameof(DetachFromCursor))] public Vector2 Position = new Vector2(100, 100); + [Checkbox("Show Icon (quest/NPC icons)", spacing = true)] + [Order(3)] + public bool ShowIcon = true; + [Checkbox("Show Level", spacing = true)] [Order(5)] public bool ShowLevel = true; diff --git a/Interface/GeneralElements/ActionBarsHud.cs b/Interface/GeneralElements/ActionBarsHud.cs index cec7804..5398709 100644 --- a/Interface/GeneralElements/ActionBarsHud.cs +++ b/Interface/GeneralElements/ActionBarsHud.cs @@ -364,7 +364,7 @@ namespace HSUI.Interface.GeneralElements if (!string.IsNullOrEmpty(title) || !string.IsNullOrEmpty(text)) { string body = string.IsNullOrEmpty(text) ? title : text; - TooltipsHelper.Instance.ShowTooltipOnCursor(body, title, slot.ActionId, ""); + TooltipsHelper.Instance.ShowTooltipOnCursor(body, title, slot.ActionId, "", slot.IconId > 0 ? slot.IconId : null); if (IsTooltipDebugEnabled()) Plugin.Logger.Information($"[HSUI Tooltip DBG] ActionBar tooltip (main overlay): slot={i} title='{title}'"); } @@ -423,7 +423,7 @@ namespace HSUI.Interface.GeneralElements if (!string.IsNullOrEmpty(title) || !string.IsNullOrEmpty(text)) { string body = string.IsNullOrEmpty(text) ? title : text; - TooltipsHelper.Instance.ShowTooltipOnCursor(body, title, slot.ActionId, ""); + TooltipsHelper.Instance.ShowTooltipOnCursor(body, title, slot.ActionId, "", slot.IconId > 0 ? slot.IconId : null); if (IsTooltipDebugEnabled()) Plugin.Logger.Information($"[HSUI Tooltip DBG] ActionBar tooltip (overlay): slot={i} title='{title}'"); } diff --git a/Interface/Party/PartyFramesCooldownListHud.cs b/Interface/Party/PartyFramesCooldownListHud.cs index d29ed74..819daee 100644 --- a/Interface/Party/PartyFramesCooldownListHud.cs +++ b/Interface/Party/PartyFramesCooldownListHud.cs @@ -283,10 +283,13 @@ namespace HSUI.Interface.Party // tooltip if (Config.ShowTooltips) { + uint? iconId = hoveringCooldown.Data.IconId > 0 ? hoveringCooldown.Data.IconId : null; TooltipsHelper.Instance.ShowTooltipOnCursor( hoveringCooldown.TooltipText(), hoveringCooldown.Data.Name, - hoveringCooldown.Data.ActionId + hoveringCooldown.Data.ActionId, + "", + iconId ); } } diff --git a/Interface/PartyCooldowns/PartyCooldownsHud.cs b/Interface/PartyCooldowns/PartyCooldownsHud.cs index 7a2c1dc..5118cdf 100644 --- a/Interface/PartyCooldowns/PartyCooldownsHud.cs +++ b/Interface/PartyCooldowns/PartyCooldownsHud.cs @@ -304,10 +304,13 @@ namespace HSUI.Interface.PartyCooldowns pos = origin + new Vector2(pos.X - size.Y + 1, pos.Y); if (Config.ShowTooltips && ImGui.IsMouseHoveringRect(pos, pos + _barConfig.Size)) { + uint? iconId = cooldown.Data.IconId > 0 ? cooldown.Data.IconId : null; TooltipsHelper.Instance.ShowTooltipOnCursor( cooldown.TooltipText(), cooldown.Data.Name, - cooldown.Data.ActionId + cooldown.Data.ActionId, + "", + iconId ); } diff --git a/Interface/StatusEffects/StatusEffectsListHud.cs b/Interface/StatusEffects/StatusEffectsListHud.cs index 075a490..fc8acb6 100644 --- a/Interface/StatusEffects/StatusEffectsListHud.cs +++ b/Interface/StatusEffects/StatusEffectsListHud.cs @@ -468,11 +468,13 @@ namespace HSUI.Interface.StatusEffects // tooltip if (Config.ShowTooltips) { + uint? iconId = data.Data.Icon > 0 ? data.Data.Icon : null; TooltipsHelper.Instance.ShowTooltipOnCursor( EncryptedStringsHelper.GetString(data.Data.Description.ToDalamudString().ToString()), EncryptedStringsHelper.GetString(data.Data.Name.ToString()), data.Status.StatusId, - GetStatusActorName(data.Status) + GetStatusActorName(data.Status), + iconId ); } diff --git a/changelog.md b/changelog.md index ed418cc..7df4213 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,7 @@ +# 1.0.6.0 +- **Tooltips**: Icons in tooltips — action bars, status effects, and party cooldowns now show the item's icon next to the tooltip text when available. +- **World Object Tooltip**: Quest/NPC icons — hovering over NPCs or objects displays their quest or state icon (e.g., exclamation marks, turn-in checks) in the tooltip. Toggle in Misc → World Tooltip → Show Icon. + # 1.0.5.0 - **World Object Tooltip**: Hover over players, NPCs, or objects in the world to see a tooltip with name, level, HP, job (players), FC tag (optional), distance, and object ID. Enable in Misc → World Tooltip; optional detach-from-cursor with fixed position. diff --git a/pluginmaster.json b/pluginmaster.json index 5d89306..f50aad7 100644 --- a/pluginmaster.json +++ b/pluginmaster.json @@ -4,9 +4,9 @@ "Name": "HSUI", "Punchline": "A modern HUD replacement built for customization.", "Description": "HSUI provides a highly configurable HUD replacement for FFXIV, recreated from DelvUI using KamiToolKit, FFXIVClientStructs, and Dalamud. Features unit frames, castbars, job gauges, nameplates, party frames, status effects, enemy list, configurable hotbars with drag-and-drop, and profiles.", - "Changelog": "World Object Tooltip: hover over players/NPCs/objects for name, level, HP, job, FC tag, distance; optional detach-from-cursor. PvP nameplates: GC icons, Role/Job icon. Main Actions tooltips. Alliance frame fixes.", + "Changelog": "Tooltips: Icons in tooltips for action bars, status effects, party cooldowns. World Object Tooltip: quest/NPC icons shown when hovering over NPCs/objects (toggle in Misc → World Tooltip).", "InternalName": "HSUI", - "AssemblyVersion": "1.0.5.0", + "AssemblyVersion": "1.0.6.0", "RepoUrl": "https://github.com/Knack117/HSUI", "ApplicableVersion": "any", "Tags": ["UI", "HUD", "Unit Frames", "Nameplates", "Party Frames", "Hotbars"], @@ -14,11 +14,11 @@ "DalamudApiLevel": 14, "IconUrl": "https://raw.githubusercontent.com/Knack117/HSUI/main/Media/Images/icon.png", "ImageUrls": [], - "DownloadLinkInstall": "https://github.com/Knack117/HSUI/releases/download/v1.0.5.0/latest.zip", + "DownloadLinkInstall": "https://github.com/Knack117/HSUI/releases/download/v1.0.6.0/latest.zip", "IsHide": false, "IsTestingExclusive": false, - "DownloadLinkTesting": "https://github.com/Knack117/HSUI/releases/download/v1.0.5.0/latest.zip", - "DownloadLinkUpdate": "https://github.com/Knack117/HSUI/releases/download/v1.0.5.0/latest.zip", - "LastUpdate": "1738368000" + "DownloadLinkTesting": "https://github.com/Knack117/HSUI/releases/download/v1.0.6.0/latest.zip", + "DownloadLinkUpdate": "https://github.com/Knack117/HSUI/releases/download/v1.0.6.0/latest.zip", + "LastUpdate": "1738454400" } ]