24 private Color _backgroundColor = Color.FromArgb(255, 32, 32, 40);
25 private Color _borderColor = Color.FromArgb(255, 135, 135, 150);
26 private Color _focusedBorderColor = Color.FromArgb(255, 105, 155, 230);
27 private SKColor _textColor = SKColors.White;
28 private SKColor _placeholderColor =
new(170, 170, 180);
30 private string _text =
string.Empty;
31 private string _placeholder =
string.Empty;
32 private int _caretIndex;
33 private int? _maxLength;
34 private double _initialKeyRepeatDelaySeconds = 1.25;
35 private double _repeatedKeyIntervalSeconds = 0.05;
36 private int? _repeatingKey;
37 private long _keyPressedTick;
38 private long _lastRepeatTick;
39 private Func<WidgetKeyboardEventArgs, char?> _characterResolver = ResolveWindowsVirtualKeyCharacter;
64 string? placeholder =
null,
65 string? nickname =
null)
68 ArgumentNullException.ThrowIfNull(view);
70 _text = NormalizeText(text);
71 _placeholder = placeholder ??
string.Empty;
72 _caretIndex = _text.Length;
74 Background = CreateBackground(renderSurfaceHost, view, bounds);
75 TextBlock = CreateTextBlock(renderSurfaceHost, view, bounds);
80 CompleteInitialization();
96 string? placeholder =
null,
97 string? nickname =
null)
100 ArgumentNullException.ThrowIfNull(sceneLayer);
102 _text = NormalizeText(text);
103 _placeholder = placeholder ??
string.Empty;
104 _caretIndex = _text.Length;
106 Background = CreateBackground(renderSurfaceHost, sceneLayer, bounds);
107 TextBlock = CreateTextBlock(renderSurfaceHost, sceneLayer, bounds);
112 CompleteInitialization();
142 _placeholder = value ??
string.Empty;
143 RefreshDisplayedText();
155 _caretIndex = Math.Clamp(value, 0, _text.Length);
156 RefreshDisplayedText();
168 if (value.HasValue && value.Value < 0)
169 throw new ArgumentOutOfRangeException(nameof(value));
173 if (_maxLength.HasValue && _text.Length > _maxLength.Value)
174 SetText(_text[.._maxLength.Value]);
184 get => _initialKeyRepeatDelaySeconds;
187 if (!
double.IsFinite(value) || value < 0d)
188 throw new ArgumentOutOfRangeException(nameof(value));
190 _initialKeyRepeatDelaySeconds = value;
201 get => _repeatedKeyIntervalSeconds;
204 if (!
double.IsFinite(value) || value < 0d)
205 throw new ArgumentOutOfRangeException(nameof(value));
207 _repeatedKeyIntervalSeconds = value;
220 ? Background.ScreenBounds
263 get => _characterResolver;
264 set => _characterResolver = value ?? NoCharacterResolver;
274 string normalized = NormalizeText(text);
277 normalized = normalized[..
MaxLength.Value];
279 if (_text == normalized)
281 _caretIndex = Math.Min(_caretIndex, _text.Length);
282 RefreshDisplayedText();
287 _caretIndex = _text.Length;
288 RefreshDisplayedText();
303 string insertion = NormalizeText(text);
304 if (insertion.Length == 0)
309 int available = MaxLength.Value - _text.Length;
313 if (insertion.Length > available)
314 insertion = insertion[..available];
317 _text = _text.Insert(_caretIndex, insertion);
318 _caretIndex += insertion.Length;
319 RefreshDisplayedText();
330 if (
IsReadOnly || _caretIndex <= 0 || _text.Length == 0)
333 _text = _text.
Remove(_caretIndex - 1, 1);
335 RefreshDisplayedText();
346 if (
IsReadOnly || _caretIndex < 0 || _caretIndex >= _text.Length)
349 _text = _text.
Remove(_caretIndex, 1);
350 RefreshDisplayedText();
362 CaretIndex = Math.Clamp(_caretIndex + delta, 0, _text.Length);
377 _backgroundColor = background;
378 _borderColor = border;
379 _focusedBorderColor = focusedBorder;
382 .SetBorderColor(
IsFocused ? _focusedBorderColor : _borderColor);
396 _placeholderColor = placeholder;
397 RefreshDisplayedText();
408 Background.ZOrder = zOrder;
416 base.OnPointerClick(args);
421 _caretIndex = _text.Length;
422 RefreshDisplayedText();
429 base.OnFocusGained();
430 Background.SetBorderColor(_focusedBorderColor);
432 RefreshDisplayedText();
439 ResetKeyRepeatState();
442 RefreshDisplayedText();
448 base.OnKeyboardInput(args);
452 if (_repeatingKey == args.
Key)
453 ResetKeyRepeatState();
461 long currentTick = HighResTimer.GetCurrentTick();
465 _repeatingKey = args.
Key;
466 _keyPressedTick = currentTick;
467 _lastRepeatTick = currentTick;
471 if (_repeatingKey != args.
Key ||
472 _keyPressedTick == 0 ||
473 HighResTimer.GetDuration(_keyPressedTick, currentTick) < _initialKeyRepeatDelaySeconds ||
474 (_lastRepeatTick != 0 &&
475 HighResTimer.GetDuration(_lastRepeatTick, currentTick) < _repeatedKeyIntervalSeconds))
481 _lastRepeatTick = currentTick;
540 if (!character.HasValue)
547 private void ResetKeyRepeatState()
549 _repeatingKey =
null;
554 private void CompleteInitialization()
559 RefreshDisplayedText();
562 private void RefreshDisplayedText()
566 string withCaret = _text.Insert(_caretIndex,
"|");
568 .
SetColors(_textColor, SKColors.Transparent);
572 bool showPlaceholder = _text.Length == 0 && _placeholder.Length > 0;
574 .
SetColors(showPlaceholder ? _placeholderColor : _textColor, SKColors.Transparent);
577 private DirectRectangle CreateBackground(RenderSurfaceHostBase host,
581 return ConfigureBackground(
new DirectRectangle(_backgroundColor, host, view, bounds));
584 private DirectRectangle CreateBackground(RenderSurfaceHostBase host,
588 return ConfigureBackground(
new DirectRectangle(_backgroundColor, host, layer, bounds));
591 private DirectRectangle ConfigureBackground(DirectRectangle background)
599 private static TextBlock CreateTextBlock(RenderSurfaceHostBase host,
603 return ConfigureTextBlock(
new TextBlock(host, view, bounds));
606 private static TextBlock CreateTextBlock(RenderSurfaceHostBase host,
610 return ConfigureTextBlock(
new TextBlock(host, layer, view:
null, worldBounds: bounds));
615 return textBlock.
SetFont(SKTypeface.Default, 16f, minSize: 10f)
616 .
SetColors(SKColors.White, SKColors.Transparent)
622 private static string NormalizeText(
string? text)
624 return (text ??
string.Empty)
629 private static char? NoCharacterResolver(WidgetKeyboardEventArgs args)
634 private static char? ResolveWindowsVirtualKeyCharacter(WidgetKeyboardEventArgs args)
642 if (key is >= 65 and <= 90)
644 char letter = (char)key;
645 return shift ? letter :
char.ToLowerInvariant(letter);
648 if (key is >= 48 and <= 57)
653 const string shiftedDigits =
")!@#$%^&*(";
654 return shiftedDigits[key - 48];
657 if (key is >= 96 and <= 105)
658 return (
char)(
'0' + key - 96);
668 186 => shift ?
':' :
';',
669 187 => shift ?
'+' :
'=',
670 188 => shift ?
'<' :
',',
671 189 => shift ?
'_' :
'-',
672 190 => shift ?
'>' :
'.',
673 191 => shift ?
'?' :
'/',
674 192 => shift ?
'~' :
'`',
675 219 => shift ?
'{' :
'[',
676 220 => shift ?
'|' :
'\\',
677 221 => shift ?
'}' :
']',
678 222 => shift ?
'"' :
'\'',
683 private static void Refresh(DirectRectangle rectangle)
688 private static Rectangle ValidateBounds(Rectangle bounds)
690 if (bounds.Width < 40 || bounds.Height < 18)
691 throw new ArgumentOutOfRangeException(nameof(bounds), bounds,
"Text-box bounds are too small.");
virtual DirectComposite Remove(IDirectCompositeChild child)
Removes a child without disposing it.
SceneLayer? SceneLayer
Gets the scene layer shared by all children in scene-layer mode.
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.
DirectDrawingMode Mode
Gets the coordinate mode shared by the composite and its children.
int ZOrder
Gets or sets the Z-order (depth) used for sorting this direct drawing relative to other drawables.
Vector2 GetPosition()
Gets the current position of this direct drawing in pixel coordinates.
void SetPosition(Vector2 p)
Sets the position of this direct drawing to the specified pixel coordinates.
Renders a configurable rectangle with support for fill, border, rounded corners, dash patterns,...
DirectRectangle SetFilled(bool isFilled)
Sets whether the rectangle interior is filled with the base color (or pattern).
DirectRectangle SetBorderColor(Color color)
Sets a distinct border (stroke) color, independent of the fill color.
DirectRectangle SetStrokeWidth(float width)
Sets the width (thickness) of the rectangle's stroke (outline) in pixels.
DirectRectangle SetCornerRadius(float radius)
Sets the radius for rounded corners in pixels.
A retained-mode text drawable with wrapping, multi-line ( ) support, horizontal/vertical alignment,...
TextBlock SetFont(SKTypeface typeface, float size, float? minSize=null)
Sets the font face and size (and optional minimum size for auto-shrink).
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.
TextBlock SetPadding(float horizontal, float vertical)
Sets symmetric horizontal and vertical padding and invalidates cached layout.
TextBlock SetColors(SKColor fg, SKColor bg)
Sets the foreground (text) and background colors using Skia colors.
TextBlock SetText(string text)
Sets the current text content and rebuilds layout as needed. If no reveal animation is active,...
Represents a base class for hosting a render surface, providing functionality for managing rendering ...
override void OnPointerClick(WidgetPointerEventArgs args)
Called when a pointer click is dispatched to the widget.
Action< string >? TextChanged
Occurs when the text changes.
TextBoxWidget InsertText(string? text)
Inserts text at the current caret position.
double InitialKeyRepeatDelaySeconds
Gets or sets the delay, in seconds, before a held key begins repeating. Pressed events remain immedia...
override void OnFocusLost()
Called when the widget loses focus.
TextBoxWidget SetText(string? text)
Replaces the text and moves the caret to the end.
Func< WidgetKeyboardEventArgs, char?> CharacterResolver
Gets or sets the function used to convert routed key events into printable characters.
TextBoxWidget SetTextColors(SKColor text, SKColor placeholder)
Sets the text and placeholder colors.
TextBoxWidget MoveCaret(int delta)
Moves the caret by the specified character delta.
override void OnKeyboardInput(WidgetKeyboardEventArgs args)
Called when keyboard input is routed to the widget.
TextBoxWidget Backspace()
Removes the character before the caret when possible.
string Placeholder
Gets or sets placeholder text shown while the control is empty and unfocused.
int RightKey
Gets or sets the key code used to move right. Defaults to 39.
string Text
Gets or sets the current text.
int LeftKey
Gets or sets the key code used to move left. Defaults to 37.
TextBoxWidget(RenderSurfaceHostBase renderSurfaceHost, View view, Rectangle bounds, string text="", string? placeholder=null, string? nickname=null)
Creates a view-level text box.
Action< string >? Submitted
Occurs when the configured submit key is pressed.
TextBoxWidget Delete()
Removes the character at the caret when possible.
bool IsReadOnly
Gets or sets whether user editing is disabled.
TextBoxWidget SetColors(Color background, Color border, Color focusedBorder)
Sets the background and border colors used for normal and focused states.
int EndKey
Gets or sets the key code used to move to the end. Defaults to 35.
DirectRectangle Background
Gets the text-box background and border drawing.
int BackspaceKey
Gets or sets the key code used for backspace. Defaults to 8.
TextBlock TextBlock
Gets the text drawing used by the text box.
override void OnFocusGained()
Called when the widget gains focus.
Rectangle Bounds
Gets the text-box bounds in its native coordinate space.
int? MaxLength
Gets or sets the maximum permitted text length. A null value means unlimited.
int DeleteKey
Gets or sets the key code used for Delete. Defaults to 46.
int SubmitKey
Gets or sets the key code used to submit the text. Defaults to Enter (13).
double RepeatedKeyIntervalSeconds
Gets or sets the minimum interval, in seconds, between repeated held-key actions after InitialKeyRepe...
TextBoxWidget(RenderSurfaceHostBase renderSurfaceHost, SceneLayer sceneLayer, Rectangle bounds, string text="", string? placeholder=null, string? nickname=null)
Creates a scene-layer text box.
int CaretIndex
Gets or sets the caret position in the range zero through Text length.
int HomeKey
Gets or sets the key code used to move to the beginning. Defaults to 36.
TextBoxWidget SetTextBoxZOrder(int zOrder)
Sets the base Z-order used by the text-box visuals.
@ Rectangle
Particles are spawned uniformly within a rectangular area. This is the default distribution.
DirectDrawingMode
Specifies where direct drawing operations are applied within the rendering pipeline.
@ View
Performs direct drawing at the view level using screen-space coordinates.