7using Microsoft.Extensions.Logging;
34 private static readonly ILogger<SplashScreen> Logger =
35 EngineLogger.GetLogger<SplashScreen>();
68 private readonly
View _view;
69 private Timer? _holdTimer;
70 private TaskCompletionSource? _holdDurationCompletionSource;
71 private bool _disposed;
94 public float HoldSec {
get;
private set; }
101 #endregion properties
103 #region factory / constructor
109 Action? onHoldingSync,
110 Func<Task>? onHoldingAsync,
111 Action? onSplashCompleted)
119 ??
throw new InvalidOperationException(
"Splash image must be attached to a view.");
120 _view.Viewport.TargetRectChanged += OnViewportTargetRectChanged;
197 public static SplashScreen?
TryCreate(Stream imageStream,
200 float fadeInSec = 0.45f,
202 float fadeOutSec = 0.45f,
203 Action? onHoldingSync =
null,
204 Func<Task>? onHoldingAsync =
null,
205 Action? onSplashCompleted =
null)
210 using var bitmap = SKBitmap.Decode(imageStream);
212 var sourceImage = bitmap is
null
214 : SKImage.FromBitmap(bitmap);
216 if (sourceImage is
null)
221 var screenBounds = viewport;
232 image.Disposing += (_, _) => sourceImage.
Dispose();
234 image.ZOrder =
int.MaxValue;
237 return new SplashScreen(
247 #endregion factory / constructor
259 _view.Viewport.TargetRectChanged -= OnViewportTargetRectChanged;
261 _holdTimer?.Dispose();
264 _holdDurationCompletionSource?.TrySetCanceled();
265 _holdDurationCompletionSource =
null;
275 Image.ScreenBounds = args.
NewRect;
278 #region splash control
280 private void StartFadeIn(Action? onHoldingSync, Func<Task>? onHoldingAsync, Action? onSplashCompleted)
282 Logger.LogDebug(
"Starting splash screen fade-in");
286 EventHandler<DirectDrawingBase>? fadeInHandler =
null;
288 fadeInHandler = (_, _) =>
290 Image.FadeToCompleted -= fadeInHandler;
297 _ = RunHoldPhaseAsync(
303 Image.FadeToCompleted += fadeInHandler;
308 private async Task RunHoldPhaseAsync(
309 Action? onHoldingSync,
310 Func<Task>? onHoldingAsync,
311 Action? onSplashCompleted)
313 Logger.LogDebug(
"Starting splash screen hold phase");
317 var holdDurationTask = WaitForHoldDurationAsync();
319 var delegateTask = ExecuteHoldDelegatesAsync(onHoldingSync, onHoldingAsync);
325 catch (OperationCanceledException)
332 Logger.LogError(ex,
"Unexpected error during SplashScreen hold phase");
336 _holdTimer?.Dispose();
339 _holdDurationCompletionSource =
null;
345 Engine.Instance.EngineDispatcher.Post(() =>
353 StartFadeOut(onSplashCompleted);
357 private Task WaitForHoldDurationAsync()
359 var completionSource =
new TaskCompletionSource(
360 TaskCreationOptions.RunContinuationsAsynchronously);
362 _holdDurationCompletionSource =
365 var timer = Timer.
Add(
372 void OnHoldTimerElapsed()
374 timer.Tick -= OnHoldTimerElapsed;
376 completionSource.TrySetResult();
379 timer.Tick += OnHoldTimerElapsed;
381 return completionSource.Task;
384 private static async Task ExecuteHoldDelegatesAsync(
385 Action? onHoldingSync,
386 Func<Task>? onHoldingAsync)
388 if (onHoldingSync is
null &&
389 onHoldingAsync is
null)
395 Engine.Instance.EngineDispatcher;
399 await dispatcher.PostAsync(async () =>
407 if (onHoldingAsync is not
null)
409 Logger.LogDebug(
"Calling onHoldingAsync in SplashScreen");
413 asyncTask = onHoldingAsync() ?? Task.CompletedTask;
417 asyncTask = Task.FromException(ex);
421 if (onHoldingSync is not
null)
423 Logger.LogDebug(
"Calling onHoldingSync in SplashScreen");
431 syncTask = Task.FromException(ex);
435 await Task.WhenAll(asyncTask, syncTask);
440 Logger.LogError(ex,
"Error executing SplashScreen hold delegates");
444 private void StartFadeOut(Action? onSplashCompleted)
446 Logger.LogDebug(
"Starting splash screen fade-out");
450 EventHandler<DirectDrawingBase>? fadeOutHandler =
null;
452 fadeOutHandler = (_, _) =>
454 Image.FadeToCompleted -= fadeOutHandler;
457 onSplashCompleted?.Invoke();
460 Image.FadeToCompleted += fadeOutHandler;
464 #endregion splash control
RenderSurfaceHostBase RenderSurfaceHost
Gets the render surface host shared by the composite and its children.
View? View
Gets the view shared by all children in view mode.
virtual DirectComposite Add(IDirectCompositeChild child, bool keepCurrentOffset=true, Vector2? explicitLocalOffsetPx=null)
Adds a composite-compatible direct drawable as a child.
void Dispose()
Releases all resources used by this DirectDrawingBase instance.
DirectDrawingBase FadeIn(float durationSec)
Fades this direct drawing from its current opacity to fully opaque over the specified duration.
Renders a bitmap or image to a rectangular region with support for scaling modes, source clipping,...
DirectImage SetScaleMode(ScaleMode mode)
Sets how the image is scaled to fit within the destination rectangle.
ScaleMode
Defines how an image is scaled to fit within the destination rectangle.
Represents a base class for hosting a render surface, providing functionality for managing rendering ...
ViewManager ViewManager
Gets the view manager that controls camera positions, viewports, and multi-view rendering for this re...
ReadOnlyCollection< View > Views
Gets a read-only collection of all views currently managed by this view manager. Views are sorted by ...
Represents a rendered view of a scene, combining a camera position with a viewport configuration to c...
Viewport Viewport
Gets the viewport that defines the screen-space rectangle, zoom level, and other rendering parameters...
Provides data for the Viewport.TargetRectChanged event, which is raised when a viewport's target rect...
Rectangle NewRect
Gets the new target rectangle after the resize or reposition operation.
Rectangle TargetRectPx
Screen-space rectangle (in SCREEN / RenderSurface pixels) where this view is drawn....
Represents a high-resolution timer that can trigger events at specified intervals and supports variou...
static Timer Add(string timerID, TimerType type, TimerCycles cycles, double length)
Creates a new timer with the specified parameters and adds it to the collection of active timers.
DirectDrawingMode
Specifies where direct drawing operations are applied within the rendering pipeline.
TimerType
Specifies when a Timer should have its Timer.Tick event raised relative to the engine's main update a...
TimerCycles
Specifies the cycle behavior of a Timer, determining whether it executes once or repeatedly at its co...