feat(alerts): add scene-local recent ticker
Model the AlertBox and standalone ticker with shared settings; scene tickers render the in-memory active and queued events without saving history. Keep AlertBox content active-event-only. Ref: https://obsproject.com/kb/obs-studio-overview
This commit is contained in:
+25
-10
@@ -21,7 +21,9 @@ public enum SourceType
|
||||
AlertBox,
|
||||
/// <summary>A privacy blur: blurs the frame beneath it inside a rect or ellipse. Live scene
|
||||
/// only, always below the brand logo, several allowed (strongest wins on overlap).</summary>
|
||||
Blur
|
||||
Blur,
|
||||
/// <summary>A scene-local feed of recent alert events.</summary>
|
||||
Ticker
|
||||
}
|
||||
|
||||
/// <summary>Distinguishes how a media source presents its content (TASK 21).</summary>
|
||||
@@ -66,6 +68,19 @@ public enum AlertDisplayMethod
|
||||
/// </summary>
|
||||
public class Source : SceneElement
|
||||
{
|
||||
private readonly Ticker _ticker = new();
|
||||
|
||||
public Source()
|
||||
{
|
||||
_ticker.PropertyChanged += (_, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(Ticker.IsEnabled)) Raise(nameof(AlertShowTicker));
|
||||
if (e.PropertyName == nameof(Ticker.DisplayMethod)) Raise(nameof(AlertDisplayMethod));
|
||||
};
|
||||
}
|
||||
|
||||
public Ticker Ticker => _ticker;
|
||||
|
||||
private SourceType _type;
|
||||
public SourceType Type
|
||||
{
|
||||
@@ -80,6 +95,7 @@ public class Source : SceneElement
|
||||
Raise(nameof(IsWebSource));
|
||||
Raise(nameof(IsTextOverlay));
|
||||
Raise(nameof(IsScoreboard));
|
||||
Raise(nameof(IsTicker));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,18 +104,19 @@ public class Source : SceneElement
|
||||
/// and sizes it in the preview, so it needs the same selection box, grab and resize handle
|
||||
/// (creator ruling 2026-11-01).</summary>
|
||||
public override bool IsDraggable => Type is SourceType.Image or SourceType.WebSource or SourceType.ChatBox
|
||||
or SourceType.AlertBox or SourceType.Blur;
|
||||
or SourceType.AlertBox or SourceType.Blur or SourceType.Ticker;
|
||||
|
||||
/// <summary>Live captures, chat, web sources and media are dynamic; static art is fixed.</summary>
|
||||
public override ElementKind Kind => Type is SourceType.DisplayCapture or SourceType.WindowCapture
|
||||
or SourceType.ChatBox or SourceType.WebSource or SourceType.MediaSource or SourceType.AlertBox
|
||||
or SourceType.Blur
|
||||
or SourceType.Blur or SourceType.Ticker
|
||||
? ElementKind.Dynamic : ElementKind.Static;
|
||||
|
||||
public override bool IsWebSource => Type == SourceType.WebSource;
|
||||
public override bool IsTextOverlay => Type == SourceType.TextOverlay;
|
||||
|
||||
public override bool IsScoreboard => Type == SourceType.Scoreboard;
|
||||
public bool IsTicker => Type == SourceType.Ticker;
|
||||
|
||||
/// <summary>The permanent brand logo layer (one per scene, directly above the background).
|
||||
/// Locked against removal; its image comes from the App Settings brand logo.</summary>
|
||||
@@ -156,7 +173,7 @@ public class Source : SceneElement
|
||||
/// VideoImageSource, and never displayed (the same shape as the invisible-alert defect).</summary>
|
||||
private bool IsFrameSource =>
|
||||
Type is SourceType.ChatBox or SourceType.WebSource or SourceType.MediaSource
|
||||
or SourceType.AlertBox or SourceType.Scoreboard;
|
||||
or SourceType.AlertBox or SourceType.Scoreboard or SourceType.Ticker;
|
||||
|
||||
public override ImageSource? DisplaySource => IsLiveCapture
|
||||
? (VideoImageSource ?? _imageSource)
|
||||
@@ -259,11 +276,10 @@ public class Source : SceneElement
|
||||
set => Set(ref _alertUseDefaultVideo, value);
|
||||
}
|
||||
|
||||
private bool _alertShowTicker = true;
|
||||
public bool AlertShowTicker
|
||||
{
|
||||
get => _alertShowTicker;
|
||||
set => Set(ref _alertShowTicker, value);
|
||||
get => _ticker.IsEnabled;
|
||||
set => _ticker.IsEnabled = value;
|
||||
}
|
||||
|
||||
private double _alertVideoVolume = 1.0;
|
||||
@@ -295,11 +311,10 @@ public class Source : SceneElement
|
||||
/// <summary>True once a file is chosen — gates the loop switch and the clear button.</summary>
|
||||
public bool HasAlertAudio => !string.IsNullOrWhiteSpace(_alertAudioPath);
|
||||
|
||||
private AlertDisplayMethod _alertDisplayMethod = AlertDisplayMethod.TickerScroll;
|
||||
public AlertDisplayMethod AlertDisplayMethod
|
||||
{
|
||||
get => _alertDisplayMethod;
|
||||
set => Set(ref _alertDisplayMethod, value);
|
||||
get => _ticker.DisplayMethod;
|
||||
set => _ticker.DisplayMethod = value;
|
||||
}
|
||||
|
||||
public override bool IsAlertBox => Type == SourceType.AlertBox;
|
||||
|
||||
Reference in New Issue
Block a user