diff --git a/managed/src/SwiftlyS2.Core/Modules/Menus/Menu.cs b/managed/src/SwiftlyS2.Core/Modules/Menus/Menu.cs
index c86b4040..5958cb9c 100644
--- a/managed/src/SwiftlyS2.Core/Modules/Menus/Menu.cs
+++ b/managed/src/SwiftlyS2.Core/Modules/Menus/Menu.cs
@@ -61,7 +61,7 @@ internal partial class Menu : IMenu
opt.OverflowStyle?.OverflowStyle == MenuHorizontalOverflowStyle.ScrollLeftLoop ||
opt.OverflowStyle?.OverflowStyle == MenuHorizontalOverflowStyle.ScrollRightLoop);
- public void Close(IPlayer player)
+ public void Close( IPlayer player )
{
NativePlayer.ClearCenterMenuRender(player.PlayerID);
OnClose?.Invoke(player);
@@ -80,7 +80,7 @@ public void Close(IPlayer player)
ScrollPauseCounts.Clear();
}
- public void MoveSelection(IPlayer player, int offset)
+ public void MoveSelection( IPlayer player, int offset )
{
if (!SelectedIndex.ContainsKey(player))
{
@@ -104,10 +104,10 @@ public void MoveSelection(IPlayer player, int offset)
OnMove?.Invoke(player);
OnItemHovered?.Invoke(player, Options[SelectedIndex[player]]);
- Rerender(player);
+ Rerender(player, false);
}
- public void Rerender(IPlayer player, bool updateDisplayText = false)
+ public void Rerender( IPlayer player, bool updateHorizontalStyle = false )
{
BeforeRender?.Invoke(player);
@@ -146,8 +146,7 @@ public void Rerender(IPlayer player, bool updateDisplayText = false)
var selectedIdx = SelectedIndex[player];
var halfVisible = maxVisibleOptions / 2;
- var (startIndex, arrowPosition) = VerticalScrollStyle switch
- {
+ var (startIndex, arrowPosition) = VerticalScrollStyle switch {
MenuVerticalScrollStyle.WaitingCenter when selectedIdx < halfVisible
=> (0, selectedIdx), // WaitingCenter: (Near top) start from 0, arrow at selected
MenuVerticalScrollStyle.WaitingCenter when selectedIdx >= totalOptions - halfVisible
@@ -187,10 +186,7 @@ MenuVerticalScrollStyle.LinearScroll when selectedIdx < maxVisibleOptions - 1
html.Append("\u00A0\u00A0\u00A0 ");
}
- if (updateDisplayText)
- {
- html.Append(option.GetDisplayText(player));
- }
+ html.Append(option.GetDisplayText(player, updateHorizontalStyle));
html.Append("
");
}
@@ -212,10 +208,7 @@ MenuVerticalScrollStyle.LinearScroll when selectedIdx < maxVisibleOptions - 1
html.Append("\u00A0\u00A0\u00A0 ");
}
- if (updateDisplayText)
- {
- html.Append(option.GetDisplayText(player));
- }
+ html.Append(option.GetDisplayText(player, updateHorizontalStyle));
html.Append("
");
}
@@ -237,21 +230,21 @@ MenuVerticalScrollStyle.LinearScroll when selectedIdx < maxVisibleOptions - 1
private string BuildFooter()
{
- var footer = new StringBuilder("");
-
- var isWASD = MenuManager.Settings.InputMode == "wasd";
- var selectDisplay = isWASD ? "D" : MenuManager.Settings.ButtonsUse.ToUpper();
- var scrollDisplay = isWASD ? "W/S" : MenuManager.Settings.ButtonsScroll.ToUpper();
- var exitDisplay = isWASD ? "A" : MenuManager.Settings.ButtonsExit.ToUpper();
+ var isWasd = MenuManager.Settings.InputMode == "wasd";
- footer.Append($"Move: {scrollDisplay}");
-
- footer.Append($" | Use: {selectDisplay}");
-
- footer.Append($" | Exit: {exitDisplay}");
-
- footer.Append("
");
- return footer.ToString();
+ var selectDisplay = isWasd ? "D" : (ButtonOverrides?.Select?.ToString() ?? MenuManager.Settings.ButtonsUse).ToUpper();
+ var scrollDisplay = isWasd ? "W/S" : (ButtonOverrides?.Move, ButtonOverrides?.MoveBack) switch {
+ (not null, not null) => $"{ButtonOverrides.Move}/{ButtonOverrides.MoveBack}",
+ _ => MenuManager.Settings.ButtonsScroll.ToUpper()
+ };
+ var exitDisplay = isWasd ? "A" : (ButtonOverrides?.Exit?.ToString() ?? MenuManager.Settings.ButtonsExit).ToUpper();
+
+ return new StringBuilder("")
+ .Append($"Move: {scrollDisplay}")
+ .Append($" | Use: {selectDisplay}")
+ .Append($" | Exit: {exitDisplay}")
+ .Append("
")
+ .ToString();
}
private void OnTickRender()
@@ -262,7 +255,7 @@ private void OnTickRender()
}
}
- public void Show(IPlayer player)
+ public void Show( IPlayer player )
{
if (!SelectedIndex.TryAdd(player, 0))
{
@@ -280,7 +273,7 @@ public void Show(IPlayer player)
_Core.Event.OnTick += OnTickRender;
}
- Rerender(player);
+ Rerender(player, true);
OnOpen?.Invoke(player);
if (ShouldFreeze == true) SetFreezeState(player, true);
@@ -297,7 +290,7 @@ public void Show(IPlayer player)
ScrollPauseCounts.Clear();
}
- public void UseSelection(IPlayer player)
+ public void UseSelection( IPlayer player )
{
var selectedOption = Options[SelectedIndex[player]];
OnItemSelected?.Invoke(player, selectedOption);
@@ -372,7 +365,7 @@ public void UseSelection(IPlayer player)
}
}
- public void UseSlideOption(IPlayer player, bool isRight)
+ public void UseSlideOption( IPlayer player, bool isRight )
{
var selectedOption = Options[SelectedIndex[player]];
@@ -389,22 +382,22 @@ public void UseSlideOption(IPlayer player, bool isRight)
break;
}
- Rerender(player);
+ Rerender(player, false);
}
- public bool IsOptionSlider(IPlayer player)
+ public bool IsOptionSlider( IPlayer player )
{
var option = Options[SelectedIndex[player]];
return option is SliderMenuButton || option is ChoiceMenuOption;
}
- public bool IsCurrentOptionSelectable(IPlayer player)
+ public bool IsCurrentOptionSelectable( IPlayer player )
{
var option = Options[SelectedIndex[player]];
return IsOptionSelectable(option);
}
- public bool IsOptionSelectable(IOption option)
+ public bool IsOptionSelectable( IOption option )
{
return option is ButtonMenuOption ||
option is ToggleMenuOption ||
@@ -414,7 +407,7 @@ option is AsyncButtonMenuOption ||
(option is DynamicMenuOption dynamic && dynamic.CanInteract(null!));
}
- public void SetFreezeState(IPlayer player, bool freeze)
+ public void SetFreezeState( IPlayer player, bool freeze )
{
if (!player.IsValid || player.IsFakeClient) return;
@@ -427,7 +420,7 @@ public void SetFreezeState(IPlayer player, bool freeze)
pawn.MoveTypeUpdated();
}
- internal string ApplyHorizontalStyle(string text, MenuHorizontalStyle? overflowStyle = null)
+ internal string ApplyHorizontalStyle( string text, MenuHorizontalStyle? overflowStyle = null, bool updateHorizontalStyle = false )
{
var activeStyle = overflowStyle ?? HorizontalStyle;
@@ -442,19 +435,18 @@ internal string ApplyHorizontalStyle(string text, MenuHorizontalStyle? overflowS
return text;
}
- return activeStyle.Value.OverflowStyle switch
- {
+ return activeStyle.Value.OverflowStyle switch {
MenuHorizontalOverflowStyle.TruncateEnd => TruncateTextEnd(text, activeStyle.Value.MaxWidth),
MenuHorizontalOverflowStyle.TruncateBothEnds => TruncateTextBothEnds(text, activeStyle.Value.MaxWidth),
- MenuHorizontalOverflowStyle.ScrollLeftFade => ScrollTextWithFade(text, activeStyle.Value.MaxWidth, true, activeStyle),
- MenuHorizontalOverflowStyle.ScrollRightFade => ScrollTextWithFade(text, activeStyle.Value.MaxWidth, false, activeStyle),
- MenuHorizontalOverflowStyle.ScrollLeftLoop => ScrollTextWithLoop($"{text.TrimEnd()} ", activeStyle.Value.MaxWidth, true, activeStyle),
- MenuHorizontalOverflowStyle.ScrollRightLoop => ScrollTextWithLoop($" {text.TrimStart()}", activeStyle.Value.MaxWidth, false, activeStyle),
+ MenuHorizontalOverflowStyle.ScrollLeftFade => ScrollTextWithFade(updateHorizontalStyle, text, activeStyle.Value.MaxWidth, true, activeStyle),
+ MenuHorizontalOverflowStyle.ScrollRightFade => ScrollTextWithFade(updateHorizontalStyle, text, activeStyle.Value.MaxWidth, false, activeStyle),
+ MenuHorizontalOverflowStyle.ScrollLeftLoop => ScrollTextWithLoop(updateHorizontalStyle, $"{text.TrimEnd()} ", activeStyle.Value.MaxWidth, true, activeStyle),
+ MenuHorizontalOverflowStyle.ScrollRightLoop => ScrollTextWithLoop(updateHorizontalStyle, $" {text.TrimStart()}", activeStyle.Value.MaxWidth, false, activeStyle),
_ => text
};
}
- private string ScrollTextWithFade(string text, float maxWidth, bool scrollLeft, MenuHorizontalStyle? style = null)
+ private string ScrollTextWithFade( bool updateHorizontalStyle, string text, float maxWidth, bool scrollLeft, MenuHorizontalStyle? style = null )
{
// Prepare scroll data and validate
var (plainChars, segments, targetCharCount) = PrepareScrollData(text, maxWidth);
@@ -468,7 +460,7 @@ private string ScrollTextWithFade(string text, float maxWidth, bool scrollLeft,
}
// Update scroll offset (allow scrolling beyond end for complete fade-out)
- var offset = UpdateScrollOffset(StripHtmlTags(text), scrollLeft, plainChars.Length + 1, style);
+ var offset = UpdateScrollOffset(updateHorizontalStyle, StripHtmlTags(text), scrollLeft, plainChars.Length + 1, style);
// Calculate visible character range
var (skipStart, skipEnd) = scrollLeft
@@ -518,7 +510,7 @@ private string ScrollTextWithFade(string text, float maxWidth, bool scrollLeft,
return result.ToString();
}
- private string ScrollTextWithLoop(string text, float maxWidth, bool scrollLeft, MenuHorizontalStyle? style = null)
+ private string ScrollTextWithLoop( bool updateHorizontalStyle, string text, float maxWidth, bool scrollLeft, MenuHorizontalStyle? style = null )
{
// Prepare scroll data and validate
var (plainChars, segments, targetCharCount) = PrepareScrollData(text, maxWidth);
@@ -532,7 +524,7 @@ private string ScrollTextWithLoop(string text, float maxWidth, bool scrollLeft,
}
// Update scroll offset for circular wrapping
- var offset = UpdateScrollOffset(StripHtmlTags(text), scrollLeft, plainChars.Length, style);
+ var offset = UpdateScrollOffset(updateHorizontalStyle, StripHtmlTags(text), scrollLeft, plainChars.Length, style);
// Build character-to-tags mapping for circular access
Dictionary> charToActiveTags = [];
@@ -607,7 +599,7 @@ private string ScrollTextWithLoop(string text, float maxWidth, bool scrollLeft,
return result.ToString();
}
- private static string TruncateTextEnd(string text, float maxWidth, string suffix = "...")
+ private static string TruncateTextEnd( string text, float maxWidth, string suffix = "..." )
{
// Reserve space for suffix
var targetWidth = maxWidth - Helper.EstimateTextWidth(suffix);
@@ -657,7 +649,7 @@ private static string TruncateTextEnd(string text, float maxWidth, string suffix
return result.ToString();
}
- private static string TruncateTextBothEnds(string text, float maxWidth)
+ private static string TruncateTextBothEnds( string text, float maxWidth )
{
if (string.IsNullOrEmpty(text))
{
@@ -753,7 +745,7 @@ internal partial class Menu
///
/// The text containing HTML tags.
/// The text with all HTML tags removed.
- private static string StripHtmlTags(string text)
+ private static string StripHtmlTags( string text )
{
if (string.IsNullOrEmpty(text))
{
@@ -768,7 +760,7 @@ private static string StripHtmlTags(string text)
///
/// The text to parse.
/// A list of segments where each segment is either a tag or plain text content.
- private static List<(string Content, bool IsTag)> ParseHtmlSegments(string text)
+ private static List<(string Content, bool IsTag)> ParseHtmlSegments( string text )
{
var tagMatches = HtmlTagRegex().Matches(text);
if (tagMatches.Count == 0)
@@ -803,10 +795,9 @@ private static string StripHtmlTags(string text)
///
/// The HTML tag to process.
/// The list of currently open tag names.
- private static void ProcessOpenTag(string tag, List openTags)
+ private static void ProcessOpenTag( string tag, List openTags )
{
- var tagName = tag switch
- {
+ var tagName = tag switch {
['<', '/', .. var rest] => new string(rest).TrimEnd('>').Split(' ', 2)[0],
['<', '!', ..] => null,
[.. var chars] when chars[^1] == '/' && chars[^2] == '>' => null,
@@ -835,7 +826,7 @@ private static void ProcessOpenTag(string tag, List openTags)
///
/// The StringBuilder to append closing tags to.
/// The list of currently open tag names.
- private static void CloseOpenTags(StringBuilder result, List openTags)
+ private static void CloseOpenTags( StringBuilder result, List openTags )
{
openTags.AsEnumerable().Reverse().ToList().ForEach(tag => result.Append($"{tag}>"));
}
@@ -846,7 +837,7 @@ private static void CloseOpenTags(StringBuilder result, List openTags)
/// The characters to measure.
/// The maximum width allowed.
/// The number of characters that fit within the width.
- private static int CalculateTargetCharCount(ReadOnlySpan plainChars, float maxWidth)
+ private static int CalculateTargetCharCount( ReadOnlySpan plainChars, float maxWidth )
{
var currentWidth = 0f;
var count = 0;
@@ -866,12 +857,13 @@ private static int CalculateTargetCharCount(ReadOnlySpan plainChars, float
/// Updates and returns the scroll offset for the given text.
/// The offset increments based on tick count and wraps around at the specified length.
///
+ /// Whether to update the horizontal style.
/// The plain text being scrolled.
/// Whether scrolling left or right.
/// The length at which the offset wraps around.
/// Optional horizontal style settings.
/// The current scroll offset.
- private int UpdateScrollOffset(string plainText, bool scrollLeft, int wrapLength, MenuHorizontalStyle? style)
+ private int UpdateScrollOffset( bool updateHorizontalStyle, string plainText, bool scrollLeft, int wrapLength, MenuHorizontalStyle? style )
{
var key = $"{plainText}_{scrollLeft}";
ScrollOffsets.TryAdd(key, 0);
@@ -889,7 +881,7 @@ private int UpdateScrollOffset(string plainText, bool scrollLeft, int wrapLength
}
// Increment call count and scroll when threshold is reached
- if (++ScrollCallCounts[key] >= ticksPerScroll)
+ if (updateHorizontalStyle && ++ScrollCallCounts[key] >= ticksPerScroll)
{
ScrollCallCounts[key] = 0;
var newOffset = (ScrollOffsets[key] + 1) % wrapLength;
@@ -912,7 +904,7 @@ private int UpdateScrollOffset(string plainText, bool scrollLeft, int wrapLength
///
/// The HTML tag content to process.
/// The list of currently active tags.
- private static void UpdateTagState(string content, List activeTags)
+ private static void UpdateTagState( string content, List activeTags )
{
if (!content.StartsWith("") && !content.StartsWith(""))
{
@@ -935,7 +927,7 @@ private static void UpdateTagState(string content, List activeTags)
/// The text to prepare for scrolling.
/// The maximum width available for display.
/// A tuple containing plain characters array, HTML segments, and target character count.
- private static (char[]? PlainChars, List<(string Content, bool IsTag)> Segments, int TargetCharCount) PrepareScrollData(string text, float maxWidth)
+ private static (char[]? PlainChars, List<(string Content, bool IsTag)> Segments, int TargetCharCount) PrepareScrollData( string text, float maxWidth )
{
var plainText = StripHtmlTags(text);
if (Helper.EstimateTextWidth(plainText) <= maxWidth)
diff --git a/managed/src/SwiftlyS2.Core/Modules/Menus/MenuManager.cs b/managed/src/SwiftlyS2.Core/Modules/Menus/MenuManager.cs
index f352a332..dea2ac72 100644
--- a/managed/src/SwiftlyS2.Core/Modules/Menus/MenuManager.cs
+++ b/managed/src/SwiftlyS2.Core/Modules/Menus/MenuManager.cs
@@ -47,13 +47,12 @@ internal class MenuManager : IMenuManager
private SoundEvent _exitSound = new();
private SoundEvent _scrollSound = new();
- public MenuManager(ISwiftlyCore core)
+ public MenuManager( ISwiftlyCore core )
{
_Core = core;
var settings = NativeEngineHelpers.GetMenuSettings();
var parts = settings.Split('\x01');
- Settings = new MenuSettings
- {
+ Settings = new MenuSettings {
NavigationPrefix = parts[0],
InputMode = parts[1],
ButtonsUse = parts[2],
@@ -92,7 +91,7 @@ public MenuManager(ISwiftlyCore core)
_Core.Event.OnMapUnload -= OnMapUnload;
}
- void KeyStateChange(IOnClientKeyStateChangedEvent @event)
+ void KeyStateChange( IOnClientKeyStateChangedEvent @event )
{
var player = _Core.PlayerManager.GetPlayer(@event.PlayerId);
var menu = GetMenu(player);
@@ -106,6 +105,17 @@ void KeyStateChange(IOnClientKeyStateChangedEvent @event)
var exitKey = menu.ButtonOverrides?.Exit ?? StringToKeyKind.GetValueOrDefault(Settings.ButtonsExit);
var useKey = menu.ButtonOverrides?.Select ?? StringToKeyKind.GetValueOrDefault(Settings.ButtonsUse);
+ new Dictionary { { "Scroll", scrollKey }, { "ScrollBack", scrollBackKey }, { "Exit", exitKey }, { "Use", useKey } }
+ .GroupBy(kvp => kvp.Value)
+ .Where(g => g.Count() > 1 && @event.Key.HasFlag(g.Key))
+ .ToList()
+ .ForEach(group =>
+ {
+ Spectre.Console.AnsiConsole.WriteException(
+ new InvalidOperationException($"Duplicate key binding detected in menu '{menu.Title}': Key '{group.Key}' is used by: {string.Join(", ", group.Select(kvp => kvp.Key))}")
+ );
+ });
+
if (@event.Key == scrollKey)
{
menu.MoveSelection(player, 1);
@@ -201,7 +211,7 @@ void KeyStateChange(IOnClientKeyStateChangedEvent @event)
}
}
- public void OnClientDisconnected(IOnClientDisconnectedEvent @event)
+ public void OnClientDisconnected( IOnClientDisconnectedEvent @event )
{
var player = _Core.PlayerManager.GetPlayer(@event.PlayerId);
if (player == null)
@@ -221,7 +231,7 @@ public void OnClientDisconnected(IOnClientDisconnectedEvent @event)
}
}
- public void OnMapUnload(IOnMapUnloadEvent _)
+ public void OnMapUnload( IOnMapUnloadEvent _ )
{
CloseAllMenus();
}
@@ -242,7 +252,7 @@ public void CloseAllMenus()
OpenMenus.Clear();
}
- public void CloseMenu(IMenu menu)
+ public void CloseMenu( IMenu menu )
{
foreach (var kvp in OpenMenus)
{
@@ -256,7 +266,7 @@ public void CloseMenu(IMenu menu)
}
}
- public void CloseMenuByTitle(string title, bool exact = false)
+ public void CloseMenuByTitle( string title, bool exact = false )
{
foreach (var kvp in OpenMenus)
{
@@ -270,7 +280,7 @@ public void CloseMenuByTitle(string title, bool exact = false)
}
}
- public void CloseMenuForPlayer(IPlayer player)
+ public void CloseMenuForPlayer( IPlayer player )
{
if (OpenMenus.TryRemove(player, out var menu))
{
@@ -283,17 +293,17 @@ public void CloseMenuForPlayer(IPlayer player)
}
}
- public IMenu CreateMenu(string title)
+ public IMenu CreateMenu( string title )
{
return new Menu { Title = title, MenuManager = this, MaxVisibleOptions = Settings.ItemsPerPage, _Core = _Core };
}
- public IMenu? GetMenu(IPlayer player)
+ public IMenu? GetMenu( IPlayer player )
{
return OpenMenus.TryGetValue(player, out var menu) ? menu : null;
}
- public void OpenMenu(IPlayer player, IMenu menu)
+ public void OpenMenu( IPlayer player, IMenu menu )
{
if (OpenMenus.TryGetValue(player, out var currentMenu))
{
@@ -306,7 +316,7 @@ public void OpenMenu(IPlayer player, IMenu menu)
OnMenuOpened?.Invoke(player, menu);
}
- public bool HasMenuOpen(IPlayer player)
+ public bool HasMenuOpen( IPlayer player )
{
return NativePlayer.HasMenuShown(player.PlayerID);
}
diff --git a/managed/src/SwiftlyS2.Core/Modules/Menus/Options/AsyncButtonMenuOption.cs b/managed/src/SwiftlyS2.Core/Modules/Menus/Options/AsyncButtonMenuOption.cs
index 626169c6..eaa5acc5 100644
--- a/managed/src/SwiftlyS2.Core/Modules/Menus/Options/AsyncButtonMenuOption.cs
+++ b/managed/src/SwiftlyS2.Core/Modules/Menus/Options/AsyncButtonMenuOption.cs
@@ -50,7 +50,7 @@ public bool CanInteract(IPlayer player)
return !IsLoading && (EnabledCheck?.Invoke(player) ?? true);
}
- public string GetDisplayText(IPlayer player)
+ public string GetDisplayText(IPlayer player, bool updateHorizontalStyle = false)
{
var sizeClass = MenuSizeHelper.GetSizeClass(Size);
@@ -59,7 +59,7 @@ public string GetDisplayText(IPlayer player)
return $"{_loadingText ?? "Loading..."}";
}
- var text = (Menu as Menus.Menu)?.ApplyHorizontalStyle(Text, OverflowStyle) ?? Text;
+ var text = (Menu as Menus.Menu)?.ApplyHorizontalStyle(Text, OverflowStyle, updateHorizontalStyle) ?? Text;
if (!CanInteract(player))
{
return $"{text}";
diff --git a/managed/src/SwiftlyS2.Core/Modules/Menus/Options/ButtonMenuOption.cs b/managed/src/SwiftlyS2.Core/Modules/Menus/Options/ButtonMenuOption.cs
index 86afbe89..d8206467 100644
--- a/managed/src/SwiftlyS2.Core/Modules/Menus/Options/ButtonMenuOption.cs
+++ b/managed/src/SwiftlyS2.Core/Modules/Menus/Options/ButtonMenuOption.cs
@@ -47,11 +47,11 @@ public bool CanInteract(IPlayer player)
return EnabledCheck?.Invoke(player) ?? true;
}
- public string GetDisplayText(IPlayer player)
+ public string GetDisplayText(IPlayer player, bool updateHorizontalStyle = false)
{
var sizeClass = MenuSizeHelper.GetSizeClass(Size);
- var text = (Menu as Menus.Menu)?.ApplyHorizontalStyle(Text, OverflowStyle) ?? Text;
+ var text = (Menu as Menus.Menu)?.ApplyHorizontalStyle(Text, OverflowStyle, updateHorizontalStyle) ?? Text;
if (!CanInteract(player))
{
return $"{text}";
diff --git a/managed/src/SwiftlyS2.Core/Modules/Menus/Options/ChoiceMenuButton.cs b/managed/src/SwiftlyS2.Core/Modules/Menus/Options/ChoiceMenuButton.cs
index 798fd42e..ef7857c0 100644
--- a/managed/src/SwiftlyS2.Core/Modules/Menus/Options/ChoiceMenuButton.cs
+++ b/managed/src/SwiftlyS2.Core/Modules/Menus/Options/ChoiceMenuButton.cs
@@ -67,13 +67,13 @@ public bool CanInteract(IPlayer player)
return EnabledCheck?.Invoke(player) ?? true;
}
- public string GetDisplayText(IPlayer player)
+ public string GetDisplayText(IPlayer player, bool updateHorizontalStyle = false)
{
var sizeClass = MenuSizeHelper.GetSizeClass(Size);
var choice = $"[{SelectedChoice}]";
- var text = (Menu as Menus.Menu)?.ApplyHorizontalStyle(Text, OverflowStyle) ?? Text;
+ var text = (Menu as Menus.Menu)?.ApplyHorizontalStyle(Text, OverflowStyle, updateHorizontalStyle) ?? Text;
if (!CanInteract(player))
{
return $"{text}: {choice}";
diff --git a/managed/src/SwiftlyS2.Core/Modules/Menus/Options/DynamicMenuOption.cs b/managed/src/SwiftlyS2.Core/Modules/Menus/Options/DynamicMenuOption.cs
index ca1d401b..ef61f4a0 100644
--- a/managed/src/SwiftlyS2.Core/Modules/Menus/Options/DynamicMenuOption.cs
+++ b/managed/src/SwiftlyS2.Core/Modules/Menus/Options/DynamicMenuOption.cs
@@ -56,7 +56,7 @@ public bool CanInteract(IPlayer player)
return _onClick != null && (_enabledCheck?.Invoke(player) ?? true);
}
- public string GetDisplayText(IPlayer player)
+ public string GetDisplayText(IPlayer player, bool updateHorizontalStyle = false)
{
var sizeClass = MenuSizeHelper.GetSizeClass(_size);
@@ -69,7 +69,7 @@ public string GetDisplayText(IPlayer player)
if (oldText != _cachedText && Menu != null)
{
- Menu.Rerender(player, true);
+ Menu.Rerender(player, false);
}
}
diff --git a/managed/src/SwiftlyS2.Core/Modules/Menus/Options/ProgressBarMenuButton.cs b/managed/src/SwiftlyS2.Core/Modules/Menus/Options/ProgressBarMenuButton.cs
index 7ca66740..c54c0631 100644
--- a/managed/src/SwiftlyS2.Core/Modules/Menus/Options/ProgressBarMenuButton.cs
+++ b/managed/src/SwiftlyS2.Core/Modules/Menus/Options/ProgressBarMenuButton.cs
@@ -22,7 +22,7 @@ internal class ProgressBarMenuOption(string text, Func progressProvider,
public bool ShouldShow(IPlayer player) => true;
public bool CanInteract(IPlayer player) => false;
- public string GetDisplayText(IPlayer player)
+ public string GetDisplayText(IPlayer player, bool updateHorizontalStyle = false)
{
var sizeClass = MenuSizeHelper.GetSizeClass(Size);
@@ -37,7 +37,7 @@ public string GetDisplayText(IPlayer player)
bar += $"{EmptyChar}";
var percentage = ShowPercentage ? $" {(int)(progress * 100)}%" : "";
- return $"{((Menu as Menus.Menu)?.ApplyHorizontalStyle(Text, OverflowStyle) ?? Text)}: {bar}{percentage}";
+ return $"{((Menu as Menus.Menu)?.ApplyHorizontalStyle(Text, OverflowStyle, updateHorizontalStyle) ?? Text)}: {bar}{percentage}";
}
public IMenuTextSize GetTextSize()
diff --git a/managed/src/SwiftlyS2.Core/Modules/Menus/Options/SeparatorMenuButton.cs b/managed/src/SwiftlyS2.Core/Modules/Menus/Options/SeparatorMenuButton.cs
index d0936b2f..0377eb30 100644
--- a/managed/src/SwiftlyS2.Core/Modules/Menus/Options/SeparatorMenuButton.cs
+++ b/managed/src/SwiftlyS2.Core/Modules/Menus/Options/SeparatorMenuButton.cs
@@ -20,7 +20,7 @@ public SeparatorMenuOption()
public bool ShouldShow(IPlayer player) => true;
public bool CanInteract(IPlayer player) => false;
- public string GetDisplayText(IPlayer player)
+ public string GetDisplayText(IPlayer player, bool updateHorizontalStyle = false)
{
return $"{Text}";
}
diff --git a/managed/src/SwiftlyS2.Core/Modules/Menus/Options/SliderMenuButton.cs b/managed/src/SwiftlyS2.Core/Modules/Menus/Options/SliderMenuButton.cs
index d5dbeb7d..c45ffa78 100644
--- a/managed/src/SwiftlyS2.Core/Modules/Menus/Options/SliderMenuButton.cs
+++ b/managed/src/SwiftlyS2.Core/Modules/Menus/Options/SliderMenuButton.cs
@@ -57,7 +57,7 @@ public bool CanInteract(IPlayer player)
return EnabledCheck?.Invoke(player) ?? true;
}
- public string GetDisplayText(IPlayer player)
+ public string GetDisplayText(IPlayer player, bool updateHorizontalStyle = false)
{
var sizeClass = MenuSizeHelper.GetSizeClass(Size);
@@ -75,7 +75,7 @@ public string GetDisplayText(IPlayer player)
}
slider += $") {Value:F1}";
- var text = (Menu as Menus.Menu)?.ApplyHorizontalStyle(Text, OverflowStyle) ?? Text;
+ var text = (Menu as Menus.Menu)?.ApplyHorizontalStyle(Text, OverflowStyle, updateHorizontalStyle) ?? Text;
if (!CanInteract(player))
{
return $"{text}: {slider}";
diff --git a/managed/src/SwiftlyS2.Core/Modules/Menus/Options/SubmenuMenuOption.cs b/managed/src/SwiftlyS2.Core/Modules/Menus/Options/SubmenuMenuOption.cs
index 6321bf99..2f0bafc2 100644
--- a/managed/src/SwiftlyS2.Core/Modules/Menus/Options/SubmenuMenuOption.cs
+++ b/managed/src/SwiftlyS2.Core/Modules/Menus/Options/SubmenuMenuOption.cs
@@ -42,7 +42,7 @@ public bool CanInteract(IPlayer player)
return EnabledCheck?.Invoke(player) ?? true;
}
- public string GetDisplayText(IPlayer player)
+ public string GetDisplayText(IPlayer player, bool updateHorizontalStyle = false)
{
var sizeClass = MenuSizeHelper.GetSizeClass(Size);
diff --git a/managed/src/SwiftlyS2.Core/Modules/Menus/Options/TextMenuOption.cs b/managed/src/SwiftlyS2.Core/Modules/Menus/Options/TextMenuOption.cs
index 565f39cd..4fca0796 100644
--- a/managed/src/SwiftlyS2.Core/Modules/Menus/Options/TextMenuOption.cs
+++ b/managed/src/SwiftlyS2.Core/Modules/Menus/Options/TextMenuOption.cs
@@ -44,11 +44,12 @@ public bool CanInteract(IPlayer player)
return true;
}
- public string GetDisplayText(IPlayer player)
+ public string GetDisplayText(IPlayer player, bool updateHorizontalStyle)
{
var text = DynamicText?.Invoke() ?? Text;
- text = (Menu as Menus.Menu)?.ApplyHorizontalStyle(text, OverflowStyle) ?? text;
+
+ text = (Menu as Menus.Menu)?.ApplyHorizontalStyle(text, OverflowStyle, updateHorizontalStyle) ?? text;
var sizeClass = MenuSizeHelper.GetSizeClass(Size);
diff --git a/managed/src/SwiftlyS2.Core/Modules/Menus/Options/ToggleMenuOption.cs b/managed/src/SwiftlyS2.Core/Modules/Menus/Options/ToggleMenuOption.cs
index d488af44..cfd4f313 100644
--- a/managed/src/SwiftlyS2.Core/Modules/Menus/Options/ToggleMenuOption.cs
+++ b/managed/src/SwiftlyS2.Core/Modules/Menus/Options/ToggleMenuOption.cs
@@ -50,13 +50,13 @@ public bool CanInteract(IPlayer player)
return EnabledCheck?.Invoke(player) ?? true;
}
- public string GetDisplayText(IPlayer player)
+ public string GetDisplayText(IPlayer player, bool updateHorizontalStyle = false)
{
var sizeClass = MenuSizeHelper.GetSizeClass(Size);
var status = Value ? "✔" : "✘";
- var text = (Menu as Menus.Menu)?.ApplyHorizontalStyle(Text, OverflowStyle) ?? Text;
+ var text = (Menu as Menus.Menu)?.ApplyHorizontalStyle(Text, OverflowStyle, updateHorizontalStyle) ?? Text;
if (!CanInteract(player))
{
return $"{text}: {status}/";
diff --git a/managed/src/SwiftlyS2.Shared/Modules/Menus/IMenu.cs b/managed/src/SwiftlyS2.Shared/Modules/Menus/IMenu.cs
index 12e6f406..ff7b515a 100644
--- a/managed/src/SwiftlyS2.Shared/Modules/Menus/IMenu.cs
+++ b/managed/src/SwiftlyS2.Shared/Modules/Menus/IMenu.cs
@@ -175,8 +175,8 @@ public interface IMenu
/// Updates the menu display with current state and options.
///
/// The player to re-render the menu for.
- /// True to update display text, false to render without updating display text.
- public void Rerender(IPlayer player, bool updateDisplayText = false);
+ /// True to update horizontal style, false to render without updating horizontal style.
+ public void Rerender(IPlayer player, bool updateHorizontalStyle = false);
///
/// Determines whether the currently selected option is selectable for the specified player.
diff --git a/managed/src/SwiftlyS2.Shared/Modules/Menus/IOption.cs b/managed/src/SwiftlyS2.Shared/Modules/Menus/IOption.cs
index 87d979bd..d8765642 100644
--- a/managed/src/SwiftlyS2.Shared/Modules/Menus/IOption.cs
+++ b/managed/src/SwiftlyS2.Shared/Modules/Menus/IOption.cs
@@ -50,8 +50,9 @@ public interface IOption
/// Gets the display text for this option as it should appear to the specified player.
///
/// The player requesting the display text.
+ /// Indicates whether to update the horizontal style of the text.
/// The formatted display text for the option.
- public string GetDisplayText(IPlayer player);
+ public string GetDisplayText(IPlayer player, bool updateHorizontalStyle);
///
/// Gets the text size configuration for this option.
diff --git a/managed/src/TestPlugin/TestPlugin.cs b/managed/src/TestPlugin/TestPlugin.cs
index 4895f243..9ae46f8b 100644
--- a/managed/src/TestPlugin/TestPlugin.cs
+++ b/managed/src/TestPlugin/TestPlugin.cs
@@ -554,6 +554,36 @@ public HookResult TestServerNetMessageHandler(CCSUsrMsg_SendPlayerItemDrops msg)
return HookResult.Continue;
}
+ [Command("i76")]
+ public void TestIssue76Command(ICommandContext context)
+ {
+ var player = context.Sender!;
+ IMenu settingsMenu = Core.Menus.CreateMenu("Settings");
+ // Add the following code to render text properly
+ //settingsMenu.Builder.AddText("123", overflowStyle: MenuHorizontalStyle.ScrollLeftLoop(25f));
+ settingsMenu.Builder.AddText("123");
+ settingsMenu.Builder.AddText("1234");
+ settingsMenu.Builder.AddText("12345");
+
+ Core.Menus.OpenMenu(player, settingsMenu);
+ }
+
+ [Command("i78")]
+ public void TestIssue78Command(ICommandContext context)
+ {
+ var player = context.Sender!;
+ IMenu settingsMenu = Core.Menus.CreateMenu("Settings");
+ settingsMenu.Builder.AddButton("123", (p) =>
+ {
+ player.SendMessage(MessageType.Chat, "Button");
+ });
+
+ settingsMenu.Builder.Design.OverrideExitButton("shift");
+ settingsMenu.Builder.Design.OverrideSelectButton("e");
+
+ Core.Menus.OpenMenu(player, settingsMenu);
+ }
+
[Command("mt")]
public void MenuTestCommand(ICommandContext context)
{