@@ -179,10 +179,20 @@ public partial class SfTextInputLayout : SfContentView, ITouchListener, IParentT
179
179
/// </summary>
180
180
readonly double _defaultLeadAndTrailViewWidth = 24 ;
181
181
182
- /// <summary>
183
- /// Gets or sets a value of leading view width.
184
- /// </summary>
185
- double _leadViewWidth = 0 ;
182
+ /// <summary>
183
+ /// The list to add the bounds values of drawing elements as nodes.
184
+ /// </summary>
185
+ readonly List < SemanticsNode > _textInputLayoutSemanticsNodes = new ( ) ;
186
+
187
+ /// <summary>
188
+ /// List of semantic nodes for numeric entry.
189
+ /// </summary>
190
+ readonly List < SemanticsNode > _numericSemanticsNodes = new ( ) ;
191
+
192
+ /// <summary>
193
+ /// Gets or sets a value of leading view width.
194
+ /// </summary>
195
+ double _leadViewWidth = 0 ;
186
196
187
197
/// <summary>
188
198
/// Gets or sets a value of trailing view width.
@@ -277,10 +287,20 @@ public partial class SfTextInputLayout : SfContentView, ITouchListener, IParentT
277
287
/// </summary>
278
288
float _fontsize = DefaultHintFontSize ;
279
289
280
- /// <summary>
281
- /// Gets the stroke color of the clear button
282
- /// </summary>
283
- static readonly Color ClearIconStrokeColor = Color . FromArgb ( "#49454F" ) ;
290
+ /// <summary>
291
+ /// The field sets and checks the new size of the drawn elements.
292
+ /// </summary>
293
+ Size _controlSize = Size . Zero ;
294
+
295
+ /// <summary>
296
+ /// The field sets and checks the text.
297
+ /// </summary>
298
+ string _text = string . Empty ;
299
+
300
+ /// <summary>
301
+ /// Gets the stroke color of the clear button
302
+ /// </summary>
303
+ static readonly Color ClearIconStrokeColor = Color . FromArgb ( "#49454F" ) ;
284
304
285
305
readonly EffectsRenderer _effectsRenderer ;
286
306
@@ -665,10 +685,109 @@ void OnPickerSelectedIndexChanged(object? sender, EventArgs e)
665
685
}
666
686
InvalidateDrawable ( ) ;
667
687
}
668
- #endregion
688
+
689
+ /// <summary>
690
+ /// Populates the list of numeric semantics nodes.
691
+ /// </summary>
692
+ /// <param name="content">The content object.</param>
693
+ void PopulateNumericSemanticsNodes ( object ? content )
694
+ {
695
+ _numericSemanticsNodes . Clear ( ) ;
696
+
697
+ switch ( content )
698
+ {
699
+ case SfNumericUpDown numericUpDown :
700
+ AddNumericUpDownNodes ( numericUpDown ) ;
701
+ break ;
702
+ case SfNumericEntry when IsClearIconVisible :
703
+ AddSemanticsNode ( _clearIconRectF , 2 , "Clear button" ) ;
704
+ break ;
705
+ }
706
+ }
707
+
708
+ /// <summary>
709
+ /// Adds semantic nodes for the numeric up-down control.
710
+ /// </summary>
711
+ /// <param name="numericUpDown">The numeric up/down control.</param>
712
+ void AddNumericUpDownNodes ( SfNumericUpDown numericUpDown )
713
+ {
714
+ bool isUpEnabled = numericUpDown . AutoReverse || numericUpDown . _valueStates != ValueStates . Maximum ;
715
+ bool isDownEnabled = numericUpDown . AutoReverse || numericUpDown . _valueStates != ValueStates . Minimum ;
716
+ AddUpDownNodes ( numericUpDown , isUpEnabled , isDownEnabled ) ;
717
+ }
718
+
719
+ /// <summary>
720
+ /// Adds semantic nodes for up-down buttons.
721
+ /// </summary>
722
+ void AddUpDownNodes ( SfNumericUpDown numericUpDown , bool isUpEnabled , bool isDownEnabled )
723
+ {
724
+ bool isVerticalInline = numericUpDown . IsInlineVerticalPlacement ( ) ;
725
+ bool isLeftAlignment = numericUpDown . UpDownButtonAlignment == UpDownButtonAlignment . Left ;
726
+ bool addClearIconFirst = isVerticalInline ? ! isLeftAlignment : ! isVerticalInline && ! isLeftAlignment ;
727
+
728
+ if ( addClearIconFirst && IsClearIconVisible )
729
+ {
730
+ AddSemanticsNode ( _clearIconRectF , 2 , "Clear button" ) ;
731
+ }
732
+
733
+ AddSemanticsNode ( isVerticalInline ? _downIconRectF : _upIconRectF , addClearIconFirst ? 3 : 2 , "Up button" , isUpEnabled ) ;
734
+ AddSemanticsNode ( isVerticalInline ? _upIconRectF : _downIconRectF , addClearIconFirst ? 4 : 3 , "Down button" , isDownEnabled ) ;
735
+
736
+ if ( ! addClearIconFirst && IsClearIconVisible )
737
+ {
738
+ AddSemanticsNode ( _clearIconRectF , 4 , "Clear button" ) ;
739
+ }
740
+ }
741
+
742
+ /// <summary>
743
+ /// Adds a semantic node with specified properties.
744
+ /// </summary>
745
+ void AddSemanticsNode ( RectF bounds , int id , string description , bool isEnabled = true )
746
+ {
747
+ string stateDescription = isEnabled ? $ "{ description } , double tap to activate" : $ "{ description } , disabled";
748
+ if ( bounds . Width > 0 && bounds . Height > 0 )
749
+ {
750
+ _numericSemanticsNodes . Add ( CreateSemanticsNode ( id , new Rect ( bounds . X , bounds . Y , bounds . Width , bounds . Height ) , stateDescription ) ) ;
751
+ }
752
+ }
753
+
754
+ /// <summary>
755
+ /// Creates a semantic node with specified ID, bounds, and description.
756
+ /// </summary>
757
+ /// <param name="id">The ID of the semantics node.</param>
758
+ /// <param name="rect">The bounds of the node.</param>
759
+ /// <param name="description">The description associated with the node.</param>
760
+ /// <returns>A newly created SemanticsNode object.</returns>
761
+ SemanticsNode CreateSemanticsNode ( int id , Rect rect , string description ) =>
762
+ new SemanticsNode
763
+ {
764
+ Id = id ,
765
+ Bounds = rect ,
766
+ Text = description
767
+ } ;
768
+ #endregion
669
769
670
770
#region Override Methods
671
771
772
+ /// <summary>
773
+ /// Returns the semantics node list.
774
+ /// </summary>
775
+ /// <param name="width">The width of the element.</param>
776
+ /// <param name="height">The height of the element.</param>
777
+ /// <returns>A list of semantics nodes.</returns>
778
+ protected override List < SemanticsNode > GetSemanticsNodesCore ( double width , double height )
779
+ {
780
+ Size newControlSize = new ( Width , Height ) ;
781
+ if ( _controlSize == newControlSize && _textInputLayoutSemanticsNodes . Count != 0 )
782
+ {
783
+ return _textInputLayoutSemanticsNodes ;
784
+ }
785
+ _controlSize = newControlSize ;
786
+ PopulateNumericSemanticsNodes ( Content ) ;
787
+ _textInputLayoutSemanticsNodes . AddRange ( _numericSemanticsNodes ) ;
788
+ return _textInputLayoutSemanticsNodes ;
789
+ }
790
+
672
791
/// <summary>
673
792
/// Invoked when the size of the element is allocated.
674
793
/// </summary>
@@ -718,8 +837,11 @@ protected override void OnContentChanged(object oldValue, object newValue)
718
837
{
719
838
if ( numericEntryContent . Children [ 0 ] is Entry numericInputView )
720
839
{
721
- numericInputView . Opacity = IsHintFloated ? 1 : ( DeviceInfo . Platform == DevicePlatform . iOS ? 0.00001 : 0 ) ;
722
- AutomationProperties . SetIsInAccessibleTree ( numericInputView , false ) ; // Exclude numeric entry view from accessibility.
840
+ #if ANDROID || IOS
841
+ numericInputView . Opacity = IsHintFloated ? 1 : 0.00001 ;
842
+ #else
843
+ numericInputView . Opacity = IsHintFloated ? 1 : 0 ;
844
+ #endif
723
845
}
724
846
}
725
847
else if ( newValue is Picker picker )
@@ -728,7 +850,6 @@ protected override void OnContentChanged(object oldValue, object newValue)
728
850
{
729
851
picker . Opacity = IsHintFloated ? 1 : ( DeviceInfo . Platform == DevicePlatform . iOS ? 0.00001 : 0 ) ;
730
852
}
731
- AutomationProperties . SetIsInAccessibleTree ( picker , false ) ; // Exclude picker from accessibility.
732
853
}
733
854
734
855
base . OnContentChanged ( oldValue , newValue ) ;
@@ -737,8 +858,8 @@ protected override void OnContentChanged(object oldValue, object newValue)
737
858
{
738
859
OnEnabledPropertyChanged ( IsEnabled ) ;
739
860
}
740
-
741
861
SetCustomDescription ( newValue ) ;
862
+ ResetSemantics ( ) ;
742
863
}
743
864
744
865
/// <summary>
0 commit comments