feat(sidebar): wire configurable expanded width through a single source

This commit is contained in:
2026-05-30 18:22:38 +02:00
parent caacb87a6a
commit a7a5aee982
3 changed files with 57 additions and 10 deletions
@@ -58,6 +58,48 @@ internal sealed class SidebarModeAutoSwitchStep : ISelfTestStep
return SelfTestStepResult.Fail; return SelfTestStepResult.Fail;
} }
// B1-3a: the expanded width must come from Config.SidebarWidth, not the
// old fixed 150 constant. Drive the REAL GetWidth (the single source
// Sidebar.Draw consumes) with concrete values and assert the OBSERVED
// effect — in-range passthrough plus clamping — instead of mirroring the
// Math.Clamp logic (SelfTests/README.md forbids re-implementing helper
// logic in the test). Restore the config in finally so the live render
// path is untouched.
var savedSidebarWidth = Plugin.Config.SidebarWidth;
try
{
Plugin.Config.SidebarWidth = 220;
if (sidebar.GetWidth(threshold + 100f) != 220f)
{
ImGui.Text(
$"GetWidth expanded = {sidebar.GetWidth(threshold + 100f)}, expected in-range Config.SidebarWidth 220"
);
return SelfTestStepResult.Fail;
}
Plugin.Config.SidebarWidth = 9999;
if (sidebar.GetWidth(threshold + 100f) != Sidebar.MaxSidebarWidth)
{
ImGui.Text(
$"GetWidth expanded = {sidebar.GetWidth(threshold + 100f)}, expected clamp to MaxSidebarWidth {Sidebar.MaxSidebarWidth}"
);
return SelfTestStepResult.Fail;
}
Plugin.Config.SidebarWidth = 1;
if (sidebar.GetWidth(threshold + 100f) != Sidebar.MinSidebarWidth)
{
ImGui.Text(
$"GetWidth expanded = {sidebar.GetWidth(threshold + 100f)}, expected clamp to MinSidebarWidth {Sidebar.MinSidebarWidth}"
);
return SelfTestStepResult.Fail;
}
}
finally
{
Plugin.Config.SidebarWidth = savedSidebarWidth;
}
return SelfTestStepResult.Pass; return SelfTestStepResult.Pass;
} }
@@ -60,16 +60,14 @@ internal sealed class ChannelsTab
() => Plugin.Config.SidebarTabView, () => Plugin.Config.SidebarTabView,
v => Plugin.Config.SidebarTabView = v v => Plugin.Config.SidebarTabView = v
); );
// Range covers the on-disk default (44) plus Master-Spec §4.1 reference // Range matches Sidebar.MinSidebarWidth/MaxSidebarWidth (40-300). The
// (38px icon-only, 150px expanded). An earlier 120-400 range would clamp // lower bound sits just above the 38px icon-only threshold; the
// the default 44 up to 120 silently. 30 leaves headroom for a future // on-disk default (44) and the 150px expanded reference both fit.
// ultra-tight icon-only mode; 300 stays above the 150 expanded reference
// without giving the slider an absurd ceiling.
DrawSliderInt( DrawSliderInt(
"Sidebar width", "Sidebar width",
() => Plugin.Config.SidebarWidth, () => Plugin.Config.SidebarWidth,
v => Plugin.Config.SidebarWidth = v, v => Plugin.Config.SidebarWidth = v,
30, 40,
300 300
); );
} }
+11 -4
View File
@@ -11,7 +11,7 @@ using Microsoft.Extensions.Logging;
namespace HellionChat.Ui.Components; namespace HellionChat.Ui.Components;
// Channel-list panel pinned to the left of the chat window. Auto-switches // Channel-list panel pinned to the left of the chat window. Auto-switches
// between an icon-only column (38px) and an expanded column (150px) once // between an icon-only column (38px) and an expanded column (Config.SidebarWidth) once
// the outer window crosses Config.SidebarAutoSwitchThresholdPx. The // the outer window crosses Config.SidebarAutoSwitchThresholdPx. The
// pop-out affordance (hover button + right-click menu) routes through the // pop-out affordance (hover button + right-click menu) routes through the
// injected ChannelPopoutPool via TryOpen, which reserves a slot and binds // injected ChannelPopoutPool via TryOpen, which reserves a slot and binds
@@ -19,7 +19,12 @@ namespace HellionChat.Ui.Components;
internal sealed class Sidebar internal sealed class Sidebar
{ {
public const float IconOnlyWidth = 38f; public const float IconOnlyWidth = 38f;
public const float ExpandedWidth = 150f;
// B1-3a: expanded sidebar width is user-configurable (Config.SidebarWidth),
// clamped to these bounds (matches the ChannelsTab slider range). Replaces
// the old fixed 150px ExpandedWidth constant.
public const float MinSidebarWidth = 40f;
public const float MaxSidebarWidth = 300f;
private const float RowHeight = 32f; private const float RowHeight = 32f;
private const float PopOutHitWidth = 22f; private const float PopOutHitWidth = 22f;
@@ -72,7 +77,9 @@ internal sealed class Sidebar
windowWidth >= Plugin.Config.SidebarAutoSwitchThresholdPx; windowWidth >= Plugin.Config.SidebarAutoSwitchThresholdPx;
public float GetWidth(float windowWidth) => public float GetWidth(float windowWidth) =>
IsExpanded(windowWidth) ? ExpandedWidth : IconOnlyWidth; IsExpanded(windowWidth)
? Math.Clamp((float)Plugin.Config.SidebarWidth, MinSidebarWidth, MaxSidebarWidth)
: IconOnlyWidth;
public void Draw(float windowWidth, IList<Tab> tabs, ref Tab? activeTab) public void Draw(float windowWidth, IList<Tab> tabs, ref Tab? activeTab)
{ {
@@ -83,7 +90,7 @@ internal sealed class Sidebar
} }
var expanded = IsExpanded(windowWidth); var expanded = IsExpanded(windowWidth);
var width = expanded ? ExpandedWidth : IconOnlyWidth; var width = GetWidth(windowWidth);
using var child = ImRaii.Child("##hellion-sidebar", new Vector2(width, 0)); using var child = ImRaii.Child("##hellion-sidebar", new Vector2(width, 0));
if (!child.Success) if (!child.Success)
return; return;