diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellContentFragment.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellContentFragment.cs index 9659b08ac823..f0967033417c 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellContentFragment.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellContentFragment.cs @@ -170,9 +170,9 @@ void Destroy() { if (_destroyed) return; - + _destroyed = true; - + // If the user taps very quickly on back button multiple times to pop a page, // the app enters background state in the middle of the animation causing the fragment to be destroyed without completing the animation. // That'll cause `IAnimationListener.onAnimationEnd` to not be called, so we need to call it manually if something is still subscribed to the event diff --git a/src/Controls/tests/DeviceTests/Elements/Shell/ShellTests.Android.cs b/src/Controls/tests/DeviceTests/Elements/Shell/ShellTests.Android.cs index ed0217798242..3494112c7c83 100644 --- a/src/Controls/tests/DeviceTests/Elements/Shell/ShellTests.Android.cs +++ b/src/Controls/tests/DeviceTests/Elements/Shell/ShellTests.Android.cs @@ -534,7 +534,7 @@ public async Task ShellContentFragmentDestroyHandlesNullShellContext() }, new ShellContent() { - Route = "Item2", + Route = "Item2", Content = new ContentPage { Title = "Page 2" } }, } @@ -555,12 +555,12 @@ await CreateHandlerAndAddToWindow(shell, async (handler) => await OnNavigatedToAsync(shell.CurrentPage); // Test null context scenario - var exception = Record.Exception(() => + var exception = Record.Exception(() => { // Create fragment with null context - this should not throw Page page = new ContentPage(); var fragment = new ShellContentFragment((IShellContext)null, page); - + // Dispose the fragment which calls Destroy internally // This validates the null-conditional operators in Destroy method fragment.Dispose(); diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Editor/EditorControlPage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Editor/EditorControlPage.xaml.cs index 0775877383cd..51c7df18214f 100644 --- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Editor/EditorControlPage.xaml.cs +++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Editor/EditorControlPage.xaml.cs @@ -5,123 +5,123 @@ namespace Maui.Controls.Sample; public class EditorControlPage : NavigationPage { - private EditorViewModel _viewModel; + private EditorViewModel _viewModel; - public EditorControlPage() - { - _viewModel = new EditorViewModel(); - PushAsync(new EditorControlMainPage(_viewModel)); - } + public EditorControlPage() + { + _viewModel = new EditorViewModel(); + PushAsync(new EditorControlMainPage(_viewModel)); + } } public partial class EditorControlMainPage : ContentPage { - private EditorViewModel _viewModel; - - public EditorControlMainPage(EditorViewModel viewModel) - { - InitializeComponent(); - _viewModel = viewModel; - BindingContext = _viewModel; - EditorControl.PropertyChanged += UpdateEditorControl; - } - - private async void NavigateToOptionsPage_Clicked(object sender, EventArgs e) - { - BindingContext = _viewModel = new EditorViewModel(); - _viewModel.Text = "Test Editor"; - _viewModel.Placeholder = "Enter text here"; - _viewModel.VerticalTextAlignment = TextAlignment.End; - _viewModel.CursorPosition = 0; - _viewModel.SelectionLength = 0; - _viewModel.HeightRequest = -1; - await Navigation.PushAsync(new EditorOptionsPage(_viewModel)); - } - - private void CursorPositionButton_Clicked(object sender, EventArgs e) - { - if (int.TryParse(CursorPositionEntry.Text, out int cursorPosition)) - { - _viewModel.CursorPosition = cursorPosition; - } - } - - private void SelectionLength_Clicked(object sender, EventArgs e) - { - if (int.TryParse(SelectionLengthEntry.Text, out int selectionLength)) - { - _viewModel.SelectionLength = selectionLength; - } - } - - private void OnUpdateCursorAndSelectionClicked(object sender, EventArgs e) - { - if (int.TryParse(CursorPositionEntry.Text, out int cursorPosition)) - { - EditorControl.Focus(); - EditorControl.CursorPosition = cursorPosition; - - if (BindingContext is EditorViewModel vm) - vm.CursorPosition = cursorPosition; - } - - if (int.TryParse(SelectionLengthEntry.Text, out int selectionLength)) - { - EditorControl.Focus(); - EditorControl.SelectionLength = selectionLength; - - if (BindingContext is EditorViewModel vm) - vm.SelectionLength = selectionLength; - } - CursorPositionEntry.Text = EditorControl.CursorPosition.ToString(); - SelectionLengthEntry.Text = EditorControl.SelectionLength.ToString(); - } - - void UpdateEditorControl(object sender, PropertyChangedEventArgs args) - { - if (args.PropertyName == Editor.CursorPositionProperty.PropertyName) - CursorPositionEntry.Text = EditorControl.CursorPosition.ToString(); - else if (args.PropertyName == Editor.SelectionLengthProperty.PropertyName) - SelectionLengthEntry.Text = EditorControl.SelectionLength.ToString(); - } - - private void EditorControl_TextChanged(object sender, TextChangedEventArgs e) - { - string eventInfo = $"TextChanged: Old='{e.OldTextValue}', New='{e.NewTextValue}'"; - - if (BindingContext is EditorViewModel vm) - { - vm.TextChangedText = eventInfo; - } - } - - private void EditorControl_Completed(object sender, EventArgs e) - { - string eventInfo = $"Completed: Event Triggered"; - - if (BindingContext is EditorViewModel vm) - { - vm.CompletedText = eventInfo; - } - } - - private void EditorControl_Focused(object sender, FocusEventArgs e) - { - string eventInfo = $"Focused: Event Triggered"; - - if (BindingContext is EditorViewModel vm) - { - vm.FocusedText = eventInfo; - } - } - - private void EditorControl_Unfocused(object sender, FocusEventArgs e) - { - string eventInfo = $"Unfocused: Event Triggered"; - - if (BindingContext is EditorViewModel vm) - { - vm.UnfocusedText = eventInfo; - } - } + private EditorViewModel _viewModel; + + public EditorControlMainPage(EditorViewModel viewModel) + { + InitializeComponent(); + _viewModel = viewModel; + BindingContext = _viewModel; + EditorControl.PropertyChanged += UpdateEditorControl; + } + + private async void NavigateToOptionsPage_Clicked(object sender, EventArgs e) + { + BindingContext = _viewModel = new EditorViewModel(); + _viewModel.Text = "Test Editor"; + _viewModel.Placeholder = "Enter text here"; + _viewModel.VerticalTextAlignment = TextAlignment.End; + _viewModel.CursorPosition = 0; + _viewModel.SelectionLength = 0; + _viewModel.HeightRequest = -1; + await Navigation.PushAsync(new EditorOptionsPage(_viewModel)); + } + + private void CursorPositionButton_Clicked(object sender, EventArgs e) + { + if (int.TryParse(CursorPositionEntry.Text, out int cursorPosition)) + { + _viewModel.CursorPosition = cursorPosition; + } + } + + private void SelectionLength_Clicked(object sender, EventArgs e) + { + if (int.TryParse(SelectionLengthEntry.Text, out int selectionLength)) + { + _viewModel.SelectionLength = selectionLength; + } + } + + private void OnUpdateCursorAndSelectionClicked(object sender, EventArgs e) + { + if (int.TryParse(CursorPositionEntry.Text, out int cursorPosition)) + { + EditorControl.Focus(); + EditorControl.CursorPosition = cursorPosition; + + if (BindingContext is EditorViewModel vm) + vm.CursorPosition = cursorPosition; + } + + if (int.TryParse(SelectionLengthEntry.Text, out int selectionLength)) + { + EditorControl.Focus(); + EditorControl.SelectionLength = selectionLength; + + if (BindingContext is EditorViewModel vm) + vm.SelectionLength = selectionLength; + } + CursorPositionEntry.Text = EditorControl.CursorPosition.ToString(); + SelectionLengthEntry.Text = EditorControl.SelectionLength.ToString(); + } + + void UpdateEditorControl(object sender, PropertyChangedEventArgs args) + { + if (args.PropertyName == Editor.CursorPositionProperty.PropertyName) + CursorPositionEntry.Text = EditorControl.CursorPosition.ToString(); + else if (args.PropertyName == Editor.SelectionLengthProperty.PropertyName) + SelectionLengthEntry.Text = EditorControl.SelectionLength.ToString(); + } + + private void EditorControl_TextChanged(object sender, TextChangedEventArgs e) + { + string eventInfo = $"TextChanged: Old='{e.OldTextValue}', New='{e.NewTextValue}'"; + + if (BindingContext is EditorViewModel vm) + { + vm.TextChangedText = eventInfo; + } + } + + private void EditorControl_Completed(object sender, EventArgs e) + { + string eventInfo = $"Completed: Event Triggered"; + + if (BindingContext is EditorViewModel vm) + { + vm.CompletedText = eventInfo; + } + } + + private void EditorControl_Focused(object sender, FocusEventArgs e) + { + string eventInfo = $"Focused: Event Triggered"; + + if (BindingContext is EditorViewModel vm) + { + vm.FocusedText = eventInfo; + } + } + + private void EditorControl_Unfocused(object sender, FocusEventArgs e) + { + string eventInfo = $"Unfocused: Event Triggered"; + + if (BindingContext is EditorViewModel vm) + { + vm.UnfocusedText = eventInfo; + } + } } \ No newline at end of file diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Editor/EditorOptionsPage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Editor/EditorOptionsPage.xaml.cs index 30790a6c2e1e..bd7a00bbd2d7 100644 --- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Editor/EditorOptionsPage.xaml.cs +++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Editor/EditorOptionsPage.xaml.cs @@ -1,263 +1,263 @@ -using Microsoft.Maui.Controls; using System; +using Microsoft.Maui.Controls; namespace Maui.Controls.Sample; public partial class EditorOptionsPage : ContentPage { - private EditorViewModel _viewModel; + private EditorViewModel _viewModel; - public EditorOptionsPage(EditorViewModel viewModel) - { - InitializeComponent(); - _viewModel = viewModel; - BindingContext = _viewModel; - } + public EditorOptionsPage(EditorViewModel viewModel) + { + InitializeComponent(); + _viewModel = viewModel; + BindingContext = _viewModel; + } - private async void ApplyButton_Clicked(object sender, EventArgs e) - { - await Navigation.PopAsync(); - } + private async void ApplyButton_Clicked(object sender, EventArgs e) + { + await Navigation.PopAsync(); + } - private void TextColorButton_Clicked(object sender, EventArgs e) - { - if (sender is Button button) - { - _viewModel.TextColor = button.BackgroundColor; - } - } + private void TextColorButton_Clicked(object sender, EventArgs e) + { + if (sender is Button button) + { + _viewModel.TextColor = button.BackgroundColor; + } + } - private void Editor_TextChanged(object sender, TextChangedEventArgs e) - { - if (BindingContext is EditorViewModel vm) - { - vm.Text = e.NewTextValue; - } - } + private void Editor_TextChanged(object sender, TextChangedEventArgs e) + { + if (BindingContext is EditorViewModel vm) + { + vm.Text = e.NewTextValue; + } + } - private void PlaceholderEditor_TextChanged(object sender, TextChangedEventArgs e) - { - if (BindingContext is EditorViewModel vm) - { - vm.Placeholder = e.NewTextValue; - } - } + private void PlaceholderEditor_TextChanged(object sender, TextChangedEventArgs e) + { + if (BindingContext is EditorViewModel vm) + { + vm.Placeholder = e.NewTextValue; + } + } - private void PlaceholderColorButton_Clicked(object sender, EventArgs e) - { - if (sender is Button button) - { - _viewModel.PlaceholderColor = button.BackgroundColor; - } - } - private void HorizontalAlignmentButton_Clicked(object sender, EventArgs e) - { - if (sender is Button button) - { - _viewModel.HorizontalTextAlignment = button.AutomationId switch - { - "HStart" => TextAlignment.Start, - "HCenter" => TextAlignment.Center, - "HEnd" => TextAlignment.End, - _ => _viewModel.HorizontalTextAlignment - }; - } - } + private void PlaceholderColorButton_Clicked(object sender, EventArgs e) + { + if (sender is Button button) + { + _viewModel.PlaceholderColor = button.BackgroundColor; + } + } + private void HorizontalAlignmentButton_Clicked(object sender, EventArgs e) + { + if (sender is Button button) + { + _viewModel.HorizontalTextAlignment = button.AutomationId switch + { + "HStart" => TextAlignment.Start, + "HCenter" => TextAlignment.Center, + "HEnd" => TextAlignment.End, + _ => _viewModel.HorizontalTextAlignment + }; + } + } - private void VerticalAlignmentButton_Clicked(object sender, EventArgs e) - { - _viewModel.HeightRequest = 100; - if (sender is Button button) - { - _viewModel.VerticalTextAlignment = button.AutomationId switch - { - "VStart" => TextAlignment.Start, - "VCenter" => TextAlignment.Center, - "VEnd" => TextAlignment.End, - _ => _viewModel.VerticalTextAlignment - }; - } - } + private void VerticalAlignmentButton_Clicked(object sender, EventArgs e) + { + _viewModel.HeightRequest = 100; + if (sender is Button button) + { + _viewModel.VerticalTextAlignment = button.AutomationId switch + { + "VStart" => TextAlignment.Start, + "VCenter" => TextAlignment.Center, + "VEnd" => TextAlignment.End, + _ => _viewModel.VerticalTextAlignment + }; + } + } - private void ReturnTypeButton_Clicked(object sender, EventArgs e) - { - if (sender is Button button) - { - _viewModel.ReturnType = button.AutomationId switch - { - "Done" => ReturnType.Done, - "Next" => ReturnType.Next, - "Go" => ReturnType.Go, - "Search" => ReturnType.Search, - "Send" => ReturnType.Send, - "Default" => ReturnType.Default, - _ => _viewModel.ReturnType - }; - } - } + private void ReturnTypeButton_Clicked(object sender, EventArgs e) + { + if (sender is Button button) + { + _viewModel.ReturnType = button.AutomationId switch + { + "Done" => ReturnType.Done, + "Next" => ReturnType.Next, + "Go" => ReturnType.Go, + "Search" => ReturnType.Search, + "Send" => ReturnType.Send, + "Default" => ReturnType.Default, + _ => _viewModel.ReturnType + }; + } + } - private void MaxLengthButton_Clicked(object sender, EventArgs e) - { - if (int.TryParse(MaxLengthEntry.Text, out int maxLength)) - { - _viewModel.MaxLength = maxLength; - } - } + private void MaxLengthButton_Clicked(object sender, EventArgs e) + { + if (int.TryParse(MaxLengthEntry.Text, out int maxLength)) + { + _viewModel.MaxLength = maxLength; + } + } - private void FontSizeEditor_TextChanged(object sender, TextChangedEventArgs e) - { - if (double.TryParse(FontSizeEntry.Text, out double fontSize)) - { - _viewModel.FontSize = fontSize; - } - } + private void FontSizeEditor_TextChanged(object sender, TextChangedEventArgs e) + { + if (double.TryParse(FontSizeEntry.Text, out double fontSize)) + { + _viewModel.FontSize = fontSize; + } + } - private void CharacterSpacing_TextChanged(object sender, TextChangedEventArgs e) - { - if (double.TryParse(CharacterSpacingEntry.Text, out double characterSpacing)) - { - _viewModel.CharacterSpacing = characterSpacing; - } - } + private void CharacterSpacing_TextChanged(object sender, TextChangedEventArgs e) + { + if (double.TryParse(CharacterSpacingEntry.Text, out double characterSpacing)) + { + _viewModel.CharacterSpacing = characterSpacing; + } + } - private void IsReadOnlyTrueOrFalse_Clicked(object sender, EventArgs e) - { - if (IsReadOnlyTrue.IsChecked) - { - _viewModel.IsReadOnly = true; - } - else if (IsReadOnlyFalse.IsChecked) - { - _viewModel.IsReadOnly = false; - } - } + private void IsReadOnlyTrueOrFalse_Clicked(object sender, EventArgs e) + { + if (IsReadOnlyTrue.IsChecked) + { + _viewModel.IsReadOnly = true; + } + else if (IsReadOnlyFalse.IsChecked) + { + _viewModel.IsReadOnly = false; + } + } - private void IsTextPredictionEnabledTrueOrFalse_Clicked(object sender, EventArgs e) - { - if (IsTextPredictionEnabledTrue.IsChecked) - { - _viewModel.IsTextPredictionEnabled = true; - } - else if (IsTextPredictionEnabledFalse.IsChecked) - { - _viewModel.IsTextPredictionEnabled = false; - } - } + private void IsTextPredictionEnabledTrueOrFalse_Clicked(object sender, EventArgs e) + { + if (IsTextPredictionEnabledTrue.IsChecked) + { + _viewModel.IsTextPredictionEnabled = true; + } + else if (IsTextPredictionEnabledFalse.IsChecked) + { + _viewModel.IsTextPredictionEnabled = false; + } + } - private void IsSpellCheckEnabledTrueOrFalse_Clicked(object sender, EventArgs e) - { - if (IsSpellCheckEnabledTrue.IsChecked) - { - _viewModel.IsSpellCheckEnabled = true; - } - else if (IsSpellCheckEnabledFalse.IsChecked) - { - _viewModel.IsSpellCheckEnabled = false; - } - } + private void IsSpellCheckEnabledTrueOrFalse_Clicked(object sender, EventArgs e) + { + if (IsSpellCheckEnabledTrue.IsChecked) + { + _viewModel.IsSpellCheckEnabled = true; + } + else if (IsSpellCheckEnabledFalse.IsChecked) + { + _viewModel.IsSpellCheckEnabled = false; + } + } - private void KeyboardButton_Clicked(object sender, EventArgs e) - { - if (sender is Button button) - { - _viewModel.Keyboard = button.AutomationId switch - { - "Default" => Keyboard.Default, - "Chat" => Keyboard.Chat, - "Email" => Keyboard.Email, - "Numeric" => Keyboard.Numeric, - "Telephone" => Keyboard.Telephone, - "Text" => Keyboard.Text, - "Url" => Keyboard.Url, - _ => _viewModel.Keyboard - }; - } - } + private void KeyboardButton_Clicked(object sender, EventArgs e) + { + if (sender is Button button) + { + _viewModel.Keyboard = button.AutomationId switch + { + "Default" => Keyboard.Default, + "Chat" => Keyboard.Chat, + "Email" => Keyboard.Email, + "Numeric" => Keyboard.Numeric, + "Telephone" => Keyboard.Telephone, + "Text" => Keyboard.Text, + "Url" => Keyboard.Url, + _ => _viewModel.Keyboard + }; + } + } - private void FontFamilyEditor_TextChanged(object sender, TextChangedEventArgs e) - { - _viewModel.FontFamily = FontFamilyEntry.Text; - } + private void FontFamilyEditor_TextChanged(object sender, TextChangedEventArgs e) + { + _viewModel.FontFamily = FontFamilyEntry.Text; + } - private void FlowDirection_CheckedChanged(object sender, CheckedChangedEventArgs e) - { - if (sender == FlowDirectionLeftToRight) - { - _viewModel.FlowDirection = FlowDirection.LeftToRight; - } - else if (sender == FlowDirectionRightToLeft) - { - _viewModel.FlowDirection = FlowDirection.RightToLeft; - } - } + private void FlowDirection_CheckedChanged(object sender, CheckedChangedEventArgs e) + { + if (sender == FlowDirectionLeftToRight) + { + _viewModel.FlowDirection = FlowDirection.LeftToRight; + } + else if (sender == FlowDirectionRightToLeft) + { + _viewModel.FlowDirection = FlowDirection.RightToLeft; + } + } - private void IsVisibleTrueOrFalse_Clicked(object sender, EventArgs e) - { - if (IsVisibleTrue.IsChecked) - { - _viewModel.IsVisible = true; - } - else if (IsVisibleFalse.IsChecked) - { - _viewModel.IsVisible = false; - } - } + private void IsVisibleTrueOrFalse_Clicked(object sender, EventArgs e) + { + if (IsVisibleTrue.IsChecked) + { + _viewModel.IsVisible = true; + } + else if (IsVisibleFalse.IsChecked) + { + _viewModel.IsVisible = false; + } + } - private void IsEnabledTrueOrFalse_Clicked(object sender, EventArgs e) - { - if (IsEnabledTrue.IsChecked) - { - _viewModel.IsEnabled = true; - } - else if (IsEnabledFalse.IsChecked) - { - _viewModel.IsEnabled = false; - } - } + private void IsEnabledTrueOrFalse_Clicked(object sender, EventArgs e) + { + if (IsEnabledTrue.IsChecked) + { + _viewModel.IsEnabled = true; + } + else if (IsEnabledFalse.IsChecked) + { + _viewModel.IsEnabled = false; + } + } - private void TextTransform_CheckedChanged(object sender, CheckedChangedEventArgs e) - { - if (sender == TextTransformLowercase) - { - _viewModel.TextTransform = TextTransform.Lowercase; - } - else if (sender == TextTransformUppercase) - { - _viewModel.TextTransform = TextTransform.Uppercase; - } - else if (sender == TextTransformDefault) - { - _viewModel.TextTransform = TextTransform.Default; - } - } + private void TextTransform_CheckedChanged(object sender, CheckedChangedEventArgs e) + { + if (sender == TextTransformLowercase) + { + _viewModel.TextTransform = TextTransform.Lowercase; + } + else if (sender == TextTransformUppercase) + { + _viewModel.TextTransform = TextTransform.Uppercase; + } + else if (sender == TextTransformDefault) + { + _viewModel.TextTransform = TextTransform.Default; + } + } - private void FontAttributes_CheckedChanged(object sender, CheckedChangedEventArgs e) - { - if (sender == FontAttributesBold) - { - _viewModel.FontAttributes = FontAttributes.Bold; - } - else if (sender == FontAttributesNone) - { - _viewModel.FontAttributes = FontAttributes.None; - } - else if (sender == FontAttributesItalic) - { - _viewModel.FontAttributes = FontAttributes.Italic; - } - } + private void FontAttributes_CheckedChanged(object sender, CheckedChangedEventArgs e) + { + if (sender == FontAttributesBold) + { + _viewModel.FontAttributes = FontAttributes.Bold; + } + else if (sender == FontAttributesNone) + { + _viewModel.FontAttributes = FontAttributes.None; + } + else if (sender == FontAttributesItalic) + { + _viewModel.FontAttributes = FontAttributes.Italic; + } + } - private void AutoSize_CheckedChanged(object sender, CheckedChangedEventArgs e) - { - if (AutoSizeTextChanges.IsChecked) - { - _viewModel.HeightRequest = -1; - _viewModel.AutoSizeOption = EditorAutoSizeOption.TextChanges; - } - else if (AutoSizeDisabled.IsChecked) - { - _viewModel.HeightRequest = -1; - _viewModel.AutoSizeOption = EditorAutoSizeOption.Disabled; - } - } + private void AutoSize_CheckedChanged(object sender, CheckedChangedEventArgs e) + { + if (AutoSizeTextChanges.IsChecked) + { + _viewModel.HeightRequest = -1; + _viewModel.AutoSizeOption = EditorAutoSizeOption.TextChanges; + } + else if (AutoSizeDisabled.IsChecked) + { + _viewModel.HeightRequest = -1; + _viewModel.AutoSizeOption = EditorAutoSizeOption.Disabled; + } + } } \ No newline at end of file diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Editor/EditorViewModel.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Editor/EditorViewModel.cs index 7339b7a0629f..db1b60392fa4 100644 --- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Editor/EditorViewModel.cs +++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Editor/EditorViewModel.cs @@ -5,272 +5,272 @@ namespace Maui.Controls.Sample; public class EditorViewModel : INotifyPropertyChanged { - private string _text = "Test Editor"; - private Color _textColor = Colors.Black; - private string _placeholder = "Enter text here"; - private Color _placeholderColor = Colors.Gray; - private double _fontSize = 14; - private double _heightrequest = -1; - private TextAlignment _horizontalTextAlignment = TextAlignment.Start; - private TextAlignment _verticalTextAlignment = TextAlignment.End; - private double _characterSpacing = 0; - private ReturnType _returnType = ReturnType.Default; - private int _maxLength = -1; - private int _cursorPosition = 0; - private int _selectionLength = 0; - private bool _isReadOnly = false; - private bool _isTextPredictionEnabled = false; - private bool _isSpellCheckEnabled = false; - private Keyboard _keyboard = Keyboard.Default; - private string _fontFamily = null; - private bool isVisible = true; - private bool _isEnabled = true; - private FlowDirection _flowDirection = FlowDirection.LeftToRight; - private bool _hasShadow = false; - private Shadow _editorShadow = null; - private TextTransform _transform = TextTransform.Default; - private FontAttributes _fontAttributes = FontAttributes.None; - private EditorAutoSizeOption _autoSizeOption = EditorAutoSizeOption.Disabled; - private string _textChangedText = "TextChanged: Not triggered"; - private string _completedText = "Completed: Not triggered"; - private string _focusedText = "Focused: Not triggered"; - private string _unfocusedText = "Unfocused: Not triggered"; + private string _text = "Test Editor"; + private Color _textColor = Colors.Black; + private string _placeholder = "Enter text here"; + private Color _placeholderColor = Colors.Gray; + private double _fontSize = 14; + private double _heightrequest = -1; + private TextAlignment _horizontalTextAlignment = TextAlignment.Start; + private TextAlignment _verticalTextAlignment = TextAlignment.End; + private double _characterSpacing = 0; + private ReturnType _returnType = ReturnType.Default; + private int _maxLength = -1; + private int _cursorPosition = 0; + private int _selectionLength = 0; + private bool _isReadOnly = false; + private bool _isTextPredictionEnabled = false; + private bool _isSpellCheckEnabled = false; + private Keyboard _keyboard = Keyboard.Default; + private string _fontFamily = null; + private bool isVisible = true; + private bool _isEnabled = true; + private FlowDirection _flowDirection = FlowDirection.LeftToRight; + private bool _hasShadow = false; + private Shadow _editorShadow = null; + private TextTransform _transform = TextTransform.Default; + private FontAttributes _fontAttributes = FontAttributes.None; + private EditorAutoSizeOption _autoSizeOption = EditorAutoSizeOption.Disabled; + private string _textChangedText = "TextChanged: Not triggered"; + private string _completedText = "Completed: Not triggered"; + private string _focusedText = "Focused: Not triggered"; + private string _unfocusedText = "Unfocused: Not triggered"; - public event PropertyChangedEventHandler PropertyChanged; + public event PropertyChangedEventHandler PropertyChanged; - public ICommand ReturnCommand { get; set; } - public EditorViewModel() - { - ReturnCommand = new Command( - execute: (entryText) => - { - if (entryText == "Test") - { - Text = "Command Executed with Parameter"; - } - } - ); - } - public string Text - { - get => _text; - set { _text = value; OnPropertyChanged(); } - } + public ICommand ReturnCommand { get; set; } + public EditorViewModel() + { + ReturnCommand = new Command( + execute: (entryText) => + { + if (entryText == "Test") + { + Text = "Command Executed with Parameter"; + } + } + ); + } + public string Text + { + get => _text; + set { _text = value; OnPropertyChanged(); } + } - public Color TextColor - { - get => _textColor; - set { _textColor = value; OnPropertyChanged(); } - } + public Color TextColor + { + get => _textColor; + set { _textColor = value; OnPropertyChanged(); } + } - public string Placeholder - { - get => _placeholder; - set { _placeholder = value; OnPropertyChanged(); } - } - public Color PlaceholderColor - { - get => _placeholderColor; - set { _placeholderColor = value; OnPropertyChanged(); } - } + public string Placeholder + { + get => _placeholder; + set { _placeholder = value; OnPropertyChanged(); } + } + public Color PlaceholderColor + { + get => _placeholderColor; + set { _placeholderColor = value; OnPropertyChanged(); } + } - public double FontSize - { - get => _fontSize; - set { _fontSize = value; OnPropertyChanged(); } - } + public double FontSize + { + get => _fontSize; + set { _fontSize = value; OnPropertyChanged(); } + } - public double HeightRequest - { - get => _heightrequest; - set { _heightrequest = value; OnPropertyChanged(); } - } + public double HeightRequest + { + get => _heightrequest; + set { _heightrequest = value; OnPropertyChanged(); } + } - public TextAlignment HorizontalTextAlignment - { - get => _horizontalTextAlignment; - set { _horizontalTextAlignment = value; OnPropertyChanged(); } - } + public TextAlignment HorizontalTextAlignment + { + get => _horizontalTextAlignment; + set { _horizontalTextAlignment = value; OnPropertyChanged(); } + } - public TextAlignment VerticalTextAlignment - { - get => _verticalTextAlignment; - set { _verticalTextAlignment = value; OnPropertyChanged(); } - } + public TextAlignment VerticalTextAlignment + { + get => _verticalTextAlignment; + set { _verticalTextAlignment = value; OnPropertyChanged(); } + } - public double CharacterSpacing - { - get => _characterSpacing; - set { _characterSpacing = value; OnPropertyChanged(); } - } + public double CharacterSpacing + { + get => _characterSpacing; + set { _characterSpacing = value; OnPropertyChanged(); } + } - public ReturnType ReturnType - { - get => _returnType; - set { _returnType = value; OnPropertyChanged(); } - } + public ReturnType ReturnType + { + get => _returnType; + set { _returnType = value; OnPropertyChanged(); } + } - public int MaxLength - { - get => _maxLength; - set { _maxLength = value; OnPropertyChanged(); } - } - public int CursorPosition - { - get => _cursorPosition; - set { _cursorPosition = value; OnPropertyChanged(); } - } - public int SelectionLength - { - get => _selectionLength; - set { _selectionLength = value; OnPropertyChanged(); } - } - public bool IsReadOnly - { - get => _isReadOnly; - set { _isReadOnly = value; OnPropertyChanged(); } - } - public bool IsTextPredictionEnabled - { - get => _isTextPredictionEnabled; - set { _isTextPredictionEnabled = value; OnPropertyChanged(); } - } - public bool IsSpellCheckEnabled - { - get => _isSpellCheckEnabled; - set { _isSpellCheckEnabled = value; OnPropertyChanged(); } - } + public int MaxLength + { + get => _maxLength; + set { _maxLength = value; OnPropertyChanged(); } + } + public int CursorPosition + { + get => _cursorPosition; + set { _cursorPosition = value; OnPropertyChanged(); } + } + public int SelectionLength + { + get => _selectionLength; + set { _selectionLength = value; OnPropertyChanged(); } + } + public bool IsReadOnly + { + get => _isReadOnly; + set { _isReadOnly = value; OnPropertyChanged(); } + } + public bool IsTextPredictionEnabled + { + get => _isTextPredictionEnabled; + set { _isTextPredictionEnabled = value; OnPropertyChanged(); } + } + public bool IsSpellCheckEnabled + { + get => _isSpellCheckEnabled; + set { _isSpellCheckEnabled = value; OnPropertyChanged(); } + } - public bool IsVisible - { - get => isVisible; - set { isVisible = value; OnPropertyChanged(); } - } + public bool IsVisible + { + get => isVisible; + set { isVisible = value; OnPropertyChanged(); } + } - public bool IsEnabled - { - get => _isEnabled; - set { _isEnabled = value; OnPropertyChanged(); } - } + public bool IsEnabled + { + get => _isEnabled; + set { _isEnabled = value; OnPropertyChanged(); } + } - public Keyboard Keyboard - { - get => _keyboard; - set { _keyboard = value; OnPropertyChanged(); } - } + public Keyboard Keyboard + { + get => _keyboard; + set { _keyboard = value; OnPropertyChanged(); } + } - public FlowDirection FlowDirection - { - get => _flowDirection; - set { _flowDirection = value; OnPropertyChanged(); } - } + public FlowDirection FlowDirection + { + get => _flowDirection; + set { _flowDirection = value; OnPropertyChanged(); } + } - public string FontFamily - { - get => _fontFamily; - set { _fontFamily = value; OnPropertyChanged(); } - } + public string FontFamily + { + get => _fontFamily; + set { _fontFamily = value; OnPropertyChanged(); } + } - public string TextChangedText - { - get => _textChangedText; - set { _textChangedText = value; OnPropertyChanged(); } - } + public string TextChangedText + { + get => _textChangedText; + set { _textChangedText = value; OnPropertyChanged(); } + } - public string CompletedText - { - get => _completedText; - set { _completedText = value; OnPropertyChanged(); } - } + public string CompletedText + { + get => _completedText; + set { _completedText = value; OnPropertyChanged(); } + } - public string FocusedText - { - get => _focusedText; - set { _focusedText = value; OnPropertyChanged(); } - } + public string FocusedText + { + get => _focusedText; + set { _focusedText = value; OnPropertyChanged(); } + } - public string UnfocusedText - { - get => _unfocusedText; - set { _unfocusedText = value; OnPropertyChanged(); } - } + public string UnfocusedText + { + get => _unfocusedText; + set { _unfocusedText = value; OnPropertyChanged(); } + } - public bool HasShadow - { - get => _hasShadow; - set - { - if (_hasShadow != value) - { - _hasShadow = value; - EditorShadow = value - ? new Shadow - { - Radius = 10, - Opacity = 1.0f, - Brush = Colors.Black.AsPaint(), - Offset = new Point(5, 5) - } - : null; - OnPropertyChanged(nameof(HasShadow)); - } - } - } + public bool HasShadow + { + get => _hasShadow; + set + { + if (_hasShadow != value) + { + _hasShadow = value; + EditorShadow = value + ? new Shadow + { + Radius = 10, + Opacity = 1.0f, + Brush = Colors.Black.AsPaint(), + Offset = new Point(5, 5) + } + : null; + OnPropertyChanged(nameof(HasShadow)); + } + } + } - public Shadow EditorShadow - { - get => _editorShadow; - private set - { - if (_editorShadow != value) - { - _editorShadow = value; - OnPropertyChanged(nameof(EditorShadow)); - } - } - } + public Shadow EditorShadow + { + get => _editorShadow; + private set + { + if (_editorShadow != value) + { + _editorShadow = value; + OnPropertyChanged(nameof(EditorShadow)); + } + } + } - public TextTransform TextTransform - { - get => _transform; - set - { - if (_transform != value) - { - _transform = value; - OnPropertyChanged(nameof(TextTransform)); - } - } - } + public TextTransform TextTransform + { + get => _transform; + set + { + if (_transform != value) + { + _transform = value; + OnPropertyChanged(nameof(TextTransform)); + } + } + } - public FontAttributes FontAttributes - { - get => _fontAttributes; - set - { - if (_fontAttributes != value) - { - _fontAttributes = value; - OnPropertyChanged(nameof(FontAttributes)); - } - } - } + public FontAttributes FontAttributes + { + get => _fontAttributes; + set + { + if (_fontAttributes != value) + { + _fontAttributes = value; + OnPropertyChanged(nameof(FontAttributes)); + } + } + } - public EditorAutoSizeOption AutoSizeOption - { - get => _autoSizeOption; - set - { - if (_autoSizeOption != value) - { - _autoSizeOption = value; - OnPropertyChanged(nameof(AutoSizeOption)); - } - } - } + public EditorAutoSizeOption AutoSizeOption + { + get => _autoSizeOption; + set + { + if (_autoSizeOption != value) + { + _autoSizeOption = value; + OnPropertyChanged(nameof(AutoSizeOption)); + } + } + } - protected void OnPropertyChanged([CallerMemberName] string propertyName = null) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } + protected void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } } \ No newline at end of file diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/GraphicsView/GraphicsViewControlPage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/GraphicsView/GraphicsViewControlPage.xaml.cs index 31d4e4cb7e53..b5e5f99e46ab 100644 --- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/GraphicsView/GraphicsViewControlPage.xaml.cs +++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/GraphicsView/GraphicsViewControlPage.xaml.cs @@ -1,114 +1,114 @@ using System; using System.ComponentModel; +using System.Diagnostics; using Microsoft.Maui.Controls; using Microsoft.Maui.Graphics; -using System.Diagnostics; namespace Maui.Controls.Sample; public class GraphicsViewControlPage : NavigationPage { - private GraphicsViewViewModel _viewModel; + private GraphicsViewViewModel _viewModel; - public GraphicsViewControlPage() - { - _viewModel = new GraphicsViewViewModel(); - PushAsync(new GraphicsViewControlMainPage(_viewModel)); - } + public GraphicsViewControlPage() + { + _viewModel = new GraphicsViewViewModel(); + PushAsync(new GraphicsViewControlMainPage(_viewModel)); + } } public partial class GraphicsViewControlMainPage : ContentPage { - private GraphicsViewViewModel _viewModel; - - public GraphicsViewControlMainPage(GraphicsViewViewModel viewModel) - { - InitializeComponent(); - _viewModel = viewModel; - BindingContext = _viewModel; - - // Ensure GraphicsView invalidates when drawable changes - _viewModel.RequestInvalidate = () => graphicsView.Invalidate(); - - // Set default drawable - _viewModel.SelectedDrawable = DrawableType.Square; - } - - private async void NavigateToOptionsPage_Clicked(object sender, EventArgs e) - { - BindingContext = _viewModel = new GraphicsViewViewModel(); // Ensure the ViewModel is set for the options page - await Navigation.PushAsync(new GraphicsViewOptionsPage(_viewModel)); - } - - private void OnStartHoverInteraction(object sender, TouchEventArgs e) - { - _viewModel.AddInteractionEvent("StartHoverInteraction"); - } - - private void OnMoveHoverInteraction(object sender, TouchEventArgs e) - { - _viewModel.AddInteractionEvent("MoveHoverInteraction"); - } - - private void OnEndHoverInteraction(object sender, EventArgs e) - { - _viewModel.AddInteractionEvent("EndHoverInteraction"); - } - - private void OnStartInteraction(object sender, TouchEventArgs e) - { - _viewModel.AddInteractionEvent("StartInteraction"); - } - - private void OnDragInteraction(object sender, TouchEventArgs e) - { - _viewModel.AddInteractionEvent("DragInteraction"); - } - - private void OnEndInteraction(object sender, EventArgs e) - { - _viewModel.AddInteractionEvent("EndInteraction"); - } - - private void OnCancelInteraction(object sender, EventArgs e) - { - _viewModel.AddInteractionEvent("CancelInteraction"); - } - - private void OnInvalidateClicked(object sender, EventArgs e) - { - // Use a constant color for consistent screenshot testing - _viewModel.CurrentDrawColor = Colors.Green; - - // Call Invalidate on the GraphicsView to force a redraw - graphicsView.Invalidate(); - - // Add the invalidate event to the history with color information - _viewModel.AddInteractionEvent($"Invalidate() called - Color changed to {_viewModel.CurrentDrawColor}"); - } - - private void OnClearEventsClicked(object sender, EventArgs e) - { - // Clear the interaction event history - _viewModel.ClearInteractionHistory(); - } - - private void DisplayStoredDimensions() - { - var dimensions = _viewModel.GetDrawableDimensions(_viewModel.SelectedDrawable); - if (dimensions.HasValue) - { - Debug.WriteLine($"Stored Dimensions for {_viewModel.SelectedDrawable}: Width = {dimensions.Value.Width}, Height = {dimensions.Value.Height}"); - } - else - { - Debug.WriteLine($"No stored dimensions found for {_viewModel.SelectedDrawable}."); - } - } - - protected override void OnAppearing() - { - base.OnAppearing(); - DisplayStoredDimensions(); - } + private GraphicsViewViewModel _viewModel; + + public GraphicsViewControlMainPage(GraphicsViewViewModel viewModel) + { + InitializeComponent(); + _viewModel = viewModel; + BindingContext = _viewModel; + + // Ensure GraphicsView invalidates when drawable changes + _viewModel.RequestInvalidate = () => graphicsView.Invalidate(); + + // Set default drawable + _viewModel.SelectedDrawable = DrawableType.Square; + } + + private async void NavigateToOptionsPage_Clicked(object sender, EventArgs e) + { + BindingContext = _viewModel = new GraphicsViewViewModel(); // Ensure the ViewModel is set for the options page + await Navigation.PushAsync(new GraphicsViewOptionsPage(_viewModel)); + } + + private void OnStartHoverInteraction(object sender, TouchEventArgs e) + { + _viewModel.AddInteractionEvent("StartHoverInteraction"); + } + + private void OnMoveHoverInteraction(object sender, TouchEventArgs e) + { + _viewModel.AddInteractionEvent("MoveHoverInteraction"); + } + + private void OnEndHoverInteraction(object sender, EventArgs e) + { + _viewModel.AddInteractionEvent("EndHoverInteraction"); + } + + private void OnStartInteraction(object sender, TouchEventArgs e) + { + _viewModel.AddInteractionEvent("StartInteraction"); + } + + private void OnDragInteraction(object sender, TouchEventArgs e) + { + _viewModel.AddInteractionEvent("DragInteraction"); + } + + private void OnEndInteraction(object sender, EventArgs e) + { + _viewModel.AddInteractionEvent("EndInteraction"); + } + + private void OnCancelInteraction(object sender, EventArgs e) + { + _viewModel.AddInteractionEvent("CancelInteraction"); + } + + private void OnInvalidateClicked(object sender, EventArgs e) + { + // Use a constant color for consistent screenshot testing + _viewModel.CurrentDrawColor = Colors.Green; + + // Call Invalidate on the GraphicsView to force a redraw + graphicsView.Invalidate(); + + // Add the invalidate event to the history with color information + _viewModel.AddInteractionEvent($"Invalidate() called - Color changed to {_viewModel.CurrentDrawColor}"); + } + + private void OnClearEventsClicked(object sender, EventArgs e) + { + // Clear the interaction event history + _viewModel.ClearInteractionHistory(); + } + + private void DisplayStoredDimensions() + { + var dimensions = _viewModel.GetDrawableDimensions(_viewModel.SelectedDrawable); + if (dimensions.HasValue) + { + Debug.WriteLine($"Stored Dimensions for {_viewModel.SelectedDrawable}: Width = {dimensions.Value.Width}, Height = {dimensions.Value.Height}"); + } + else + { + Debug.WriteLine($"No stored dimensions found for {_viewModel.SelectedDrawable}."); + } + } + + protected override void OnAppearing() + { + base.OnAppearing(); + DisplayStoredDimensions(); + } } diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/GraphicsView/GraphicsViewViewModel.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/GraphicsView/GraphicsViewViewModel.cs index 484584e4a8ad..35739d0f7671 100644 --- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/GraphicsView/GraphicsViewViewModel.cs +++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/GraphicsView/GraphicsViewViewModel.cs @@ -2,815 +2,815 @@ using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; -using System.Runtime.CompilerServices; using System.Reflection; +using System.Runtime.CompilerServices; using Microsoft.Maui.Controls; namespace Maui.Controls.Sample; public enum DrawableType { - Square, - Triangle, - Ellipse, - Line, - String, - Image, - TransparentEllipse, + Square, + Triangle, + Ellipse, + Line, + String, + Image, + TransparentEllipse, } public class GraphicsViewViewModel : INotifyPropertyChanged { - public Action RequestInvalidate { get; set; } - public string DrawableTypeLabel => SelectedDrawable.ToString(); - private DrawableType _selectedDrawable = DrawableType.Square; - private bool _isEnabled = true; - private bool _isVisible = true; - private Color _currentDrawColor = Colors.Blue; - private Random _random = new Random(); - - public event PropertyChangedEventHandler PropertyChanged; - - public Color CurrentDrawColor - { - get => _currentDrawColor; - set - { - if (_currentDrawColor != value) - { - _currentDrawColor = value; - OnPropertyChanged(); - } - } - } - - public DrawableType SelectedDrawable - { - get => _selectedDrawable; - set - { - if (_selectedDrawable != value) - { - _selectedDrawable = value; - OnPropertyChanged(); - OnPropertyChanged(nameof(DrawableTypeLabel)); - UpdateDrawable(); - RequestInvalidate?.Invoke(); - } - } - } - - public bool IsEnabled - { - get => _isEnabled; - set - { - if (_isEnabled != value) - { - _isEnabled = value; - OnPropertyChanged(); - } - } - } - - public bool IsVisible - { - get => _isVisible; - set - { - if (_isVisible != value) - { - _isVisible = value; - OnPropertyChanged(); - } - } - } - - private string _interactionEvent = "No interactions yet"; - private List _interactionHistory = new List(); - - public string InteractionEvent - { - get => _interactionEvent; - set - { - if (_interactionEvent != value) - { - _interactionEvent = value; - OnPropertyChanged(); - } - } - } - - public void AddInteractionEvent(string eventName) - { - //string timestamp = DateTime.Now.ToString("HH:mm:ss.fff"); - string eventLabel = $"{eventName}"; - - _interactionHistory.Add(eventLabel); - - // Keep only the last 10 events to prevent UI from becoming too cluttered - if (_interactionHistory.Count > 5) - { - _interactionHistory.RemoveAt(0); - } - - // Update the displayed text with all events - InteractionEvent = string.Join("\n", _interactionHistory); - } - - public void ClearInteractionHistory() - { - _interactionHistory.Clear(); - InteractionEvent = "No interactions yet"; - } - - private double _heightRequest = 100; - public double HeightRequest - { - get => _heightRequest; - set - { - if (_heightRequest != value) - { - _heightRequest = value; - OnPropertyChanged(); - } - } - } - - private double _widthRequest = 100; - public double WidthRequest - { - get => _widthRequest; - set - { - if (_widthRequest != value) - { - _widthRequest = value; - OnPropertyChanged(); - - } - } - } - - private Shadow _shadow; - public Shadow Shadow - { - get => _shadow; - set - { - if (_shadow != value) - { - _shadow = value; - OnPropertyChanged(); - } - } - } - - public IDrawable Drawable { get; private set; } - - private readonly Dictionary _drawableDimensions = new(); - - public GraphicsViewViewModel() - { - UpdateDrawable(); - } - - private void UpdateDrawable() - { - Drawable = GetDrawable(SelectedDrawable); - OnPropertyChanged(nameof(Drawable)); - - // Ensure RectF height and width match HeightRequest and WidthRequest - if (Drawable is IDrawable drawable) - { - RectF rect = new RectF(0, 0, (float)WidthRequest, (float)HeightRequest); - Debug.WriteLine($"RectF Height: {rect.Height}, Width: {rect.Width}"); - - // Notify changes for DrawableDimensions - OnPropertyChanged(nameof(DrawableDimensions)); - } - - // Force GraphicsView to redraw - RequestInvalidate?.Invoke(); - } - - private IDrawable GetDrawable(DrawableType drawableType) - { - return drawableType switch - { - DrawableType.Square => new SquareDrawable(this), - DrawableType.Triangle => new TriangleDrawable(this), - DrawableType.Ellipse => new EllipseDrawable(this), - DrawableType.Line => new LineDrawable(this), - DrawableType.String => new StringDrawable(this), - DrawableType.Image => new ImageDrawable(this), - DrawableType.TransparentEllipse => new TransparentEllipseDrawable(this), - _ => null, - }; - } - - protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - - public double SquareDrawableHeight => Drawable is SquareDrawable squareDrawable ? squareDrawable.RectFHeight : 0; - public double SquareDrawableWidth => Drawable is SquareDrawable squareDrawable ? squareDrawable.RectFWidth : 0; - public double TriangleDrawableHeight => Drawable is TriangleDrawable triangleDrawable ? triangleDrawable.RectFHeight : 0; - public double TriangleDrawableWidth => Drawable is TriangleDrawable triangleDrawable ? triangleDrawable.RectFWidth : 0; - - public double DrawableHeight - { - get - { - return Drawable switch - { - SquareDrawable squareDrawable => squareDrawable.RectFHeight, - TriangleDrawable triangleDrawable => triangleDrawable.RectFHeight, - _ => 0 - }; - } - } - - public double DrawableWidth - { - get - { - return Drawable switch - { - SquareDrawable squareDrawable => squareDrawable.RectFWidth, - TriangleDrawable triangleDrawable => triangleDrawable.RectFWidth, - _ => 0 - }; - } - } - - public string DrawableDimensions - { - get - { - var dimensions = GetDrawableDimensions(SelectedDrawable); - return dimensions.HasValue - ? $"X: {dimensions.Value.X:F1}, Y: {dimensions.Value.Y:F1}, Width: {dimensions.Value.Width:F1}, Height: {dimensions.Value.Height:F1}" - : "X: 0, Y: 0, Width: 0, Height: 0"; - } - } - - public void UpdateDrawableDimensions(DrawableType type, double x, double y, double width, double height) - { - _drawableDimensions[type] = (x, y, width, height); - OnPropertyChanged(nameof(DrawableDimensions)); - } - - public void StoreDrawableDimensions(DrawableType drawableType, double x, double y, double width, double height) - { - _drawableDimensions[drawableType] = (x, y, width, height); - } - - public (double X, double Y, double Width, double Height)? GetDrawableDimensions(DrawableType drawableType) - { - return _drawableDimensions.TryGetValue(drawableType, out var dimensions) ? dimensions : null; - } - - public void UpdateDrawable(IDrawable drawable) - { - Drawable = drawable; - } - - public void UpdateDrawableDimensions(double width, double height) - { - StoreDrawableDimensions(SelectedDrawable, 0, 0, width, height); - OnPropertyChanged(nameof(DrawableDimensions)); - } - - public void UpdateDrawableDimensions(double x, double y, double width, double height) - { - StoreDrawableDimensions(SelectedDrawable, x, y, width, height); - OnPropertyChanged(nameof(DrawableDimensions)); - } + public Action RequestInvalidate { get; set; } + public string DrawableTypeLabel => SelectedDrawable.ToString(); + private DrawableType _selectedDrawable = DrawableType.Square; + private bool _isEnabled = true; + private bool _isVisible = true; + private Color _currentDrawColor = Colors.Blue; + private Random _random = new Random(); + + public event PropertyChangedEventHandler PropertyChanged; + + public Color CurrentDrawColor + { + get => _currentDrawColor; + set + { + if (_currentDrawColor != value) + { + _currentDrawColor = value; + OnPropertyChanged(); + } + } + } + + public DrawableType SelectedDrawable + { + get => _selectedDrawable; + set + { + if (_selectedDrawable != value) + { + _selectedDrawable = value; + OnPropertyChanged(); + OnPropertyChanged(nameof(DrawableTypeLabel)); + UpdateDrawable(); + RequestInvalidate?.Invoke(); + } + } + } + + public bool IsEnabled + { + get => _isEnabled; + set + { + if (_isEnabled != value) + { + _isEnabled = value; + OnPropertyChanged(); + } + } + } + + public bool IsVisible + { + get => _isVisible; + set + { + if (_isVisible != value) + { + _isVisible = value; + OnPropertyChanged(); + } + } + } + + private string _interactionEvent = "No interactions yet"; + private List _interactionHistory = new List(); + + public string InteractionEvent + { + get => _interactionEvent; + set + { + if (_interactionEvent != value) + { + _interactionEvent = value; + OnPropertyChanged(); + } + } + } + + public void AddInteractionEvent(string eventName) + { + //string timestamp = DateTime.Now.ToString("HH:mm:ss.fff"); + string eventLabel = $"{eventName}"; + + _interactionHistory.Add(eventLabel); + + // Keep only the last 10 events to prevent UI from becoming too cluttered + if (_interactionHistory.Count > 5) + { + _interactionHistory.RemoveAt(0); + } + + // Update the displayed text with all events + InteractionEvent = string.Join("\n", _interactionHistory); + } + + public void ClearInteractionHistory() + { + _interactionHistory.Clear(); + InteractionEvent = "No interactions yet"; + } + + private double _heightRequest = 100; + public double HeightRequest + { + get => _heightRequest; + set + { + if (_heightRequest != value) + { + _heightRequest = value; + OnPropertyChanged(); + } + } + } + + private double _widthRequest = 100; + public double WidthRequest + { + get => _widthRequest; + set + { + if (_widthRequest != value) + { + _widthRequest = value; + OnPropertyChanged(); + + } + } + } + + private Shadow _shadow; + public Shadow Shadow + { + get => _shadow; + set + { + if (_shadow != value) + { + _shadow = value; + OnPropertyChanged(); + } + } + } + + public IDrawable Drawable { get; private set; } + + private readonly Dictionary _drawableDimensions = new(); + + public GraphicsViewViewModel() + { + UpdateDrawable(); + } + + private void UpdateDrawable() + { + Drawable = GetDrawable(SelectedDrawable); + OnPropertyChanged(nameof(Drawable)); + + // Ensure RectF height and width match HeightRequest and WidthRequest + if (Drawable is IDrawable drawable) + { + RectF rect = new RectF(0, 0, (float)WidthRequest, (float)HeightRequest); + Debug.WriteLine($"RectF Height: {rect.Height}, Width: {rect.Width}"); + + // Notify changes for DrawableDimensions + OnPropertyChanged(nameof(DrawableDimensions)); + } + + // Force GraphicsView to redraw + RequestInvalidate?.Invoke(); + } + + private IDrawable GetDrawable(DrawableType drawableType) + { + return drawableType switch + { + DrawableType.Square => new SquareDrawable(this), + DrawableType.Triangle => new TriangleDrawable(this), + DrawableType.Ellipse => new EllipseDrawable(this), + DrawableType.Line => new LineDrawable(this), + DrawableType.String => new StringDrawable(this), + DrawableType.Image => new ImageDrawable(this), + DrawableType.TransparentEllipse => new TransparentEllipseDrawable(this), + _ => null, + }; + } + + protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + public double SquareDrawableHeight => Drawable is SquareDrawable squareDrawable ? squareDrawable.RectFHeight : 0; + public double SquareDrawableWidth => Drawable is SquareDrawable squareDrawable ? squareDrawable.RectFWidth : 0; + public double TriangleDrawableHeight => Drawable is TriangleDrawable triangleDrawable ? triangleDrawable.RectFHeight : 0; + public double TriangleDrawableWidth => Drawable is TriangleDrawable triangleDrawable ? triangleDrawable.RectFWidth : 0; + + public double DrawableHeight + { + get + { + return Drawable switch + { + SquareDrawable squareDrawable => squareDrawable.RectFHeight, + TriangleDrawable triangleDrawable => triangleDrawable.RectFHeight, + _ => 0 + }; + } + } + + public double DrawableWidth + { + get + { + return Drawable switch + { + SquareDrawable squareDrawable => squareDrawable.RectFWidth, + TriangleDrawable triangleDrawable => triangleDrawable.RectFWidth, + _ => 0 + }; + } + } + + public string DrawableDimensions + { + get + { + var dimensions = GetDrawableDimensions(SelectedDrawable); + return dimensions.HasValue + ? $"X: {dimensions.Value.X:F1}, Y: {dimensions.Value.Y:F1}, Width: {dimensions.Value.Width:F1}, Height: {dimensions.Value.Height:F1}" + : "X: 0, Y: 0, Width: 0, Height: 0"; + } + } + + public void UpdateDrawableDimensions(DrawableType type, double x, double y, double width, double height) + { + _drawableDimensions[type] = (x, y, width, height); + OnPropertyChanged(nameof(DrawableDimensions)); + } + + public void StoreDrawableDimensions(DrawableType drawableType, double x, double y, double width, double height) + { + _drawableDimensions[drawableType] = (x, y, width, height); + } + + public (double X, double Y, double Width, double Height)? GetDrawableDimensions(DrawableType drawableType) + { + return _drawableDimensions.TryGetValue(drawableType, out var dimensions) ? dimensions : null; + } + + public void UpdateDrawable(IDrawable drawable) + { + Drawable = drawable; + } + + public void UpdateDrawableDimensions(double width, double height) + { + StoreDrawableDimensions(SelectedDrawable, 0, 0, width, height); + OnPropertyChanged(nameof(DrawableDimensions)); + } + + public void UpdateDrawableDimensions(double x, double y, double width, double height) + { + StoreDrawableDimensions(SelectedDrawable, x, y, width, height); + OnPropertyChanged(nameof(DrawableDimensions)); + } } public class SquareDrawable : IDrawable { - private readonly GraphicsViewViewModel _viewModel; - - public SquareDrawable(GraphicsViewViewModel viewModel) - { - _viewModel = viewModel; - } - - public double RectFHeight { get; private set; } - public double RectFWidth { get; private set; } - - public void Draw(ICanvas canvas, RectF dirtyRect) - { - canvas.SaveState(); - - RectFHeight = dirtyRect.Height; - RectFWidth = dirtyRect.Width; - Debug.WriteLine(dirtyRect); - // Notify ViewModel about updated dimensions including X and Y - _viewModel.UpdateDrawableDimensions(dirtyRect.X, dirtyRect.Y, RectFWidth, RectFHeight); - - // Check if GraphicsView has valid dimensions (like your simple sample) - if (dirtyRect.Width <= 0 || dirtyRect.Height <= 0) - { - canvas.RestoreState(); - return; - } - - float centerX = dirtyRect.Width / 2; - float centerY = dirtyRect.Height / 2; - - // Use the actual rectangle dimensions with scaling, not just the minimum - float width = dirtyRect.Width * 0.8f; // Use 80% of width - float height = dirtyRect.Height * 0.8f; // Use 80% of height - - // Draw a rectangle that respects the actual GraphicsView proportions - float rectX = centerX - width / 2; - float rectY = centerY - height / 2; - - // SetShadow - used once across all drawables - canvas.SetShadow(new SizeF(3, 3), 5, Colors.Gray); - - // FillRoundedRectangle - using actual dimensions - canvas.FillColor = _viewModel.CurrentDrawColor; - float cornerRadius = 5; - canvas.FillRoundedRectangle(rectX, rectY, width, height, cornerRadius); - - // DrawRoundedRectangle - outline the rounded rectangle - canvas.StrokeColor = Colors.Black; - canvas.StrokeSize = 2; - canvas.DrawRoundedRectangle(rectX, rectY, width, height, cornerRadius); - - canvas.RestoreState(); - } + private readonly GraphicsViewViewModel _viewModel; + + public SquareDrawable(GraphicsViewViewModel viewModel) + { + _viewModel = viewModel; + } + + public double RectFHeight { get; private set; } + public double RectFWidth { get; private set; } + + public void Draw(ICanvas canvas, RectF dirtyRect) + { + canvas.SaveState(); + + RectFHeight = dirtyRect.Height; + RectFWidth = dirtyRect.Width; + Debug.WriteLine(dirtyRect); + // Notify ViewModel about updated dimensions including X and Y + _viewModel.UpdateDrawableDimensions(dirtyRect.X, dirtyRect.Y, RectFWidth, RectFHeight); + + // Check if GraphicsView has valid dimensions (like your simple sample) + if (dirtyRect.Width <= 0 || dirtyRect.Height <= 0) + { + canvas.RestoreState(); + return; + } + + float centerX = dirtyRect.Width / 2; + float centerY = dirtyRect.Height / 2; + + // Use the actual rectangle dimensions with scaling, not just the minimum + float width = dirtyRect.Width * 0.8f; // Use 80% of width + float height = dirtyRect.Height * 0.8f; // Use 80% of height + + // Draw a rectangle that respects the actual GraphicsView proportions + float rectX = centerX - width / 2; + float rectY = centerY - height / 2; + + // SetShadow - used once across all drawables + canvas.SetShadow(new SizeF(3, 3), 5, Colors.Gray); + + // FillRoundedRectangle - using actual dimensions + canvas.FillColor = _viewModel.CurrentDrawColor; + float cornerRadius = 5; + canvas.FillRoundedRectangle(rectX, rectY, width, height, cornerRadius); + + // DrawRoundedRectangle - outline the rounded rectangle + canvas.StrokeColor = Colors.Black; + canvas.StrokeSize = 2; + canvas.DrawRoundedRectangle(rectX, rectY, width, height, cornerRadius); + + canvas.RestoreState(); + } } public class TriangleDrawable : IDrawable { - private readonly GraphicsViewViewModel _viewModel; - - public TriangleDrawable(GraphicsViewViewModel viewModel) - { - _viewModel = viewModel; - } - - public double RectFHeight { get; private set; } - public double RectFWidth { get; private set; } - - public void Draw(ICanvas canvas, RectF dirtyRect) - { - canvas.SaveState(); - - RectFHeight = dirtyRect.Height; - RectFWidth = dirtyRect.Width; - - // Notify ViewModel about updated dimensions including X and Y - _viewModel.UpdateDrawableDimensions(dirtyRect.X, dirtyRect.Y, RectFWidth, RectFHeight); - - // Check if GraphicsView has valid dimensions - if (dirtyRect.Width <= 0 || dirtyRect.Height <= 0) - { - canvas.RestoreState(); - return; - } - - float centerX = dirtyRect.Width / 2; - float centerY = dirtyRect.Height / 2; - - // Use the actual rectangle dimensions with scaling - float width = dirtyRect.Width * 0.8f; // Use 80% of width - float height = dirtyRect.Height * 0.8f; // Use 80% of height - - // Calculate triangle bounds that respect the actual GraphicsView dimensions - float triangleX = centerX - width / 2; - float triangleY = centerY - height / 2; - - float strokeWidth = 3; - - RectF triangleRect = new RectF(triangleX, triangleY, width, height); - - // DrawPath/FillPath - triangle that fills the available rectangle - PathF path = new PathF(); - path.MoveTo(triangleRect.Left, triangleRect.Bottom); - path.LineTo(triangleRect.Right, triangleRect.Bottom); - path.LineTo(triangleRect.Center.X, triangleRect.Top); - path.Close(); - - canvas.FillColor = _viewModel.CurrentDrawColor; - canvas.FillPath(path); - - // StrokeLineJoin - used once across all drawables - canvas.StrokeColor = Colors.Black; - canvas.StrokeSize = strokeWidth; - canvas.StrokeLineJoin = LineJoin.Round; - canvas.DrawPath(path); - - canvas.RestoreState(); - } + private readonly GraphicsViewViewModel _viewModel; + + public TriangleDrawable(GraphicsViewViewModel viewModel) + { + _viewModel = viewModel; + } + + public double RectFHeight { get; private set; } + public double RectFWidth { get; private set; } + + public void Draw(ICanvas canvas, RectF dirtyRect) + { + canvas.SaveState(); + + RectFHeight = dirtyRect.Height; + RectFWidth = dirtyRect.Width; + + // Notify ViewModel about updated dimensions including X and Y + _viewModel.UpdateDrawableDimensions(dirtyRect.X, dirtyRect.Y, RectFWidth, RectFHeight); + + // Check if GraphicsView has valid dimensions + if (dirtyRect.Width <= 0 || dirtyRect.Height <= 0) + { + canvas.RestoreState(); + return; + } + + float centerX = dirtyRect.Width / 2; + float centerY = dirtyRect.Height / 2; + + // Use the actual rectangle dimensions with scaling + float width = dirtyRect.Width * 0.8f; // Use 80% of width + float height = dirtyRect.Height * 0.8f; // Use 80% of height + + // Calculate triangle bounds that respect the actual GraphicsView dimensions + float triangleX = centerX - width / 2; + float triangleY = centerY - height / 2; + + float strokeWidth = 3; + + RectF triangleRect = new RectF(triangleX, triangleY, width, height); + + // DrawPath/FillPath - triangle that fills the available rectangle + PathF path = new PathF(); + path.MoveTo(triangleRect.Left, triangleRect.Bottom); + path.LineTo(triangleRect.Right, triangleRect.Bottom); + path.LineTo(triangleRect.Center.X, triangleRect.Top); + path.Close(); + + canvas.FillColor = _viewModel.CurrentDrawColor; + canvas.FillPath(path); + + // StrokeLineJoin - used once across all drawables + canvas.StrokeColor = Colors.Black; + canvas.StrokeSize = strokeWidth; + canvas.StrokeLineJoin = LineJoin.Round; + canvas.DrawPath(path); + + canvas.RestoreState(); + } } public class EllipseDrawable : IDrawable { - private readonly GraphicsViewViewModel _viewModel; - - public EllipseDrawable(GraphicsViewViewModel viewModel) - { - _viewModel = viewModel; - } - - public void Draw(ICanvas canvas, RectF dirtyRect) - { - canvas.SaveState(); - - // Notify ViewModel about updated dimensions including X and Y - _viewModel.UpdateDrawableDimensions(dirtyRect.X, dirtyRect.Y, dirtyRect.Width, dirtyRect.Height); - - // Check if GraphicsView has valid dimensions - if (dirtyRect.Width <= 0 || dirtyRect.Height <= 0) - { - canvas.RestoreState(); - return; - } - - float centerX = dirtyRect.Width / 2; - float centerY = dirtyRect.Height / 2; - - // Use the actual rectangle dimensions with scaling - float width = dirtyRect.Width * 0.8f; // Use 80% of width - float height = dirtyRect.Height * 0.8f; // Use 80% of height - - float strokeWidth = 2; - - // Create ellipse rect that respects the actual GraphicsView dimensions - RectF ellipseRect = new RectF( - centerX - width / 2, - centerY - height / 2, - width, - height - ); - - // Fill the ellipse with the selected color - canvas.FillColor = _viewModel.CurrentDrawColor; - canvas.FillEllipse(ellipseRect); - - // DrawEllipse - outline the ellipse with proper bounds - canvas.StrokeColor = Colors.Black; - canvas.StrokeSize = strokeWidth; - canvas.DrawEllipse(ellipseRect); - - // DrawArc and FillArc - scaled to the ellipse size - float arcWidth = width / 4; - float arcHeight = height / 4; - - // Ensure arc with stroke fits within bounds - float arcStrokeWidth = 3; - float maxArcWidth = arcWidth - (arcStrokeWidth / 2 + 1); - float maxArcHeight = arcHeight - (arcStrokeWidth / 2 + 1); - - if (maxArcWidth > 0 && maxArcHeight > 0) - { - canvas.FillColor = Colors.Yellow; - canvas.FillArc(centerX - maxArcWidth, centerY - maxArcHeight, maxArcWidth * 2, maxArcHeight * 2, 0, 90, true); - - canvas.StrokeColor = Colors.Orange; - canvas.StrokeSize = arcStrokeWidth; - canvas.DrawArc(centerX - maxArcWidth, centerY - maxArcHeight, maxArcWidth * 2, maxArcHeight * 2, 180, 90, true, false); - } - - canvas.RestoreState(); - } + private readonly GraphicsViewViewModel _viewModel; + + public EllipseDrawable(GraphicsViewViewModel viewModel) + { + _viewModel = viewModel; + } + + public void Draw(ICanvas canvas, RectF dirtyRect) + { + canvas.SaveState(); + + // Notify ViewModel about updated dimensions including X and Y + _viewModel.UpdateDrawableDimensions(dirtyRect.X, dirtyRect.Y, dirtyRect.Width, dirtyRect.Height); + + // Check if GraphicsView has valid dimensions + if (dirtyRect.Width <= 0 || dirtyRect.Height <= 0) + { + canvas.RestoreState(); + return; + } + + float centerX = dirtyRect.Width / 2; + float centerY = dirtyRect.Height / 2; + + // Use the actual rectangle dimensions with scaling + float width = dirtyRect.Width * 0.8f; // Use 80% of width + float height = dirtyRect.Height * 0.8f; // Use 80% of height + + float strokeWidth = 2; + + // Create ellipse rect that respects the actual GraphicsView dimensions + RectF ellipseRect = new RectF( + centerX - width / 2, + centerY - height / 2, + width, + height + ); + + // Fill the ellipse with the selected color + canvas.FillColor = _viewModel.CurrentDrawColor; + canvas.FillEllipse(ellipseRect); + + // DrawEllipse - outline the ellipse with proper bounds + canvas.StrokeColor = Colors.Black; + canvas.StrokeSize = strokeWidth; + canvas.DrawEllipse(ellipseRect); + + // DrawArc and FillArc - scaled to the ellipse size + float arcWidth = width / 4; + float arcHeight = height / 4; + + // Ensure arc with stroke fits within bounds + float arcStrokeWidth = 3; + float maxArcWidth = arcWidth - (arcStrokeWidth / 2 + 1); + float maxArcHeight = arcHeight - (arcStrokeWidth / 2 + 1); + + if (maxArcWidth > 0 && maxArcHeight > 0) + { + canvas.FillColor = Colors.Yellow; + canvas.FillArc(centerX - maxArcWidth, centerY - maxArcHeight, maxArcWidth * 2, maxArcHeight * 2, 0, 90, true); + + canvas.StrokeColor = Colors.Orange; + canvas.StrokeSize = arcStrokeWidth; + canvas.DrawArc(centerX - maxArcWidth, centerY - maxArcHeight, maxArcWidth * 2, maxArcHeight * 2, 180, 90, true, false); + } + + canvas.RestoreState(); + } } public class LineDrawable : IDrawable { - private readonly GraphicsViewViewModel _viewModel; - - public LineDrawable(GraphicsViewViewModel viewModel) - { - _viewModel = viewModel; - } - - public void Draw(ICanvas canvas, RectF dirtyRect) - { - canvas.SaveState(); - - // Notify ViewModel about updated dimensions including X and Y - _viewModel.UpdateDrawableDimensions(dirtyRect.X, dirtyRect.Y, dirtyRect.Width, dirtyRect.Height); - - // Check if GraphicsView has valid dimensions - if (dirtyRect.Width <= 0 || dirtyRect.Height <= 0) - { - canvas.RestoreState(); - return; - } - - float centerX = dirtyRect.Width / 2; - float centerY = dirtyRect.Height / 2; - float size = Math.Min(dirtyRect.Width, dirtyRect.Height) * 0.7f; - - float strokeWidth = 4; - float padding = strokeWidth / 2 + 1; - - // Calculate line bounds centered in the canvas - float lineSize = size; - float lineX1 = centerX - lineSize / 2; - float lineY1 = centerY - lineSize / 2; - float lineX2 = centerX + lineSize / 2; - float lineY2 = centerY + lineSize / 2; - - // StrokeLineCap and StrokeDashPattern - used once across all drawables - canvas.StrokeColor = _viewModel.CurrentDrawColor; - canvas.StrokeSize = strokeWidth; - canvas.StrokeLineCap = LineCap.Round; - canvas.StrokeDashPattern = new float[] { 15, 5, 10, 5 }; - - // Draw diagonal line centered in the canvas - canvas.DrawLine(lineX1, lineY1, lineX2, lineY2); - - canvas.RestoreState(); - } + private readonly GraphicsViewViewModel _viewModel; + + public LineDrawable(GraphicsViewViewModel viewModel) + { + _viewModel = viewModel; + } + + public void Draw(ICanvas canvas, RectF dirtyRect) + { + canvas.SaveState(); + + // Notify ViewModel about updated dimensions including X and Y + _viewModel.UpdateDrawableDimensions(dirtyRect.X, dirtyRect.Y, dirtyRect.Width, dirtyRect.Height); + + // Check if GraphicsView has valid dimensions + if (dirtyRect.Width <= 0 || dirtyRect.Height <= 0) + { + canvas.RestoreState(); + return; + } + + float centerX = dirtyRect.Width / 2; + float centerY = dirtyRect.Height / 2; + float size = Math.Min(dirtyRect.Width, dirtyRect.Height) * 0.7f; + + float strokeWidth = 4; + float padding = strokeWidth / 2 + 1; + + // Calculate line bounds centered in the canvas + float lineSize = size; + float lineX1 = centerX - lineSize / 2; + float lineY1 = centerY - lineSize / 2; + float lineX2 = centerX + lineSize / 2; + float lineY2 = centerY + lineSize / 2; + + // StrokeLineCap and StrokeDashPattern - used once across all drawables + canvas.StrokeColor = _viewModel.CurrentDrawColor; + canvas.StrokeSize = strokeWidth; + canvas.StrokeLineCap = LineCap.Round; + canvas.StrokeDashPattern = new float[] { 15, 5, 10, 5 }; + + // Draw diagonal line centered in the canvas + canvas.DrawLine(lineX1, lineY1, lineX2, lineY2); + + canvas.RestoreState(); + } } public class StringDrawable : IDrawable { - private readonly GraphicsViewViewModel _viewModel; - - public StringDrawable(GraphicsViewViewModel viewModel) - { - _viewModel = viewModel; - } - - public void Draw(ICanvas canvas, RectF dirtyRect) - { - canvas.SaveState(); - - // Notify ViewModel about updated dimensions including X and Y - _viewModel.UpdateDrawableDimensions(dirtyRect.X, dirtyRect.Y, dirtyRect.Width, dirtyRect.Height); - - // Check if GraphicsView has valid dimensions - if (dirtyRect.Width <= 0 || dirtyRect.Height <= 0) - { - canvas.RestoreState(); - return; - } - - // Calculate center and size for the string drawable (like your simple sample) - float centerX = dirtyRect.Width / 2; - float centerY = dirtyRect.Height / 2; - float size = Math.Min(dirtyRect.Width, dirtyRect.Height) * 0.7f; - - // ClipPath - used once across all drawables - PathF clipPath = new PathF(); - float clipSize = size; - float clipX = centerX - clipSize / 2; - float clipY = centerY - clipSize / 2; - - clipPath.MoveTo(clipX + 10, clipY); - clipPath.LineTo(clipX + clipSize - 10, clipY); - clipPath.LineTo(clipX + clipSize, clipY + 10); - clipPath.LineTo(clipX + clipSize, clipY + clipSize - 10); - clipPath.LineTo(clipX + clipSize - 10, clipY + clipSize); - clipPath.LineTo(clipX + 10, clipY + clipSize); - clipPath.LineTo(clipX, clipY + clipSize - 10); - clipPath.LineTo(clipX, clipY + 10); - clipPath.Close(); - canvas.ClipPath(clipPath); - - float maxRadius = size / 3; - - float strokeWidth = 2; - float circleRadius = maxRadius - (strokeWidth / 2 + 1); - - if (circleRadius > 0) - { - canvas.FillColor = Colors.LightYellow; - canvas.FillCircle(centerX, centerY, circleRadius); - - canvas.StrokeColor = Colors.Blue; - canvas.StrokeSize = strokeWidth; - canvas.DrawCircle(centerX, centerY, circleRadius); - - canvas.FontColor = Colors.Blue; - canvas.FontSize = 14; - canvas.Font = Microsoft.Maui.Graphics.Font.Default; - - string displayText = "Hello"; - - // Position text at the center of the circle, not relative to the clip area - float textWidth = circleRadius * 2; // Use full circle diameter for text width - float textHeight = 20; // Reduce height to fit better - float textX = centerX - textWidth / 2; - float textY = centerY - textHeight / 2; - - // Ensure text area is valid and draw it centered in the circle - if (textWidth > 0 && textHeight > 0) - { - canvas.DrawString(displayText, textX, textY, textWidth, textHeight, HorizontalAlignment.Center, VerticalAlignment.Center); - } - } - - canvas.RestoreState(); - } + private readonly GraphicsViewViewModel _viewModel; + + public StringDrawable(GraphicsViewViewModel viewModel) + { + _viewModel = viewModel; + } + + public void Draw(ICanvas canvas, RectF dirtyRect) + { + canvas.SaveState(); + + // Notify ViewModel about updated dimensions including X and Y + _viewModel.UpdateDrawableDimensions(dirtyRect.X, dirtyRect.Y, dirtyRect.Width, dirtyRect.Height); + + // Check if GraphicsView has valid dimensions + if (dirtyRect.Width <= 0 || dirtyRect.Height <= 0) + { + canvas.RestoreState(); + return; + } + + // Calculate center and size for the string drawable (like your simple sample) + float centerX = dirtyRect.Width / 2; + float centerY = dirtyRect.Height / 2; + float size = Math.Min(dirtyRect.Width, dirtyRect.Height) * 0.7f; + + // ClipPath - used once across all drawables + PathF clipPath = new PathF(); + float clipSize = size; + float clipX = centerX - clipSize / 2; + float clipY = centerY - clipSize / 2; + + clipPath.MoveTo(clipX + 10, clipY); + clipPath.LineTo(clipX + clipSize - 10, clipY); + clipPath.LineTo(clipX + clipSize, clipY + 10); + clipPath.LineTo(clipX + clipSize, clipY + clipSize - 10); + clipPath.LineTo(clipX + clipSize - 10, clipY + clipSize); + clipPath.LineTo(clipX + 10, clipY + clipSize); + clipPath.LineTo(clipX, clipY + clipSize - 10); + clipPath.LineTo(clipX, clipY + 10); + clipPath.Close(); + canvas.ClipPath(clipPath); + + float maxRadius = size / 3; + + float strokeWidth = 2; + float circleRadius = maxRadius - (strokeWidth / 2 + 1); + + if (circleRadius > 0) + { + canvas.FillColor = Colors.LightYellow; + canvas.FillCircle(centerX, centerY, circleRadius); + + canvas.StrokeColor = Colors.Blue; + canvas.StrokeSize = strokeWidth; + canvas.DrawCircle(centerX, centerY, circleRadius); + + canvas.FontColor = Colors.Blue; + canvas.FontSize = 14; + canvas.Font = Microsoft.Maui.Graphics.Font.Default; + + string displayText = "Hello"; + + // Position text at the center of the circle, not relative to the clip area + float textWidth = circleRadius * 2; // Use full circle diameter for text width + float textHeight = 20; // Reduce height to fit better + float textX = centerX - textWidth / 2; + float textY = centerY - textHeight / 2; + + // Ensure text area is valid and draw it centered in the circle + if (textWidth > 0 && textHeight > 0) + { + canvas.DrawString(displayText, textX, textY, textWidth, textHeight, HorizontalAlignment.Center, VerticalAlignment.Center); + } + } + + canvas.RestoreState(); + } } public class ImageDrawable : IDrawable { - private readonly GraphicsViewViewModel _viewModel; - private Microsoft.Maui.Graphics.IImage _image; - - public ImageDrawable(GraphicsViewViewModel viewModel) - { - _viewModel = viewModel; - LoadImage(); - } - - private void LoadImage() - { - try - { - var assembly = GetType().GetTypeInfo().Assembly; - - // Try different possible resource names - string[] possibleNames = { - "Maui.Controls.Sample.Resources.Images.royals.png", - "Controls.TestCases.HostApp.Resources.Images.royals.png", - "royals.png", - "Resources.Images.royals.png" - }; - - foreach (var resourceName in possibleNames) - { - using (var stream = assembly.GetManifestResourceStream(resourceName)) - { - if (stream != null) - { - _image = Microsoft.Maui.Graphics.Platform.PlatformImage.FromStream(stream); - Debug.WriteLine($"Successfully loaded image with resource name: {resourceName}"); - return; - } - } - } - - // If we get here, none of the resource names worked - Debug.WriteLine("Could not find embedded image resource. Available resources:"); - foreach (var name in assembly.GetManifestResourceNames()) - { - Debug.WriteLine($" - {name}"); - } - } - catch (Exception ex) - { - Debug.WriteLine($"Error loading image: {ex.Message}"); - _image = null; - } - } - - public void Draw(ICanvas canvas, RectF dirtyRect) - { - canvas.SaveState(); - - // Notify ViewModel about updated dimensions including X and Y - _viewModel.UpdateDrawableDimensions(dirtyRect.X, dirtyRect.Y, dirtyRect.Width, dirtyRect.Height); - - // Check if GraphicsView has valid dimensions - if (dirtyRect.Width <= 0 || dirtyRect.Height <= 0) - { - canvas.RestoreState(); - return; - } - - // Calculate center and size for the image (like your simple sample) - float centerX = dirtyRect.Width / 2; - float centerY = dirtyRect.Height / 2; - float size = Math.Min(dirtyRect.Width, dirtyRect.Height) * 0.7f; - - // Draw a simple background frame - float frameWidth = 10; - float frameSize = size; - float frameX = centerX - frameSize / 2; - float frameY = centerY - frameSize / 2; - - // Draw frame background - canvas.FillColor = Colors.Gold; - canvas.FillRectangle(frameX, frameY, frameSize, frameSize); - - // Draw inner area for the image - float innerX = frameX + frameWidth; - float innerY = frameY + frameWidth; - float innerSize = frameSize - (2 * frameWidth); - - canvas.FillColor = Colors.LightGray; - canvas.FillRectangle(innerX, innerY, innerSize, innerSize); - - if (_image != null) - { - // Calculate image position and size to fit within the inner area - float padding = 5; - float availableWidth = innerSize - (2 * padding); - float availableHeight = innerSize - (2 * padding); - - if (availableWidth > 0 && availableHeight > 0) - { - // Calculate scale to fit image while maintaining aspect ratio - float scaleX = availableWidth / _image.Width; - float scaleY = availableHeight / _image.Height; - float scale = Math.Min(scaleX, scaleY); - - float imageWidth = _image.Width * scale; - float imageHeight = _image.Height * scale; - - // Center the image within the inner area - float imageX = innerX + (innerSize - imageWidth) / 2; - float imageY = innerY + (innerSize - imageHeight) / 2; - - // Draw the actual image - canvas.DrawImage(_image, imageX, imageY, imageWidth, imageHeight); - - Debug.WriteLine($"Drew image at ({imageX}, {imageY}) with size ({imageWidth}, {imageHeight})"); - } - } - else - { - // Fallback: draw text if image couldn't be loaded - canvas.FontColor = Colors.Red; - canvas.FontSize = 12; - canvas.Font = Microsoft.Maui.Graphics.Font.Default; - - string errorText = "Image not found"; - float textX = innerX + 5; - float textY = innerY + 20; - float textWidth = innerSize - 10; - float textHeight = 20; - - if (textWidth > 0 && textHeight > 0) - { - canvas.DrawString(errorText, textX, textY, textWidth, textHeight, HorizontalAlignment.Center, VerticalAlignment.Top); - } - - Debug.WriteLine("Image could not be loaded, showing error message"); - } - - canvas.RestoreState(); - } + private readonly GraphicsViewViewModel _viewModel; + private Microsoft.Maui.Graphics.IImage _image; + + public ImageDrawable(GraphicsViewViewModel viewModel) + { + _viewModel = viewModel; + LoadImage(); + } + + private void LoadImage() + { + try + { + var assembly = GetType().GetTypeInfo().Assembly; + + // Try different possible resource names + string[] possibleNames = { + "Maui.Controls.Sample.Resources.Images.royals.png", + "Controls.TestCases.HostApp.Resources.Images.royals.png", + "royals.png", + "Resources.Images.royals.png" + }; + + foreach (var resourceName in possibleNames) + { + using (var stream = assembly.GetManifestResourceStream(resourceName)) + { + if (stream != null) + { + _image = Microsoft.Maui.Graphics.Platform.PlatformImage.FromStream(stream); + Debug.WriteLine($"Successfully loaded image with resource name: {resourceName}"); + return; + } + } + } + + // If we get here, none of the resource names worked + Debug.WriteLine("Could not find embedded image resource. Available resources:"); + foreach (var name in assembly.GetManifestResourceNames()) + { + Debug.WriteLine($" - {name}"); + } + } + catch (Exception ex) + { + Debug.WriteLine($"Error loading image: {ex.Message}"); + _image = null; + } + } + + public void Draw(ICanvas canvas, RectF dirtyRect) + { + canvas.SaveState(); + + // Notify ViewModel about updated dimensions including X and Y + _viewModel.UpdateDrawableDimensions(dirtyRect.X, dirtyRect.Y, dirtyRect.Width, dirtyRect.Height); + + // Check if GraphicsView has valid dimensions + if (dirtyRect.Width <= 0 || dirtyRect.Height <= 0) + { + canvas.RestoreState(); + return; + } + + // Calculate center and size for the image (like your simple sample) + float centerX = dirtyRect.Width / 2; + float centerY = dirtyRect.Height / 2; + float size = Math.Min(dirtyRect.Width, dirtyRect.Height) * 0.7f; + + // Draw a simple background frame + float frameWidth = 10; + float frameSize = size; + float frameX = centerX - frameSize / 2; + float frameY = centerY - frameSize / 2; + + // Draw frame background + canvas.FillColor = Colors.Gold; + canvas.FillRectangle(frameX, frameY, frameSize, frameSize); + + // Draw inner area for the image + float innerX = frameX + frameWidth; + float innerY = frameY + frameWidth; + float innerSize = frameSize - (2 * frameWidth); + + canvas.FillColor = Colors.LightGray; + canvas.FillRectangle(innerX, innerY, innerSize, innerSize); + + if (_image != null) + { + // Calculate image position and size to fit within the inner area + float padding = 5; + float availableWidth = innerSize - (2 * padding); + float availableHeight = innerSize - (2 * padding); + + if (availableWidth > 0 && availableHeight > 0) + { + // Calculate scale to fit image while maintaining aspect ratio + float scaleX = availableWidth / _image.Width; + float scaleY = availableHeight / _image.Height; + float scale = Math.Min(scaleX, scaleY); + + float imageWidth = _image.Width * scale; + float imageHeight = _image.Height * scale; + + // Center the image within the inner area + float imageX = innerX + (innerSize - imageWidth) / 2; + float imageY = innerY + (innerSize - imageHeight) / 2; + + // Draw the actual image + canvas.DrawImage(_image, imageX, imageY, imageWidth, imageHeight); + + Debug.WriteLine($"Drew image at ({imageX}, {imageY}) with size ({imageWidth}, {imageHeight})"); + } + } + else + { + // Fallback: draw text if image couldn't be loaded + canvas.FontColor = Colors.Red; + canvas.FontSize = 12; + canvas.Font = Microsoft.Maui.Graphics.Font.Default; + + string errorText = "Image not found"; + float textX = innerX + 5; + float textY = innerY + 20; + float textWidth = innerSize - 10; + float textHeight = 20; + + if (textWidth > 0 && textHeight > 0) + { + canvas.DrawString(errorText, textX, textY, textWidth, textHeight, HorizontalAlignment.Center, VerticalAlignment.Top); + } + + Debug.WriteLine("Image could not be loaded, showing error message"); + } + + canvas.RestoreState(); + } } public class TransparentEllipseDrawable : IDrawable { - private readonly GraphicsViewViewModel _viewModel; - - public TransparentEllipseDrawable(GraphicsViewViewModel viewModel) - { - _viewModel = viewModel; - } - - public void Draw(ICanvas canvas, RectF dirtyRect) - { - canvas.SaveState(); - - // Notify ViewModel about updated dimensions including X and Y - _viewModel.UpdateDrawableDimensions(dirtyRect.X, dirtyRect.Y, dirtyRect.Width, dirtyRect.Height); - - // Check if GraphicsView has valid dimensions - if (dirtyRect.Width <= 0 || dirtyRect.Height <= 0) - { - canvas.RestoreState(); - return; - } - - // Calculate center and size for the ellipse (following the scaling pattern) - float centerX = dirtyRect.Width / 2; - float centerY = dirtyRect.Height / 2; - float size = Math.Min(dirtyRect.Width, dirtyRect.Height) * 0.7f; - - float ellipseSize = size * 0.8f; // Make it slightly smaller for better visibility - float ellipseX = centerX - ellipseSize / 2; - float ellipseY = centerY - ellipseSize / 2; - var outerRect = new RectF(ellipseX, ellipseY, ellipseSize, ellipseSize); - - // Set transparent fill color - this demonstrates the issue on Android - Color transparentColor = Colors.Transparent; - - canvas.SetFillPaint(transparentColor.AsPaint(), outerRect); - - canvas.SetShadow(new SizeF(0, 2), - Microsoft.Maui.Devices.DeviceInfo.Platform == Microsoft.Maui.Devices.DevicePlatform.Android ? 4 : 3, - Color.FromArgb("#59000000")); - - // Draw the transparent ellipse with shadow - canvas.FillEllipse(outerRect.X, outerRect.Y, outerRect.Width, outerRect.Height); - - float arcSize = ellipseSize * 0.5f; - float arcX = outerRect.X + (outerRect.Width - arcSize) / 2; - float arcY = outerRect.Y + (outerRect.Height - arcSize) / 2; - - canvas.FillColor = Colors.Blue; - canvas.DrawArc(arcX, arcY, arcSize, arcSize, 45, 90, true, false); - - canvas.RestoreState(); - } + private readonly GraphicsViewViewModel _viewModel; + + public TransparentEllipseDrawable(GraphicsViewViewModel viewModel) + { + _viewModel = viewModel; + } + + public void Draw(ICanvas canvas, RectF dirtyRect) + { + canvas.SaveState(); + + // Notify ViewModel about updated dimensions including X and Y + _viewModel.UpdateDrawableDimensions(dirtyRect.X, dirtyRect.Y, dirtyRect.Width, dirtyRect.Height); + + // Check if GraphicsView has valid dimensions + if (dirtyRect.Width <= 0 || dirtyRect.Height <= 0) + { + canvas.RestoreState(); + return; + } + + // Calculate center and size for the ellipse (following the scaling pattern) + float centerX = dirtyRect.Width / 2; + float centerY = dirtyRect.Height / 2; + float size = Math.Min(dirtyRect.Width, dirtyRect.Height) * 0.7f; + + float ellipseSize = size * 0.8f; // Make it slightly smaller for better visibility + float ellipseX = centerX - ellipseSize / 2; + float ellipseY = centerY - ellipseSize / 2; + var outerRect = new RectF(ellipseX, ellipseY, ellipseSize, ellipseSize); + + // Set transparent fill color - this demonstrates the issue on Android + Color transparentColor = Colors.Transparent; + + canvas.SetFillPaint(transparentColor.AsPaint(), outerRect); + + canvas.SetShadow(new SizeF(0, 2), + Microsoft.Maui.Devices.DeviceInfo.Platform == Microsoft.Maui.Devices.DevicePlatform.Android ? 4 : 3, + Color.FromArgb("#59000000")); + + // Draw the transparent ellipse with shadow + canvas.FillEllipse(outerRect.X, outerRect.Y, outerRect.Width, outerRect.Height); + + float arcSize = ellipseSize * 0.5f; + float arcX = outerRect.X + (outerRect.Width - arcSize) / 2; + float arcY = outerRect.Y + (outerRect.Height - arcSize) / 2; + + canvas.FillColor = Colors.Blue; + canvas.DrawArc(arcX, arcY, arcSize, arcSize, 45, 90, true, false); + + canvas.RestoreState(); + } } diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/GraphicsViewFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/GraphicsViewFeatureTests.cs index 3aab2739eacf..c2071396bfd7 100644 --- a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/GraphicsViewFeatureTests.cs +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/GraphicsViewFeatureTests.cs @@ -148,7 +148,7 @@ public void GraphicsView_ImageDrawable_VerifyTypeAndRendering() VerifyShapeScreenshot(); } -#if TEST_FAILS_ON_ANDROID //See issue : https://github.com/dotnet/maui/issues/29394 +#if TEST_FAILS_ON_ANDROID //See issue : https://github.com/dotnet/maui/issues/29394 [Test] [Category(UITestCategories.GraphicsView)] public void GraphicsView_TransparentEllipseDrawable_VerifyTypeAndRendering() @@ -267,119 +267,119 @@ public void GraphicsView_ChangeBothDimensions_VerifyDimensionsUpdate() #region Shadow Tests #if TEST_FAILS_ON_WINDOWS -// Note: Shadow tests are currently disabled on Windows due to known issues with GraphicsView -//See Issue : https://github.com/dotnet/maui/issues/30778 - [Test] - [Category(UITestCategories.GraphicsView)] - public void GraphicsView_SetShadowProperties_VerifyVisualState() - { - App.WaitForElement("Options"); - App.Tap("Options"); - App.WaitForElement("Triangle"); - App.Tap("Triangle"); - App.WaitForElement("ShadowInputEntry"); - App.EnterText("ShadowInputEntry", "5,5,10,0.5"); - App.WaitForElement("Apply"); - App.Tap("Apply"); - App.WaitForElement("DrawableTypeLabel"); - App.Tap("DrawableTypeLabel"); - VerifyShapeScreenshot(); - } + // Note: Shadow tests are currently disabled on Windows due to known issues with GraphicsView + //See Issue : https://github.com/dotnet/maui/issues/30778 + [Test] + [Category(UITestCategories.GraphicsView)] + public void GraphicsView_SetShadowProperties_VerifyVisualState() + { + App.WaitForElement("Options"); + App.Tap("Options"); + App.WaitForElement("Triangle"); + App.Tap("Triangle"); + App.WaitForElement("ShadowInputEntry"); + App.EnterText("ShadowInputEntry", "5,5,10,0.5"); + App.WaitForElement("Apply"); + App.Tap("Apply"); + App.WaitForElement("DrawableTypeLabel"); + App.Tap("DrawableTypeLabel"); + VerifyShapeScreenshot(); + } - [Test] - [Category(UITestCategories.GraphicsView)] - public void GraphicsView_SetInvalidShadowProperties_VerifyGracefulHandling() - { - App.WaitForElement("Options"); - App.Tap("Options"); - App.WaitForElement("Triangle"); - App.Tap("Triangle"); - App.WaitForElement("ShadowInputEntry"); - App.EnterText("ShadowInputEntry", "invalid,input"); - App.WaitForElement("Apply"); - App.Tap("Apply"); - App.WaitForElement("DrawableTypeLabel"); - App.Tap("DrawableTypeLabel"); - VerifyShapeScreenshot(); - } + [Test] + [Category(UITestCategories.GraphicsView)] + public void GraphicsView_SetInvalidShadowProperties_VerifyGracefulHandling() + { + App.WaitForElement("Options"); + App.Tap("Options"); + App.WaitForElement("Triangle"); + App.Tap("Triangle"); + App.WaitForElement("ShadowInputEntry"); + App.EnterText("ShadowInputEntry", "invalid,input"); + App.WaitForElement("Apply"); + App.Tap("Apply"); + App.WaitForElement("DrawableTypeLabel"); + App.Tap("DrawableTypeLabel"); + VerifyShapeScreenshot(); + } #endif #endregion #if TEST_FAILS_ON_WINDOWS //Note:These tests are currently disabled on Windows due to a Graphicsview automationid doesn't work. - - #region Interaction Event Tests - [Test] - [Category(UITestCategories.GraphicsView)] - public void GraphicsView_StartInteraction_VerifyEventTriggered() - { - App.WaitForElement("ClearEventsButton"); - App.Tap("ClearEventsButton"); - App.WaitForElement("GraphicsViewControl"); - App.Tap("GraphicsViewControl"); - - var interactionLabel = App.FindElement("InteractionEventLabel"); - Assert.That(interactionLabel.GetText(), Does.Contain("StartInteraction")); - } + #region Interaction Event Tests - [Test] - [Category(UITestCategories.GraphicsView)] - public void GraphicsView_EndInteraction_VerifyEventTriggered() - { - App.WaitForElement("ClearEventsButton"); - App.Tap("ClearEventsButton"); - App.WaitForElement("GraphicsViewControl"); - App.Tap("GraphicsViewControl"); - - var interactionLabel = App.FindElement("InteractionEventLabel"); - Assert.That(interactionLabel.GetText(), Does.Contain("EndInteraction")); - } + [Test] + [Category(UITestCategories.GraphicsView)] + public void GraphicsView_StartInteraction_VerifyEventTriggered() + { + App.WaitForElement("ClearEventsButton"); + App.Tap("ClearEventsButton"); + App.WaitForElement("GraphicsViewControl"); + App.Tap("GraphicsViewControl"); - [Test] - [Category(UITestCategories.GraphicsView)] - [Category(UITestCategories.Gestures)] - public void GraphicsView_DragInteraction_VerifyEventTriggered() - { - App.WaitForElement("ClearEventsButton"); - App.Tap("ClearEventsButton"); - App.WaitForElement("GraphicsViewControl"); - - // Perform a longer drag gesture with multiple points to ensure DragInteraction is triggered - var graphicsView = App.FindElement("GraphicsViewControl"); - var bounds = graphicsView.GetRect(); - - var startX = (float)(bounds.X + (bounds.Width * 0.3)); - var startY = (float)(bounds.Y + (bounds.Height * 0.3)); - var endX = (float)(bounds.X + (bounds.Width * 0.7)); - var endY = (float)(bounds.Y + (bounds.Height * 0.7)); - - App.DragCoordinates(startX, startY, endX, endY); - - var interactionLabel = App.FindElement("InteractionEventLabel"); - var eventText = interactionLabel.GetText(); - - Assert.That(eventText, Does.Contain("DragInteraction")); - } - #endregion + var interactionLabel = App.FindElement("InteractionEventLabel"); + Assert.That(interactionLabel.GetText(), Does.Contain("StartInteraction")); + } - #region IsEnabled Tests + [Test] + [Category(UITestCategories.GraphicsView)] + public void GraphicsView_EndInteraction_VerifyEventTriggered() + { + App.WaitForElement("ClearEventsButton"); + App.Tap("ClearEventsButton"); + App.WaitForElement("GraphicsViewControl"); + App.Tap("GraphicsViewControl"); - [Test] - [Category(UITestCategories.GraphicsView)] - public void GraphicsView_SetEnabledToTrue_VerifyEnabledState() - { - App.WaitForElement("Options"); - App.Tap("Options"); - App.WaitForElement("IsEnabledTrueRadio"); - App.Tap("IsEnabledTrueRadio"); - App.WaitForElement("Apply"); - App.Tap("Apply"); - App.WaitForElement("Options"); + var interactionLabel = App.FindElement("InteractionEventLabel"); + Assert.That(interactionLabel.GetText(), Does.Contain("EndInteraction")); + } - var graphicsView = App.FindElement("GraphicsViewControl"); - Assert.That(graphicsView.IsEnabled(), Is.True); - } + [Test] + [Category(UITestCategories.GraphicsView)] + [Category(UITestCategories.Gestures)] + public void GraphicsView_DragInteraction_VerifyEventTriggered() + { + App.WaitForElement("ClearEventsButton"); + App.Tap("ClearEventsButton"); + App.WaitForElement("GraphicsViewControl"); + + // Perform a longer drag gesture with multiple points to ensure DragInteraction is triggered + var graphicsView = App.FindElement("GraphicsViewControl"); + var bounds = graphicsView.GetRect(); + + var startX = (float)(bounds.X + (bounds.Width * 0.3)); + var startY = (float)(bounds.Y + (bounds.Height * 0.3)); + var endX = (float)(bounds.X + (bounds.Width * 0.7)); + var endY = (float)(bounds.Y + (bounds.Height * 0.7)); + + App.DragCoordinates(startX, startY, endX, endY); + + var interactionLabel = App.FindElement("InteractionEventLabel"); + var eventText = interactionLabel.GetText(); + + Assert.That(eventText, Does.Contain("DragInteraction")); + } + #endregion + + #region IsEnabled Tests + + [Test] + [Category(UITestCategories.GraphicsView)] + public void GraphicsView_SetEnabledToTrue_VerifyEnabledState() + { + App.WaitForElement("Options"); + App.Tap("Options"); + App.WaitForElement("IsEnabledTrueRadio"); + App.Tap("IsEnabledTrueRadio"); + App.WaitForElement("Apply"); + App.Tap("Apply"); + App.WaitForElement("Options"); + + var graphicsView = App.FindElement("GraphicsViewControl"); + Assert.That(graphicsView.IsEnabled(), Is.True); + } #if TEST_FAILS_ON_MACCATALYST && TEST_FAILS_ON_IOS && TEST_FAILS_ON_ANDROID //See Issue : https://github.com/dotnet/maui/issues/30649 @@ -487,8 +487,8 @@ public void GraphicsView_SetDimensionsEdgeCases_VerifyHandling(string height, st #if WINDOWS VerifyShapeScreenshot(); #else - var graphicsView = App.FindElement("GraphicsViewControl"); - Assert.That(graphicsView, Is.Not.Null); + var graphicsView = App.FindElement("GraphicsViewControl"); + Assert.That(graphicsView, Is.Not.Null); #endif } else diff --git a/src/Core/src/Platform/Android/MauiHybridWebViewClient.cs b/src/Core/src/Platform/Android/MauiHybridWebViewClient.cs index 15868adeb787..a83ac2547c63 100644 --- a/src/Core/src/Platform/Android/MauiHybridWebViewClient.cs +++ b/src/Core/src/Platform/Android/MauiHybridWebViewClient.cs @@ -124,7 +124,7 @@ public MauiHybridWebViewClient(HybridWebViewHandler handler) return null; } } - + private protected static IDictionary GetHeaders(string contentType) => new Dictionary { { "Content-Type", contentType }, diff --git a/src/Core/src/Platform/iOS/MauiScrollView.cs b/src/Core/src/Platform/iOS/MauiScrollView.cs index aeb44b769951..882f2a30a1a9 100644 --- a/src/Core/src/Platform/iOS/MauiScrollView.cs +++ b/src/Core/src/Platform/iOS/MauiScrollView.cs @@ -78,7 +78,7 @@ public override void LayoutSubviews() crossPlatformLayout.CrossPlatformArrange(new Rect(new Point(-horizontalOffset, 0), crossPlatformBounds)); ContentOffset = new CGPoint(horizontalOffset, 0); } - else if(_previousEffectiveUserInterfaceLayoutDirection is not null) + else if (_previousEffectiveUserInterfaceLayoutDirection is not null) { ContentOffset = new CGPoint(0, ContentOffset.Y); }