@@ -226,11 +226,6 @@ public partial class SfTextInputLayout : SfContentView, ITouchListener, IParentT
226
226
internal bool IsIconPressed { get ; private set ; } = false ;
227
227
#endif
228
228
229
- /// <summary>
230
- /// Indicates if the description has been set by the user.
231
- /// </summary>
232
- bool _isDescriptionNotSetByUser ;
233
-
234
229
/// <summary>
235
230
/// Gets or sets a value indicating whether the layout has been tapped.
236
231
/// </summary>
@@ -348,7 +343,9 @@ public partial class SfTextInputLayout : SfContentView, ITouchListener, IParentT
348
343
UIKit . UITextField ? uiEntry ;
349
344
#endif
350
345
351
- #endregion
346
+ private string _initialContentDescription = string . Empty ;
347
+
348
+ #endregion
352
349
353
350
#region Constructor
354
351
@@ -668,210 +665,6 @@ void OnPickerSelectedIndexChanged(object? sender, EventArgs e)
668
665
669
666
#region Override Methods
670
667
671
- /// <summary>
672
- /// The list to add the bounds values of drawing elements as nodes.
673
- /// </summary>
674
- private readonly List < SemanticsNode > textInputLayoutSemanticsNodes = new ( ) ;
675
-
676
- /// <summary>
677
- /// List of semantic nodes for numeric entry.
678
- /// </summary>
679
- private readonly List < SemanticsNode > numericSemanticsNodes = new ( ) ;
680
-
681
- /// <summary>
682
- /// Returns the semantics node list.
683
- /// </summary>
684
- /// <param name="width">The width of the element.</param>
685
- /// <param name="height">The height of the element.</param>
686
- /// <returns>A list of semantics nodes.</returns>
687
- protected override List < SemanticsNode > GetSemanticsNodesCore ( double width , double height )
688
- {
689
- textInputLayoutSemanticsNodes . Clear ( ) ;
690
-
691
- if ( width > 0 && height > 0 )
692
- {
693
- AddLayoutNode ( ) ;
694
- }
695
-
696
- PopulateNumericSemanticsNodes ( Content ) ;
697
- textInputLayoutSemanticsNodes . AddRange ( numericSemanticsNodes ) ;
698
-
699
- if ( ShowHelperText && ! string . IsNullOrEmpty ( HelperText ) && _helperTextRect . Width > 0 && _helperTextRect . Height > 0 )
700
- {
701
- AddHelperTextNode ( ) ;
702
- }
703
-
704
- return textInputLayoutSemanticsNodes ;
705
- }
706
-
707
- /// <summary>
708
- /// Retrieves the user-defined semantic description.
709
- /// </summary>
710
- /// <returns>The semantic description if available; otherwise, an empty string.</returns>
711
- private string GetUserDescription ( )
712
- {
713
- var description = SemanticProperties . GetDescription ( this ) ;
714
- IsDescriptionNotSetByUser = string . IsNullOrEmpty ( description ) ;
715
- return description ?? string . Empty ;
716
- }
717
-
718
- /// <summary>
719
- /// Adds a semantic node for the text input layout.
720
- /// </summary>
721
- private void AddLayoutNode ( )
722
- {
723
- string contentDescription = GetContentDescription ( Content ) ;
724
- string userDescription = GetUserDescription ( ) ;
725
- string hintText = IsDescriptionNotSetByUser && ! string . IsNullOrEmpty ( Hint ) ? Hint : userDescription ;
726
-
727
- var layoutNode = CreateSemanticsNode (
728
- 1 ,
729
- new Rect ( 0 , 0 , _backgroundRectF . Width , _backgroundRectF . Height ) ,
730
- $ "{ hintText } , { contentDescription } "
731
- ) ;
732
- textInputLayoutSemanticsNodes . Add ( layoutNode ) ;
733
- }
734
-
735
- /// <summary>
736
- /// Adds a semantic node for the helper text.
737
- /// </summary>
738
- private void AddHelperTextNode ( )
739
- {
740
- var helperTextNode = CreateSemanticsNode ( 5 , _helperTextRect , HelperText ) ;
741
- textInputLayoutSemanticsNodes . Add ( helperTextNode ) ;
742
- }
743
-
744
- /// <summary>
745
- /// Retrieves the content description based on the provided content type.
746
- /// </summary>
747
- /// <param name="content">The content object.</param>
748
- /// <returns>A string containing the content description.</returns>
749
- private static string GetContentDescription ( object ? content ) =>
750
- content switch
751
- {
752
- InputView entryEditorContent => GetDescription ( entryEditorContent . IsFocused , entryEditorContent . Text , entryEditorContent . Placeholder ) ,
753
- SfView numericEntryContent when numericEntryContent . Children . FirstOrDefault ( ) is Entry numericInputView => GetDescription ( numericInputView . IsFocused , numericInputView . Text , numericInputView . Placeholder ) ,
754
- Picker picker => GetDescription ( picker . IsFocused , GetSelectedItemText ( picker ) , "" ) ,
755
- _ => string . Empty
756
- } ;
757
-
758
- /// <summary>
759
- /// Generates a formatted description based on the text, placeholder, and focus state.
760
- /// </summary>
761
- /// <param name="isFocused">Indicates if the element is focused.</param>
762
- /// <param name="text">The text content.</param>
763
- /// <param name="placeholder">The placeholder text.</param>
764
- /// <returns>A formatted string for semantic description.</returns>
765
- private static string GetDescription ( bool isFocused , string ? text , string ? placeholder )
766
- {
767
- string formattedText = string . IsNullOrEmpty ( text ) ? placeholder ?? string . Empty :
768
- text . EndsWith ( "." ) ? text : text + "." ;
769
-
770
- if ( isFocused )
771
- {
772
- return formattedText ;
773
- }
774
-
775
- return $ "{ formattedText } \n \n \n \n \n \n \n \n \n Double-tap to edit text.";
776
- }
777
-
778
- /// <summary>
779
- /// Retrieves the selected item text from a picker.
780
- /// </summary>
781
- /// <param name="picker">The picker control.</param>
782
- /// <returns>The selected item text.</returns>
783
- private static string ? GetSelectedItemText ( Picker picker )
784
- {
785
- if ( picker . SelectedItem == null )
786
- return null ;
787
-
788
- if ( picker . ItemDisplayBinding is Binding binding && binding . Path != null )
789
- {
790
- var property = picker . SelectedItem . GetType ( ) . GetProperty ( binding . Path ) ;
791
- return property ? . GetValue ( picker . SelectedItem ) ? . ToString ( ) ;
792
- }
793
-
794
- return picker . SelectedItem . ToString ( ) ;
795
- }
796
-
797
- /// <summary>
798
- /// Populates the list of numeric semantics nodes.
799
- /// </summary>
800
- /// <param name="content">The content object.</param>
801
- private void PopulateNumericSemanticsNodes ( object ? content )
802
- {
803
- numericSemanticsNodes . Clear ( ) ;
804
-
805
- switch ( content )
806
- {
807
- case SfNumericUpDown numericUpDown :
808
- AddNumericUpDownNodes ( numericUpDown ) ;
809
- break ;
810
- case SfNumericEntry when IsClearIconVisible :
811
- AddSemanticsNode ( _clearIconRectF , 2 , "Clear button" ) ;
812
- break ;
813
- }
814
- }
815
-
816
- /// <summary>
817
- /// Adds semantic nodes for the numeric up-down control.
818
- /// </summary>
819
- /// <param name="numericUpDown">The numeric up/down control.</param>
820
- private void AddNumericUpDownNodes ( SfNumericUpDown numericUpDown )
821
- {
822
- bool isUpEnabled = numericUpDown . AutoReverse || numericUpDown . _valueStates != ValueStates . Maximum ;
823
- bool isDownEnabled = numericUpDown . AutoReverse || numericUpDown . _valueStates != ValueStates . Minimum ;
824
- AddUpDownNodes ( numericUpDown , isUpEnabled , isDownEnabled ) ;
825
- }
826
-
827
- /// <summary>
828
- /// Adds semantic nodes for up-down buttons.
829
- /// </summary>
830
- private void AddUpDownNodes ( SfNumericUpDown numericUpDown , bool isUpEnabled , bool isDownEnabled )
831
- {
832
- bool isVerticalInline = numericUpDown . IsInlineVerticalPlacement ( ) ;
833
- bool isLeftAlignment = numericUpDown . UpDownButtonAlignment == UpDownButtonAlignment . Left ;
834
- bool addClearIconFirst = isVerticalInline ? ! isLeftAlignment : ! isVerticalInline && ! isLeftAlignment ;
835
-
836
- if ( addClearIconFirst && IsClearIconVisible )
837
- {
838
- AddSemanticsNode ( _clearIconRectF , 2 , "Clear button" ) ;
839
- }
840
- AddSemanticsNode ( _upIconRectF , addClearIconFirst ? 3 : 2 , "Up button" , isUpEnabled ) ;
841
- AddSemanticsNode ( _downIconRectF , addClearIconFirst ? 4 : 3 , "Down button" , isDownEnabled ) ;
842
- if ( ! addClearIconFirst && IsClearIconVisible )
843
- {
844
- AddSemanticsNode ( _clearIconRectF , 4 , "Clear button" ) ;
845
- }
846
- }
847
-
848
- /// <summary>
849
- /// Creates a semantic node with specified ID, bounds, and description.
850
- /// </summary>
851
- /// <param name="id">The ID of the semantics node.</param>
852
- /// <param name="rect">The bounds of the node.</param>
853
- /// <param name="description">The description associated with the node.</param>
854
- /// <returns>A newly created SemanticsNode object.</returns>
855
- private SemanticsNode CreateSemanticsNode ( int id , Rect rect , string description ) =>
856
- new SemanticsNode
857
- {
858
- Id = id ,
859
- Bounds = rect ,
860
- Text = description
861
- } ;
862
-
863
- /// <summary>
864
- /// Adds a semantic node with specified properties.
865
- /// </summary>
866
- private void AddSemanticsNode ( RectF bounds , int id , string description , bool isEnabled = true )
867
- {
868
- string stateDescription = isEnabled ? $ "{ description } , double tap to activate" : $ "{ description } , disabled";
869
- if ( bounds . Width > 0 && bounds . Height > 0 )
870
- {
871
- numericSemanticsNodes . Add ( CreateSemanticsNode ( id , new Rect ( bounds . X , bounds . Y , bounds . Width , bounds . Height ) , stateDescription ) ) ;
872
- }
873
- }
874
-
875
668
/// <summary>
876
669
/// Invoked when the size of the element is allocated.
877
670
/// </summary>
@@ -911,8 +704,12 @@ protected override void OnContentChanged(object oldValue, object newValue)
911
704
//Adjusted Opacity from 0 to 0.00001 to ensure the content remains functionally active while enabling the ReturnType property.
912
705
if ( newValue is InputView entryEditorContent )
913
706
{
914
- entryEditorContent . Opacity = IsHintFloated ? 1 : ( DeviceInfo . Platform == DevicePlatform . iOS ? 0.00001 : 0 ) ;
915
- AutomationProperties . SetIsInAccessibleTree ( entryEditorContent , false ) ; // Exclude entry content from accessibility.
707
+ #if ANDROID || IOS
708
+ entryEditorContent . Opacity = IsHintFloated ? 1 : 0.00001 ;
709
+ #else
710
+ entryEditorContent . Opacity = IsHintFloated ? 1 : 0 ;
711
+ #endif
712
+ _initialContentDescription = SemanticProperties . GetDescription ( entryEditorContent ) ;
916
713
}
917
714
else if ( newValue is SfView numericEntryContent && numericEntryContent . Children . Count > 0 )
918
715
{
@@ -937,8 +734,63 @@ protected override void OnContentChanged(object oldValue, object newValue)
937
734
{
938
735
OnEnabledPropertyChanged ( IsEnabled ) ;
939
736
}
737
+
738
+ SetCustomDescription ( newValue ) ;
940
739
}
941
740
741
+ /// <summary>
742
+ /// Sets a custom semantic description for the content.
743
+ /// </summary>
744
+ private void SetCustomDescription ( object content )
745
+ {
746
+
747
+ if ( this . Content == null || content == null )
748
+ return ;
749
+
750
+ var customDescription = string . Empty ;
751
+ #if ANDROID || MACCATALYST || IOS
752
+
753
+ if ( content is InputView entryEditorContent )
754
+ {
755
+ customDescription = ( string . IsNullOrEmpty ( entryEditorContent . Text ) && ! string . IsNullOrEmpty ( entryEditorContent . Placeholder ) && DeviceInfo . Platform == DevicePlatform . Android ) ? entryEditorContent . Placeholder : string . Empty ;
756
+ }
757
+
758
+ var layoutDescription = GetLayoutDescription ( ) ;
759
+
760
+ var contentDescription = layoutDescription + ( IsHintFloated ? customDescription + _initialContentDescription : string . Empty ) ;
761
+
762
+ SemanticProperties . SetDescription ( this . Content , contentDescription ) ;
763
+ #elif WINDOWS
764
+
765
+ customDescription = SemanticProperties . GetDescription ( this ) ;
766
+
767
+ if ( string . IsNullOrEmpty ( customDescription ) )
768
+ {
769
+ customDescription = ShowHint && ! string . IsNullOrEmpty ( Hint ) ? Hint : string . Empty ;
770
+ SemanticProperties . SetDescription ( this , customDescription ) ;
771
+ }
772
+ #endif
773
+ }
774
+
775
+ #if ANDROID || MACCATALYST || IOS
776
+ /// <summary>
777
+ /// Retrieves the layout semantic description.
778
+ /// </summary>
779
+ private string GetLayoutDescription ( )
780
+ {
781
+ var description = SemanticProperties . GetDescription ( this ) ;
782
+
783
+ if ( string . IsNullOrEmpty ( description ) )
784
+ {
785
+ description = ShowHint && ! string . IsNullOrEmpty ( Hint ) ? Hint : string . Empty ;
786
+ }
787
+ if ( ! string . IsNullOrEmpty ( description ) )
788
+ {
789
+ description = description . EndsWith ( "." ) ? description : description + ". " ;
790
+ }
791
+ return description ?? string . Empty ;
792
+ }
793
+ #endif
942
794
/// <summary>
943
795
/// Measures the size requirements for the content of the element.
944
796
/// </summary>
0 commit comments