Skip to content

Conversation

@SyedAbdulAzeemSF4852
Copy link
Contributor

Description of Change

  • This pull request introduces support for a Material Design 3 editor (MaterialEditorHandler) on Android, enabling the use of Material-styled text editors when the Material 3 feature is enabled. It includes the implementation of the new handler, integration into the handler registration pipeline, and updates to property mapping logic to support the new handler.

Material Design 3 Editor Handler Integration:

  • Added a new MaterialEditorHandler class for Android, implementing Material Design 3 styling and behavior for the Editor control, including property and command mappers, lifecycle management, and event handling.
  • Introduced a new MauiMaterialEditText class, extending TextInputEditText to support selection change events required by MaterialEditorHandler.

Handler Registration and Property Mapping:

  • Updated the handler registration logic to conditionally use MaterialEditorHandler for Editor controls when Material 3 is enabled, otherwise defaulting to the standard EditorHandler.
  • Modified property mapping setup to register MaterialEditorHandler mappings for text-related properties (Text, TextTransform) when Material 3 is enabled on Android.
  • Added a Material-specific overload for MapText to handle text updates correctly in the context of the new handler.

Issues Fixed

Fixes #33476

Output

Material2 Material3

@dotnet-policy-service dotnet-policy-service bot added community ✨ Community Contribution partner/syncfusion Issues / PR's with Syncfusion collaboration labels Jan 12, 2026
@sheiksyedm sheiksyedm marked this pull request as ready for review January 12, 2026 13:28
Copilot AI review requested due to automatic review settings January 12, 2026 13:28
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request implements Material Design 3 support for the Editor control on Android by introducing a new MaterialEditorHandler and MauiMaterialEditText control. The implementation enables Material 3 styling for editors when the Material 3 runtime feature is enabled, providing a more modern and consistent UI experience.

Changes:

  • Added MauiMaterialEditText class that extends TextInputEditText to support Material 3 styling with selection change events
  • Implemented MaterialEditorHandler with full property and command mapping support for Material 3 editors
  • Updated handler registration logic to conditionally use MaterialEditorHandler when Material 3 is enabled
  • Extended property mapping to support the new Material 3 handler for text-related properties

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/Core/src/Platform/Android/Material3Controls/MauiMaterialEditText.cs New Material 3 EditText control with selection change event support
src/Core/src/Handlers/Editor/MaterialEditorHandler.Android.cs New handler implementing Material 3 styling and behavior for Editor control
src/Controls/src/Core/Hosting/AppHostBuilderExtensions.cs Conditional handler registration based on Material 3 feature flag
src/Controls/src/Core/Editor/Editor.Mapper.cs Property mapping registration for Material 3 handler
src/Controls/src/Core/Editor/Editor.Android.cs Material 3-specific MapText implementation with data flow handling

if (RuntimeFeature.IsMaterial3Enabled)
{
MaterialEditorHandler.Mapper.ReplaceMapping<Editor, MaterialEditorHandler>(nameof(Text), MapText);
MaterialEditorHandler.Mapper.ReplaceMapping<Editor, MaterialEditorHandler>(nameof(TextTransform), MapText);
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing Focus command mapping for MaterialEditorHandler. The standard EditorHandler has a Focus command mapping appended at line 31 of Editor.Mapper.cs, but this is not being applied to MaterialEditorHandler. This could result in the Focus command not working correctly for Material 3 editors. Consider adding a similar mapping for MaterialEditorHandler when Material 3 is enabled.

Suggested change
MaterialEditorHandler.Mapper.ReplaceMapping<Editor, MaterialEditorHandler>(nameof(TextTransform), MapText);
MaterialEditorHandler.Mapper.ReplaceMapping<Editor, MaterialEditorHandler>(nameof(TextTransform), MapText);
MaterialEditorHandler.CommandMapper.PrependToMapping(nameof(IEditor.Focus), InputView.MapFocus);

Copilot uses AI. Check for mistakes.
Comment on lines +21 to +25
public MauiMaterialEditText(Context context, IAttributeSet? attrs) : base(context, attrs)
{
}

public MauiMaterialEditText(Context context, IAttributeSet? attrs, int defStyleAttr) : base(context, attrs, defStyleAttr)
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These constructors do not wrap the context with MauiMaterialContextThemeWrapper.Create(context) like the primary constructor does. This is inconsistent with the pattern used in other Material controls (e.g., MauiMaterialButton) and means that when the EditText is inflated from XML or created with these constructors, it won't have the proper Material 3 theme applied. All constructors should consistently wrap the context parameter.

Suggested change
public MauiMaterialEditText(Context context, IAttributeSet? attrs) : base(context, attrs)
{
}
public MauiMaterialEditText(Context context, IAttributeSet? attrs, int defStyleAttr) : base(context, attrs, defStyleAttr)
public MauiMaterialEditText(Context context, IAttributeSet? attrs) : base(MauiMaterialContextThemeWrapper.Create(context), attrs)
{
}
public MauiMaterialEditText(Context context, IAttributeSet? attrs, int defStyleAttr) : base(MauiMaterialContextThemeWrapper.Create(context), attrs, defStyleAttr)

Copilot uses AI. Check for mistakes.
Comment on lines +10 to +220
internal class MaterialEditorHandler : ViewHandler<IEditor, MauiMaterialEditText>
{
bool _set;

public static PropertyMapper<IEditor, MaterialEditorHandler> Mapper =
new(ViewMapper)
{
[nameof(IEditor.Background)] = MapBackground,
[nameof(IEditor.CharacterSpacing)] = MapCharacterSpacing,
[nameof(IEditor.Font)] = MapFont,
[nameof(IEditor.IsReadOnly)] = MapIsReadOnly,
[nameof(IEditor.IsTextPredictionEnabled)] = MapIsTextPredictionEnabled,
[nameof(IEditor.IsSpellCheckEnabled)] = MapIsSpellCheckEnabled,
[nameof(IEditor.MaxLength)] = MapMaxLength,
[nameof(IEditor.Placeholder)] = MapPlaceholder,
[nameof(IEditor.PlaceholderColor)] = MapPlaceholderColor,
[nameof(IEditor.Text)] = MapText,
[nameof(IEditor.TextColor)] = MapTextColor,
[nameof(IEditor.HorizontalTextAlignment)] = MapHorizontalTextAlignment,
[nameof(IEditor.VerticalTextAlignment)] = MapVerticalTextAlignment,
[nameof(IEditor.Keyboard)] = MapKeyboard,
[nameof(IEditor.CursorPosition)] = MapCursorPosition,
[nameof(IEditor.SelectionLength)] = MapSelectionLength,
};

public static CommandMapper<IEditor, MaterialEditorHandler> CommandMapper =
new(ViewCommandMapper)
{
[nameof(IEditor.Focus)] = MapFocus
};

public MaterialEditorHandler() : base(Mapper, CommandMapper)
{
}

protected override MauiMaterialEditText CreatePlatformView()
{
var editText = new MauiMaterialEditText(Context)
{
ImeOptions = ImeAction.Done,
Gravity = GravityFlags.Top,
TextAlignment = global::Android.Views.TextAlignment.ViewStart,
};

editText.SetSingleLine(false);
editText.SetHorizontallyScrolling(false);

return editText;
}

public override void SetVirtualView(IView view)
{
base.SetVirtualView(view);

if (!_set)
{
PlatformView.SelectionChanged += OnSelectionChanged;
}

_set = true;
}

protected override void ConnectHandler(MauiMaterialEditText platformView)
{
platformView.TextChanged += OnTextChanged;
platformView.FocusChange += OnFocusChange;
}

protected override void DisconnectHandler(MauiMaterialEditText platformView)
{
platformView.TextChanged -= OnTextChanged;
platformView.FocusChange -= OnFocusChange;

if (_set)
{
platformView.SelectionChanged -= OnSelectionChanged;
}

_set = false;
}

public static void MapBackground(MaterialEditorHandler handler, IEditor editor)
{
handler.PlatformView?.UpdateBackground(editor);
}

public static void MapText(MaterialEditorHandler handler, IEditor editor)
{
handler.PlatformView?.UpdateText(editor);
}

public static void MapTextColor(MaterialEditorHandler handler, IEditor editor)
{
handler.PlatformView?.UpdateTextColor(editor);
}

public static void MapPlaceholder(MaterialEditorHandler handler, IEditor editor)
{
handler.PlatformView?.UpdatePlaceholder(editor);
}

public static void MapPlaceholderColor(MaterialEditorHandler handler, IEditor editor)
{
handler.PlatformView?.UpdatePlaceholderColor(editor);
}

public static void MapCharacterSpacing(MaterialEditorHandler handler, IEditor editor)
{
handler.PlatformView?.UpdateCharacterSpacing(editor);
}

public static void MapMaxLength(MaterialEditorHandler handler, IEditor editor)
{
handler.PlatformView?.UpdateMaxLength(editor);
}

public static void MapIsReadOnly(MaterialEditorHandler handler, IEditor editor)
{
handler.PlatformView?.UpdateIsReadOnly(editor);
}

public static void MapIsTextPredictionEnabled(MaterialEditorHandler handler, IEditor editor)
{
handler.PlatformView?.UpdateIsTextPredictionEnabled(editor);
}

public static void MapIsSpellCheckEnabled(MaterialEditorHandler handler, IEditor editor)
{
handler.PlatformView?.UpdateIsSpellCheckEnabled(editor);
}

public static void MapFont(MaterialEditorHandler handler, IEditor editor)
{
handler.PlatformView?.UpdateFont(editor, handler.GetRequiredService<IFontManager>());
}

public static void MapHorizontalTextAlignment(MaterialEditorHandler handler, IEditor editor)
{
handler.PlatformView?.UpdateHorizontalTextAlignment(editor);
}

public static void MapVerticalTextAlignment(MaterialEditorHandler handler, IEditor editor)
{
handler.PlatformView?.UpdateVerticalTextAlignment(editor);
}

public static void MapKeyboard(MaterialEditorHandler handler, IEditor editor)
{
handler.UpdateValue(nameof(IEditor.Text));

handler.PlatformView?.UpdateKeyboard(editor);
}

public static void MapCursorPosition(MaterialEditorHandler handler, ITextInput editor)
{
handler.PlatformView?.UpdateCursorPosition(editor);
}

public static void MapSelectionLength(MaterialEditorHandler handler, ITextInput editor)
{
handler.PlatformView?.UpdateSelectionLength(editor);
}

static void MapFocus(MaterialEditorHandler handler, IEditor editor, object? args)
{
if (args is FocusRequest request)
{
handler.PlatformView.Focus(request);
}
}

void OnTextChanged(object? sender, TextChangedEventArgs e)
{
// Let the mapping know that the update is coming from changes to the platform control
DataFlowDirection = DataFlowDirection.FromPlatform;
VirtualView?.UpdateText(e);

// Reset to the default direction
DataFlowDirection = DataFlowDirection.ToPlatform;
}

private void OnFocusChange(object? sender, FocusChangeEventArgs e)
{
if (!e.HasFocus)
{
VirtualView?.Completed();
}
}

void OnSelectionChanged(object? sender, EventArgs e)
{
var cursorPosition = PlatformView.GetCursorPosition();
var selectedTextLength = PlatformView.GetSelectedTextLength();

if (VirtualView.CursorPosition != cursorPosition)
{
VirtualView.CursorPosition = cursorPosition;
}

if (VirtualView.SelectionLength != selectedTextLength)
{
VirtualView.SelectionLength = selectedTextLength;
}
}

public override void PlatformArrange(Rect frame)
{
this.PrepareForTextViewArrange(frame);
base.PlatformArrange(frame);
}
} No newline at end of file
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MaterialEditorHandler lacks test coverage for Material 3-specific behavior. Consider adding tests to verify that the Material 3 handler is properly registered when Material 3 is enabled, and that it handles all editor properties correctly with the Material-styled control. This could be done by extending the existing EditorHandlerTests with Material 3 conditional tests, similar to how other platform-specific tests are structured.

Copilot uses AI. Check for mistakes.
Comment on lines +74 to +81
if (RuntimeFeature.IsMaterial3Enabled)
{
handlersCollection.AddHandler<Editor, MaterialEditorHandler>();
}
else
{
handlersCollection.AddHandler<Editor, EditorHandler>();
}
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The handler registration code is duplicated with separate if/else blocks. This could be simplified by combining the conditional logic into a single handler registration statement using a ternary expression or extracting the type selection into a variable, improving maintainability and reducing code duplication.

Suggested change
if (RuntimeFeature.IsMaterial3Enabled)
{
handlersCollection.AddHandler<Editor, MaterialEditorHandler>();
}
else
{
handlersCollection.AddHandler<Editor, EditorHandler>();
}
handlersCollection.AddHandler(
typeof(Editor),
RuntimeFeature.IsMaterial3Enabled ? typeof(MaterialEditorHandler) : typeof(EditorHandler));

Copilot uses AI. Check for mistakes.
if (RuntimeFeature.IsMaterial3Enabled)
{
MaterialEditorHandler.Mapper.ReplaceMapping<Editor, MaterialEditorHandler>(nameof(Text), MapText);
MaterialEditorHandler.Mapper.ReplaceMapping<Editor, MaterialEditorHandler>(nameof(TextTransform), MapText);
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing IsFocused mapping for MaterialEditorHandler. The standard EditorHandler has a mapping for IsFocused on Android (line 27 in Editor.Mapper.cs), but this mapping is not being applied to MaterialEditorHandler. This could result in focus state not being properly handled for Material 3 editors. Consider adding a similar mapping for MaterialEditorHandler when Material 3 is enabled.

Suggested change
MaterialEditorHandler.Mapper.ReplaceMapping<Editor, MaterialEditorHandler>(nameof(TextTransform), MapText);
MaterialEditorHandler.Mapper.ReplaceMapping<Editor, MaterialEditorHandler>(nameof(TextTransform), MapText);
MaterialEditorHandler.Mapper.AppendToMapping(nameof(VisualElement.IsFocused), InputView.MapIsFocused);

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-controls-editor Editor community ✨ Community Contribution material3 partner/syncfusion Issues / PR's with Syncfusion collaboration platform/android

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement Material3 Support for Editor

2 participants