Skip to content

Commit

Permalink
Merge pull request #2589 from cwensley/curtis/nativecontrolhost
Browse files Browse the repository at this point in the history
Add NativeControlHost
  • Loading branch information
cwensley authored Dec 5, 2023
2 parents 012f320 + 8392bda commit 7a11c1b
Show file tree
Hide file tree
Showing 21 changed files with 464 additions and 21 deletions.
36 changes: 34 additions & 2 deletions src/Eto.Gtk/Forms/Controls/NativeControlHandler.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,43 @@
namespace Eto.GtkSharp.Forms.Controls

namespace Eto.GtkSharp.Forms.Controls
{
public class NativeControlHandler : GtkControl<Gtk.Widget, Control, Control.ICallback>
public class NativeControlHandler : GtkControl<Gtk.Widget, Control, Control.ICallback>, NativeControlHost.IHandler
{

Gtk.EventBox _eventBox = new Gtk.EventBox();
public override Gtk.Widget ContainerControl => _eventBox;

public NativeControlHandler(Gtk.Widget nativeControl)
{
Control = nativeControl;
}

public NativeControlHandler()
{
}

public void Create(object controlObject)
{
if (controlObject == null)
{
Control = _eventBox;
}
else if (controlObject is Gtk.Widget widget)
{
Control = widget;
_eventBox.Child = widget;
}
else if (controlObject is IntPtr handle)
{
widget = GLib.Object.GetObject(handle) as Gtk.Widget;
if (widget == null)
throw new InvalidOperationException("Could not convert handle to Gtk.Widget");
Control = widget;
_eventBox.Child = widget;
}
else
throw new NotSupportedException($"controlObject of type {controlObject.GetType()} is not supported by this platform");
}
}
}

1 change: 1 addition & 0 deletions src/Eto.Gtk/Platform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ public static void AddTo(Eto.Platform p)
p.Add<DataObject.IHandler>(() => new DataObjectHandler());
p.Add<DataFormats.IHandler>(() => new DataFormatsHandler());
p.Add<Window.IWindowHandler>(() => new WindowHandler());
p.Add<NativeControlHost.IHandler>(() => new NativeControlHandler());
if (EtoEnvironment.Platform.IsLinux)
{
p.Add<TrayIndicator.IHandler>(() => new LinuxTrayIndicatorHandler());
Expand Down
27 changes: 26 additions & 1 deletion src/Eto.Mac/Forms/Controls/NativeControlHandler.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
namespace Eto.Mac.Forms.Controls
{
public class NativeControlHandler : MacView<NSView, Control, Control.ICallback>
public class NativeControlHandler : MacView<NSView, Control, Control.ICallback>, NativeControlHost.IHandler
{
NSViewController controller;

public NativeControlHandler(NSView nativeControl)
{
Control = nativeControl;
}

public NativeControlHandler()
{
}

public override SizeF GetPreferredSize(SizeF availableSize)
{
Expand All @@ -21,6 +25,27 @@ public NativeControlHandler(NSViewController nativeControl)
}

public override NSView ContainerControl { get { return Control; } }

public void Create(object controlObject)
{
if (controlObject == null)
{
Control = new NSView();
}
else if (controlObject is NSView view)
{
Control = view;
}
else if (controlObject is IntPtr handle)
{
view = Runtime.GetNSObject(handle) as NSView;
if (view == null)
throw new InvalidOperationException("supplied handle is invalid or does not refer to an object derived from NSView");
Control = view;
}
else
throw new NotSupportedException($"controlObject of type {controlObject.GetType()} is not supported by this platform");
}
}
}

1 change: 1 addition & 0 deletions src/Eto.Mac/Platform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ public static void AddTo(Eto.Platform p)
p.Add<ToggleButton.IHandler>(() => new ToggleButtonHandler());
p.Add<PropertyGrid.IHandler>(() => new ThemedPropertyGridHandler());
p.Add<CollectionEditor.IHandler>(() => new ThemedCollectionEditorHandler());
p.Add<NativeControlHost.IHandler>(() => new NativeControlHandler());

// Forms.Menu
p.Add<CheckMenuItem.IHandler>(() => new CheckMenuItemHandler());
Expand Down
49 changes: 47 additions & 2 deletions src/Eto.WinForms/Forms/Controls/NativeControlHandler.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,56 @@
namespace Eto.WinForms.Forms.Controls


namespace Eto.WinForms.Forms.Controls
{
public class NativeControlHandler : WindowsControl<swf.Control, Control, Control.ICallback>
public class NativeControlHandler : WindowsControl<swf.Control, Control, Control.ICallback>, NativeControlHost.IHandler
{
swf.IWin32Window _win32Window;
public NativeControlHandler(swf.Control nativeControl)
{
Control = nativeControl;
}

public NativeControlHandler()
{
}


public void Create(object controlObject)
{
if (controlObject == null)
{
Control = new swf.UserControl();
}
else if (controlObject is swf.Control control)
{
Control = control;
}
else if (controlObject is IntPtr handle)
{
CreateWithHandle(handle);
}
else if (controlObject is swf.IWin32Window win32Window)
{
// keep a reference so it doesn't get GC'd
_win32Window = win32Window;
CreateWithHandle(win32Window.Handle);
}
else
throw new NotSupportedException($"controlObject of type {controlObject.GetType()} is not supported by this platform");
}

private void CreateWithHandle(IntPtr handle)
{
Control = new swf.Control();
Win32.GetWindowRect(handle, out var rect);
Win32.SetParent(handle, Control.Handle);
Control.Size = rect.ToSD().Size;
Widget.SizeChanged += (sender, e) =>
{
var size = Control.Size;
Win32.SetWindowPos(handle, IntPtr.Zero, 0, 0, size.Width, size.Height, Win32.SWP.NOZORDER);
};
}
}
}

1 change: 1 addition & 0 deletions src/Eto.WinForms/Platform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ public static void AddTo(Eto.Platform p)
p.Add<ToggleButton.IHandler>(() => new ToggleButtonHandler());
p.Add<PropertyGrid.IHandler>(() => new ThemedPropertyGridHandler());
p.Add<CollectionEditor.IHandler>(() => new ThemedCollectionEditorHandler());
p.Add<NativeControlHost.IHandler>(() => new NativeControlHandler());

// Forms.Menu
p.Add<CheckMenuItem.IHandler>(() => new CheckMenuItemHandler());
Expand Down
12 changes: 12 additions & 0 deletions src/Eto.WinForms/Win32.cs
Original file line number Diff line number Diff line change
Expand Up @@ -620,5 +620,17 @@ public struct SCROLLINFO
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetScrollInfo(IntPtr hwnd, int fnBar, ref SCROLLINFO lpsi);

[DllImport("user32.dll")]
public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);

[DllImport("user32.dll")]
public static extern IntPtr CreateWindowEx(uint dwExStyle, string lpClassName, string lpWindowName, uint dwStyle, int x, int y, int nWidth, int nHeight, IntPtr hWndParent, IntPtr hMenu, IntPtr hInstance, IntPtr lpParam);

[DllImport("user32.dll")]
public static extern bool DestroyWindow(IntPtr hWnd);

[DllImport("User32.dll", SetLastError = true)]
public static extern int SetWindowRgn(IntPtr hWnd, IntPtr hRgn, bool bRedraw);
}
}
165 changes: 152 additions & 13 deletions src/Eto.Wpf/Forms/Controls/NativeControlHandler.cs
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,22 +1,161 @@
namespace Eto.Wpf.Forms.Controls
using Eto.Forms;
using System.Windows.Interop;
using IWin32WindowWinForms = System.Windows.Forms.IWin32Window;
using IWin32WindowInterop = System.Windows.Interop.IWin32Window;

namespace Eto.Wpf.Forms.Controls;
public class NativeControlHandler : WpfFrameworkElement<sw.FrameworkElement, Control, Control.ICallback>, NativeControlHost.IHandler
{
public class NativeControlHandler : WpfFrameworkElement<sw.FrameworkElement, Control, Control.ICallback>
public override IntPtr NativeHandle
{
get
{
if (Control is EtoHwndHost host)
return host.Handle;
return base.NativeHandle;
}
}

public NativeControlHandler()
{
}

public NativeControlHandler(sw.FrameworkElement nativeControl)
{
Control = nativeControl;
}

public override Color BackgroundColor
{
public NativeControlHandler(sw.FrameworkElement nativeControl)
get => throw new NotSupportedException("You cannot get this property for native controls");
set => throw new NotSupportedException("You cannot set this property for native controls");
}

public void Create(object controlObject)
{
if (controlObject == null)
{
var host = new EtoHwndHost(null);
host.GetPreferredSize += () => Size.Round(UserPreferredSize.ToEto() * (Widget.ParentWindow?.Screen?.LogicalPixelSize ?? 1));
Control = host;
}
else if (controlObject is sw.FrameworkElement element)
{
Control = element;
}
else if (controlObject is IntPtr handle)
{
Control = new EtoHwndHost(new HandleRef(this, handle));
}
else if (controlObject is IWin32WindowWinForms win32Window)
{
// keep a reference to the win32window object
var host = new EtoHwndHost(new HandleRef(win32Window, win32Window.Handle));
host.GetPreferredSize += () => Size.Round(UserPreferredSize.ToEto() * (Widget.ParentWindow?.Screen?.LogicalPixelSize ?? 1));
Control = host;
}
else if (controlObject is IWin32WindowInterop win32WindowWpf)
{
Control = nativeControl;
// keep a reference to the win32window object
var host = new EtoHwndHost(new HandleRef(win32WindowWpf, win32WindowWpf.Handle));
host.GetPreferredSize += () => Size.Round(UserPreferredSize.ToEto() * (Widget.ParentWindow?.Screen?.LogicalPixelSize ?? 1));
Control = host;
}
else
throw new NotSupportedException($"controlObject of type {controlObject.GetType()} is not supported by this platform");
}
}

sealed class EtoHwndHost : HwndHost
{
HandleRef? _hwnd;
swc.ScrollViewer _parentScrollViewer;
sd.Rectangle _regionRect = sd.Rectangle.Empty;

public Func<Size> GetPreferredSize { get; set; }

public override Eto.Drawing.Color BackgroundColor
public EtoHwndHost(HandleRef? hwnd)
{
_hwnd = hwnd;
}

protected override HandleRef BuildWindowCore(HandleRef hwndParent)
{
if (_hwnd == null)
{
get
{
throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "You cannot get this property for native controls"));
}
set
{
throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "You cannot set this property for native controls"));
}
// create a new WinForms Usercontrol to host whatever we want
var size = GetPreferredSize?.Invoke() ?? new Size(100, 100);
var ctl = new swf.UserControl();
ctl.Size = size.ToSD();
_hwnd = new HandleRef(ctl, ctl.Handle);
}
Win32.SetParent(_hwnd.Value.Handle, hwndParent.Handle);
HookParentScrollViewer();
return _hwnd.Value;
}

protected override void OnVisualParentChanged(sw.DependencyObject oldParent)
{
base.OnVisualParentChanged(oldParent);
HookParentScrollViewer();
}

void HookParentScrollViewer()
{
if (_parentScrollViewer != null)
_parentScrollViewer.ScrollChanged -= ParentScrollViewerScrollChanged;
_regionRect = sd.Rectangle.Empty;
_parentScrollViewer = this.GetVisualParent<swc.ScrollViewer>();
if (_parentScrollViewer != null)
_parentScrollViewer.ScrollChanged += ParentScrollViewerScrollChanged;
}

protected override void DestroyWindowCore(HandleRef hwnd)
{
}

private void ParentScrollViewerScrollChanged(object sender, swc.ScrollChangedEventArgs e)
{
UpdateRegion();
}

double LogicalPixelSize => sw.PresentationSource.FromVisual(this)?.CompositionTarget.TransformToDevice.M11 ?? 1.0;

sd.Size ScaledSize(double w, double h)
{
var pixelSize = LogicalPixelSize;
return new sd.Size((int)Math.Round(w * pixelSize), (int)Math.Round(h * pixelSize));
}

void UpdateRegion()
{
if (_parentScrollViewer == null || !IsVisible)
return;

if (_parentScrollViewer.Content is not sw.FrameworkElement content)
return;

var transform = content.TransformToDescendant(this);
var offset = transform.Transform(new sw.Point(_parentScrollViewer.HorizontalOffset, _parentScrollViewer.VerticalOffset));


var loc = ScaledSize(offset.X, offset.Y);
var size = ScaledSize(_parentScrollViewer.ViewportWidth, _parentScrollViewer.ViewportHeight);

var rect = new sd.Rectangle(new sd.Point(loc), size);
if (rect != _regionRect)
{
SetRegion(rect);
_regionRect = rect;
}
}

private void SetRegion(sd.Rectangle rect)
{
using (var graphics = sd.Graphics.FromHwnd(Handle))
Win32.SetWindowRgn(Handle, new sd.Region(rect).GetHrgn(graphics), true);
}



}
1 change: 1 addition & 0 deletions src/Eto.Wpf/Platform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ public static void AddTo(Eto.Platform p)
p.Add<ToggleButton.IHandler>(() => new ToggleButtonHandler());
p.Add<PropertyGrid.IHandler>(() => new ThemedPropertyGridHandler());
p.Add<CollectionEditor.IHandler>(() => new ThemedCollectionEditorHandler());
p.Add<NativeControlHost.IHandler>(() => new NativeControlHandler());

// Forms.Menu
p.Add<CheckMenuItem.IHandler>(() => new CheckMenuItemHandler());
Expand Down
2 changes: 1 addition & 1 deletion src/Eto.Wpf/WpfHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public static Control ToEto(this sw.FrameworkElement nativeControl)
{
if (nativeControl == null)
return null;
return new Control(new NativeControlHandler(nativeControl));
return new NativeControlHost(nativeControl);
}

/// <summary>
Expand Down
Loading

0 comments on commit 7a11c1b

Please sign in to comment.