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;
+ }
+}