diff --git a/HellionChat/Resources/HellionStrings.Designer.cs b/HellionChat/Resources/HellionStrings.Designer.cs index f72737d..9bbfa0c 100644 --- a/HellionChat/Resources/HellionStrings.Designer.cs +++ b/HellionChat/Resources/HellionStrings.Designer.cs @@ -389,6 +389,7 @@ internal class HellionStrings internal static string Settings_Preview_TitleMock => Get(nameof(Settings_Preview_TitleMock)); internal static string Settings_Preview_StatusOpen => Get(nameof(Settings_Preview_StatusOpen)); internal static string ChannelHeader_NotLoggedIn => Get(nameof(ChannelHeader_NotLoggedIn)); + internal static string InputBar_More_Tooltip => Get(nameof(InputBar_More_Tooltip)); internal static string Settings_Section_Links => Get(nameof(Settings_Section_Links)); internal static string Settings_Section_Behaviour => Get(nameof(Settings_Section_Behaviour)); internal static string Settings_Section_Keybinds => Get(nameof(Settings_Section_Keybinds)); diff --git a/HellionChat/Resources/HellionStrings.ca.resx b/HellionChat/Resources/HellionStrings.ca.resx index bc4c079..c843d03 100644 --- a/HellionChat/Resources/HellionStrings.ca.resx +++ b/HellionChat/Resources/HellionStrings.ca.resx @@ -1361,4 +1361,7 @@ Sessió no iniciada + + Més accions + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.cs.resx b/HellionChat/Resources/HellionStrings.cs.resx index ca16c67..048653e 100644 --- a/HellionChat/Resources/HellionStrings.cs.resx +++ b/HellionChat/Resources/HellionStrings.cs.resx @@ -1360,4 +1360,7 @@ Nepřihlášen + + Další akce + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.da.resx b/HellionChat/Resources/HellionStrings.da.resx index b0c3a86..2c9743c 100644 --- a/HellionChat/Resources/HellionStrings.da.resx +++ b/HellionChat/Resources/HellionStrings.da.resx @@ -1360,4 +1360,7 @@ Ikke logget ind + + Flere handlinger + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.de.resx b/HellionChat/Resources/HellionStrings.de.resx index d4a2e8c..0ebcdee 100644 --- a/HellionChat/Resources/HellionStrings.de.resx +++ b/HellionChat/Resources/HellionStrings.de.resx @@ -1355,4 +1355,7 @@ Nicht eingeloggt + + Weitere Aktionen + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.el.resx b/HellionChat/Resources/HellionStrings.el.resx index 7aad3ea..33afe56 100644 --- a/HellionChat/Resources/HellionStrings.el.resx +++ b/HellionChat/Resources/HellionStrings.el.resx @@ -1360,4 +1360,7 @@ Εκτός σύνδεσης + + Περισσότερες ενέργειες + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.es.resx b/HellionChat/Resources/HellionStrings.es.resx index e274935..81d99d8 100644 --- a/HellionChat/Resources/HellionStrings.es.resx +++ b/HellionChat/Resources/HellionStrings.es.resx @@ -1361,4 +1361,7 @@ Sesión no iniciada + + Más acciones + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.fi.resx b/HellionChat/Resources/HellionStrings.fi.resx index ec8548c..e9641cf 100644 --- a/HellionChat/Resources/HellionStrings.fi.resx +++ b/HellionChat/Resources/HellionStrings.fi.resx @@ -1360,4 +1360,7 @@ Ei kirjautuneena + + Lisää toimintoja + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.fr.resx b/HellionChat/Resources/HellionStrings.fr.resx index c1679c2..a1baa48 100644 --- a/HellionChat/Resources/HellionStrings.fr.resx +++ b/HellionChat/Resources/HellionStrings.fr.resx @@ -1361,4 +1361,7 @@ Non connecté + + Plus d'actions + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.hu.resx b/HellionChat/Resources/HellionStrings.hu.resx index edcab59..f00d6b3 100644 --- a/HellionChat/Resources/HellionStrings.hu.resx +++ b/HellionChat/Resources/HellionStrings.hu.resx @@ -1360,4 +1360,7 @@ Nincs bejelentkezve + + További műveletek + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.it.resx b/HellionChat/Resources/HellionStrings.it.resx index f821293..ef3f54e 100644 --- a/HellionChat/Resources/HellionStrings.it.resx +++ b/HellionChat/Resources/HellionStrings.it.resx @@ -1361,4 +1361,7 @@ Non connesso + + Altre azioni + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.ja.resx b/HellionChat/Resources/HellionStrings.ja.resx index 67029bb..9d700ef 100644 --- a/HellionChat/Resources/HellionStrings.ja.resx +++ b/HellionChat/Resources/HellionStrings.ja.resx @@ -1361,4 +1361,7 @@ 未ログイン + + その他の操作 + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.ko.resx b/HellionChat/Resources/HellionStrings.ko.resx index 8bf133b..eb431f0 100644 --- a/HellionChat/Resources/HellionStrings.ko.resx +++ b/HellionChat/Resources/HellionStrings.ko.resx @@ -1361,4 +1361,7 @@ 로그인되지 않음 + + 추가 작업 + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.nb.resx b/HellionChat/Resources/HellionStrings.nb.resx index bd4eba9..0c5073e 100644 --- a/HellionChat/Resources/HellionStrings.nb.resx +++ b/HellionChat/Resources/HellionStrings.nb.resx @@ -1360,4 +1360,7 @@ Ikke innlogget + + Flere handlinger + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.nl.resx b/HellionChat/Resources/HellionStrings.nl.resx index a98ec25..a44f9b3 100644 --- a/HellionChat/Resources/HellionStrings.nl.resx +++ b/HellionChat/Resources/HellionStrings.nl.resx @@ -1361,4 +1361,7 @@ Niet ingelogd + + Meer acties + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.pl.resx b/HellionChat/Resources/HellionStrings.pl.resx index 1002fb9..567421d 100644 --- a/HellionChat/Resources/HellionStrings.pl.resx +++ b/HellionChat/Resources/HellionStrings.pl.resx @@ -1360,4 +1360,7 @@ Niezalogowany + + Więcej działań + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.pt-BR.resx b/HellionChat/Resources/HellionStrings.pt-BR.resx index 3c17c35..fe29447 100644 --- a/HellionChat/Resources/HellionStrings.pt-BR.resx +++ b/HellionChat/Resources/HellionStrings.pt-BR.resx @@ -1361,4 +1361,7 @@ Não conectado + + Mais ações + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.pt-PT.resx b/HellionChat/Resources/HellionStrings.pt-PT.resx index 708b673..8a0b209 100644 --- a/HellionChat/Resources/HellionStrings.pt-PT.resx +++ b/HellionChat/Resources/HellionStrings.pt-PT.resx @@ -1360,4 +1360,7 @@ Sem sessão iniciada + + Mais ações + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.resx b/HellionChat/Resources/HellionStrings.resx index c0dfed0..4d12f3d 100644 --- a/HellionChat/Resources/HellionStrings.resx +++ b/HellionChat/Resources/HellionStrings.resx @@ -1372,4 +1372,7 @@ Not logged in + + More actions + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.ro.resx b/HellionChat/Resources/HellionStrings.ro.resx index adae878..2e97185 100644 --- a/HellionChat/Resources/HellionStrings.ro.resx +++ b/HellionChat/Resources/HellionStrings.ro.resx @@ -1361,4 +1361,7 @@ Neconectat + + Mai multe acțiuni + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.ru.resx b/HellionChat/Resources/HellionStrings.ru.resx index d7da149..d3e84fd 100644 --- a/HellionChat/Resources/HellionStrings.ru.resx +++ b/HellionChat/Resources/HellionStrings.ru.resx @@ -1361,4 +1361,7 @@ Вход не выполнен + + Другие действия + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.sv.resx b/HellionChat/Resources/HellionStrings.sv.resx index b175ce6..62059bc 100644 --- a/HellionChat/Resources/HellionStrings.sv.resx +++ b/HellionChat/Resources/HellionStrings.sv.resx @@ -1361,4 +1361,7 @@ Inte inloggad + + Fler åtgärder + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.tr.resx b/HellionChat/Resources/HellionStrings.tr.resx index b99d3e7..5d8b520 100644 --- a/HellionChat/Resources/HellionStrings.tr.resx +++ b/HellionChat/Resources/HellionStrings.tr.resx @@ -1360,4 +1360,7 @@ Oturum açılmadı + + Diğer işlemler + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.uk.resx b/HellionChat/Resources/HellionStrings.uk.resx index 6942e83..4274ec3 100644 --- a/HellionChat/Resources/HellionStrings.uk.resx +++ b/HellionChat/Resources/HellionStrings.uk.resx @@ -1360,4 +1360,7 @@ Вхід не виконано + + Інші дії + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.zh-Hans.resx b/HellionChat/Resources/HellionStrings.zh-Hans.resx index ea43134..5605975 100644 --- a/HellionChat/Resources/HellionStrings.zh-Hans.resx +++ b/HellionChat/Resources/HellionStrings.zh-Hans.resx @@ -1361,4 +1361,7 @@ 未登录 + + 更多操作 + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.zh-Hant.resx b/HellionChat/Resources/HellionStrings.zh-Hant.resx index d06e35e..337e273 100644 --- a/HellionChat/Resources/HellionStrings.zh-Hant.resx +++ b/HellionChat/Resources/HellionStrings.zh-Hant.resx @@ -1361,4 +1361,7 @@ 未登入 + + 更多操作 + \ No newline at end of file diff --git a/HellionChat/Ui/Components/InputBar.cs b/HellionChat/Ui/Components/InputBar.cs index 545c741..031df50 100644 --- a/HellionChat/Ui/Components/InputBar.cs +++ b/HellionChat/Ui/Components/InputBar.cs @@ -273,6 +273,18 @@ internal sealed class InputBar StyleEngine.Widgets.Pill.Draw(origin, label, pillAbgr, textAbgr); + // The CS+ pill treatment: a faint white gradient from the top and a + // one-pixel light along the upper edge. Reads as depth in every palette + // because it is white over the fill, not a second hue. + var pillDl = ImGui.GetWindowDrawList(); + var pillScale = StyleEngine.Metrics.Scale; + pillDl.DrawVerticalGradient(origin, origin + size, 0x28FFFFFFu, 0u); + pillDl.AddRectFilled( + new Vector2(origin.X + 6f * pillScale, origin.Y), + new Vector2(origin.X + size.X - 6f * pillScale, origin.Y + 1f * pillScale), + 0x50FFFFFFu + ); + // Hit area over the rendered pill so a click opens the channel // picker. InvisibleButton both reserves the layout slot and gives // the popup a stable anchor item. @@ -283,7 +295,7 @@ internal sealed class InputBar DrawChannelPickerPopup(tab); } - private static void DrawChannelPickerPopup(Tab? tab) + private void DrawChannelPickerPopup(Tab? tab) { if (!ImGui.BeginPopup("##hellion-channel-picker")) return; @@ -296,14 +308,27 @@ internal sealed class InputBar return; } + ImGui.Dummy(new Vector2(150f * StyleEngine.Metrics.Scale, 0f)); + + var i = 0; foreach (var chatType in tab.SelectedChannels.Keys) { if (chatType.ToInputChannel() is not { } input) continue; var isCurrent = tab.CurrentChannel.Channel == input; - if (ImGui.Selectable(input.ToChatType().Name(), isCurrent)) + if ( + StyleEngine.Widgets.PopupRow.Draw( + $"##ch-{i++}", + input.ToChatType().Name(), + isCurrent, + _fonts + ) + ) + { tab.CurrentChannel.SetChannel(input); + ImGui.CloseCurrentPopup(); + } } } finally @@ -325,6 +350,33 @@ internal sealed class InputBar Activate = false; } + // Boutique.Inputs technique: paint the surface, then hand ImGui a + // transparent frame so the widget draws only text and caret. The focus + // rail is the same two-pixel accent bar the rows and popups carry -- + // one vocabulary for "this is where you are". + var scale = StyleEngine.Metrics.Scale; + var fieldOrigin = ImGui.GetCursorScreenPos(); + var fieldSize = new Vector2( + ImGui.GetContentRegionAvail().X - QuickButtonsReserve, + ImGui.GetFrameHeight() + ); + ImGui + .GetWindowDrawList() + .AddRectFilled( + fieldOrigin, + fieldOrigin + fieldSize, + ColourUtil.RgbaToAbgr(_themes.Active.Colors.FrameBg), + 6f * scale + ); + + using var frameBg = ImRaii.PushColor(ImGuiCol.FrameBg, Vector4.Zero); + using var frameBgHovered = ImRaii.PushColor(ImGuiCol.FrameBgHovered, Vector4.Zero); + using var frameBgActive = ImRaii.PushColor(ImGuiCol.FrameBgActive, Vector4.Zero); + using var framePad = ImRaii.PushStyle( + ImGuiStyleVar.FramePadding, + new Vector2(10f * scale, ImGui.GetStyle().FramePadding.Y) + ); + ImGui.SetNextItemWidth(-QuickButtonsReserve); if ( ImGui.InputText( @@ -343,6 +395,15 @@ internal sealed class InputBar _commandHelpWindow.IsOpen = false; TrySend(activeTab); } + + if (ImGui.IsItemActive()) + ImGui + .GetWindowDrawList() + .AddRectFilled( + fieldOrigin, + new Vector2(fieldOrigin.X + 2f * scale, fieldOrigin.Y + fieldSize.Y), + ColourUtil.RgbaToAbgr(_themes.Active.Colors.Accent) + ); DrawInputContextMenu(); _isFocused = ImGui.IsItemFocused(); @@ -627,106 +688,175 @@ internal sealed class InputBar return (current == InputChannel.Invalid ? text : $"{current.Prefix()} {text}", false); } + // Two ghost buttons and a menu, where five filled plates used to sit. The + // plates were ImGui defaults in a row whose pill and status bar are drawn; + // the ghosts follow the sidebar's icon buttons instead -- nothing at rest, a + // held hover fill, the glyph lifting toward the accent. + // + // Symbols stay outside the menu because they are used mid-sentence; a click + // through a drawer for that would be a regression. The screenshot toggle may + // live in the menu only because its state moved to the status bar first -- + // the whole point of that mode is knowing what your screen shows BEFORE the + // screenshot key, and a state behind a closed menu answers nothing. private void DrawQuickButtons() { - // Collected here and drawn after the icon font is popped. Inside the push - // it rendered against the FontAwesome atlas, which has no ASCII glyphs, so - // every tooltip came out as an empty box. string? tooltip = null; - using (_fonts.FontAwesome.Push()) - { - if (ImGui.Button(FontAwesomeIcon.SmileBeam.ToIconString())) - _symbolPicker.OpenPopup(); - if (ImGui.IsItemHovered()) - tooltip = HellionStrings.InputBar_InsertSymbol_Tooltip; + if (DrawGhostButton("##qb-symbols", FontAwesomeIcon.SmileBeam)) + _symbolPicker.OpenPopup(); + if (ImGui.IsItemHovered()) + tooltip = HellionStrings.InputBar_InsertSymbol_Tooltip; - if (_themeQuickPicker is not null) - { - ImGui.SameLine(); - if (ImGui.Button(FontAwesomeIcon.Palette.ToIconString())) - _themeQuickPicker.OpenPopup(); - if (ImGui.IsItemHovered()) - tooltip = HellionStrings.Settings_QuickPicker_Tooltip; - } + ImGui.SameLine(0f, 4f * StyleEngine.Metrics.Scale); - ImGui.SameLine(); - if (ImGui.Button(FontAwesomeIcon.Cog.ToIconString())) - { - _onOpenSettings(); - } - if (ImGui.IsItemHovered()) - tooltip = HellionStrings.InputBar_Settings_Tooltip; + if (DrawGhostButton("##qb-more", FontAwesomeIcon.EllipsisH)) + ImGui.OpenPopup("##hellion-more-menu"); + if (ImGui.IsItemHovered()) + tooltip = HellionStrings.InputBar_More_Tooltip; - // Screenshot mode. It lived only in the right-click menu on a player - // name, which for a privacy feature is the same as not existing -- - // reported as missing by a tester who had been using the plugin for - // months. Lit when active, because a mode you cannot see the state of - // is worse than no mode. - ImGui.SameLine(); - var shooting = Plugin.Config.ScreenshotMode; - using ( - ImRaii.PushColor( - ImGuiCol.Text, - ColourUtil.RgbaToVector4( - _resolver.Resolve(Token.AccentEmber, _themes.Active.Colors) - ), - shooting - ) - ) - { - if (ImGui.Button(FontAwesomeIcon.Camera.ToIconString())) - Plugin.Config.ScreenshotMode = !Plugin.Config.ScreenshotMode; - } - if (ImGui.IsItemHovered()) - tooltip = Language.Context_ScreenshotMode; - - // Hides the window (1.5.6 UserHide). One-way — Enter brings it back. - // Main window only; last in the row there. - if (Plugin.Config.ShowHideButton && _onHideWindow is not null) - { - ImGui.SameLine(); - if (ImGui.Button(FontAwesomeIcon.EyeSlash.ToIconString())) - _onHideWindow(); - if (ImGui.IsItemHovered()) - tooltip = HellionStrings.InputBar_HideChat_Tooltip; - } - - // Pop-in, in the pop-out windows only. It sits here rather than in a - // header row because a pop-out with its title bar on had no header at - // all -- and the title bar carries no close button, since closing has - // to go through the pool to release the slot. - if (OnPopIn is not null) - { - ImGui.SameLine(); - // Red, and contrast-checked against the button plate it sits on - // rather than taken raw: several themes ship a danger colour that - // is nearly invisible on their own button fill. - var danger = ColourUtil.RgbaToAbgr( - _resolver.Resolve(Token.StatusDanger, _themes.Active.Colors) - ); - var plate = ColourUtil.Vector4ToAbgr(ImGui.GetStyle().Colors[(int)ImGuiCol.Button]); - using ( - ImRaii.PushColor( - ImGuiCol.Text, - ColourUtil.RgbaToVector4( - ColourUtil.RgbaToAbgr(ColourUtil.EnsureContrast(danger, plate, 3f)) - ) - ) - ) - { - if (ImGui.Button(FontAwesomeIcon.Times.ToIconString())) - OnPopIn(); - } - if (ImGui.IsItemHovered()) - tooltip = HellionStrings.InputBar_PopIn_Tooltip; - } - } + DrawMoreMenu(); if (tooltip is not null) ImGui.SetTooltip(tooltip); } + // Sidebar language: no plate at rest, held hover fill, glyph toward accent. + private bool DrawGhostButton(string id, FontAwesomeIcon icon, bool lit = false) + { + var scale = StyleEngine.Metrics.Scale; + var side = MathF.Round(24f * scale); + var origin = ImGui.GetCursorScreenPos(); + var max = origin + new Vector2(side, side); + + var clicked = ImGui.InvisibleButton(id, new Vector2(side, side)); + var hovered = ImGui.IsItemHovered(); + var amount = StyleEngine.HoverState.Query(ImGui.GetID(id), hovered); + + var c = _themes.Active.Colors; + var dl = ImGui.GetWindowDrawList(); + + if (amount > 0f) + dl.AddRectFilled( + origin, + max, + ColourUtil.ApplyAlpha(ColourUtil.RgbaToAbgr(c.SurfaceHover), amount), + 3f * scale + ); + + var accent = ColourUtil.RgbaToAbgr(c.Accent); + var tint = ColourUtil.RgbaToAbgr(ColourUtil.EnsureContrast(c.TextMuted, c.ChildBg, 4.5f)); + if (lit) + tint = accent; + else if (amount > 0f) + tint = ColourUtil.Lerp(tint, accent, amount * 0.6f); + + using (_fonts.FontAwesome.Push()) + { + var glyph = icon.ToIconString(); + var size = ImGui.CalcTextSize(glyph); + dl.AddText(origin + (max - origin - size) * 0.5f, tint, glyph); + } + + return clicked; + } + + private void DrawMoreMenu() + { + if (!ImGui.BeginPopup("##hellion-more-menu")) + return; + + // Actions run after EndPopup: the theme picker opens its own popup at + // this component's ID scope, not inside the menu's. + var openTheme = false; + + try + { + ImGui.Dummy(new Vector2(160f * StyleEngine.Metrics.Scale, 0f)); + + if ( + _themeQuickPicker is not null + && StyleEngine.Widgets.PopupRow.Draw( + "##mm-theme", + HellionStrings.Settings_QuickPicker_Tooltip, + active: false, + _fonts, + FontAwesomeIcon.Palette + ) + ) + { + openTheme = true; + ImGui.CloseCurrentPopup(); + } + + if ( + StyleEngine.Widgets.PopupRow.Draw( + "##mm-settings", + HellionStrings.InputBar_Settings_Tooltip, + active: false, + _fonts, + FontAwesomeIcon.Cog + ) + ) + { + _onOpenSettings(); + ImGui.CloseCurrentPopup(); + } + + // Shows its state right here as the active row, and again in the + // status bar once on -- the menu closes, the awareness must not. + if ( + StyleEngine.Widgets.PopupRow.Draw( + "##mm-screenshot", + Language.Context_ScreenshotMode, + active: Plugin.Config.ScreenshotMode, + _fonts, + FontAwesomeIcon.Camera + ) + ) + { + Plugin.Config.ScreenshotMode = !Plugin.Config.ScreenshotMode; + } + + if ( + Plugin.Config.ShowHideButton + && _onHideWindow is not null + && StyleEngine.Widgets.PopupRow.Draw( + "##mm-hide", + HellionStrings.InputBar_HideChat_Tooltip, + active: false, + _fonts, + FontAwesomeIcon.EyeSlash + ) + ) + { + _onHideWindow(); + ImGui.CloseCurrentPopup(); + } + + if ( + OnPopIn is not null + && StyleEngine.Widgets.PopupRow.Draw( + "##mm-popin", + HellionStrings.InputBar_PopIn_Tooltip, + active: false, + _fonts, + FontAwesomeIcon.Times + ) + ) + { + OnPopIn(); + ImGui.CloseCurrentPopup(); + } + } + finally + { + ImGui.EndPopup(); + } + + if (openTheme) + _themeQuickPicker!.OpenPopup(); + } + // Test-only hook; do not call from production code. internal void TestSetPendingMessageForSelfTest(string value) => _pendingMessage = value; diff --git a/HellionChat/Ui/Components/StatusBar.cs b/HellionChat/Ui/Components/StatusBar.cs index b7c17b1..e3fe143 100644 --- a/HellionChat/Ui/Components/StatusBar.cs +++ b/HellionChat/Ui/Components/StatusBar.cs @@ -203,6 +203,30 @@ internal sealed class StatusBar FontAwesomeIcon.Lock, _fonts.FontAwesome ); + // Screenshot mode announces itself here while it is on. The toggle sits + // in the input row's menu, and a closed menu answers nothing -- the whole + // point of the mode is knowing what the screen shows before the + // screenshot key is pressed. Accent text, because this one is a state + // the user chose, not ambient information. + if (Plugin.Config.ScreenshotMode) + { + var camWidth = StyleEngine.Widgets.Pill.MeasureIcon( + FontAwesomeIcon.Camera, + _fonts.FontAwesome + ); + var camLabel = Language.Context_ScreenshotMode; + if (Fits(camLabel, withDot: false, camWidth)) + x += + DrawSlot( + new Vector2(x, top), + camLabel, + pillFill, + palette.Abgr(Token.AccentPrimary, colors), + null, + (FontAwesomeIcon.Camera, _fonts.FontAwesome) + ) + gap; + } + if (Fits(privacyLabel, withDot: false, lockWidth)) x += DrawSlot( diff --git a/HellionChat/Ui/StyleEngine/Metrics.cs b/HellionChat/Ui/StyleEngine/Metrics.cs index 2535881..a836dff 100644 --- a/HellionChat/Ui/StyleEngine/Metrics.cs +++ b/HellionChat/Ui/StyleEngine/Metrics.cs @@ -27,10 +27,10 @@ internal static class Metrics // --- Input bar --- internal const float InputBarHeightRaw = 32f; - // Raised from 130 in v1.13.0 for the screenshot-mode button. Five buttons in - // the main window (symbols, theme, screenshot, settings, hide) and five in a - // pop-out, where pop-in takes the place of hide. - internal const float InputQuickButtonsReserveRaw = 156f; + // Two ghost buttons since v1.14.0 -- symbols and the more-menu. Everything + // else moved into the menu, and the hundred-odd pixels went back to the + // input field. + internal const float InputQuickButtonsReserveRaw = 62f; // --- Honorific header --- internal const float HonorificHeightRaw = 30f; diff --git a/HellionChat/Ui/StyleEngine/Widgets/PopupRow.cs b/HellionChat/Ui/StyleEngine/Widgets/PopupRow.cs new file mode 100644 index 0000000..c502462 --- /dev/null +++ b/HellionChat/Ui/StyleEngine/Widgets/PopupRow.cs @@ -0,0 +1,94 @@ +using System.Numerics; +using Dalamud.Bindings.ImGui; +using Dalamud.Interface; +using HellionChat.Util; + +namespace HellionChat.Ui.StyleEngine.Widgets; + +// The row a popup is made of. Replaces ImGui.Selectable inside the plugin's +// popups, which carried the right colours through GlobalStyleScope but none of +// the shape the rest of the window speaks: no held hover, no accent bar on the +// active entry. +// +// The shape is the sidebar row's, and it is also what Character Select+ draws +// for its popup entries (Boutique.Dropdowns, DrawPopupRow): a soft fill on +// hover, a two-pixel accent bar when active. One conversation, wherever a list +// appears. +internal static class PopupRow +{ + private const float PadXRaw = 10f; + private const float PadYRaw = 5f; + private const float IconGapRaw = 8f; + private const float AccentBarRaw = 2f; + + internal static bool Draw( + string id, + string label, + bool active, + FontManager fonts, + FontAwesomeIcon? icon = null + ) + { + var scale = Metrics.Scale; + var theme = Plugin.Instance.ThemeRegistry.Active; + var c = theme.Colors; + + var height = ImGui.GetTextLineHeight() + MathF.Round(PadYRaw * 2f * scale); + var width = ImGui.GetContentRegionAvail().X; + var origin = ImGui.GetCursorScreenPos(); + var max = origin + new Vector2(width, height); + + var clicked = ImGui.InvisibleButton(id, new Vector2(width, height)); + var hovered = ImGui.IsItemHovered(); + var amount = HoverState.Query(ImGui.GetID(id), hovered); + + var dl = ImGui.GetWindowDrawList(); + var accentAbgr = ColourUtil.RgbaToAbgr(c.Accent); + + // Active first, hover on top -- an active entry still answers the mouse. + if (active) + dl.AddRectFilled(origin, max, ColourUtil.ApplyAlpha(accentAbgr, 0.10f), 3f * scale); + + if (amount > 0f) + dl.AddRectFilled( + origin, + max, + ColourUtil.ApplyAlpha(ColourUtil.RgbaToAbgr(c.SurfaceHover), amount), + 3f * scale + ); + + if (active) + dl.AddRectFilled( + origin, + new Vector2(origin.X + AccentBarRaw * scale, max.Y), + accentAbgr + ); + + var textAbgr = ColourUtil.RgbaToAbgr( + ColourUtil.EnsureContrast(c.TextPrimary, c.ChildBg, 4.5f) + ); + if (active || amount > 0f) + textAbgr = ColourUtil.Lerp(textAbgr, accentAbgr, active ? 0.6f : amount * 0.4f); + + var x = origin.X + PadXRaw * scale; + + if (icon is { } glyph) + { + using (fonts.FontAwesome.Push()) + { + var s = glyph.ToIconString(); + var size = ImGui.CalcTextSize(s); + dl.AddText(new Vector2(x, origin.Y + (height - size.Y) * 0.5f), textAbgr, s); + x += size.X + IconGapRaw * scale; + } + } + + dl.AddText( + new Vector2(x, origin.Y + (height - ImGui.GetTextLineHeight()) * 0.5f), + textAbgr, + label + ); + + return clicked; + } +}