Lumi/companion/plugins/Lumi.Companion.Overlay/OverlaySettings.cs
2026-07-26 22:06:34 +02:00

193 lines
8.8 KiB
C#

using System.Text.Json.Serialization;
namespace Lumi.Companion.Overlay;
public enum OverlayVisibilityMode { Off, WhenLive, On }
public enum OverlayLayoutMode { Unified, DividedHorizontal, DividedVertical, Separate }
public enum OverlayAnchor { TopLeft, Top, TopRight, Left, Right, BottomLeft, Bottom, BottomRight }
public enum OverlayHorizontalAlignment { Left, Middle, Right }
public enum OverlayVerticalAlignment { Top, Middle, Bottom }
public enum OverlayNewestDirection { Start, End }
public enum OverlayAnimation { None, Fade, Slide, FadeSlide, ScaleFade }
public sealed class OverlaySourceSetting
{
public string Id { get; set; } = "";
public string Platform { get; set; } = "";
public string Label { get; set; } = "";
public bool Selected { get; set; }
}
public sealed class OverlayContainerSettings
{
public OverlayAnchor Anchor { get; set; } = OverlayAnchor.BottomLeft;
public int PaddingTop { get; set; } = 24;
public int PaddingRight { get; set; } = 24;
public int PaddingBottom { get; set; } = 24;
public int PaddingLeft { get; set; } = 24;
public int Width { get; set; } = 700;
public int Height { get; set; } = 600;
public OverlayHorizontalAlignment HorizontalAlignment { get; set; } = OverlayHorizontalAlignment.Left;
public OverlayVerticalAlignment VerticalAlignment { get; set; } = OverlayVerticalAlignment.Bottom;
public OverlayNewestDirection NewestDirection { get; set; } = OverlayNewestDirection.End;
public string CardBackground { get; set; } = "#12121266";
public string TextColor { get; set; } = "#bbbbbb";
public string FontFamily { get; set; } = "Segoe UI";
public double FontSize { get; set; } = 22;
public string TextShadow { get; set; } = "#000000aa";
public int ShadowX { get; set; } = 1;
public int ShadowY { get; set; } = 2;
public int ShadowBlur { get; set; } = 3;
public bool ShowAvatar { get; set; } = true;
public bool ShowUsername { get; set; } = true;
public bool ShowPlatform { get; set; } = true;
public OverlayAnimation EntryAnimation { get; set; } = OverlayAnimation.FadeSlide;
public OverlayAnimation ExitAnimation { get; set; } = OverlayAnimation.FadeSlide;
public int EntryDurationMs { get; set; } = 260;
public int ExitDurationMs { get; set; } = 220;
public string Easing { get; set; } = "cubic-out";
public int SlideDistance { get; set; } = 24;
public double ScaleAmount { get; set; } = 0.92;
public int LifetimeMs { get; set; } = 12000;
public int QueueLimit { get; set; } = 12;
public OverlayContainerSettings CreateCopy() => new()
{
Anchor = Anchor,
PaddingTop = PaddingTop,
PaddingRight = PaddingRight,
PaddingBottom = PaddingBottom,
PaddingLeft = PaddingLeft,
Width = Width,
Height = Height,
HorizontalAlignment = HorizontalAlignment,
VerticalAlignment = VerticalAlignment,
NewestDirection = NewestDirection,
CardBackground = CardBackground,
TextColor = TextColor,
FontFamily = FontFamily,
FontSize = FontSize,
TextShadow = TextShadow,
ShadowX = ShadowX,
ShadowY = ShadowY,
ShadowBlur = ShadowBlur,
ShowAvatar = ShowAvatar,
ShowUsername = ShowUsername,
ShowPlatform = ShowPlatform,
EntryAnimation = EntryAnimation,
ExitAnimation = ExitAnimation,
EntryDurationMs = EntryDurationMs,
ExitDurationMs = ExitDurationMs,
Easing = Easing,
SlideDistance = SlideDistance,
ScaleAmount = ScaleAmount,
LifetimeMs = LifetimeMs,
QueueLimit = QueueLimit
};
}
public sealed class LumiOverlaySettings
{
public int SchemaVersion { get; set; } = 1;
public string ActiveProfileId { get; set; } = "default";
public bool Enabled { get; set; } = true;
public OverlayVisibilityMode Visibility { get; set; } = OverlayVisibilityMode.On;
public string? MonitorId { get; set; }
public List<OverlaySourceSetting> Sources { get; set; } = [];
public HashSet<string> EventTypes { get; set; } = [];
public OverlayLayoutMode Layout { get; set; } = OverlayLayoutMode.Unified;
public int ChatPercentage { get; set; } = 70;
public OverlayContainerSettings Unified { get; set; } = new();
public OverlayContainerSettings Chat { get; set; } = new();
public OverlayContainerSettings Events { get; set; } = new() { Anchor = OverlayAnchor.TopRight, Width = 520, Height = 420 };
public string ToggleVisibilityHotkey { get; set; } = "Ctrl+Alt+L";
public string TogglePreviewHotkey { get; set; } = "Ctrl+Alt+P";
[JsonIgnore]
public IEnumerable<string> SelectedSourceIds => Sources.Where(source => source.Selected).Select(source => source.Id);
public LumiOverlaySettings CreateCopy() => new()
{
SchemaVersion = SchemaVersion,
ActiveProfileId = ActiveProfileId,
Enabled = Enabled,
Visibility = Visibility,
MonitorId = MonitorId,
Sources = Sources.Select(source => new OverlaySourceSetting
{
Id = source.Id,
Platform = source.Platform,
Label = source.Label,
Selected = source.Selected
}).ToList(),
EventTypes = EventTypes.ToHashSet(StringComparer.Ordinal),
Layout = Layout,
ChatPercentage = ChatPercentage,
Unified = Unified.CreateCopy(),
Chat = Chat.CreateCopy(),
Events = Events.CreateCopy(),
ToggleVisibilityHotkey = ToggleVisibilityHotkey,
TogglePreviewHotkey = TogglePreviewHotkey
};
public void Normalize()
{
SchemaVersion = 1;
ActiveProfileId = Clean(ActiveProfileId, 80, "default");
MonitorId = string.IsNullOrWhiteSpace(MonitorId) ? null : MonitorId.Trim()[..Math.Min(MonitorId.Trim().Length, 300)];
Sources = Sources.Where(source => !string.IsNullOrWhiteSpace(source.Id))
.GroupBy(source => source.Id, StringComparer.Ordinal).Select(group => group.First()).Take(250).ToList();
foreach (var source in Sources)
{
source.Id = Clean(source.Id, 300, "");
source.Platform = Clean(source.Platform, 24, "");
source.Label = Clean(source.Label, 300, source.Id);
}
EventTypes = EventTypes.Where(value => !string.IsNullOrWhiteSpace(value)).Select(value => Clean(value, 120, "")).Take(100).ToHashSet();
ChatPercentage = Math.Clamp(ChatPercentage, 10, 90);
NormalizeContainer(Unified);
NormalizeContainer(Chat);
NormalizeContainer(Events);
ToggleVisibilityHotkey = Clean(ToggleVisibilityHotkey, 80, "Ctrl+Alt+L");
TogglePreviewHotkey = Clean(TogglePreviewHotkey, 80, "Ctrl+Alt+P");
}
private static void NormalizeContainer(OverlayContainerSettings value)
{
value.Width = Math.Clamp(value.Width, 160, 16000);
value.Height = Math.Clamp(value.Height, 100, 16000);
value.PaddingTop = Math.Clamp(value.PaddingTop, 0, 4000);
value.PaddingRight = Math.Clamp(value.PaddingRight, 0, 4000);
value.PaddingBottom = Math.Clamp(value.PaddingBottom, 0, 4000);
value.PaddingLeft = Math.Clamp(value.PaddingLeft, 0, 4000);
AnchorLayout.ClampPadding(value);
value.CardBackground = ColorValue(value.CardBackground, true, "#12121266");
value.TextColor = ColorValue(value.TextColor, false, "#bbbbbb");
value.TextShadow = ColorValue(value.TextShadow, true, "#000000aa");
value.FontFamily = Clean(value.FontFamily, 120, "Segoe UI");
value.FontSize = Math.Clamp(value.FontSize, 8, 120);
value.ShadowX = Math.Clamp(value.ShadowX, -40, 40);
value.ShadowY = Math.Clamp(value.ShadowY, -40, 40);
value.ShadowBlur = Math.Clamp(value.ShadowBlur, 0, 40);
value.EntryDurationMs = Math.Clamp(value.EntryDurationMs, 0, 5000);
value.ExitDurationMs = Math.Clamp(value.ExitDurationMs, 0, 5000);
value.SlideDistance = Math.Clamp(value.SlideDistance, 0, 1000);
value.ScaleAmount = Math.Clamp(value.ScaleAmount, 0.1, 2);
value.LifetimeMs = Math.Clamp(value.LifetimeMs, 1000, 300000);
value.QueueLimit = Math.Clamp(value.QueueLimit, 1, 100);
value.Easing = Clean(value.Easing, 40, "cubic-out");
}
private static string Clean(string? value, int max, string fallback)
{
var clean = (value ?? "").Trim();
return clean.Length == 0 ? fallback : clean[..Math.Min(clean.Length, max)];
}
private static string ColorValue(string? value, bool alpha, string fallback) =>
System.Text.RegularExpressions.Regex.IsMatch(value ?? "", alpha ? "^#[0-9a-fA-F]{8}$" : "^#[0-9a-fA-F]{6}$") ? value! : fallback;
}
public sealed record OverlaySource(string Id, string Platform, string Label, bool? Live, bool Available, string Status);
public sealed record OverlayEventType(string Id, string Label, string Platform);