From ab36ea3cebff25047a21cca83bf48ccc2c9fbe96 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Sat, 1 Oct 2022 11:48:55 +0200 Subject: [PATCH 01/30] feat: Add support at Pointer Wheel --- .../Input/LibInput/LibInputBackend.Pointer.cs | 110 ++++++++++++++++++ .../Input/LibInput/LibInputBackend.cs | 59 ++-------- .../LibInput/LibInputNativeUnsafeMethods.cs | 53 ++++++++- 3 files changed, 166 insertions(+), 56 deletions(-) create mode 100644 src/Linux/Avalonia.LinuxFramebuffer/Input/LibInput/LibInputBackend.Pointer.cs diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Input/LibInput/LibInputBackend.Pointer.cs b/src/Linux/Avalonia.LinuxFramebuffer/Input/LibInput/LibInputBackend.Pointer.cs new file mode 100644 index 0000000000..ef039a38bc --- /dev/null +++ b/src/Linux/Avalonia.LinuxFramebuffer/Input/LibInput/LibInputBackend.Pointer.cs @@ -0,0 +1,110 @@ +using System; +using Avalonia.Input; +using Avalonia.Input.Raw; +using Avalonia.Logging; +using static Avalonia.LinuxFramebuffer.Input.LibInput.LibInputNativeUnsafeMethods; + +namespace Avalonia.LinuxFramebuffer.Input.LibInput; + +public partial class LibInputBackend +{ + private MouseDevice _mouse = new MouseDevice(); + private Point _mousePosition; + private const string Pointer = LibInput + "/" + nameof(Pointer); + + private void HandlePointer(IntPtr ev, LibInputEventType type) + { + var modifiers = RawInputModifiers.None; //TODO: support input modifiers + var pev = libinput_event_get_pointer_event(ev); + var info = _screen.ScaledSize; + var ts = libinput_event_pointer_get_time_usec(pev) / 1000; + switch (type) + { + case LibInputEventType.LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE: + _mousePosition = new Point(libinput_event_pointer_get_absolute_x_transformed(pev, (int)info.Width), + libinput_event_pointer_get_absolute_y_transformed(pev, (int)info.Height)); + ScheduleInput(new RawPointerEventArgs(_mouse, ts, _inputRoot, RawPointerEventType.Move, _mousePosition, + modifiers)); + break; + case LibInputEventType.LIBINPUT_EVENT_POINTER_BUTTON: + { + var button = (EvKey)libinput_event_pointer_get_button(pev); + var buttonState = libinput_event_pointer_get_button_state(pev); + + RawPointerEventArgs evnt = button switch + { + EvKey.BTN_LEFT when buttonState == 1 + => new(_mouse, ts, _inputRoot, RawPointerEventType.LeftButtonDown, _mousePosition, modifiers), + EvKey.BTN_LEFT when buttonState == 0 + => new(_mouse, ts, _inputRoot, RawPointerEventType.LeftButtonUp, _mousePosition, modifiers), + EvKey.BTN_RIGHT when buttonState == 1 + => new(_mouse, ts, _inputRoot, RawPointerEventType.RightButtonUp, _mousePosition, modifiers), + EvKey.BTN_RIGHT when buttonState == 2 + => new(_mouse, ts, _inputRoot, RawPointerEventType.RightButtonDown, _mousePosition, modifiers), + EvKey.BTN_MIDDLE when buttonState == 1 + => new(_mouse, ts, _inputRoot, RawPointerEventType.MiddleButtonDown, _mousePosition, modifiers), + EvKey.BTN_MIDDLE when buttonState == 2 + => new(_mouse, ts, _inputRoot, RawPointerEventType.MiddleButtonUp, _mousePosition, modifiers), + _ => default, + }; + if (evnt is not null) + { + ScheduleInput(evnt); + } + else + { + Logger.TryGet(LogEventLevel.Warning, Pointer) + ?.Log(this, $"The button {button} is not associated"); + } + } + break; + // Backward compatibility with low-res wheel + case LibInputEventType.LIBINPUT_EVENT_POINTER_AXIS: + { + var sourceAxis = libinput_event_pointer_get_axis_source(pev); + switch (sourceAxis) + { + case LibInputPointerAxisSource.LIBINPUT_POINTER_AXIS_SOURCE_WHEEL: + { + var value = libinput_event_pointer_get_axis_value_discrete(pev, + LibInputPointerAxis.LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL); + ScheduleInput(new RawMouseWheelEventArgs(_mouse + , ts + , _inputRoot + , _mousePosition + , new Vector(0, -value) + , modifiers)); + } + break; + case LibInputPointerAxisSource.LIBINPUT_POINTER_AXIS_SOURCE_FINGER: + case LibInputPointerAxisSource.LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS: + case LibInputPointerAxisSource.LIBINPUT_POINTER_AXIS_SOURCE_WHEEL_TILT: + default: + Logger.TryGet(LogEventLevel.Debug, Pointer) + ?.Log(this, $"The pointer axis {sourceAxis} is not managed."); + break; + } + } + break; + // Hi-Res wheel + case LibInputEventType.LIBINPUT_EVENT_POINTER_SCROLL_WHEEL: + { + var value = new Vector(0, + -libinput_event_pointer_get_scroll_value_v120(pev, + LibInputPointerAxis.LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL) / 120); + ScheduleInput(new RawMouseWheelEventArgs(_mouse + , ts + , _inputRoot + , _mousePosition + , value + , modifiers)); + } + break; + default: + Logger.TryGet(LogEventLevel.Warning, Pointer) + ?.Log(this, $"The pointer event {type} is not mapped."); + break; + } + + } +} diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Input/LibInput/LibInputBackend.cs b/src/Linux/Avalonia.LinuxFramebuffer/Input/LibInput/LibInputBackend.cs index 702ae3f8e5..77e8202fac 100644 --- a/src/Linux/Avalonia.LinuxFramebuffer/Input/LibInput/LibInputBackend.cs +++ b/src/Linux/Avalonia.LinuxFramebuffer/Input/LibInput/LibInputBackend.cs @@ -4,18 +4,16 @@ using System.IO; using System.Threading; using Avalonia.Input; using Avalonia.Input.Raw; -using static Avalonia.LinuxFramebuffer.Input.LibInput.LibInputNativeUnsafeMethods; +using static Avalonia.LinuxFramebuffer.Input.LibInput.LibInputNativeUnsafeMethods; namespace Avalonia.LinuxFramebuffer.Input.LibInput { - public class LibInputBackend : IInputBackend + public partial class LibInputBackend : IInputBackend { private IScreenInfoProvider _screen; private IInputRoot _inputRoot; private readonly Queue _inputThreadActions = new Queue(); private TouchDevice _touch = new TouchDevice(); - private MouseDevice _mouse = new MouseDevice(); - private Point _mousePosition; - + private const string LibInput = nameof(Avalonia.LinuxFramebuffer) + "/" + nameof(Avalonia.LinuxFramebuffer.Input) + "/" + nameof(LibInput); private readonly RawEventGroupingThreadingHelper _inputQueue; private Action _onInput; private Dictionary _pointers = new Dictionary(); @@ -24,13 +22,13 @@ namespace Avalonia.LinuxFramebuffer.Input.LibInput { var ctx = libinput_path_create_context(); _inputQueue = new(e => _onInput?.Invoke(e)); - new Thread(()=>InputThread(ctx)).Start(); + new Thread(() => InputThread(ctx)).Start(); } private unsafe void InputThread(IntPtr ctx) { var fd = libinput_get_fd(ctx); - + var timeval = stackalloc IntPtr[2]; @@ -38,12 +36,11 @@ namespace Avalonia.LinuxFramebuffer.Input.LibInput libinput_path_add_device(ctx, f); while (true) { - IntPtr ev; libinput_dispatch(ctx); while ((ev = libinput_get_event(ctx)) != IntPtr.Zero) { - + var type = libinput_event_get_type(ev); if (type >= LibInputEventType.LIBINPUT_EVENT_TOUCH_DOWN && type <= LibInputEventType.LIBINPUT_EVENT_TOUCH_CANCEL) @@ -52,12 +49,12 @@ namespace Avalonia.LinuxFramebuffer.Input.LibInput if (type >= LibInputEventType.LIBINPUT_EVENT_POINTER_MOTION && type <= LibInputEventType.LIBINPUT_EVENT_POINTER_AXIS) HandlePointer(ev, type); - + libinput_event_destroy(ev); libinput_dispatch(ctx); } - pollfd pfd = new pollfd {fd = fd, events = 1}; + pollfd pfd = new pollfd { fd = fd, events = 1 }; NativeUnsafeMethods.poll(&pfd, new IntPtr(1), 10); } } @@ -67,7 +64,7 @@ namespace Avalonia.LinuxFramebuffer.Input.LibInput private void HandleTouch(IntPtr ev, LibInputEventType type) { var tev = libinput_event_get_touch_event(ev); - if(tev == IntPtr.Zero) + if (tev == IntPtr.Zero) return; if (type < LibInputEventType.LIBINPUT_EVENT_TOUCH_FRAME) { @@ -102,44 +99,6 @@ namespace Avalonia.LinuxFramebuffer.Input.LibInput } } - private void HandlePointer(IntPtr ev, LibInputEventType type) - { - //TODO: support input modifiers - var pev = libinput_event_get_pointer_event(ev); - var info = _screen.ScaledSize; - var ts = libinput_event_pointer_get_time_usec(pev) / 1000; - if (type == LibInputEventType.LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE) - { - _mousePosition = new Point(libinput_event_pointer_get_absolute_x_transformed(pev, (int)info.Width), - libinput_event_pointer_get_absolute_y_transformed(pev, (int)info.Height)); - ScheduleInput(new RawPointerEventArgs(_mouse, ts, _inputRoot, RawPointerEventType.Move, _mousePosition, - RawInputModifiers.None)); - } - else if (type == LibInputEventType.LIBINPUT_EVENT_POINTER_BUTTON) - { - var button = (EvKey)libinput_event_pointer_get_button(pev); - var buttonState = libinput_event_pointer_get_button_state(pev); - - - var evnt = button == EvKey.BTN_LEFT ? - (buttonState == 1 ? RawPointerEventType.LeftButtonDown : RawPointerEventType.LeftButtonUp) : - button == EvKey.BTN_MIDDLE ? - (buttonState == 1 ? RawPointerEventType.MiddleButtonDown : RawPointerEventType.MiddleButtonUp) : - button == EvKey.BTN_RIGHT ? - (buttonState == 1 ? - RawPointerEventType.RightButtonDown : - RawPointerEventType.RightButtonUp) : - (RawPointerEventType)(-1); - if (evnt == (RawPointerEventType)(-1)) - return; - - - ScheduleInput( - new RawPointerEventArgs(_mouse, ts, _inputRoot, evnt, _mousePosition, RawInputModifiers.None)); - } - - } - public void Initialize(IScreenInfoProvider screen, Action onInput) { _screen = screen; diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Input/LibInput/LibInputNativeUnsafeMethods.cs b/src/Linux/Avalonia.LinuxFramebuffer/Input/LibInput/LibInputNativeUnsafeMethods.cs index 0492090461..df6defb653 100644 --- a/src/Linux/Avalonia.LinuxFramebuffer/Input/LibInput/LibInputNativeUnsafeMethods.cs +++ b/src/Linux/Avalonia.LinuxFramebuffer/Input/LibInput/LibInputNativeUnsafeMethods.cs @@ -77,6 +77,9 @@ namespace Avalonia.LinuxFramebuffer.Input.LibInput LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE, LIBINPUT_EVENT_POINTER_BUTTON, LIBINPUT_EVENT_POINTER_AXIS, + LIBINPUT_EVENT_POINTER_SCROLL_WHEEL, + LIBINPUT_EVENT_POINTER_SCROLL_FINGER, + LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS, LIBINPUT_EVENT_TOUCH_DOWN = 500, LIBINPUT_EVENT_TOUCH_UP, LIBINPUT_EVENT_TOUCH_MOTION, @@ -97,8 +100,38 @@ namespace Avalonia.LinuxFramebuffer.Input.LibInput LIBINPUT_EVENT_GESTURE_PINCH_END, LIBINPUT_EVENT_SWITCH_TOGGLE = 900, } - - + + public enum LibInputPointerAxisSource + { + /** + * The event is caused by the rotation of a wheel. + **/ + LIBINPUT_POINTER_AXIS_SOURCE_WHEEL = 1, + /** + * The event is caused by the movement of one or more fingers on a device. + **/ + LIBINPUT_POINTER_AXIS_SOURCE_FINGER, + /** + * The event is caused by the motion of some device. + **/ + LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS, + /** + * The event is caused by the tilting of a mouse wheel rather than + * its rotation. This method is commonly used on mice without + * separate horizontal scroll wheels. + * @deprecated This axis source is deprecated as of libinput 1.16. + * It was never used by any device before libinput 1.16. All wheel + * tilt devices use @ref LIBINPUT_POINTER_AXIS_SOURCE_WHEEL instead. + **/ + LIBINPUT_POINTER_AXIS_SOURCE_WHEEL_TILT, + }; + + public enum LibInputPointerAxis + { + LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL = 0, + LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL = 1, + }; + [DllImport(LibInput)] public extern static void libinput_event_destroy(IntPtr ev); @@ -119,21 +152,29 @@ namespace Avalonia.LinuxFramebuffer.Input.LibInput [DllImport(LibInput)] public extern static IntPtr libinput_event_get_pointer_event(IntPtr ev); - - + [DllImport(LibInput)] public extern static ulong libinput_event_pointer_get_time_usec(IntPtr ev); [DllImport(LibInput)] public extern static double libinput_event_pointer_get_absolute_x_transformed(IntPtr ev, int width); - + [DllImport(LibInput)] public extern static double libinput_event_pointer_get_absolute_y_transformed(IntPtr ev, int height); - + [DllImport(LibInput)] public extern static int libinput_event_pointer_get_button(IntPtr ev); [DllImport(LibInput)] public extern static int libinput_event_pointer_get_button_state(IntPtr ev); + + [DllImport(LibInput)] + public extern static LibInputPointerAxisSource libinput_event_pointer_get_axis_source(IntPtr ev); + + [DllImport((LibInput))] + public extern static double libinput_event_pointer_get_axis_value_discrete(IntPtr ev, LibInputPointerAxis axis); + + [DllImport(LibInput)] + public extern static double libinput_event_pointer_get_scroll_value_v120(IntPtr ev, LibInputPointerAxis axis); } } From 80b0c3d54c8247a6b87170a2b288e86e8cb42838 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 3 Nov 2022 00:24:17 +0100 Subject: [PATCH 02/30] Allow AccessibilityView to override peer. Setting `AutomationProperties.AccessibilityView` can now override the `IsControlElementCore` and `IsContentElementCore` settings returned from the automation peer. --- .../Automation/AutomationProperties.cs | 8 ++++++-- .../Automation/Peers/AutomationPeer.cs | 14 ++++++++++++-- .../Automation/Peers/ControlAutomationPeer.cs | 16 ++++++++++++++-- 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Controls/Automation/AutomationProperties.cs b/src/Avalonia.Controls/Automation/AutomationProperties.cs index c20af148b8..a192ef6420 100644 --- a/src/Avalonia.Controls/Automation/AutomationProperties.cs +++ b/src/Avalonia.Controls/Automation/AutomationProperties.cs @@ -9,6 +9,11 @@ namespace Avalonia.Automation /// public enum AccessibilityView { + /// + /// The control's view is defined by its automation peer. + /// + Default, + /// /// The control is included in the Raw view of the automation tree. /// @@ -44,8 +49,7 @@ namespace Avalonia.Automation public static readonly AttachedProperty AccessibilityViewProperty = AvaloniaProperty.RegisterAttached( "AccessibilityView", - typeof(AutomationProperties), - defaultValue: AccessibilityView.Content); + typeof(AutomationProperties)); /// /// Defines the AutomationProperties.AccessKey attached property diff --git a/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs index 71421ac136..3d3fe35d29 100644 --- a/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs +++ b/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs @@ -128,13 +128,13 @@ namespace Avalonia.Automation.Peers /// Gets a value that indicates whether the element that is associated with this automation /// peer contains data that is presented to the user. /// - public bool IsContentElement() => IsControlElement() && IsContentElementCore(); + public bool IsContentElement() => IsContentElementOverrideCore(); /// /// Gets a value that indicates whether the element is understood by the user as /// interactive or as contributing to the logical structure of the control in the GUI. /// - public bool IsControlElement() => IsControlElementCore(); + public bool IsControlElement() => IsControlElementOverrideCore(); /// /// Gets a value indicating whether the control is enabled for user interaction. @@ -247,6 +247,16 @@ namespace Avalonia.Automation.Peers return GetAutomationControlTypeCore(); } + protected virtual bool IsContentElementOverrideCore() + { + return IsControlElement() && IsContentElementCore(); + } + + protected virtual bool IsControlElementOverrideCore() + { + return IsControlElementCore(); + } + protected virtual object? GetProviderCore(Type providerType) { return providerType.IsAssignableFrom(this.GetType()) ? this : null; diff --git a/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs index a93d3fa7dd..25172b2d1c 100644 --- a/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs +++ b/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs @@ -149,8 +149,8 @@ namespace Avalonia.Automation.Peers protected override Rect GetBoundingRectangleCore() => GetBounds(Owner); protected override string GetClassNameCore() => Owner.GetType().Name; protected override bool HasKeyboardFocusCore() => Owner.IsFocused; - protected override bool IsContentElementCore() => AutomationProperties.GetAccessibilityView(Owner) >= AccessibilityView.Content; - protected override bool IsControlElementCore() => AutomationProperties.GetAccessibilityView(Owner) >= AccessibilityView.Control; + protected override bool IsContentElementCore() => true; + protected override bool IsControlElementCore() => true; protected override bool IsEnabledCore() => Owner.IsEnabled; protected override bool IsKeyboardFocusableCore() => Owner.Focusable; protected override void SetFocusCore() => Owner.Focus(); @@ -160,6 +160,18 @@ namespace Avalonia.Automation.Peers return AutomationProperties.GetControlTypeOverride(Owner) ?? GetAutomationControlTypeCore(); } + protected override bool IsContentElementOverrideCore() + { + var view = AutomationProperties.GetAccessibilityView(Owner); + return view == AccessibilityView.Default ? IsContentElementCore() : view >= AccessibilityView.Content; + } + + protected override bool IsControlElementOverrideCore() + { + var view = AutomationProperties.GetAccessibilityView(Owner); + return view == AccessibilityView.Default ? IsControlElementCore() : view >= AccessibilityView.Control; + } + private static Rect GetBounds(Control control) { var root = control.GetVisualRoot(); From 9a50b9ee0ccab7d96bac6e144c55aa0422303f02 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 3 Nov 2022 15:36:02 +0100 Subject: [PATCH 03/30] Added integration tests for tapped gestures. Some skipped on Windows as right button actions aren't supported by WinAppDriver. --- samples/IntegrationTestApp/MainWindow.axaml | 12 ++ .../IntegrationTestApp/MainWindow.axaml.cs | 10 ++ .../GestureTests.cs | 151 ++++++++++++++++++ 3 files changed, 173 insertions(+) create mode 100644 tests/Avalonia.IntegrationTests.Appium/GestureTests.cs diff --git a/samples/IntegrationTestApp/MainWindow.axaml b/samples/IntegrationTestApp/MainWindow.axaml index 3377979199..2a0a758852 100644 --- a/samples/IntegrationTestApp/MainWindow.axaml +++ b/samples/IntegrationTestApp/MainWindow.axaml @@ -69,6 +69,18 @@ + + + + + + + + + + diff --git a/samples/IntegrationTestApp/MainWindow.axaml.cs b/samples/IntegrationTestApp/MainWindow.axaml.cs index f72f83fcb8..791e221d50 100644 --- a/samples/IntegrationTestApp/MainWindow.axaml.cs +++ b/samples/IntegrationTestApp/MainWindow.axaml.cs @@ -4,6 +4,7 @@ using Avalonia; using Avalonia.Automation; using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Markup.Xaml; using Avalonia.VisualTree; @@ -20,6 +21,15 @@ namespace IntegrationTestApp this.AttachDevTools(); AddHandler(Button.ClickEvent, OnButtonClick); ListBoxItems = Enumerable.Range(0, 100).Select(x => "Item " + x).ToList(); + + var gestureBorder = this.GetControl("GestureBorder"); + var lastGesture = this.GetControl("LastGesture"); + var clearLastGesture = this.GetControl + - + + + + diff --git a/samples/IntegrationTestApp/MainWindow.axaml.cs b/samples/IntegrationTestApp/MainWindow.axaml.cs index 791e221d50..e8fb455c35 100644 --- a/samples/IntegrationTestApp/MainWindow.axaml.cs +++ b/samples/IntegrationTestApp/MainWindow.axaml.cs @@ -18,18 +18,10 @@ namespace IntegrationTestApp { InitializeComponent(); InitializeViewMenu(); + InitializeGesturesTab(); this.AttachDevTools(); AddHandler(Button.ClickEvent, OnButtonClick); ListBoxItems = Enumerable.Range(0, 100).Select(x => "Item " + x).ToList(); - - var gestureBorder = this.GetControl("GestureBorder"); - var lastGesture = this.GetControl("LastGesture"); - var clearLastGesture = this.GetControl void SetPreeditText(string? text); + + void SetComposingRegion(ComposingRegion? region); /// /// Indicates if text input client is capable of providing the text around the cursor /// @@ -51,4 +53,26 @@ namespace Avalonia.Input.TextInput public int CursorOffset { get; set; } public int AnchorOffset { get; set; } } + + public readonly struct ComposingRegion + { + private readonly int _start = -1; + private readonly int _end = -1; + + public ComposingRegion(int start, int end) + { + _start = start; + _end = end; + } + + public int Start => _start; + public int End => _end; + + public bool Intersects(ComposingRegion region) + { + return _start <= region.Start && _end >= region.Start || + _end >= region.End && _start <= region.End || + _start >= region.Start && _end <= region.End; + } + } } diff --git a/src/Avalonia.Base/Media/TextFormatting/FormattedTextSource.cs b/src/Avalonia.Base/Media/TextFormatting/FormattedTextSource.cs index 7ab67ea34d..fb8e699d8e 100644 --- a/src/Avalonia.Base/Media/TextFormatting/FormattedTextSource.cs +++ b/src/Avalonia.Base/Media/TextFormatting/FormattedTextSource.cs @@ -140,7 +140,7 @@ namespace Avalonia.Media.TextFormatting } } - return length; + return Math.Min(length, text.Length); } } } diff --git a/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs b/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs index bcfa35ae30..efe6a94856 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs @@ -74,15 +74,6 @@ namespace Avalonia.Media.TextFormatting if (TryGetShapeableLength(text, currentTypeface, null, out var count, out var script)) { - if (script == Script.Common && previousTypeface is not null) - { - if (TryGetShapeableLength(text, previousTypeface.Value, null, out var fallbackCount, out _)) - { - return new ShapeableTextCharacters(text.Take(fallbackCount), - defaultProperties.WithTypeface(previousTypeface.Value), biDiLevel); - } - } - return new ShapeableTextCharacters(text.Take(count), defaultProperties.WithTypeface(currentTypeface), biDiLevel); } @@ -182,7 +173,7 @@ namespace Avalonia.Media.TextFormatting var currentScript = currentGrapheme.FirstCodepoint.Script; - if (!currentGrapheme.FirstCodepoint.IsWhiteSpace && defaultFont != null && defaultFont.TryGetGlyph(currentGrapheme.FirstCodepoint, out _)) + if (defaultFont != null && defaultFont.TryGetGlyph(currentGrapheme.FirstCodepoint, out _)) { break; } diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index adf0569551..230c57d34b 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -9,6 +9,7 @@ using Avalonia.VisualTree; using Avalonia.Layout; using Avalonia.Media.Immutable; using Avalonia.Controls.Documents; +using Avalonia.Input.TextInput; namespace Avalonia.Controls.Presenters { @@ -331,6 +332,8 @@ namespace Avalonia.Controls.Presenters protected override bool BypassFlowDirectionPolicies => true; + public ComposingRegion? ComposingRegion { get; internal set; } + /// /// Creates the used to render the text. /// @@ -514,7 +517,12 @@ namespace Avalonia.Controls.Presenters { if (!string.IsNullOrEmpty(_preeditText)) { - var text = _text?.Substring(0, _caretIndex) + _preeditText + _text?.Substring(_caretIndex); + if (string.IsNullOrEmpty(_text) || _caretIndex > _text.Length) + { + return _preeditText; + } + + var text = _text.Substring(0, _caretIndex) + _preeditText + _text.Substring(_caretIndex); return text; } @@ -545,7 +553,7 @@ namespace Avalonia.Controls.Presenters if (!string.IsNullOrEmpty(_preeditText)) { - var preeditHighlight = new ValueSpan(_caretIndex, _preeditText.Length, + var preeditHighlight = new ValueSpan(ComposingRegion.HasValue ? ComposingRegion.Value.Start : _caretIndex, _preeditText.Length, new GenericTextRunProperties(typeface, FontSize, foregroundBrush: foreground, textDecorations: TextDecorations.Underline)); @@ -868,28 +876,11 @@ namespace Avalonia.Controls.Presenters if (string.IsNullOrEmpty(newValue)) { - if (!string.IsNullOrEmpty(oldValue)) - { - var textPosition = _compositionStartHit.FirstCharacterIndex + _compositionStartHit.TrailingLength + newValue?.Length ?? 0; - - var characterHit = GetCharacterHitFromTextPosition(textPosition); - - UpdateCaret(characterHit, true); - } - - _compositionStartHit = new CharacterHit(-1); + UpdateCaret(_lastCharacterHit); } else { - if (_compositionStartHit.FirstCharacterIndex == -1) - { - _compositionStartHit = _lastCharacterHit; - } - } - - if (_compositionStartHit.FirstCharacterIndex != -1) - { - var textPosition = _compositionStartHit.FirstCharacterIndex + _compositionStartHit.TrailingLength + newValue?.Length ?? 0; + var textPosition = (ComposingRegion.HasValue? ComposingRegion.Value.Start : _caretIndex) + newValue?.Length ?? 0; var characterHit = GetCharacterHitFromTextPosition(textPosition); diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 85c1c9a9d1..d5b45398e7 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -1212,7 +1212,7 @@ namespace Avalonia.Controls protected override void OnPointerPressed(PointerPressedEventArgs e) { - if (_presenter == null || !string.IsNullOrEmpty(_presenter.PreeditText)) + if (_presenter == null ) { return; } diff --git a/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs b/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs index 5d5ffcc381..61ac1d3c1f 100644 --- a/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs +++ b/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs @@ -108,6 +108,11 @@ namespace Avalonia.Controls } _presenter.PreeditText = text; + + if(text == null) + { + _presenter.ComposingRegion = null; + } } public void SelectInSurroundingText(int start, int end) @@ -182,5 +187,15 @@ namespace Avalonia.Controls }, DispatcherPriority.Input); } + + public void SetComposingRegion(ComposingRegion? region) + { + if(_presenter == null) + { + return; + } + + _presenter.ComposingRegion = region; + } } } From a589d2e43b762a1d51c8be4142c85a57bb3e5fc2 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Mon, 7 Nov 2022 18:17:09 +0100 Subject: [PATCH 11/30] Relocate CompositionRegion --- .../Avalonia.Android/AndroidInputMethod.cs | 15 ++++++++++++ .../Platform/SkiaPlatform/TopLevelImpl.cs | 2 -- .../Input/TextInput/ITextInputMethodClient.cs | 23 ------------------- .../TextBoxTextInputMethodClient.cs | 10 -------- 4 files changed, 15 insertions(+), 35 deletions(-) diff --git a/src/Android/Avalonia.Android/AndroidInputMethod.cs b/src/Android/Avalonia.Android/AndroidInputMethod.cs index 22d2f812bc..b6adbde738 100644 --- a/src/Android/Avalonia.Android/AndroidInputMethod.cs +++ b/src/Android/Avalonia.Android/AndroidInputMethod.cs @@ -162,4 +162,19 @@ namespace Avalonia.Android }); } } + + public readonly struct ComposingRegion + { + private readonly int _start = -1; + private readonly int _end = -1; + + public ComposingRegion(int start, int end) + { + _start = start; + _end = end; + } + + public int Start => _start; + public int End => _end; + } } diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs index e69780b1b3..932dc185ff 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs @@ -287,8 +287,6 @@ namespace Avalonia.Android.Platform.SkiaPlatform ComposingRegion = new ComposingRegion(start, end); - _inputMethod.Client?.SetComposingRegion(ComposingRegion); - return base.SetComposingRegion(start, end); } diff --git a/src/Avalonia.Base/Input/TextInput/ITextInputMethodClient.cs b/src/Avalonia.Base/Input/TextInput/ITextInputMethodClient.cs index 0ff9299873..325d745782 100644 --- a/src/Avalonia.Base/Input/TextInput/ITextInputMethodClient.cs +++ b/src/Avalonia.Base/Input/TextInput/ITextInputMethodClient.cs @@ -30,7 +30,6 @@ namespace Avalonia.Input.TextInput /// void SetPreeditText(string? text); - void SetComposingRegion(ComposingRegion? region); /// /// Indicates if text input client is capable of providing the text around the cursor /// @@ -53,26 +52,4 @@ namespace Avalonia.Input.TextInput public int CursorOffset { get; set; } public int AnchorOffset { get; set; } } - - public readonly struct ComposingRegion - { - private readonly int _start = -1; - private readonly int _end = -1; - - public ComposingRegion(int start, int end) - { - _start = start; - _end = end; - } - - public int Start => _start; - public int End => _end; - - public bool Intersects(ComposingRegion region) - { - return _start <= region.Start && _end >= region.Start || - _end >= region.End && _start <= region.End || - _start >= region.Start && _end <= region.End; - } - } } diff --git a/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs b/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs index 61ac1d3c1f..79a16281cd 100644 --- a/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs +++ b/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs @@ -187,15 +187,5 @@ namespace Avalonia.Controls }, DispatcherPriority.Input); } - - public void SetComposingRegion(ComposingRegion? region) - { - if(_presenter == null) - { - return; - } - - _presenter.ComposingRegion = region; - } } } From 7e3024ede20490eadb0511a50a7059f8bd436ab2 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Mon, 7 Nov 2022 19:46:02 +0100 Subject: [PATCH 12/30] Fix fallback handling --- .../Media/TextFormatting/TextCharacters.cs | 11 ++++++++++- src/Avalonia.Controls/Presenters/TextPresenter.cs | 6 ++---- src/Avalonia.Controls/TextBoxTextInputMethodClient.cs | 5 ----- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs b/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs index efe6a94856..bcfa35ae30 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs @@ -74,6 +74,15 @@ namespace Avalonia.Media.TextFormatting if (TryGetShapeableLength(text, currentTypeface, null, out var count, out var script)) { + if (script == Script.Common && previousTypeface is not null) + { + if (TryGetShapeableLength(text, previousTypeface.Value, null, out var fallbackCount, out _)) + { + return new ShapeableTextCharacters(text.Take(fallbackCount), + defaultProperties.WithTypeface(previousTypeface.Value), biDiLevel); + } + } + return new ShapeableTextCharacters(text.Take(count), defaultProperties.WithTypeface(currentTypeface), biDiLevel); } @@ -173,7 +182,7 @@ namespace Avalonia.Media.TextFormatting var currentScript = currentGrapheme.FirstCodepoint.Script; - if (defaultFont != null && defaultFont.TryGetGlyph(currentGrapheme.FirstCodepoint, out _)) + if (!currentGrapheme.FirstCodepoint.IsWhiteSpace && defaultFont != null && defaultFont.TryGetGlyph(currentGrapheme.FirstCodepoint, out _)) { break; } diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index 230c57d34b..cc1fa6c513 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -332,8 +332,6 @@ namespace Avalonia.Controls.Presenters protected override bool BypassFlowDirectionPolicies => true; - public ComposingRegion? ComposingRegion { get; internal set; } - /// /// Creates the used to render the text. /// @@ -553,7 +551,7 @@ namespace Avalonia.Controls.Presenters if (!string.IsNullOrEmpty(_preeditText)) { - var preeditHighlight = new ValueSpan(ComposingRegion.HasValue ? ComposingRegion.Value.Start : _caretIndex, _preeditText.Length, + var preeditHighlight = new ValueSpan(_caretIndex, _preeditText.Length, new GenericTextRunProperties(typeface, FontSize, foregroundBrush: foreground, textDecorations: TextDecorations.Underline)); @@ -880,7 +878,7 @@ namespace Avalonia.Controls.Presenters } else { - var textPosition = (ComposingRegion.HasValue? ComposingRegion.Value.Start : _caretIndex) + newValue?.Length ?? 0; + var textPosition = _caretIndex + newValue?.Length ?? 0; var characterHit = GetCharacterHitFromTextPosition(textPosition); diff --git a/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs b/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs index 79a16281cd..5d5ffcc381 100644 --- a/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs +++ b/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs @@ -108,11 +108,6 @@ namespace Avalonia.Controls } _presenter.PreeditText = text; - - if(text == null) - { - _presenter.ComposingRegion = null; - } } public void SelectInSurroundingText(int start, int end) From 562d687daab221ef8666e0c9bab9127006d1eebd Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Mon, 7 Nov 2022 22:58:47 +0300 Subject: [PATCH 13/30] Use 7.0.100 SDK --- global.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global.json b/global.json index dc6da556b3..a9318b212f 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "7.0.100-rc.2.22477.23", + "version": "7.0.100", "rollForward": "latestFeature" }, "msbuild-sdks": { From b5cf18bd4d148809918ff68cb263fa87c6d603dd Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Mon, 7 Nov 2022 23:01:29 +0300 Subject: [PATCH 14/30] Install 7.0.100 --- azure-pipelines.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 903f9e3843..ee78a79e89 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -37,7 +37,7 @@ jobs: - task: UseDotNet@2 displayName: 'Use .NET Core SDK 7.0.100-rc.2.22477.23' inputs: - version: 7.0.100-rc.2.22477.23 + version: 7.0.100 - task: CmdLine@2 displayName: 'Install Workloads' @@ -74,7 +74,7 @@ jobs: - task: UseDotNet@2 displayName: 'Use .NET Core SDK 7.0.100-rc.2.22477.23' inputs: - version: 7.0.100-rc.2.22477.23 + version: 7.0.100 - task: CmdLine@2 displayName: 'Install Workloads' @@ -145,7 +145,7 @@ jobs: - task: UseDotNet@2 displayName: 'Use .NET Core SDK 7.0.100-rc.2.22477.23' inputs: - version: 7.0.100-rc.2.22477.23 + version: 7.0.100 - task: CmdLine@2 displayName: 'Install Workloads' From 6c32c97ec5f5871507691f1439fbcb8483cdb9f2 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Mon, 7 Nov 2022 23:04:35 +0300 Subject: [PATCH 15/30] Update azure-pipelines.yml --- azure-pipelines.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index ee78a79e89..a3bbc33418 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -35,7 +35,7 @@ jobs: version: 6.0.401 - task: UseDotNet@2 - displayName: 'Use .NET Core SDK 7.0.100-rc.2.22477.23' + displayName: 'Use .NET Core SDK 7.0' inputs: version: 7.0.100 @@ -72,7 +72,7 @@ jobs: version: 6.0.401 - task: UseDotNet@2 - displayName: 'Use .NET Core SDK 7.0.100-rc.2.22477.23' + displayName: 'Use .NET Core SDK 7.0.100' inputs: version: 7.0.100 @@ -143,7 +143,7 @@ jobs: version: 6.0.401 - task: UseDotNet@2 - displayName: 'Use .NET Core SDK 7.0.100-rc.2.22477.23' + displayName: 'Use .NET Core SDK 7.0.100' inputs: version: 7.0.100 From ff620db5bbd072094e56aa9e9f143b95f3ea9a8e Mon Sep 17 00:00:00 2001 From: Tristan Labelle Date: Mon, 7 Nov 2022 20:36:27 -0500 Subject: [PATCH 16/30] Android: Force punch a hole in the SurfaceView to workaround issue #9230 --- .../Platform/SkiaPlatform/TopLevelImpl.cs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs index 984eb775b5..4e972d504a 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs @@ -165,6 +165,28 @@ namespace Avalonia.Android.Platform.SkiaPlatform _tl.Draw(); } + protected override void DispatchDraw(global::Android.Graphics.Canvas canvas) + { + // Workaround issue #9230 on where screen remains gray after splash screen. + // base.DispatchDraw should punch a hole into the canvas so the surface + // can be seen below, but it does not. + if (OperatingSystem.IsAndroidVersionAtLeast(29)) + { + // Android 10+ does this (BlendMode was new) + var paint = new Paint(); + paint.SetColor(0); + paint.BlendMode = BlendMode.Clear; + canvas.DrawRect(0, 0, Width, Height, paint); + } + else + { + // Android 9 did this + canvas.DrawColor(Color.Transparent, PorterDuff.Mode.Clear); + } + + base.DispatchDraw(canvas); + } + protected override bool DispatchGenericPointerEvent(MotionEvent e) { bool callBase; From f6f89f4e33b86f8a9ae0f6ccb5ade9ac8d69e20e Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Tue, 8 Nov 2022 06:59:16 +0100 Subject: [PATCH 17/30] Remove redundant method call --- .../Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs index 932dc185ff..24ee418480 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs @@ -268,8 +268,6 @@ namespace Avalonia.Android.Platform.SkiaPlatform { _topLevel = topLevel; _inputMethod = inputMethod; - - _inputMethod.Client?.SetComposingRegion(null); } public TextInputMethodSurroundingText SurroundingText { get; set; } From a52696bcd6efbdc74e294ab41a267a4092bd9e53 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 8 Nov 2022 13:41:08 -0500 Subject: [PATCH 18/30] Update Microsoft.Build.Framework --- nukebuild/_build.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nukebuild/_build.csproj b/nukebuild/_build.csproj index 865d935ad7..efad0d69f4 100644 --- a/nukebuild/_build.csproj +++ b/nukebuild/_build.csproj @@ -17,7 +17,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive From 9003dfde5164f5ba7ca47b87fbc6827e8c7bb554 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 8 Nov 2022 13:52:43 -0500 Subject: [PATCH 19/30] Parse target framework manually --- nukebuild/Build.cs | 19 ++++++++++++++++++- nukebuild/_build.csproj | 3 +-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs index 7425c344c3..2295c0beda 100644 --- a/nukebuild/Build.cs +++ b/nukebuild/Build.cs @@ -145,8 +145,25 @@ partial class Build : NukeBuild { Information($"Running tests from {projectName}"); var project = Solution.GetProject(projectName).NotNull("project != null"); + // Nuke and MSBuild tools have build-in helpers to get target frameworks from the project. + // Unfortunately, it gets broken with every second SDK update, so we had to do it manually. + var fileXml = XDocument.Parse(File.ReadAllText(project.Path)); + var targetFrameworks = fileXml.Descendants("TargetFrameworks") + .FirstOrDefault()?.Value.Split(';').Select(f => f.Trim()); + if (targetFrameworks is null) + { + var targetFramework = fileXml.Descendants("TargetFramework").FirstOrDefault()?.Value; + if (targetFramework is not null) + { + targetFrameworks = new[] { targetFramework }; + } + } + if (targetFrameworks is null) + { + throw new InvalidOperationException("No target frameworks were found in the test project"); + } - foreach (var fw in project.GetTargetFrameworks()) + foreach (var fw in targetFrameworks) { if (fw.StartsWith("net4") && RuntimeInformation.IsOSPlatform(OSPlatform.Linux) diff --git a/nukebuild/_build.csproj b/nukebuild/_build.csproj index efad0d69f4..591c72445b 100644 --- a/nukebuild/_build.csproj +++ b/nukebuild/_build.csproj @@ -16,8 +16,7 @@ - - + all runtime; build; native; contentfiles; analyzers; buildtransitive From 6b080eabe02588d3cd28e7c7b389ba7c40b70197 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 8 Nov 2022 14:28:16 -0500 Subject: [PATCH 20/30] Restore Microsoft.Build.Framework dependency --- nukebuild/_build.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/nukebuild/_build.csproj b/nukebuild/_build.csproj index 591c72445b..92d9732e91 100644 --- a/nukebuild/_build.csproj +++ b/nukebuild/_build.csproj @@ -17,6 +17,7 @@ + all runtime; build; native; contentfiles; analyzers; buildtransitive From 4808846d82c0cae6ae572495e4e6b725f99cd48a Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 8 Nov 2022 22:37:57 +0000 Subject: [PATCH 21/30] Update Avalonia.Web.targets --- src/Web/Avalonia.Web/Avalonia.Web.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Web/Avalonia.Web/Avalonia.Web.targets b/src/Web/Avalonia.Web/Avalonia.Web.targets index b6a09b33ef..b5db8e5dd3 100644 --- a/src/Web/Avalonia.Web/Avalonia.Web.targets +++ b/src/Web/Avalonia.Web/Avalonia.Web.targets @@ -12,7 +12,7 @@ true - + true full true From ace159369ef6313d4b65e0284bf30c9f099a7d30 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 9 Nov 2022 09:56:47 +0100 Subject: [PATCH 22/30] Update ncrunch config due to recent changes. --- .ncrunch/Avalonia.Web.Blazor.v3.ncrunchproject | 5 +++++ .ncrunch/Avalonia.Web.v3.ncrunchproject | 5 +++++ .ncrunch/Avalonia.Win32.net6.0.v3.ncrunchproject | 6 +----- .ncrunch/Avalonia.Win32.netstandard2.0.v3.ncrunchproject | 6 +----- .ncrunch/ControlCatalog.Blazor.Web.v3.ncrunchproject | 5 +++++ .ncrunch/MobileSandbox.Android.v3.ncrunchproject | 5 +++++ .ncrunch/MobileSandbox.Desktop.v3.ncrunchproject | 5 +++++ .ncrunch/MobileSandbox.iOS.v3.ncrunchproject | 5 +++++ .ncrunch/MobileSandbox.net6.0.v3.ncrunchproject | 5 +++++ .ncrunch/MobileSandbox.netstandard2.0.v3.ncrunchproject | 5 +++++ .ncrunch/_build.v3.ncrunchproject | 5 +++++ 11 files changed, 47 insertions(+), 10 deletions(-) create mode 100644 .ncrunch/Avalonia.Web.Blazor.v3.ncrunchproject create mode 100644 .ncrunch/Avalonia.Web.v3.ncrunchproject create mode 100644 .ncrunch/ControlCatalog.Blazor.Web.v3.ncrunchproject create mode 100644 .ncrunch/MobileSandbox.Android.v3.ncrunchproject create mode 100644 .ncrunch/MobileSandbox.Desktop.v3.ncrunchproject create mode 100644 .ncrunch/MobileSandbox.iOS.v3.ncrunchproject create mode 100644 .ncrunch/MobileSandbox.net6.0.v3.ncrunchproject create mode 100644 .ncrunch/MobileSandbox.netstandard2.0.v3.ncrunchproject create mode 100644 .ncrunch/_build.v3.ncrunchproject diff --git a/.ncrunch/Avalonia.Web.Blazor.v3.ncrunchproject b/.ncrunch/Avalonia.Web.Blazor.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/Avalonia.Web.Blazor.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Web.v3.ncrunchproject b/.ncrunch/Avalonia.Web.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/Avalonia.Web.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Win32.net6.0.v3.ncrunchproject b/.ncrunch/Avalonia.Win32.net6.0.v3.ncrunchproject index 28b692bb51..95a483b433 100644 --- a/.ncrunch/Avalonia.Win32.net6.0.v3.ncrunchproject +++ b/.ncrunch/Avalonia.Win32.net6.0.v3.ncrunchproject @@ -1,7 +1,3 @@  - - - ..\..\tools\MicroComGenerator\bin\Debug\net6.0\**.* - - + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Win32.netstandard2.0.v3.ncrunchproject b/.ncrunch/Avalonia.Win32.netstandard2.0.v3.ncrunchproject index 28b692bb51..95a483b433 100644 --- a/.ncrunch/Avalonia.Win32.netstandard2.0.v3.ncrunchproject +++ b/.ncrunch/Avalonia.Win32.netstandard2.0.v3.ncrunchproject @@ -1,7 +1,3 @@  - - - ..\..\tools\MicroComGenerator\bin\Debug\net6.0\**.* - - + \ No newline at end of file diff --git a/.ncrunch/ControlCatalog.Blazor.Web.v3.ncrunchproject b/.ncrunch/ControlCatalog.Blazor.Web.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/ControlCatalog.Blazor.Web.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/MobileSandbox.Android.v3.ncrunchproject b/.ncrunch/MobileSandbox.Android.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/MobileSandbox.Android.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/MobileSandbox.Desktop.v3.ncrunchproject b/.ncrunch/MobileSandbox.Desktop.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/MobileSandbox.Desktop.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/MobileSandbox.iOS.v3.ncrunchproject b/.ncrunch/MobileSandbox.iOS.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/MobileSandbox.iOS.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/MobileSandbox.net6.0.v3.ncrunchproject b/.ncrunch/MobileSandbox.net6.0.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/MobileSandbox.net6.0.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/MobileSandbox.netstandard2.0.v3.ncrunchproject b/.ncrunch/MobileSandbox.netstandard2.0.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/MobileSandbox.netstandard2.0.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/_build.v3.ncrunchproject b/.ncrunch/_build.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/_build.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file From cbf072efb6454b0725d6cdcd1dcb936833ee7497 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 9 Nov 2022 12:50:10 +0000 Subject: [PATCH 23/30] fix condition in targets file. --- src/Web/Avalonia.Web/Avalonia.Web.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Web/Avalonia.Web/Avalonia.Web.targets b/src/Web/Avalonia.Web/Avalonia.Web.targets index b5db8e5dd3..22363b33d8 100644 --- a/src/Web/Avalonia.Web/Avalonia.Web.targets +++ b/src/Web/Avalonia.Web/Avalonia.Web.targets @@ -12,7 +12,7 @@ true - + true full true From a62c56186fef420cb6326c862a218ac172aaaf30 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 9 Nov 2022 15:22:30 +0100 Subject: [PATCH 24/30] Use GetRequiredService in touch/mouse device. --- src/Avalonia.Base/Input/MouseDevice.cs | 6 +++--- src/Avalonia.Base/Input/TouchDevice.cs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.Base/Input/MouseDevice.cs b/src/Avalonia.Base/Input/MouseDevice.cs index 808efb3931..b4bc419223 100644 --- a/src/Avalonia.Base/Input/MouseDevice.cs +++ b/src/Avalonia.Base/Input/MouseDevice.cs @@ -127,9 +127,9 @@ namespace Avalonia.Input _pointer.Capture(source); if (source != null) { - var settings = AvaloniaLocator.Current.GetService(); - var doubleClickTime = settings?.GetDoubleTapTime(PointerType.Mouse).TotalMilliseconds ?? 500; - var doubleClickSize = settings?.GetDoubleTapSize(PointerType.Mouse) ?? new Size(4, 4); + var settings = AvaloniaLocator.Current.GetRequiredService(); + var doubleClickTime = settings.GetDoubleTapTime(PointerType.Mouse).TotalMilliseconds; + var doubleClickSize = settings.GetDoubleTapSize(PointerType.Mouse); if (!_lastClickRect.Contains(p) || timestamp - _lastClickTime > doubleClickTime) { diff --git a/src/Avalonia.Base/Input/TouchDevice.cs b/src/Avalonia.Base/Input/TouchDevice.cs index 05b0a89e01..c98944359d 100644 --- a/src/Avalonia.Base/Input/TouchDevice.cs +++ b/src/Avalonia.Base/Input/TouchDevice.cs @@ -60,8 +60,8 @@ namespace Avalonia.Input else { var settings = AvaloniaLocator.Current.GetRequiredService(); - var doubleClickTime = settings?.GetDoubleTapTime(PointerType.Touch).TotalMilliseconds ?? 500; - var doubleClickSize = settings?.GetDoubleTapSize(PointerType.Touch) ?? new Size(4, 4); + var doubleClickTime = settings.GetDoubleTapTime(PointerType.Touch).TotalMilliseconds; + var doubleClickSize = settings.GetDoubleTapSize(PointerType.Touch); if (!_lastClickRect.Contains(args.Position) || ev.Timestamp - _lastClickTime > doubleClickTime) From 0e951befdf45cb66422fb31a10d8193b57b4fc21 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 9 Nov 2022 15:27:46 +0100 Subject: [PATCH 25/30] Use DefaultPlatformSettings in Web platform. --- src/Web/Avalonia.Web/WindowingPlatform.cs | 24 ++--------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/src/Web/Avalonia.Web/WindowingPlatform.cs b/src/Web/Avalonia.Web/WindowingPlatform.cs index 29c797660f..c399c22c61 100644 --- a/src/Web/Avalonia.Web/WindowingPlatform.cs +++ b/src/Web/Avalonia.Web/WindowingPlatform.cs @@ -8,7 +8,7 @@ using Avalonia.Threading; namespace Avalonia.Web { - internal class BrowserWindowingPlatform : IWindowingPlatform, IPlatformSettings, IPlatformThreadingInterface + internal class BrowserWindowingPlatform : IWindowingPlatform, IPlatformThreadingInterface { private bool _signaled; private static KeyboardDevice? s_keyboard; @@ -36,7 +36,7 @@ namespace Avalonia.Web .Bind().ToSingleton() .Bind().ToSingleton() .Bind().ToConstant(s_keyboard) - .Bind().ToConstant(instance) + .Bind().ToSingleton() .Bind().ToConstant(instance) .Bind().ToConstant(new RenderLoop()) .Bind().ToConstant(ManualTriggerRenderTimer.Instance) @@ -94,25 +94,5 @@ namespace Avalonia.Web { return AvaloniaLocator.Current.GetRequiredService(); } - - Size IPlatformSettings.GetTapSize(PointerType type) - { - return type switch - { - PointerType.Touch => new(10, 10), - _ => new(4, 4), - }; - } - - Size IPlatformSettings.GetDoubleTapSize(PointerType type) - { - return type switch - { - PointerType.Touch => new(16, 16), - _ => new(4, 4), - }; - } - - TimeSpan IPlatformSettings.GetDoubleTapTime(PointerType type) => TimeSpan.FromMilliseconds(500); } } From e1f9809de6338f68aba3ca83cffc3a94ff4d65d0 Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 9 Nov 2022 15:32:11 +0100 Subject: [PATCH 26/30] Add missing ClipToBounds="True" --- src/Avalonia.Themes.Fluent/Controls/TextBox.xaml | 6 ++++-- src/Avalonia.Themes.Simple/Controls/TextBox.xaml | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/TextBox.xaml b/src/Avalonia.Themes.Fluent/Controls/TextBox.xaml index db487ef76b..224264b51d 100644 --- a/src/Avalonia.Themes.Fluent/Controls/TextBox.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/TextBox.xaml @@ -235,7 +235,8 @@ From 1fc811f82c0d31ae956effe9e4017307d6120b14 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 9 Nov 2022 18:55:14 -0500 Subject: [PATCH 27/30] Fix touch tests --- tests/Avalonia.Base.UnitTests/Input/TouchDeviceTests.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/Avalonia.Base.UnitTests/Input/TouchDeviceTests.cs b/tests/Avalonia.Base.UnitTests/Input/TouchDeviceTests.cs index 1070138221..c0c0182622 100644 --- a/tests/Avalonia.Base.UnitTests/Input/TouchDeviceTests.cs +++ b/tests/Avalonia.Base.UnitTests/Input/TouchDeviceTests.cs @@ -210,6 +210,8 @@ namespace Avalonia.Input.UnitTests new TestServices(inputManager: new InputManager())); var iSettingsMock = new Mock(); iSettingsMock.Setup(x => x.GetDoubleTapTime(It.IsAny())).Returns(doubleClickTime); + iSettingsMock.Setup(x => x.GetDoubleTapSize(It.IsAny())).Returns(new Size(16, 16)); + iSettingsMock.Setup(x => x.GetTapSize(It.IsAny())).Returns(new Size(16, 16)); AvaloniaLocator.CurrentMutable.BindToSelf(this) .Bind().ToConstant(iSettingsMock.Object); return unitTestApp; From 0b3f2adce91af8e732b7dd8fc8af19afb70d76a7 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 9 Nov 2022 18:55:27 -0500 Subject: [PATCH 28/30] Use DefaultPlatformSettings for android too, fix build error --- src/Android/Avalonia.Android/AndroidPlatform.cs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/Android/Avalonia.Android/AndroidPlatform.cs b/src/Android/Avalonia.Android/AndroidPlatform.cs index ed5b46d398..0d77a4c905 100644 --- a/src/Android/Avalonia.Android/AndroidPlatform.cs +++ b/src/Android/Avalonia.Android/AndroidPlatform.cs @@ -31,16 +31,6 @@ namespace Avalonia.Android public static readonly AndroidPlatform Instance = new AndroidPlatform(); public static AndroidPlatformOptions Options { get; private set; } - /// - public Size TouchDoubleClickSize => new Size(4, 4); - - /// - public TimeSpan TouchDoubleClickTime => TimeSpan.FromMilliseconds(200); - - public Size DoubleClickSize => TouchDoubleClickSize; - - public TimeSpan DoubleClickTime => TimeSpan.FromMilliseconds(500); - internal static Compositor Compositor { get; private set; } public static void Initialize() @@ -52,7 +42,7 @@ namespace Avalonia.Android .Bind().ToTransient() .Bind().ToConstant(new WindowingPlatformStub()) .Bind().ToSingleton() - .Bind().ToConstant(Instance) + .Bind().ToSingleton() .Bind().ToConstant(new AndroidThreadingInterface()) .Bind().ToSingleton() .Bind().ToConstant(new ChoreographerTimer()) From bb0c4d399b11fc69cb9dea4b78532e73ea398bd4 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 9 Nov 2022 19:02:00 -0500 Subject: [PATCH 29/30] Remove missed interface --- src/Android/Avalonia.Android/AndroidPlatform.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Android/Avalonia.Android/AndroidPlatform.cs b/src/Android/Avalonia.Android/AndroidPlatform.cs index 0d77a4c905..2b6d29e7c5 100644 --- a/src/Android/Avalonia.Android/AndroidPlatform.cs +++ b/src/Android/Avalonia.Android/AndroidPlatform.cs @@ -26,7 +26,7 @@ namespace Avalonia namespace Avalonia.Android { - class AndroidPlatform : IPlatformSettings + class AndroidPlatform { public static readonly AndroidPlatform Instance = new AndroidPlatform(); public static AndroidPlatformOptions Options { get; private set; } From 58cc00898fd115215c7cc075a8530ba22ff33d29 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 9 Nov 2022 19:28:25 -0500 Subject: [PATCH 30/30] Fix IME overflow on x86 --- src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs index c4136a239e..8aa4746b5a 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs @@ -672,7 +672,10 @@ namespace Avalonia.Win32 } case WindowsMessage.WM_IME_SETCONTEXT: { - DefWindowProc(Hwnd, msg, wParam, (IntPtr)(lParam.ToInt64() & ~ISC_SHOWUICOMPOSITIONWINDOW)); + unchecked + { + DefWindowProc(Hwnd, msg, wParam, lParam & ~(nint)ISC_SHOWUICOMPOSITIONWINDOW); + } UpdateInputMethod(GetKeyboardLayout(0));