Bladeren bron

Merge branch 'master' into features/NetAnalyzers/CA1822

workgroupengineering 3 jaren geleden
bovenliggende
commit
daffd4a1b8

+ 1 - 0
.github/FUNDING.yml

@@ -1 +1,2 @@
+github: avaloniaui
 open_collective: avalonia

+ 2 - 2
native/Avalonia.Native/src/OSX/Screens.mm

@@ -41,9 +41,9 @@ public:
             ret->WorkingArea.X = [screen visibleFrame].origin.x;
             ret->WorkingArea.Y = ConvertPointY(ToAvnPoint([screen visibleFrame].origin)).Y - ret->WorkingArea.Height;
             
-            ret->PixelDensity = [screen backingScaleFactor];
+            ret->Scaling = [screen backingScaleFactor];
             
-            ret->Primary = index == 0;
+            ret->IsPrimary = index == 0;
             
             return S_OK;
         }

+ 2 - 3
samples/ControlCatalog.Web/ControlCatalog.Web.csproj

@@ -16,9 +16,8 @@
     <TrimMode>full</TrimMode>
     <WasmBuildNative>true</WasmBuildNative>
     <InvariantGlobalization>true</InvariantGlobalization>
-    <WasmEnableSIMD>true</WasmEnableSIMD>
-    <EmccCompileOptimizationFlag>-O3</EmccCompileOptimizationFlag>
-    <EmccLinkOptimizationFlag>-O3</EmccLinkOptimizationFlag>
+    <EmccCompileOptimizationFlag>-O2</EmccCompileOptimizationFlag>
+    <EmccLinkOptimizationFlag>-O2</EmccLinkOptimizationFlag>
   </PropertyGroup>
 
   <ItemGroup>

+ 1 - 0
samples/ControlCatalog.Web/Roots.xml

@@ -3,4 +3,5 @@
   <assembly fullname="ControlCatalog.Web" preserve="All" />
   <assembly fullname="Avalonia.Themes.Fluent" preserve="All" />
   <assembly fullname="Avalonia.Themes.Simple" preserve="All" />
+  <assembly fullname="Avalonia.Controls.ColorPicker" preserve="All" />
 </linker>

+ 3 - 2
samples/ControlCatalog/Pages/ScreenPage.cs

@@ -62,10 +62,11 @@ namespace ControlCatalog.Pages
                         ScreenPage.CreateFormattedText($"WorkArea: {screen.WorkingArea.Width}:{screen.WorkingArea.Height}");
                     context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 20));
 
-                    formattedText = ScreenPage.CreateFormattedText($"Scaling: {screen.PixelDensity * 100}%");
+                    formattedText = CreateFormattedText($"Scaling: {screen.Scaling * 100}%");
                     context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 40));
 
-                    formattedText = ScreenPage.CreateFormattedText($"Primary: {screen.Primary}");
+                    formattedText = CreateFormattedText($"IsPrimary: {screen.IsPrimary}");
+
                     context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 60));
 
                     formattedText =

+ 2 - 2
src/Avalonia.Base/Media/PathMarkupParser.cs

@@ -188,7 +188,7 @@ namespace Avalonia.Media
             _isOpen = true;
         }
 
-        private void SetFillRule(ref ReadOnlySpan<char> span)
+        private void SetFillRule(scoped ref ReadOnlySpan<char> span)
         {
             ThrowIfDisposed();
 
@@ -452,7 +452,7 @@ namespace Avalonia.Media
             return !span.IsEmpty && (span[0] == ',' || span[0] == '-' || span[0] == '.' || char.IsDigit(span[0]));
         }
 
-        private static bool ReadArgument(ref ReadOnlySpan<char> remaining, out ReadOnlySpan<char> argument)
+        private static bool ReadArgument(scoped ref ReadOnlySpan<char> remaining, out ReadOnlySpan<char> argument)
         {
             remaining = SkipWhitespace(remaining);
             if (remaining.IsEmpty)

+ 6 - 0
src/Avalonia.Controls/Platform/IScreenImpl.cs

@@ -6,8 +6,14 @@ namespace Avalonia.Platform
     [Unstable]
     public interface IScreenImpl
     {
+        /// <summary>
+        /// Gets the total number of screens available on the device.
+        /// </summary>
         int ScreenCount { get; }
 
+        /// <summary>
+        /// Gets the list of all screens available on the device.
+        /// </summary>
         IReadOnlyList<Screen> AllScreens { get; }
 
         Screen? ScreenFromWindow(IWindowBaseImpl window);

+ 50 - 7
src/Avalonia.Controls/Platform/Screen.cs

@@ -1,21 +1,64 @@
-namespace Avalonia.Platform
+using System;
+
+namespace Avalonia.Platform
 {
+    /// <summary>
+    /// Represents a single display screen.
+    /// </summary>
     public class Screen
     {
-        public double PixelDensity { get; }
+        /// <summary>
+        /// Gets the scaling factor applied to the screen by the operating system.
+        /// </summary>
+        /// <remarks>
+        /// Multiply this value by 100 to get a percentage.
+        /// Both X and Y scaling factors are assumed uniform.
+        /// </remarks>
+        public double Scaling { get; }
+
+        /// <inheritdoc cref="Scaling"/>
+        [Obsolete("Use the Scaling property instead.")]
+        public double PixelDensity => Scaling;
 
+        /// <summary>
+        /// Gets the overall pixel-size of the screen.
+        /// </summary>
+        /// <remarks>
+        /// This generally is the raw pixel counts in both the X and Y direction.
+        /// </remarks>
         public PixelRect Bounds { get; }
 
+        /// <summary>
+        /// Gets the actual working-area pixel-size of the screen.
+        /// </summary>
+        /// <remarks>
+        /// This area may be smaller than <see href="Bounds"/> to account for notches and
+        /// other block-out areas such as taskbars etc.
+        /// </remarks>
         public PixelRect WorkingArea { get; }
 
-        public bool Primary { get; }
-        
-        public Screen(double pixelDensity, PixelRect bounds, PixelRect workingArea, bool primary)
+        /// <summary>
+        /// Gets a value indicating whether the screen is the primary one.
+        /// </summary>
+        public bool IsPrimary { get; }
+
+        /// <inheritdoc cref="IsPrimary"/>
+        [Obsolete("Use the IsPrimary property instead.")]
+        public bool Primary => IsPrimary;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="Screen"/> class.
+        /// </summary>
+        /// <param name="scaling">The scaling factor applied to the screen by the operating system.</param>
+        /// <param name="bounds">The overall pixel-size of the screen.</param>
+        /// <param name="workingArea">The actual working-area pixel-size of the screen.</param>
+        /// <param name="isPrimary">Whether the screen is the primary one.</param>
+        public Screen(double scaling, PixelRect bounds, PixelRect workingArea, bool isPrimary)
         {
-            this.PixelDensity = pixelDensity;
+            this.Scaling = scaling;
             this.Bounds = bounds;
             this.WorkingArea = workingArea;
-            this.Primary = primary;
+            this.IsPrimary = isPrimary;
         } 
     }
 }

+ 20 - 3
src/Avalonia.Controls/Screens.cs

@@ -8,14 +8,31 @@ using Avalonia.VisualTree;
 
 namespace Avalonia.Controls
 {
+    /// <summary>
+    /// Represents all screens available on a device.
+    /// </summary>
     public class Screens
     {
         private readonly IScreenImpl _iScreenImpl;
 
+        /// <summary>
+        /// Gets the total number of screens available on the device.
+        /// </summary>
         public int ScreenCount => _iScreenImpl?.ScreenCount ?? 0;
+
+        /// <summary>
+        /// Gets the list of all screens available on the device.
+        /// </summary>
         public IReadOnlyList<Screen> All => _iScreenImpl?.AllScreens ?? Array.Empty<Screen>();
-        public Screen? Primary => All.FirstOrDefault(x => x.Primary);
 
+        /// <summary>
+        /// Gets the primary screen on the device.
+        /// </summary>
+        public Screen? Primary => All.FirstOrDefault(x => x.IsPrimary);
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="Screens"/> class.
+        /// </summary>
         public Screens(IScreenImpl iScreenImpl)
         {
             _iScreenImpl = iScreenImpl;
@@ -25,14 +42,14 @@ namespace Avalonia.Controls
         {
             return _iScreenImpl.ScreenFromRect(bounds);
         }
-        
+
         public Screen? ScreenFromWindow(IWindowBaseImpl window)
         {
             return _iScreenImpl.ScreenFromWindow(window);
         }
 
         public Screen? ScreenFromPoint(PixelPoint point)
-        {      
+        {
             return _iScreenImpl.ScreenFromPoint(point);
         }
 

+ 2 - 2
src/Avalonia.Native/ScreenImpl.cs

@@ -30,10 +30,10 @@ namespace Avalonia.Native
                         var screen = _native.GetScreen(i);
 
                         result[i] = new Screen(
-                            screen.PixelDensity,
+                            screen.Scaling,
                             screen.Bounds.ToAvaloniaPixelRect(),
                             screen.WorkingArea.ToAvaloniaPixelRect(),
-                            screen.Primary.FromComBool());
+                            screen.IsPrimary.FromComBool());
                     }
 
                     return result;

+ 1 - 1
src/Avalonia.Native/WindowImplBase.cs

@@ -92,7 +92,7 @@ namespace Avalonia.Native
             _savedScaling = RenderScaling;
             _nativeControlHost = new NativeControlHostImpl(_native.CreateNativeControlHost());
 
-            var monitor = Screen.AllScreens.OrderBy(x => x.PixelDensity)
+            var monitor = Screen.AllScreens.OrderBy(x => x.Scaling)
                     .FirstOrDefault(m => m.Bounds.Contains(Position));
 
             Resize(new Size(monitor.WorkingArea.Width * 0.75d, monitor.WorkingArea.Height * 0.7d), PlatformResizeReason.Layout);

+ 2 - 2
src/Avalonia.Native/avn.idl

@@ -256,8 +256,8 @@ struct AvnScreen
 {
     AvnRect Bounds;
     AvnRect WorkingArea;
-    float PixelDensity;
-    bool Primary;
+    float Scaling;
+    bool IsPrimary;
 }
 
 enum AvnPixelFormat

+ 16 - 12
src/Avalonia.X11/X11Screens.cs

@@ -9,7 +9,7 @@ using JetBrains.Annotations;
 
 namespace Avalonia.X11
 {
-    class X11Screens  : IScreenImpl
+    class X11Screens : IScreenImpl
     {
         private IX11Screens _impl;
 
@@ -218,7 +218,7 @@ namespace Avalonia.X11
         public int ScreenCount => _impl.Screens.Length;
 
         public IReadOnlyList<Screen> AllScreens =>
-            _impl.Screens.Select(s => new Screen(s.PixelDensity, s.Bounds, s.WorkingArea, s.Primary)).ToArray();
+            _impl.Screens.Select(s => new Screen(s.Scaling, s.Bounds, s.WorkingArea, s.IsPrimary)).ToArray();
     }
 
     interface IX11Screens
@@ -281,30 +281,34 @@ namespace Avalonia.X11
     {
         private const int FullHDWidth = 1920;
         private const int FullHDHeight = 1080;
-        public bool Primary { get; }
+        public bool IsPrimary { get; }
         public string Name { get; set; }
         public PixelRect Bounds { get; set; }
         public Size? PhysicalSize { get; set; }
-        public double PixelDensity { get; set; }
+        public double Scaling { get; set; }
         public PixelRect WorkingArea { get; set; }
 
-        public X11Screen(PixelRect bounds, bool primary,
-            string name, Size? physicalSize, double? pixelDensity)
+        public X11Screen(
+            PixelRect bounds,
+            bool isPrimary,
+            string name,
+            Size? physicalSize,
+            double? scaling)
         {
-            Primary = primary;
+            IsPrimary = isPrimary;
             Name = name;
             Bounds = bounds;
-            if (physicalSize == null && pixelDensity == null)
+            if (physicalSize == null && scaling == null)
             {
-                PixelDensity = 1;
+                Scaling = 1;
             }
-            else if (pixelDensity == null)
+            else if (scaling == null)
             {
-                PixelDensity = GuessPixelDensity(bounds, physicalSize.Value);
+                Scaling = GuessPixelDensity(bounds, physicalSize.Value);
             }
             else
             {
-                PixelDensity = pixelDensity.Value;
+                Scaling = scaling.Value;
                 PhysicalSize = physicalSize;
             }
         }

+ 4 - 4
src/Avalonia.X11/X11Window.cs

@@ -118,7 +118,7 @@ namespace Avalonia.X11
 
             if (!_popup && Screen != null)
             {
-                var monitor = Screen.AllScreens.OrderBy(x => x.PixelDensity)
+                var monitor = Screen.AllScreens.OrderBy(x => x.Scaling)
                    .FirstOrDefault(m => m.Bounds.Contains(Position));
 
                 if (monitor != null)
@@ -568,9 +568,9 @@ namespace Avalonia.X11
                 newScaling = _scalingOverride.Value;
             else
             {
-                var monitor = _platform.X11Screens.Screens.OrderBy(x => x.PixelDensity)
+                var monitor = _platform.X11Screens.Screens.OrderBy(x => x.Scaling)
                     .FirstOrDefault(m => m.Bounds.Contains(Position));
-                newScaling = monitor?.PixelDensity ?? RenderScaling;
+                newScaling = monitor?.Scaling ?? RenderScaling;
             }
 
             if (RenderScaling != newScaling)
@@ -992,7 +992,7 @@ namespace Avalonia.X11
 
         public IScreenImpl Screen => _platform.Screens;
 
-        public Size MaxAutoSizeHint => _platform.X11Screens.Screens.Select(s => s.Bounds.Size.ToSize(s.PixelDensity))
+        public Size MaxAutoSizeHint => _platform.X11Screens.Screens.Select(s => s.Bounds.Size.ToSize(s.Scaling))
             .OrderByDescending(x => x.Width + x.Height).FirstOrDefault();
 
 

+ 11 - 3
src/Web/Avalonia.Web/AvaloniaView.cs

@@ -77,8 +77,7 @@ namespace Avalonia.Web
 
             _topLevelImpl.SetCssCursor = (cursor) =>
             {
-                InputHelper.SetCursor(_containerElement, cursor); // macOS
-                InputHelper.SetCursor(_canvas, cursor); // windows
+                InputHelper.SetCursor(_containerElement, cursor);
             };
 
             _topLevel.Prepare();
@@ -136,6 +135,8 @@ namespace Avalonia.Web
             DomHelper.ObserveSize(host, null, OnSizeChanged);
 
             CanvasHelper.RequestAnimationFrame(_canvas, true);
+            
+            InputHelper.FocusElement(_containerElement);
         }
 
         private static RawPointerPoint ExtractRawPointerFromJSArgs(JSObject args)
@@ -252,7 +253,14 @@ namespace Avalonia.Web
 
         private bool OnKeyDown (string code, string key, int modifier)
         {
-            return _topLevelImpl.RawKeyboardEvent(RawKeyEventType.KeyDown, code, key, (RawInputModifiers)modifier);
+            var handled = _topLevelImpl.RawKeyboardEvent(RawKeyEventType.KeyDown, code, key, (RawInputModifiers)modifier);
+
+            if (!handled && key.Length == 1)
+            {
+                handled = _topLevelImpl.RawTextEvent(key);
+            }
+
+            return handled;
         }
 
         private bool OnKeyUp(string code, string key, int modifier)

+ 1 - 1
src/Web/Avalonia.Web/webapp/build.js

@@ -5,7 +5,7 @@ require("esbuild").build({
     ],
     outdir: "../wwwroot",
     bundle: true,
-    minify: false,
+    minify: true,
     format: "esm",
     target: "es2016",
     platform: "browser",

+ 16 - 19
src/Web/Avalonia.Web/webapp/modules/avalonia/canvas.ts

@@ -106,12 +106,6 @@ export class Canvas {
 
         // add the draw to the next frame
         this.renderLoopRequest = window.requestAnimationFrame(() => {
-            if (this.glInfo) {
-                const GL = (globalThis as any).AvaloniaGL;
-                // make current
-                GL.makeContextCurrent(this.glInfo.context);
-            }
-
             if (this.htmlCanvas.width !== this.newWidth) {
                 this.htmlCanvas.width = this.newWidth ?? 0;
             }
@@ -131,6 +125,11 @@ export class Canvas {
     }
 
     public setCanvasSize(width: number, height: number): void {
+        if (this.renderLoopRequest !== 0) {
+            window.cancelAnimationFrame(this.renderLoopRequest);
+            this.renderLoopRequest = 0;
+        }
+
         this.newWidth = width;
         this.newHeight = height;
 
@@ -142,11 +141,7 @@ export class Canvas {
             this.htmlCanvas.height = this.newHeight;
         }
 
-        if (this.glInfo) {
-            const GL = (globalThis as any).AvaloniaGL;
-            // make current
-            GL.makeContextCurrent(this.glInfo.context);
-        }
+        this.requestAnimationFrame();
     }
 
     public static setCanvasSize(element: HTMLCanvasElement, width: number, height: number): void {
@@ -210,23 +205,25 @@ interface SizeWatcherInstance {
 export class SizeWatcher {
     static observer: ResizeObserver;
     static elements: Map<string, HTMLElement>;
+    private static lastMove: number;
 
     public static observe(element: HTMLElement, elementId: string | undefined, callback: (width: number, height: number) => void): void {
         if (!element || !callback) {
             return;
         }
 
-        SizeWatcher.init();
+        SizeWatcher.lastMove = Date.now();
 
-        const watcherElement = element as SizeWatcherElement;
-        watcherElement.SizeWatcher = {
-            callback
-        };
+        callback(element.clientWidth, element.clientHeight);
 
-        SizeWatcher.elements.set(elementId ?? element.id, element);
-        SizeWatcher.observer.observe(element);
+        const handleResize = (args: UIEvent) => {
+            if (Date.now() - SizeWatcher.lastMove > 33) {
+                callback(element.clientWidth, element.clientHeight);
+                SizeWatcher.lastMove = Date.now();
+            }
+        };
 
-        SizeWatcher.invoke(element);
+        window.addEventListener("resize", handleResize);
     }
 
     public static unobserve(elementId: string): void {

+ 3 - 3
src/Web/Avalonia.Web/webapp/modules/avalonia/dom.ts

@@ -11,6 +11,7 @@ export class AvaloniaDOM {
         host.tabIndex = 0;
         host.oncontextmenu = function () { return false; };
         host.style.overflow = "hidden";
+        host.style.touchAction = "none";
 
         // Rendering target canvas
         const canvas = document.createElement("canvas");
@@ -18,12 +19,11 @@ export class AvaloniaDOM {
         canvas.classList.add("avalonia-canvas");
         canvas.style.backgroundColor = "#ccc";
         canvas.style.width = "100%";
-        canvas.style.height = "100%";
         canvas.style.position = "absolute";
 
         // Native controls host
         const nativeHost = document.createElement("div");
-        canvas.id = `nativeHost${randomIdPart}`;
+        nativeHost.id = `nativeHost${randomIdPart}`;
         nativeHost.classList.add("avalonia-native-host");
         nativeHost.style.left = "0px";
         nativeHost.style.top = "0px";
@@ -33,7 +33,7 @@ export class AvaloniaDOM {
 
         // IME
         const inputElement = document.createElement("input");
-        canvas.id = `input${randomIdPart}`;
+        inputElement.id = `inputElement${randomIdPart}`;
         inputElement.classList.add("avalonia-input-element");
         inputElement.autocapitalize = "none";
         inputElement.type = "text";

+ 5 - 1
src/Web/Avalonia.Web/webapp/modules/avalonia/input.ts

@@ -159,7 +159,11 @@ export class InputHelper {
     }
 
     public static setCursor(inputElement: HTMLInputElement, kind: string) {
-        inputElement.style.cursor = kind;
+        if (kind === "pointer") {
+            inputElement.style.removeProperty("cursor");
+        } else {
+            inputElement.style.cursor = kind;
+        }
     }
 
     public static setBounds(inputElement: HTMLInputElement, x: number, y: number, caretWidth: number, caretHeight: number, caret: number) {

+ 4 - 1
src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs

@@ -1878,7 +1878,10 @@ namespace Avalonia.Win32.Interop
 
         public static uint LGID(IntPtr HKL)
         {
-            return (uint)(HKL.ToInt32() & 0xffff);
+            unchecked
+            {
+                return (uint)((ulong)HKL & 0xffff);
+            }
         }
 
         public const int SORT_DEFAULT = 0;

+ 4 - 4
src/Windows/Avalonia.Win32/TrayIconImpl.cs

@@ -216,7 +216,7 @@ namespace Avalonia.Win32
                 {
                     Anchor = PopupAnchor.TopLeft,
                     Gravity = PopupGravity.BottomRight,
-                    AnchorRectangle = new Rect(Position.ToPoint(1) / Screens.Primary.PixelDensity, new Size(1, 1)),
+                    AnchorRectangle = new Rect(Position.ToPoint(1) / Screens.Primary.Scaling, new Size(1, 1)),
                     Size = finalRect.Size,
                     ConstraintAdjustment = PopupPositionerConstraintAdjustment.FlipX | PopupPositionerConstraintAdjustment.FlipY,
                 });
@@ -244,16 +244,16 @@ namespace Avalonia.Win32
                     {
                         var point = _hiddenWindow.Screens.Primary.Bounds.TopLeft;
                         var size = _hiddenWindow.Screens.Primary.Bounds.Size;
-                        return new Rect(point.X, point.Y, size.Width * _hiddenWindow.Screens.Primary.PixelDensity, size.Height * _hiddenWindow.Screens.Primary.PixelDensity);
+                        return new Rect(point.X, point.Y, size.Width * _hiddenWindow.Screens.Primary.Scaling, size.Height * _hiddenWindow.Screens.Primary.Scaling);
                     }
                 }
 
                 public void MoveAndResize(Point devicePoint, Size virtualSize)
                 {
-                    _moveResize(new PixelPoint((int)devicePoint.X, (int)devicePoint.Y), virtualSize, _hiddenWindow.Screens.Primary.PixelDensity);
+                    _moveResize(new PixelPoint((int)devicePoint.X, (int)devicePoint.Y), virtualSize, _hiddenWindow.Screens.Primary.Scaling);
                 }
 
-                public double Scaling => _hiddenWindow.Screens.Primary.PixelDensity;
+                public double Scaling => _hiddenWindow.Screens.Primary.Scaling;
             }
         }
 

+ 2 - 1
src/Windows/Avalonia.Win32/WinScreen.cs

@@ -7,7 +7,8 @@ namespace Avalonia.Win32
     {
         private readonly IntPtr _hMonitor;
 
-        public WinScreen(double pixelDensity, PixelRect bounds, PixelRect workingArea, bool primary, IntPtr hMonitor) : base(pixelDensity, bounds, workingArea, primary)
+        public WinScreen(double scaling, PixelRect bounds, PixelRect workingArea, bool isPrimary, IntPtr hMonitor)
+            : base(scaling, bounds, workingArea, isPrimary)
         {
             _hMonitor = hMonitor;
         }

+ 1 - 1
src/Windows/Avalonia.Win32/WindowImpl.cs

@@ -224,7 +224,7 @@ namespace Avalonia.Win32
             }
         }
 
-        private double PrimaryScreenRenderScaling => Screen.AllScreens.FirstOrDefault(screen => screen.Primary)?.PixelDensity ?? 1;
+        private double PrimaryScreenRenderScaling => Screen.AllScreens.FirstOrDefault(screen => screen.IsPrimary)?.Scaling ?? 1;
 
         public double RenderScaling => _scaling;