2using System.Runtime.CompilerServices;
3using System.Text.RegularExpressions;
75 private string _text =
string.Empty;
76 private readonly List<string> _lines =
new();
77 private float _lineHeight;
78 private bool _layoutDirty =
true;
80 private SKColor _foreColor = SKColors.White;
81 private SKColor _backColor = SKColors.Transparent;
83 private float _fontSize = 16f;
84 private float? _minFontSize =
null;
85 private SKTypeface? _typeface =
null;
87 private bool _useOutline =
false;
88 private bool _wrapText =
true;
89 private int? _maxLines =
null;
91 private bool _useShadow =
false;
92 private float _shadowDx = 2f, _shadowDy = 2f;
93 private byte _shadowAlpha = 128;
94 private float _shadowBlurSigma = 1.5f;
96 private SKTextAlign _hAlign = SKTextAlign.Left;
98 private float _verticalScrollOffsetPx;
99 private float _verticalScrollEndPaddingPx;
102 private bool _pulseTextEnabled;
103 private SKColor _pulseFrom, _pulseTo;
104 private float _pulsePeriodSec = 1f;
108 private long? _pulseLastTick;
109 private float _timeSec;
112 private long _revealLastTick;
114 private int _revealCharCount;
115 private int _revealTargetCharCount;
116 private float _revealRate;
117 private float _revealAccum;
120 private int[]? _wordEndCharIndexes;
121 private int _wordIndex;
124 private bool _pauseEnabled =
true;
125 private float _pauseLongSec = 0.25f;
126 private float _pauseShortSec = 0.10f;
129 private SKColor _resolvedForeColor;
145 Rectangle? screenBounds,
146 Rectangle? worldBounds,
147 string? nickname =
null)
148 : base(renderSurfaceHost, mode, sceneLayer, view, screenBounds, worldBounds, nickname)
150 _resolvedForeColor = _foreColor;
164 Rectangle worldBounds,
165 string? nickname =
null)
166 : this(renderSurfaceHost,
171 worldBounds: worldBounds,
184 Rectangle screenBounds,
185 string? nickname =
null)
186 : this(renderSurfaceHost,
190 screenBounds: screenBounds,
219 get => _verticalScrollOffsetPx;
222 if (!
float.IsFinite(value) || value < 0f)
223 throw new ArgumentOutOfRangeException(nameof(value));
225 if (Math.Abs(_verticalScrollOffsetPx - value) < 0.001f)
228 _verticalScrollOffsetPx = value;
242 get => _verticalScrollEndPaddingPx;
245 if (!
float.IsFinite(value) || value < 0f)
246 throw new ArgumentOutOfRangeException(nameof(value));
248 if (Math.Abs(_verticalScrollEndPaddingPx - value) < 0.001f)
251 _verticalScrollEndPaddingPx = value;
267 float innerHeight = Math.Max(0f, bounds.Height -
VerticalPadding * 2f);
269 _typeface ??= SKTypeface.Default;
270 using var paint =
new SKPaint
272 Typeface = _typeface,
273 TextSize = _fontSize,
277 float fontSize = _fontSize;
278 float minimumFontSize = _minFontSize ?? 0f;
282 paint.TextSize = fontSize;
283 RebuildLayout(paint, innerWidth);
285 int drawableLines = _maxLines.HasValue
286 ? Math.Min(_lines.Count, _maxLines.Value)
288 float contentHeight = drawableLines * _lineHeight;
289 bool exceedsHeight = contentHeight > innerHeight;
290 bool exceedsWidth = _lines.Any(line => paint.MeasureText(line) > innerWidth);
292 if (_minFontSize.HasValue &&
293 (exceedsHeight || exceedsWidth) &&
294 fontSize > minimumFontSize)
296 fontSize = Math.Max(minimumFontSize, fontSize - 1f);
300 float maximumOffset = Math.Max(0f, contentHeight - innerHeight);
301 if (maximumOffset > 0f)
307 return maximumOffset;
317 public TextBlock
SetPadding(
float horizontal,
float vertical)
319 if (!
float.IsFinite(horizontal) || horizontal < 0f)
320 throw new ArgumentOutOfRangeException(nameof(horizontal));
321 if (!
float.IsFinite(vertical) || vertical < 0f)
322 throw new ArgumentOutOfRangeException(nameof(vertical));
338 if (size.Width <= 0 || size.Height <= 0)
339 throw new ArgumentOutOfRangeException(nameof(size), size,
"Text block size must be positive.");
360 _text = text ??
string.Empty;
366 _revealCharCount = _text.Length;
371 _revealTargetCharCount = _text.Length;
374 if (_revealCharCount > _revealTargetCharCount)
375 _revealCharCount = _revealTargetCharCount;
394 public TextBlock
SetFont(SKTypeface typeface,
float size,
float? minSize =
null)
396 _typeface = typeface;
398 _minFontSize = minSize;
414 _resolvedForeColor = fg;
476 public TextBlock
SetShadow(
float dx,
float dy,
byte alpha = 128,
float blurSigma = 1.5f)
480 _shadowAlpha = alpha;
481 _shadowBlurSigma = MathF.Max(0f, blurSigma);
493 _useOutline = enable;
518 _maxLines = maxLines;
532 public TextBlock
PulseColor(Color from, Color to,
float periodSec,
bool enabled =
true,
bool triangle =
false)
534 _pulseTextEnabled = enabled;
535 _pulseFrom = from.ToSKColor();
536 _pulseTo = to.ToSKColor();
537 _pulsePeriodSec = MathF.Max(0.0001f, periodSec);
541 _resolvedForeColor = _foreColor;
552 _pulseTextEnabled =
false;
553 _resolvedForeColor = _foreColor;
568 int? maxChars =
null,
569 bool enablePauses =
true,
570 float longPauseSec = 0.25f,
571 float shortPauseSec = 0.10f)
574 _revealRate = MathF.Max(0f, charsPerSecond);
575 _revealCharCount = 0;
576 _revealTargetCharCount = maxChars.HasValue ? Math.Min(maxChars.Value, _text.Length) : _text.Length;
578 _wordEndCharIndexes =
null;
583 _pauseEnabled = enablePauses;
584 _pauseLongSec = longPauseSec;
585 _pauseShortSec = shortPauseSec;
600 bool enablePauses =
true,
601 float longPauseSec = 0.25f,
602 float shortPauseSec = 0.10f)
605 _revealRate = MathF.Max(0f, wordsPerSecond);
606 _revealCharCount = 0;
607 _revealTargetCharCount = _text.Length;
613 _wordEndCharIndexes = Regex.Matches(_text,
@"\S+\s*")
614 .Select(m => m.Index + m.Length)
619 _pauseEnabled = enablePauses;
620 _pauseLongSec = longPauseSec;
621 _pauseShortSec = shortPauseSec;
636 _pauseEnabled = enabled;
637 _pauseLongSec = longPauseSec;
638 _pauseShortSec = shortPauseSec;
650 _revealCharCount = Math.Clamp(charCount, 0, _text.Length);
651 _revealTargetCharCount = _text.Length;
663 _revealCharCount = _text.Length;
673 private float PauseFor(
char c)
675 if (!_pauseEnabled)
return 0f;
679 '.' or
'!' or
'?' => _pauseLongSec,
680 ',' or
';' or
':' => _pauseShortSec,
697 if (_pulseLastTick.HasValue)
698 dtPulse = HighResTimer.GetDuration(_pulseLastTick.Value, tick);
700 _pulseLastTick = tick;
702 if (dtPulse > 0f && dtPulse < 1f)
705 if (_pulseTextEnabled)
707 float t = PulseT(_timeSec, _pulsePeriodSec, _pulseWave);
708 var c = LerpColor(_pulseFrom, _pulseTo, t);
709 if (c != _resolvedForeColor)
711 _resolvedForeColor = c;
718 if (_resolvedForeColor != _foreColor)
720 _resolvedForeColor = _foreColor;
727 if (_revealLastTick == 0)
729 _revealLastTick = tick;
733 dtReveal = HighResTimer.GetDuration(_revealLastTick, tick);
734 _revealLastTick = tick;
737 if (dtReveal > 0.1f) dtReveal = 0.1f;
738 if (dtReveal < 0f) dtReveal = 0f;
744 _revealAccum += dtReveal;
745 if (_revealRate > 0f)
747 int step = (int)(_revealAccum * _revealRate);
750 _revealAccum -= step / _revealRate;
752 int old = _revealCharCount;
753 _revealCharCount = Math.Min(_revealCharCount + step, _revealTargetCharCount);
756 for (
int i = old; i < _revealCharCount; i++)
757 _revealAccum -= PauseFor(_text[i]);
763 if (_revealCharCount > old)
765 var revealed = _text.Substring(0, _revealCharCount);
770 if (_revealCharCount >= _revealTargetCharCount)
779 else if (_textRevealMode ==
TextRevealMode.WordsPerSecond && _wordEndCharIndexes is not
null)
781 _revealAccum += dtReveal;
782 if (_revealRate > 0f)
784 int step = (int)(_revealAccum * _revealRate);
787 _revealAccum -= step / _revealRate;
789 int oldCount = _revealCharCount;
791 _wordIndex = Math.Min(_wordIndex + step, _wordEndCharIndexes.Length);
792 _revealCharCount = _wordIndex == 0 ? 0 : _wordEndCharIndexes[_wordIndex - 1];
795 if (_revealCharCount > 0)
796 _revealAccum -= PauseFor(_text[_revealCharCount - 1]);
801 if (_revealCharCount > oldCount)
803 var revealed = _text.Substring(0, _revealCharCount);
807 if (_wordIndex >= _wordEndCharIndexes.Length)
828 var canvas = backbuffer.
Canvas;
829 var rect = destRectScreen.ToSKRect();
831 float zoom = ResolveTextScale(
Mode, RenderContext.Current?.ViewportZoom ?? 1f);
836 float shadowDx = _shadowDx * zoom;
837 float shadowDy = _shadowDy * zoom;
838 float shadowBlur = _shadowBlurSigma * zoom;
839 float outlineWidth = 1.5f * zoom;
842 if (_backColor.Alpha != 0)
844 using var bg =
new SKPaint { Color = _backColor };
845 canvas.DrawRect(rect, bg);
848 _typeface ??= SKTypeface.Default;
851 using var paint =
new SKPaint
853 Typeface = _typeface ?? SKTypeface.Default,
854 TextSize = _fontSize * zoom,
855 Color = _resolvedForeColor,
862 float fontSize = _fontSize * zoom;
863 float minFontSize = _minFontSize.HasValue ? _minFontSize.Value * zoom : 0f;
865 float innerW = Math.Max(0, rect.Width - hPad * 2f);
866 float innerH = Math.Max(0, rect.Height - vPad * 2f);
870 paint.TextSize = fontSize;
873 if (_layoutDirty || zoom != 1f)
874 RebuildLayout(paint, innerW);
876 int drawableLines = _maxLines.HasValue ? Math.Min(_lines.Count, _maxLines.Value) : _lines.Count;
877 float totalH = drawableLines * _lineHeight;
879 bool exceedsHeight = totalH > innerH;
880 bool exceedsWidth = _lines.Any(line => paint.MeasureText(line) > innerW);
882 if (_minFontSize.HasValue &&
883 (exceedsHeight || exceedsWidth) &&
884 fontSize > minFontSize)
886 fontSize = Math.Max(minFontSize, fontSize - 1f);
889 if (fontSize <= minFontSize)
900 ? Math.Clamp(_revealCharCount, 0, _text.Length)
905 List<string> drawLines =
new(_lines.Count);
906 if (visibleChars <= 0)
910 else if (visibleChars >= _text.Length)
912 drawLines.AddRange(_lines);
916 int remaining = visibleChars;
917 foreach (var ln
in _lines)
922 if (ln.Length <= remaining)
925 remaining -= ln.Length;
929 drawLines.Add(ln.Substring(0, remaining));
935 bool wasTruncatedByMaxLines = _maxLines.HasValue && drawLines.Count > _maxLines.Value;
938 int linesToDraw = _maxLines.HasValue ? Math.Min(drawLines.Count, _maxLines.Value) : drawLines.Count;
940 if (linesToDraw > 0 && wasTruncatedByMaxLines)
942 drawLines[linesToDraw - 1] = FitLineWithEllipsis(paint, drawLines[linesToDraw - 1], innerW);
946 var fm = paint.FontMetrics;
947 float baselineShift = -fm.Ascent;
949 float contentH = linesToDraw * _lineHeight;
950 float maximumScrollOffset = Math.Max(0f, contentH - innerH);
951 if (maximumScrollOffset > 0f)
953 float yStart = maximumScrollOffset > 0f
962 float scrollOffset = Math.Min(_verticalScrollOffsetPx * zoom, maximumScrollOffset);
963 yStart -= scrollOffset;
966 float xAnchorLeft = rect.Left + hPad;
967 float xAnchorCenter = rect.MidX;
968 float xAnchorRight = rect.Right - hPad;
971 canvas.ClipRect(rect);
974 for (
int i = 0; i < linesToDraw; i++)
976 var line = drawLines[i];
977 float x = _hAlign
switch
979 SKTextAlign.Center => xAnchorCenter,
980 SKTextAlign.Right => xAnchorRight,
986 using var shadow = paint.Clone();
987 shadow.IsStroke =
false;
988 shadow.MaskFilter = shadowBlur > 0f
989 ? SKMaskFilter.CreateBlur(SKBlurStyle.Normal, shadowBlur)
991 shadow.Color =
new SKColor(0, 0, 0, _shadowAlpha);
994 canvas.DrawText(line, x + shadowDx, y + baselineShift + shadowDy, shadow);
999 using var outline = paint.Clone();
1000 outline.IsStroke =
true;
1001 outline.StrokeWidth = outlineWidth;
1002 outline.Color = SKColors.Black;
1003 canvas.DrawText(line, x, y + baselineShift, outline);
1006 canvas.DrawText(line, x, y + baselineShift, paint);
1010 if (y > rect.Bottom)
1017 internal static float ResolveTextScale(
DirectDrawingMode mode,
float viewportZoom)
1022 return viewportZoom > 0f ? viewportZoom : 1f;
1025 private void RebuildLayout(SKPaint paint,
float maxWidth)
1029 var fm = paint.FontMetrics;
1032 var paragraphs = _text.Replace(
"\r\n",
"\n").Split(
'\n');
1034 foreach (var para
in paragraphs)
1036 if (
string.IsNullOrEmpty(para))
1038 _lines.Add(
string.Empty);
1048 var words = para.Split(
' ', StringSplitOptions.None);
1049 var current =
string.Empty;
1051 foreach (var word
in words)
1053 string candidate =
string.IsNullOrEmpty(current) ? word : current +
" " + word;
1054 float candidateWidth = paint.MeasureText(candidate);
1056 if (candidateWidth <= maxWidth)
1058 current = candidate;
1063 if (!
string.IsNullOrEmpty(current))
1065 _lines.Add(current);
1066 current =
string.Empty;
1070 if (paint.MeasureText(word) <= maxWidth)
1077 var chunk =
string.Empty;
1078 foreach (
char ch
in word)
1080 string next = chunk + ch;
1081 if (paint.MeasureText(next) <= maxWidth ||
string.IsNullOrEmpty(chunk))
1088 chunk = ch.ToString();
1092 if (!
string.IsNullOrEmpty(chunk))
1096 if (!
string.IsNullOrEmpty(current))
1097 _lines.Add(current);
1100 _layoutDirty =
false;
1103 [MethodImpl(MethodImplOptions.AggressiveInlining)]
1104 private static float PulseT(
float timeSec,
float periodSec,
PulseWave wave)
1106 float phase = (timeSec / periodSec) % 1f;
1108 ? 0.5f * (1f + MathF.Sin(phase * MathF.PI * 2f))
1109 : (phase < 0.5f ? phase * 2f : 1f - (phase - 0.5f) * 2f);
1112 [MethodImpl(MethodImplOptions.AggressiveInlining)]
1113 private static SKColor LerpColor(SKColor a, SKColor b,
float t01)
1115 t01 = Math.Clamp(t01, 0f, 1f);
1116 byte r = (byte)(a.Red + (b.Red - a.Red) * t01);
1117 byte g = (byte)(a.Green + (b.Green - a.Green) * t01);
1118 byte bl = (byte)(a.Blue + (b.Blue - a.Blue) * t01);
1119 byte al = (byte)(a.Alpha + (b.Alpha - a.Alpha) * t01);
1120 return new SKColor(r, g, bl, al);
1123 private static string FitLineWithEllipsis(SKPaint paint,
string text,
float maxWidth)
1125 const string ellipsis =
"...";
1127 if (
string.IsNullOrEmpty(text))
1130 if (paint.MeasureText(text) <= maxWidth)
1133 float ellipsisWidth = paint.MeasureText(ellipsis);
1134 if (ellipsisWidth > maxWidth)
1135 return string.Empty;
1137 int length = text.Length;
1140 string candidate = text.Substring(0, length).TrimEnd() + ellipsis;
1141 if (paint.MeasureText(candidate) <= maxWidth)
1147 return ellipsisWidth <= maxWidth ? ellipsis :
string.Empty;
1150 #region public readonly properties
1239 #endregion public readonly properties
1267 private readonly
object _eventLock =
new();
1284 base.Dispose(disposing);
View? View
Gets the view to which this direct drawing is attached, or null if the drawing is in scene-layer mode...
long _lastTick
The last engine tick value used for delta-time calculations in Update.
SceneLayer? SceneLayer
Gets the scene layer to which this direct drawing is attached, or null if the drawing is in view mode...
Rectangle WorldBounds
Gets or sets the world-space bounds of this direct drawing in pixels.
DirectDrawingMode Mode
Gets the drawing mode that determines how this direct drawing is positioned and transformed.
Rectangle ScreenBounds
Gets or sets the screen-space bounds of this direct drawing in pixels.
DirectDrawingMovableBase(RenderSurfaceHostBase renderSurfaceHost, DirectDrawingMode mode, SceneLayer? sceneLayer, View? view, Rectangle? screenBounds, Rectangle? worldBounds, string? name=null)
Initializes a new instance of the DirectDrawingMovableBase class with integrated movement capabilitie...
TextBlock SetShadow(float dx, float dy, byte alpha=128, float blurSigma=1.5f)
Configures the drop shadow effect for text rendering.
TextBlock UseOutline(bool enable=true)
Enables or disables a 1-pixel outline stroke around the text glyphs.
float PunctuationPauseShortSec
Gets the short punctuation pause duration (seconds) used for ',', ';', ':'.
float ShadowDy
Gets the vertical shadow offset (in pixels).
PulseWave
Selects the waveform used by PulseColor(Color, Color, float, bool, bool).
@ Triangle
Triangle wave: linear fade-in/out between colors.
@ Sine
Sine wave: smooth continuous pulse between colors.
SKColor ResolvedForeColor
Gets the currently resolved (effective) foreground color used for drawing.
float PulsePeriodSec
Gets the pulse period in seconds.
TextBlock(RenderSurfaceHostBase renderSurfaceHost, SceneLayer sceneLayer, View? view, Rectangle worldBounds, string? nickname=null)
Creates a new TextBlock that draws in world space on the specified scene layer.
TextBlock SetColors(Color fg, Color bg)
Sets the foreground (text) and background colors using System.Drawing colors.
float ShadowBlurSigma
Gets the blur radius for the shadow (sigma, in pixels).
SKColor PulseFrom
Gets the starting color for the pulse effect.
VerticalAlign AlignVert
Gets the vertical alignment used when drawing lines.
float HorizontalPadding
Gets or sets the horizontal padding (in pixels) on both left and right sides of the text.
float PunctuationPauseLongSec
Gets the long punctuation pause duration (seconds) used for '.', '!', '?'.
TextBlock SetFont(SKTypeface typeface, float size, float? minSize=null)
Sets the font face and size (and optional minimum size for auto-shrink).
bool WrapText
Gets a value indicating whether word wrapping is enabled.
TextBlock(RenderSurfaceHostBase renderSurfaceHost, View view, Rectangle screenBounds, string? nickname=null)
Creates a new TextBlock that draws in screen space relative to a view.
SKColor ForeColor
Gets the configured foreground (text) color.
TextBlock EnableWrapping(bool enable=true)
Enables or disables word wrapping when laying out paragraphs.
TextBlock SetAlignment(SKTextAlign h, VerticalAlign v)
Sets the horizontal and vertical alignment used when drawing the laid-out lines within the bounds.
float? MinFontSize
Gets the optional minimum font size used by auto-shrink (or null if disabled).
TextBlock SetPadding(float horizontal, float vertical)
Sets symmetric horizontal and vertical padding and invalidates cached layout.
SKTypeface? TypeFace
Gets the configured typeface (or null to use SKTypeface.Default).
TextBlock StartWordReveal(float wordsPerSecond, bool enablePauses=true, float longPauseSec=0.25f, float shortPauseSec=0.10f)
Begins a word-by-word reveal. Words are detected by \S+\s* and include their trailing whitespace/punc...
VerticalAlign
Vertical alignment options for multi-line text within the control's bounds.
@ Bottom
Align text to the bottom of the inner content area.
@ Center
Center text vertically within the inner content area.
@ Top
Align text to the top of the inner content area.
SKColor BackColor
Gets the configured background color.
TextBlock UseShadow(bool enable=true)
Enables or disables drawing a drop shadow behind the text (parameters are configured via SetShadow(fl...
TextBlock SetColors(SKColor fg, SKColor bg)
Sets the foreground (text) and background colors using Skia colors.
float LineSpacingMultiplier
Gets or sets a scale factor applied to the computed line height (1.0 = natural spacing).
override void OnDraw(BackbufferBase backbuffer, RectangleF destRectScreen)
Draws the laid-out text block contents into the destination rectangle.
bool PulseTextEnabled
Gets a value indicating whether color pulsing is enabled.
float VerticalScrollEndPaddingPx
Gets or sets extra scrollable space after the final laid-out line.
Action< string >? TextRevealComplete
Raised once when the reveal completes. Argument is the full text content. Note: when the final chunk ...
override void Dispose(bool disposing)
Releases managed resources used by the text block.
TextBlock RevealSetCount(int charCount)
Sets the currently revealed character count directly (manual mode).
float FontSize
Gets the requested font size (before any auto-shrink adjustments).
PulseWave PulseWaveValue
Gets the pulse waveform currently in use.
byte ShadowAlpha
Gets the shadow opacity (0-255).
float MeasureMaximumVerticalScrollOffsetPx()
Measures the maximum vertical scroll offset required to expose all currently laid-out text.
bool OutlineEnabled
Gets a value indicating whether an outline stroke is enabled.
SKColor PulseTo
Gets the ending color for the pulse effect.
TextRevealMode TextRevealModeValue
Gets the active text reveal mode.
bool ShadowEnabled
Gets a value indicating whether a drop shadow is enabled.
int? MaxLines
Gets the optional maximum number of lines to draw (or null for no limit).
float ShadowDx
Gets the horizontal shadow offset (in pixels).
TextBlock SetPunctuationPauses(bool enabled, float longPauseSec=0.25f, float shortPauseSec=0.10f)
Configures punctuation pause behavior used by the current or next reveal animation.
float VerticalPadding
Gets or sets the vertical padding (in pixels) on both top and bottom sides of the text.
List< string > Lines
Gets the laid-out lines (after wrapping and paragraph processing).
TextBlock StopColorPulse()
Stops text color pulsing and restores the base foreground color.
float VerticalScrollOffsetPx
Gets or sets the vertical text-content scroll offset in native pixels.
TextRevealMode
Describes the mode used for revealing text over time.
@ CharactersPerSecond
Reveal advances by characters per second.
@ None
No reveal is active; all text is shown.
@ ManualCount
Reveal amount is controlled directly via RevealSetCount(int).
@ WordsPerSecond
Reveal advances by words per second.
TextBlock RevealStop()
Stops any active reveal animation and shows the full text immediately.
SKTextAlign AlignHoriz
Gets the horizontal alignment used when drawing lines.
Action< string >? TextRevealed
Raised whenever the revealed text portion advances. The argument is the cumulative text currently rev...
TextBlock SetText(string text)
Sets the current text content and rebuilds layout as needed. If no reveal animation is active,...
float LineHeight
Gets the measured line height (in pixels) used for drawing.
TextBlock PulseColor(Color from, Color to, float periodSec, bool enabled=true, bool triangle=false)
Animates the text color between from and to over periodSec seconds.
string Text
Gets the current raw text content.
TextBlock SetSize(Size size)
Sets the text-block size while preserving its current position and invalidates cached layout.
TextBlock SetMaxLines(int? maxLines)
Sets a maximum number of lines to draw. If null, all laid-out lines are drawn.
override void Update(long tick)
Advances internal animations (pulse, typewriter/word reveal) based on the current tick,...
float TextRevealRate
Gets the configured reveal rate (characters/sec or words/sec depending on mode).
bool PuctuationPauseEnabled
Gets a value indicating whether punctuation pauses are enabled.
TextBlock StartTypewriter(float charsPerSecond, int? maxChars=null, bool enablePauses=true, float longPauseSec=0.25f, float shortPauseSec=0.10f)
Begins a character-by-character reveal (typewriter effect).
Represents a base class for managing a graphical backbuffer, and is the in-memory surface where rende...
SKCanvas Canvas
Gets the SkiaSharp canvas used for drawing operations on this backbuffer.
Represents a base class for hosting a render surface, providing functionality for managing rendering ...
DirectDrawingMode
Specifies where direct drawing operations are applied within the rendering pipeline.
@ View
Performs direct drawing at the view level using screen-space coordinates.