@@ -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,25 @@ 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
+ /// Indicates whether semantics need to be reset.
297
+ /// </summary>
298
+ bool _hasResetSemantics = false ;
299
+
300
+ /// <summary>
301
+ /// The field sets and checks the text.
302
+ /// </summary>
303
+ string _text = string . Empty ;
304
+
305
+ /// <summary>
306
+ /// Gets the stroke color of the clear button
307
+ /// </summary>
308
+ static readonly Color ClearIconStrokeColor = Color . FromArgb ( "#49454F" ) ;
284
309
285
310
readonly EffectsRenderer _effectsRenderer ;
286
311
@@ -665,10 +690,119 @@ void OnPickerSelectedIndexChanged(object? sender, EventArgs e)
665
690
}
666
691
InvalidateDrawable ( ) ;
667
692
}
668
- #endregion
693
+
694
+ /// <summary>
695
+ /// Populates the list of numeric semantics nodes.
696
+ /// </summary>
697
+ /// <param name="content">The content object.</param>
698
+ void PopulateNumericSemanticsNodes ( object ? content )
699
+ {
700
+ _numericSemanticsNodes . Clear ( ) ;
701
+
702
+ switch ( content )
703
+ {
704
+ case SfNumericUpDown numericUpDown :
705
+ AddNumericUpDownNodes ( numericUpDown ) ;
706
+ break ;
707
+ case SfNumericEntry when IsClearIconVisible :
708
+ AddClearButtonNodes ( 2 ) ;
709
+ break ;
710
+ }
711
+ }
712
+
713
+ /// <summary>
714
+ /// Adds semantic nodes for the numeric up-down control.
715
+ /// </summary>
716
+ /// <param name="numericUpDown">The numeric up/down control.</param>
717
+ void AddNumericUpDownNodes ( SfNumericUpDown numericUpDown )
718
+ {
719
+ bool isUpEnabled = numericUpDown . AutoReverse || numericUpDown . _valueStates != ValueStates . Maximum ;
720
+ bool isDownEnabled = numericUpDown . AutoReverse || numericUpDown . _valueStates != ValueStates . Minimum ;
721
+ AddUpDownNodes ( numericUpDown , isUpEnabled , isDownEnabled ) ;
722
+ }
723
+
724
+ /// <summary>
725
+ /// Adds semantic nodes for up-down buttons.
726
+ /// </summary>
727
+ void AddUpDownNodes ( SfNumericUpDown numericUpDown , bool isUpEnabled , bool isDownEnabled )
728
+ {
729
+ bool isVerticalInline = numericUpDown . IsInlineVerticalPlacement ( ) ;
730
+ bool isLeftAlignment = numericUpDown . UpDownButtonAlignment == UpDownButtonAlignment . Left ;
731
+ bool addClearIconFirst = isVerticalInline ? ! isLeftAlignment : ! isVerticalInline && ! isLeftAlignment ;
732
+
733
+ if ( addClearIconFirst && IsClearIconVisible )
734
+ {
735
+ AddClearButtonNodes ( 2 ) ;
736
+ }
737
+
738
+ AddSemanticsNode ( isVerticalInline ? _downIconRectF : _upIconRectF , addClearIconFirst ? 3 : 2 , "Up button" , isUpEnabled ) ;
739
+ AddSemanticsNode ( isVerticalInline ? _upIconRectF : _downIconRectF , addClearIconFirst ? 4 : 3 , "Down button" , isDownEnabled ) ;
740
+
741
+ if ( ! addClearIconFirst && IsClearIconVisible )
742
+ {
743
+ AddClearButtonNodes ( 4 ) ;
744
+ }
745
+ }
746
+
747
+ /// <summary>
748
+ /// Adds a semantic node for clear button.
749
+ /// </summary>
750
+ void AddClearButtonNodes ( int index )
751
+ {
752
+ var tempBounds = _clearIconRectF ;
753
+ tempBounds . Width = tempBounds . Height = IconSize ;
754
+ AddSemanticsNode ( tempBounds , index , "Clear button" ) ;
755
+ }
756
+
757
+ /// <summary>
758
+ /// Adds a semantic node with specified properties.
759
+ /// </summary>
760
+ void AddSemanticsNode ( RectF bounds , int id , string description , bool isEnabled = true )
761
+ {
762
+ string stateDescription = isEnabled ? $ "{ description } , double tap to activate" : $ "{ description } , disabled";
763
+ if ( bounds . Width > 0 && bounds . Height > 0 )
764
+ {
765
+ _numericSemanticsNodes . Add ( CreateSemanticsNode ( id , new Rect ( bounds . X , bounds . Y , bounds . Width , bounds . Height ) , stateDescription ) ) ;
766
+ }
767
+ }
768
+
769
+ /// <summary>
770
+ /// Creates a semantic node with specified ID, bounds, and description.
771
+ /// </summary>
772
+ /// <param name="id">The ID of the semantics node.</param>
773
+ /// <param name="rect">The bounds of the node.</param>
774
+ /// <param name="description">The description associated with the node.</param>
775
+ /// <returns>A newly created SemanticsNode object.</returns>
776
+ SemanticsNode CreateSemanticsNode ( int id , Rect rect , string description ) =>
777
+ new SemanticsNode
778
+ {
779
+ Id = id ,
780
+ Bounds = rect ,
781
+ Text = description
782
+ } ;
783
+ #endregion
669
784
670
785
#region Override Methods
671
786
787
+ /// <summary>
788
+ /// Returns the semantics node list.
789
+ /// </summary>
790
+ /// <param name="width">The width of the element.</param>
791
+ /// <param name="height">The height of the element.</param>
792
+ /// <returns>A list of semantics nodes.</returns>
793
+ protected override List < SemanticsNode > GetSemanticsNodesCore ( double width , double height )
794
+ {
795
+ Size newControlSize = new ( Width , Height ) ;
796
+ if ( _controlSize == newControlSize && _textInputLayoutSemanticsNodes . Count != 0 )
797
+ {
798
+ return _textInputLayoutSemanticsNodes ;
799
+ }
800
+ _controlSize = newControlSize ;
801
+ PopulateNumericSemanticsNodes ( Content ) ;
802
+ _textInputLayoutSemanticsNodes . AddRange ( _numericSemanticsNodes ) ;
803
+ return _textInputLayoutSemanticsNodes ;
804
+ }
805
+
672
806
/// <summary>
673
807
/// Invoked when the size of the element is allocated.
674
808
/// </summary>
@@ -718,8 +852,11 @@ protected override void OnContentChanged(object oldValue, object newValue)
718
852
{
719
853
if ( numericEntryContent . Children [ 0 ] is Entry numericInputView )
720
854
{
721
- numericInputView . Opacity = IsHintFloated ? 1 : ( DeviceInfo . Platform == DevicePlatform . iOS ? 0.00001 : 0 ) ;
722
- AutomationProperties . SetIsInAccessibleTree ( numericInputView , false ) ; // Exclude numeric entry view from accessibility.
855
+ #if ANDROID || IOS
856
+ numericInputView . Opacity = IsHintFloated ? 1 : 0.00001 ;
857
+ #else
858
+ numericInputView . Opacity = IsHintFloated ? 1 : 0 ;
859
+ #endif
723
860
}
724
861
}
725
862
else if ( newValue is Picker picker )
@@ -728,7 +865,6 @@ protected override void OnContentChanged(object oldValue, object newValue)
728
865
{
729
866
picker . Opacity = IsHintFloated ? 1 : ( DeviceInfo . Platform == DevicePlatform . iOS ? 0.00001 : 0 ) ;
730
867
}
731
- AutomationProperties . SetIsInAccessibleTree ( picker , false ) ; // Exclude picker from accessibility.
732
868
}
733
869
734
870
base . OnContentChanged ( oldValue , newValue ) ;
@@ -737,8 +873,8 @@ protected override void OnContentChanged(object oldValue, object newValue)
737
873
{
738
874
OnEnabledPropertyChanged ( IsEnabled ) ;
739
875
}
740
-
741
876
SetCustomDescription ( newValue ) ;
877
+ ResetSemantics ( ) ;
742
878
}
743
879
744
880
/// <summary>
0 commit comments