浏览代码

Fixed some focus issues

Nikita Tsukanov 9 年之前
父节点
当前提交
8ee9d17ff6

+ 1 - 1
src/Android/Avalonia.Android/AndroidPlatform.cs

@@ -57,7 +57,7 @@ namespace Avalonia.Android
             return new WindowImpl();
         }
 
-        public IWindowImpl CreateEmbeddableWindow()
+        public IEmbeddableWindowImpl CreateEmbeddableWindow()
         {
             throw new NotImplementedException();
         }

+ 1 - 0
src/Avalonia.Controls/Avalonia.Controls.csproj

@@ -57,6 +57,7 @@
     <Compile Include="IApplicationLifecycle.cs" />
     <Compile Include="IScrollable.cs" />
     <Compile Include="EmbeddableControl.cs" />
+    <Compile Include="Platform\IEmbeddableWindowImpl.cs" />
     <Compile Include="WindowIcon.cs" />
     <Compile Include="IPseudoClasses.cs" />
     <Compile Include="DropDownItem.cs" />

+ 3 - 1
src/Avalonia.Controls/EmbeddableControl.cs

@@ -11,13 +11,15 @@ using Avalonia.Styling;
 
 namespace Avalonia.Controls
 {
-    public class EmbeddableControl : TopLevel, IStyleable, IFocusScope, ILayoutRoot, INameScope
+    public class EmbeddableControl : TopLevel, IStyleable, IFocusScope, INameScope
     {
         public EmbeddableControl() : base(PlatformManager.CreateEmbeddableWindow())
         {
             PlatformImpl.Show();
         }
 
+        public new IEmbeddableWindowImpl PlatformImpl => (IEmbeddableWindowImpl) base.PlatformImpl;
+
         public void Prepare()
         {
             EnsureInitialized();

+ 15 - 0
src/Avalonia.Controls/Platform/IEmbeddableWindowImpl.cs

@@ -0,0 +1,15 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
+
+namespace Avalonia.Platform
+{
+    /// <summary>
+    /// Defines a platform-specific embeddable window implementation.
+    /// </summary>
+    public interface IEmbeddableWindowImpl : IWindowImpl
+    {
+        event Action LostFocus;
+    }
+}

+ 1 - 1
src/Avalonia.Controls/Platform/IWindowingPlatform.cs

@@ -9,7 +9,7 @@ namespace Avalonia.Platform
     public interface IWindowingPlatform
     {
         IWindowImpl CreateWindow();
-        IWindowImpl CreateEmbeddableWindow();
+        IEmbeddableWindowImpl CreateEmbeddableWindow();
         IPopupImpl CreatePopup();
     }
 }

+ 1 - 1
src/Gtk/Avalonia.Gtk/GtkPlatform.cs

@@ -105,7 +105,7 @@ namespace Avalonia.Gtk
             return new WindowImpl();
         }
 
-        public IWindowImpl CreateEmbeddableWindow()
+        public IEmbeddableWindowImpl CreateEmbeddableWindow()
         {
             throw new NotSupportedException();
         }

+ 13 - 3
src/Windows/Avalonia.Win32/Embedding/EmbeddedWindowImpl.cs

@@ -2,15 +2,16 @@
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
 using System;
+using Avalonia.Platform;
 using Avalonia.Win32.Interop;
 
 namespace Avalonia.Win32
 {
-    class EmbeddedWindowImpl : WindowImpl
+    class EmbeddedWindowImpl : WindowImpl, IEmbeddableWindowImpl
     {
         private static readonly System.Windows.Forms.UserControl WinFormsControl = new System.Windows.Forms.UserControl();
 
-        public static IntPtr DefaultParentWindow => WinFormsControl.Handle;
+        public static IntPtr DefaultParentWindow = WinFormsControl.Handle;
 
         protected override IntPtr CreateWindowOverride(ushort atom)
         {
@@ -23,11 +24,20 @@ namespace Avalonia.Win32
                 0,
                 640,
                 480,
-                WinFormsControl.Handle,
+                DefaultParentWindow,
                 IntPtr.Zero,
                 IntPtr.Zero,
                 IntPtr.Zero);
             return hWnd;
         }
+
+        protected override IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
+        {
+            if(msg == (uint)UnmanagedMethods.WindowsMessage.WM_KILLFOCUS)
+                LostFocus?.Invoke();
+            return base.WndProc(hWnd, msg, wParam, lParam);
+        }
+
+        public event Action LostFocus;
     }
 }

+ 45 - 1
src/Windows/Avalonia.Win32/Embedding/WinFormsAvaloniaControlHost.cs

@@ -2,6 +2,8 @@
 using System.ComponentModel;
 using System.Windows.Forms;
 using Avalonia.Controls;
+using Avalonia.Input;
+using Avalonia.VisualTree;
 using Avalonia.Win32.Interop;
 using Control = System.Windows.Forms.Control;
 
@@ -22,7 +24,10 @@ namespace Avalonia.Win32.Embedding
                                                 value.PlatformImpl.Handle.HandleDescriptor);
                 if (_child != null)
                 {
+                    _child.GotFocus -= _child_GotFocus;
+                    _child.PlatformImpl.LostFocus -= PlatformImpl_LostFocus;
                     _child.PlatformImpl.Hide();
+                    Unfocus();
                     UnmanagedMethods.SetParent(_child.PlatformImpl.Handle.Handle, EmbeddedWindowImpl.DefaultParentWindow);
                 }
                 _child = value;
@@ -30,12 +35,51 @@ namespace Avalonia.Win32.Embedding
                 {
                     UnmanagedMethods.SetParent(_child.PlatformImpl.Handle.Handle, Handle);
                     _child.Prepare();
+                    if (_child.IsFocused)
+                        FocusManager.Instance.Focus(null);
+                    _child.GotFocus += _child_GotFocus;
+                    _child.PlatformImpl.LostFocus += PlatformImpl_LostFocus;
                     FixPosition();
-                    
+                    if(Focused)
+                        UnmanagedMethods.SetFocus(_child.PlatformImpl.Handle.Handle);
                 }
             }
         }
 
+        void Unfocus()
+        {
+            var focused = (IVisual)FocusManager.Instance.Current;
+            if (focused == null)
+                return;
+            while (focused.VisualParent != null)
+                focused = focused.VisualParent;
+
+            if (focused == _child)
+                KeyboardDevice.Instance.SetFocusedElement(null, NavigationMethod.Unspecified, InputModifiers.None);
+        }
+
+        private void PlatformImpl_LostFocus()
+        {
+            Unfocus();
+        }
+
+        protected override void Dispose(bool disposing)
+        {
+            Child = null;
+            base.Dispose(disposing);
+        }
+
+        private void _child_GotFocus(object sender, Interactivity.RoutedEventArgs e)
+        {
+            UnmanagedMethods.SetFocus(_child.PlatformImpl.Handle.Handle);
+        }
+
+        protected override void OnGotFocus(EventArgs e)
+        {
+            if (_child != null)
+                UnmanagedMethods.SetFocus(_child.PlatformImpl.Handle.Handle);
+        }
+
 
         void FixPosition()
         {

+ 2 - 0
src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs

@@ -732,6 +732,8 @@ namespace Avalonia.Win32.Interop
         [DllImport("user32.dll")]
         public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, SetWindowPosFlags uFlags);
         [DllImport("user32.dll")]
+        public static extern bool SetFocus(IntPtr hWnd);
+        [DllImport("user32.dll")]
         public static extern bool SetParent(IntPtr hWnd, IntPtr hWndNewParent);
         [DllImport("user32.dll")]
         public static extern bool ShowWindow(IntPtr hWnd, ShowWindowCommand nCmdShow);

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

@@ -179,7 +179,7 @@ namespace Avalonia.Win32
             return new WindowImpl();
         }
 
-        public IWindowImpl CreateEmbeddableWindow()
+        public IEmbeddableWindowImpl CreateEmbeddableWindow()
         {
             return new EmbeddedWindowImpl();
         }

+ 1 - 1
src/iOS/Avalonia.iOS/WindowingPlatformImpl.cs

@@ -20,7 +20,7 @@ namespace Avalonia.iOS
             return _window;
         }
 
-        public IWindowImpl CreateEmbeddableWindow()
+        public IEmbeddableWindowImpl CreateEmbeddableWindow()
         {
             throw new NotImplementedException();
         }

+ 1 - 1
tests/Avalonia.Controls.UnitTests/WindowingPlatformMock.cs

@@ -20,7 +20,7 @@ namespace Avalonia.Controls.UnitTests
             return _windowImpl?.Invoke() ?? Mock.Of<IWindowImpl>(x => x.Scaling == 1);
         }
 
-        public IWindowImpl CreateEmbeddableWindow()
+        public IEmbeddableWindowImpl CreateEmbeddableWindow()
         {
             throw new NotImplementedException();
         }

+ 1 - 1
tests/Avalonia.UnitTests/MockWindowingPlatform.cs

@@ -20,7 +20,7 @@ namespace Avalonia.UnitTests
             return _windowImpl?.Invoke() ?? Mock.Of<IWindowImpl>(x => x.Scaling == 1);
         }
 
-        public IWindowImpl CreateEmbeddableWindow()
+        public IEmbeddableWindowImpl CreateEmbeddableWindow()
         {
             throw new NotImplementedException();
         }