committed by
GitHub
48 changed files with 1330 additions and 656 deletions
@ -0,0 +1,35 @@ |
|||||
|
using System; |
||||
|
using Avalonia.Platform; |
||||
|
using Avalonia.Android; |
||||
|
using ControlCatalog.Pages; |
||||
|
|
||||
|
namespace ControlCatalog.Android; |
||||
|
|
||||
|
public class EmbedSampleAndroid : INativeDemoControl |
||||
|
{ |
||||
|
public IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func<IPlatformHandle> createDefault) |
||||
|
{ |
||||
|
var parentContext = (parent as AndroidViewControlHandle)?.View.Context |
||||
|
?? global::Android.App.Application.Context; |
||||
|
|
||||
|
if (isSecond) |
||||
|
{ |
||||
|
var webView = new global::Android.Webkit.WebView(parentContext); |
||||
|
webView.LoadUrl("https://www.android.com/"); |
||||
|
|
||||
|
return new AndroidViewControlHandle(webView); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
var button = new global::Android.Widget.Button(parentContext) { Text = "Hello world" }; |
||||
|
var clickCount = 0; |
||||
|
button.Click += (sender, args) => |
||||
|
{ |
||||
|
clickCount++; |
||||
|
button.Text = $"Click count {clickCount}"; |
||||
|
}; |
||||
|
|
||||
|
return new AndroidViewControlHandle(button); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,35 @@ |
|||||
|
using System.IO; |
||||
|
using System.Diagnostics; |
||||
|
using Avalonia.Platform; |
||||
|
using Avalonia.Controls.Platform; |
||||
|
using System; |
||||
|
using ControlCatalog.Pages; |
||||
|
|
||||
|
namespace ControlCatalog.NetCore; |
||||
|
|
||||
|
public class EmbedSampleGtk : INativeDemoControl |
||||
|
{ |
||||
|
private Process _mplayer; |
||||
|
|
||||
|
public IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func<IPlatformHandle> createDefault) |
||||
|
{ |
||||
|
if (isSecond) |
||||
|
{ |
||||
|
var chooser = GtkHelper.CreateGtkFileChooser(parent.Handle); |
||||
|
if (chooser != null) |
||||
|
return chooser; |
||||
|
} |
||||
|
|
||||
|
var control = createDefault(); |
||||
|
var nodes = Path.GetFullPath(Path.Combine(typeof(EmbedSample).Assembly.GetModules()[0].FullyQualifiedName, |
||||
|
"..", |
||||
|
"nodes.mp4")); |
||||
|
_mplayer = Process.Start(new ProcessStartInfo("mplayer", |
||||
|
$"-vo x11 -zoom -loop 0 -wid {control.Handle.ToInt64()} \"{nodes}\"") |
||||
|
{ |
||||
|
UseShellExecute = false, |
||||
|
|
||||
|
}); |
||||
|
return control; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,58 @@ |
|||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Avalonia.Controls.Platform; |
||||
|
using Avalonia.Platform.Interop; |
||||
|
using Avalonia.X11.NativeDialogs; |
||||
|
using static Avalonia.X11.NativeDialogs.Gtk; |
||||
|
using static Avalonia.X11.NativeDialogs.Glib; |
||||
|
|
||||
|
namespace ControlCatalog.NetCore; |
||||
|
|
||||
|
internal class GtkHelper |
||||
|
{ |
||||
|
private static Task<bool> s_gtkTask; |
||||
|
|
||||
|
class FileChooser : INativeControlHostDestroyableControlHandle |
||||
|
{ |
||||
|
private readonly IntPtr _widget; |
||||
|
|
||||
|
public FileChooser(IntPtr widget, IntPtr xid) |
||||
|
{ |
||||
|
_widget = widget; |
||||
|
Handle = xid; |
||||
|
} |
||||
|
|
||||
|
public IntPtr Handle { get; } |
||||
|
public string HandleDescriptor => "XID"; |
||||
|
|
||||
|
public void Destroy() |
||||
|
{ |
||||
|
RunOnGlibThread(() => |
||||
|
{ |
||||
|
gtk_widget_destroy(_widget); |
||||
|
return 0; |
||||
|
}).Wait(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public static INativeControlHostDestroyableControlHandle CreateGtkFileChooser(IntPtr parentXid) |
||||
|
{ |
||||
|
if (s_gtkTask == null) |
||||
|
s_gtkTask = StartGtk(); |
||||
|
if (!s_gtkTask.Result) |
||||
|
return null; |
||||
|
return RunOnGlibThread(() => |
||||
|
{ |
||||
|
using (var title = new Utf8Buffer("Embedded")) |
||||
|
{ |
||||
|
var widget = gtk_file_chooser_dialog_new(title, IntPtr.Zero, GtkFileChooserAction.SelectFolder, |
||||
|
IntPtr.Zero); |
||||
|
gtk_widget_realize(widget); |
||||
|
var xid = gdk_x11_window_get_xid(gtk_widget_get_window(widget)); |
||||
|
gtk_window_present(widget); |
||||
|
return new FileChooser(widget, xid); |
||||
|
} |
||||
|
}).Result; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,29 @@ |
|||||
|
using System; |
||||
|
|
||||
|
using Avalonia.Platform; |
||||
|
using Avalonia.Threading; |
||||
|
|
||||
|
using ControlCatalog.Pages; |
||||
|
|
||||
|
using MonoMac.Foundation; |
||||
|
using MonoMac.WebKit; |
||||
|
|
||||
|
namespace ControlCatalog.NetCore; |
||||
|
|
||||
|
public class EmbedSampleMac : INativeDemoControl |
||||
|
{ |
||||
|
public IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func<IPlatformHandle> createDefault) |
||||
|
{ |
||||
|
// Note: We are using MonoMac for example purposes
|
||||
|
// It shouldn't be used in production apps
|
||||
|
MacHelper.EnsureInitialized(); |
||||
|
|
||||
|
var webView = new WebView(); |
||||
|
Dispatcher.UIThread.Post(() => |
||||
|
{ |
||||
|
webView.MainFrame.LoadRequest(new NSUrlRequest(new NSUrl( |
||||
|
isSecond ? "https://bing.com" : "https://google.com/"))); |
||||
|
}); |
||||
|
return new MacOSViewHandle(webView); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,38 @@ |
|||||
|
using System; |
||||
|
|
||||
|
using Avalonia.Controls.Platform; |
||||
|
using MonoMac.AppKit; |
||||
|
|
||||
|
namespace ControlCatalog.NetCore; |
||||
|
|
||||
|
internal class MacHelper |
||||
|
{ |
||||
|
private static bool _isInitialized; |
||||
|
|
||||
|
public static void EnsureInitialized() |
||||
|
{ |
||||
|
if (_isInitialized) |
||||
|
return; |
||||
|
_isInitialized = true; |
||||
|
NSApplication.Init(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
internal class MacOSViewHandle : INativeControlHostDestroyableControlHandle |
||||
|
{ |
||||
|
private NSView _view; |
||||
|
|
||||
|
public MacOSViewHandle(NSView view) |
||||
|
{ |
||||
|
_view = view; |
||||
|
} |
||||
|
|
||||
|
public IntPtr Handle => _view?.Handle ?? IntPtr.Zero; |
||||
|
public string HandleDescriptor => "NSView"; |
||||
|
|
||||
|
public void Destroy() |
||||
|
{ |
||||
|
_view.Dispose(); |
||||
|
_view = null; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,45 @@ |
|||||
|
using System; |
||||
|
using System.Text; |
||||
|
|
||||
|
using Avalonia.Controls.Platform; |
||||
|
using Avalonia.Platform; |
||||
|
|
||||
|
using ControlCatalog.Pages; |
||||
|
|
||||
|
namespace ControlCatalog.NetCore; |
||||
|
|
||||
|
public class EmbedSampleWin : INativeDemoControl |
||||
|
{ |
||||
|
private const string RichText = |
||||
|
@"{\rtf1\ansi\ansicpg1251\deff0\nouicompat\deflang1049{\fonttbl{\f0\fnil\fcharset0 Calibri;}}
|
||||
|
{\colortbl ;\red255\green0\blue0;\red0\green77\blue187;\red0\green176\blue80;\red155\green0\blue211;\red247\green150\blue70;\red75\green172\blue198;} |
||||
|
{\*\generator Riched20 6.3.9600}\viewkind4\uc1 |
||||
|
\pard\sa200\sl276\slmult1\f0\fs22\lang9 <PREFIX>I \i am\i0 a \cf1\b Rich Text \cf0\b0\fs24 control\cf2\fs28 !\cf3\fs32 !\cf4\fs36 !\cf1\fs40 !\cf5\fs44 !\cf6\fs48 !\cf0\fs44\par |
||||
|
}";
|
||||
|
|
||||
|
public IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func<IPlatformHandle> createDefault) |
||||
|
{ |
||||
|
WinApi.LoadLibrary("Msftedit.dll"); |
||||
|
var handle = WinApi.CreateWindowEx(0, "RICHEDIT50W", |
||||
|
@"Rich Edit", |
||||
|
0x800000 | 0x10000000 | 0x40000000 | 0x800000 | 0x10000 | 0x0004, 0, 0, 1, 1, parent.Handle, |
||||
|
IntPtr.Zero, WinApi.GetModuleHandle(null), IntPtr.Zero); |
||||
|
var st = new WinApi.SETTEXTEX { Codepage = 65001, Flags = 0x00000008 }; |
||||
|
var text = RichText.Replace("<PREFIX>", isSecond ? "\\qr " : ""); |
||||
|
var bytes = Encoding.UTF8.GetBytes(text); |
||||
|
WinApi.SendMessage(handle, 0x0400 + 97, ref st, bytes); |
||||
|
return new Win32WindowControlHandle(handle, "HWND"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
internal class Win32WindowControlHandle : PlatformHandle, INativeControlHostDestroyableControlHandle |
||||
|
{ |
||||
|
public Win32WindowControlHandle(IntPtr handle, string descriptor) : base(handle, descriptor) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public void Destroy() |
||||
|
{ |
||||
|
_ = WinApi.DestroyWindow(Handle); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,73 @@ |
|||||
|
using System; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
namespace ControlCatalog.NetCore; |
||||
|
|
||||
|
internal unsafe class WinApi |
||||
|
{ |
||||
|
public enum CommonControls : uint |
||||
|
{ |
||||
|
ICC_LISTVIEW_CLASSES = 0x00000001, // listview, header
|
||||
|
ICC_TREEVIEW_CLASSES = 0x00000002, // treeview, tooltips
|
||||
|
ICC_BAR_CLASSES = 0x00000004, // toolbar, statusbar, trackbar, tooltips
|
||||
|
ICC_TAB_CLASSES = 0x00000008, // tab, tooltips
|
||||
|
ICC_UPDOWN_CLASS = 0x00000010, // updown
|
||||
|
ICC_PROGRESS_CLASS = 0x00000020, // progress
|
||||
|
ICC_HOTKEY_CLASS = 0x00000040, // hotkey
|
||||
|
ICC_ANIMATE_CLASS = 0x00000080, // animate
|
||||
|
ICC_WIN95_CLASSES = 0x000000FF, |
||||
|
ICC_DATE_CLASSES = 0x00000100, // month picker, date picker, time picker, updown
|
||||
|
ICC_USEREX_CLASSES = 0x00000200, // comboex
|
||||
|
ICC_COOL_CLASSES = 0x00000400, // rebar (coolbar) control
|
||||
|
ICC_INTERNET_CLASSES = 0x00000800, |
||||
|
ICC_PAGESCROLLER_CLASS = 0x00001000, // page scroller
|
||||
|
ICC_NATIVEFNTCTL_CLASS = 0x00002000, // native font control
|
||||
|
ICC_STANDARD_CLASSES = 0x00004000, |
||||
|
ICC_LINK_CLASS = 0x00008000 |
||||
|
} |
||||
|
|
||||
|
[StructLayout(LayoutKind.Sequential)] |
||||
|
public struct INITCOMMONCONTROLSEX |
||||
|
{ |
||||
|
public int dwSize; |
||||
|
public uint dwICC; |
||||
|
} |
||||
|
|
||||
|
[DllImport("Comctl32.dll")] |
||||
|
public static extern void InitCommonControlsEx(ref INITCOMMONCONTROLSEX init); |
||||
|
|
||||
|
[DllImport("user32.dll", SetLastError = true)] |
||||
|
public static extern bool DestroyWindow(IntPtr hwnd); |
||||
|
|
||||
|
[DllImport("kernel32.dll")] |
||||
|
public static extern IntPtr LoadLibrary(string lib); |
||||
|
|
||||
|
|
||||
|
[DllImport("kernel32.dll")] |
||||
|
public static extern IntPtr GetModuleHandle(string lpModuleName); |
||||
|
|
||||
|
[DllImport("user32.dll", SetLastError = true)] |
||||
|
public static extern IntPtr CreateWindowEx( |
||||
|
int dwExStyle, |
||||
|
string lpClassName, |
||||
|
string lpWindowName, |
||||
|
uint dwStyle, |
||||
|
int x, |
||||
|
int y, |
||||
|
int nWidth, |
||||
|
int nHeight, |
||||
|
IntPtr hWndParent, |
||||
|
IntPtr hMenu, |
||||
|
IntPtr hInstance, |
||||
|
IntPtr lpParam); |
||||
|
|
||||
|
[StructLayout(LayoutKind.Sequential)] |
||||
|
public struct SETTEXTEX |
||||
|
{ |
||||
|
public uint Flags; |
||||
|
public uint Codepage; |
||||
|
} |
||||
|
|
||||
|
[DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "SendMessageW")] |
||||
|
public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, ref SETTEXTEX wParam, byte[] lParam); |
||||
|
} |
||||
@ -0,0 +1,28 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1"> |
||||
|
<assemblyIdentity version="1.0.0.0" name="ControlCatalog.app"/> |
||||
|
|
||||
|
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> |
||||
|
<application> |
||||
|
<!-- A list of the Windows versions that this application has been tested on |
||||
|
and is designed to work with. Uncomment the appropriate elements |
||||
|
and Windows will automatically select the most compatible environment. --> |
||||
|
|
||||
|
<!-- Windows Vista --> |
||||
|
<!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />--> |
||||
|
|
||||
|
<!-- Windows 7 --> |
||||
|
<!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />--> |
||||
|
|
||||
|
<!-- Windows 8 --> |
||||
|
<!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />--> |
||||
|
|
||||
|
<!-- Windows 8.1 --> |
||||
|
<!--<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />--> |
||||
|
|
||||
|
<!-- Windows 10 --> |
||||
|
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" /> |
||||
|
|
||||
|
</application> |
||||
|
</compatibility> |
||||
|
</assembly> |
||||
@ -0,0 +1,34 @@ |
|||||
|
using System; |
||||
|
|
||||
|
using Avalonia; |
||||
|
using Avalonia.Platform; |
||||
|
using Avalonia.Web.Blazor; |
||||
|
|
||||
|
using ControlCatalog.Pages; |
||||
|
|
||||
|
using Microsoft.JSInterop; |
||||
|
|
||||
|
namespace ControlCatalog.Web; |
||||
|
|
||||
|
public class EmbedSampleWeb : INativeDemoControl |
||||
|
{ |
||||
|
public IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func<IPlatformHandle> createDefault) |
||||
|
{ |
||||
|
var runtime = AvaloniaLocator.Current.GetRequiredService<IJSInProcessRuntime>(); |
||||
|
|
||||
|
if (isSecond) |
||||
|
{ |
||||
|
var iframe = runtime.Invoke<IJSInProcessObjectReference>("document.createElement", "iframe"); |
||||
|
iframe.InvokeVoid("setAttribute", "src", "https://www.youtube.com/embed/kZCIporjJ70"); |
||||
|
|
||||
|
return new JSObjectControlHandle(iframe); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
// window.createAppButton source is defined in "app.js" file.
|
||||
|
var button = runtime.Invoke<IJSInProcessObjectReference>("window.createAppButton"); |
||||
|
|
||||
|
return new JSObjectControlHandle(button); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,70 +0,0 @@ |
|||||
.page { |
|
||||
position: relative; |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
} |
|
||||
|
|
||||
.main { |
|
||||
flex: 1; |
|
||||
} |
|
||||
|
|
||||
.sidebar { |
|
||||
background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); |
|
||||
} |
|
||||
|
|
||||
.top-row { |
|
||||
background-color: #f7f7f7; |
|
||||
border-bottom: 1px solid #d6d5d5; |
|
||||
justify-content: flex-end; |
|
||||
height: 3.5rem; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
} |
|
||||
|
|
||||
.top-row ::deep a, .top-row .btn-link { |
|
||||
white-space: nowrap; |
|
||||
margin-left: 1.5rem; |
|
||||
} |
|
||||
|
|
||||
.top-row a:first-child { |
|
||||
overflow: hidden; |
|
||||
text-overflow: ellipsis; |
|
||||
} |
|
||||
|
|
||||
@media (max-width: 640.98px) { |
|
||||
.top-row:not(.auth) { |
|
||||
display: none; |
|
||||
} |
|
||||
|
|
||||
.top-row.auth { |
|
||||
justify-content: space-between; |
|
||||
} |
|
||||
|
|
||||
.top-row a, .top-row .btn-link { |
|
||||
margin-left: 0; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@media (min-width: 641px) { |
|
||||
.page { |
|
||||
flex-direction: row; |
|
||||
} |
|
||||
|
|
||||
.sidebar { |
|
||||
width: 250px; |
|
||||
height: 100vh; |
|
||||
position: sticky; |
|
||||
top: 0; |
|
||||
} |
|
||||
|
|
||||
.top-row { |
|
||||
position: sticky; |
|
||||
top: 0; |
|
||||
z-index: 1; |
|
||||
} |
|
||||
|
|
||||
.main > div { |
|
||||
padding-left: 2rem !important; |
|
||||
padding-right: 1.5rem !important; |
|
||||
} |
|
||||
} |
|
||||
@ -1 +1,10 @@ |
|||||
|
window.createAppButton = function () { |
||||
|
var button = document.createElement('button'); |
||||
|
button.innerText = 'Hello world'; |
||||
|
var clickCount = 0; |
||||
|
button.onclick = () => { |
||||
|
clickCount++; |
||||
|
button.innerText = 'Click count ' + clickCount; |
||||
|
}; |
||||
|
return button; |
||||
|
} |
||||
|
|||||
@ -0,0 +1,38 @@ |
|||||
|
using System; |
||||
|
using Avalonia.Platform; |
||||
|
using CoreGraphics; |
||||
|
using Foundation; |
||||
|
using UIKit; |
||||
|
using WebKit; |
||||
|
using Avalonia.iOS; |
||||
|
using ControlCatalog.Pages; |
||||
|
|
||||
|
namespace ControlCatalog; |
||||
|
|
||||
|
public class EmbedSampleIOS : INativeDemoControl |
||||
|
{ |
||||
|
public IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func<IPlatformHandle> createDefault) |
||||
|
{ |
||||
|
if (isSecond) |
||||
|
{ |
||||
|
var webView = new WKWebView(CGRect.Empty, new WKWebViewConfiguration()); |
||||
|
webView.LoadRequest(new NSUrlRequest(new NSUrl("https://www.apple.com/"))); |
||||
|
|
||||
|
return new UIViewControlHandle(webView); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
var button = new UIButton(); |
||||
|
var clickCount = 0; |
||||
|
button.SetTitle("Hello world", UIControlState.Normal); |
||||
|
button.BackgroundColor = UIColor.Blue; |
||||
|
button.AddTarget((_, _) => |
||||
|
{ |
||||
|
clickCount++; |
||||
|
button.SetTitle($"Click count {clickCount}", UIControlState.Normal); |
||||
|
}, UIControlEvent.TouchDown); |
||||
|
|
||||
|
return new UIViewControlHandle(button); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,68 @@ |
|||||
|
<UserControl xmlns="https://github.com/avaloniaui" |
||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" |
||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" |
||||
|
xmlns:local="using:ControlCatalog.Pages" |
||||
|
d:DesignHeight="800" |
||||
|
d:DesignWidth="400" |
||||
|
x:Class="ControlCatalog.Pages.NativeEmbedPage"> |
||||
|
<DockPanel> |
||||
|
<Menu DockPanel.Dock="Top"> |
||||
|
<MenuItem Header="Test"> |
||||
|
<MenuItem Header="SubMenu"> |
||||
|
<MenuItem Header="Item 1"/> |
||||
|
<MenuItem Header="Item 2"/> |
||||
|
<MenuItem Header="Item 3"/> |
||||
|
</MenuItem> |
||||
|
<MenuItem Header="Item 1"/> |
||||
|
<MenuItem Header="Item 2"/> |
||||
|
<MenuItem Header="Item 3"/> |
||||
|
</MenuItem> |
||||
|
</Menu> |
||||
|
<DockPanel DockPanel.Dock="Top"> |
||||
|
<Button DockPanel.Dock="Right" Click="ShowPopupDelay">Show popup (delay)</Button> |
||||
|
<Button DockPanel.Dock="Right" Click="ShowPopup">Show popup</Button> |
||||
|
<Border DockPanel.Dock="Right" Background="#c0c0c0"> |
||||
|
<ToolTip.Tip> |
||||
|
<ToolTip> |
||||
|
<TextBlock>Text</TextBlock> |
||||
|
</ToolTip> |
||||
|
</ToolTip.Tip> |
||||
|
<TextBlock VerticalAlignment="Center">Tooltip</TextBlock> |
||||
|
</Border> |
||||
|
<TextBox Text="Lorem ipsum dolor sit amet"/> |
||||
|
|
||||
|
</DockPanel> |
||||
|
<Grid ColumnDefinitions="*,5,*" |
||||
|
RowDefinitions="*,5,*"> |
||||
|
<Grid.Styles> |
||||
|
<Style Selector="DockPanel#FirstPanel:not(.mobile), DockPanel#SecondPanel:not(.mobile)"> |
||||
|
<Setter Property="Grid.RowSpan" Value="3" /> |
||||
|
</Style> |
||||
|
<Style Selector="DockPanel#SecondPanel:not(.mobile)"> |
||||
|
<Setter Property="Grid.Column" Value="2" /> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="DockPanel#FirstPanel.mobile, DockPanel#SecondPanel.mobile"> |
||||
|
<Setter Property="Grid.ColumnSpan" Value="3" /> |
||||
|
</Style> |
||||
|
<Style Selector="DockPanel#SecondPanel.mobile"> |
||||
|
<Setter Property="Grid.Row" Value="2" /> |
||||
|
</Style> |
||||
|
</Grid.Styles> |
||||
|
|
||||
|
<DockPanel x:Name="FirstPanel"> |
||||
|
<CheckBox x:Name="firstVisible" DockPanel.Dock="Top" |
||||
|
IsChecked="True" Content="Visible" /> |
||||
|
<local:EmbedSample IsVisible="{Binding #firstVisible.IsChecked}"/> |
||||
|
</DockPanel> |
||||
|
<GridSplitter Grid.Row="0" Grid.RowSpan="3" Grid.Column="1" Width="5" HorizontalAlignment="Stretch" /> |
||||
|
<GridSplitter Grid.Column="0" Grid.ColumnSpan="3" Grid.Row="1" Height="5" VerticalAlignment="Stretch" /> |
||||
|
<DockPanel x:Name="SecondPanel"> |
||||
|
<CheckBox x:Name="secondVisible" DockPanel.Dock="Top" |
||||
|
IsChecked="True" Content="Visible" /> |
||||
|
<local:EmbedSample IsSecond="True" IsVisible="{Binding #secondVisible.IsChecked}"/> |
||||
|
</DockPanel> |
||||
|
</Grid> |
||||
|
</DockPanel> |
||||
|
</UserControl> |
||||
@ -0,0 +1,84 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
using Avalonia.Platform; |
||||
|
using Avalonia.Interactivity; |
||||
|
using Avalonia.Controls; |
||||
|
using Avalonia.Controls.Platform; |
||||
|
using Avalonia.Markup.Xaml; |
||||
|
using Avalonia; |
||||
|
|
||||
|
namespace ControlCatalog.Pages |
||||
|
{ |
||||
|
public class NativeEmbedPage : UserControl |
||||
|
{ |
||||
|
public NativeEmbedPage() |
||||
|
{ |
||||
|
this.InitializeComponent(); |
||||
|
} |
||||
|
|
||||
|
private void InitializeComponent() |
||||
|
{ |
||||
|
AvaloniaXamlLoader.Load(this); |
||||
|
} |
||||
|
|
||||
|
public async void ShowPopupDelay(object sender, RoutedEventArgs args) |
||||
|
{ |
||||
|
await Task.Delay(3000); |
||||
|
ShowPopup(sender, args); |
||||
|
} |
||||
|
|
||||
|
public void ShowPopup(object sender, RoutedEventArgs args) |
||||
|
{ |
||||
|
new ContextMenu() |
||||
|
{ |
||||
|
Items = new List<MenuItem> |
||||
|
{ |
||||
|
new MenuItem() { Header = "Test" }, new MenuItem() { Header = "Test" } |
||||
|
} |
||||
|
}.Open((Control)sender); |
||||
|
} |
||||
|
|
||||
|
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) |
||||
|
{ |
||||
|
base.OnPropertyChanged(change); |
||||
|
|
||||
|
if (change.Property == BoundsProperty) |
||||
|
{ |
||||
|
var isMobile = change.GetNewValue<Rect>().Width < 1200; |
||||
|
this.Find<DockPanel>("FirstPanel")!.Classes.Set("mobile", isMobile); |
||||
|
this.Find<DockPanel>("SecondPanel")!.Classes.Set("mobile", isMobile); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class EmbedSample : NativeControlHost |
||||
|
{ |
||||
|
public static INativeDemoControl? Implementation { get; set; } |
||||
|
|
||||
|
static EmbedSample() |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public bool IsSecond { get; set; } |
||||
|
|
||||
|
protected override IPlatformHandle CreateNativeControlCore(IPlatformHandle parent) |
||||
|
{ |
||||
|
return Implementation?.CreateControl(IsSecond, parent, () => base.CreateNativeControlCore(parent)) |
||||
|
?? base.CreateNativeControlCore(parent); |
||||
|
} |
||||
|
|
||||
|
protected override void DestroyNativeControlCore(IPlatformHandle control) |
||||
|
{ |
||||
|
base.DestroyNativeControlCore(control); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public interface INativeDemoControl |
||||
|
{ |
||||
|
/// <param name="isSecond">Used to specify which control should be displayed as a demo</param>
|
||||
|
IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func<IPlatformHandle> createDefault); |
||||
|
} |
||||
|
} |
||||
@ -1,8 +0,0 @@ |
|||||
<Application xmlns="https://github.com/avaloniaui" |
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
|
||||
x:Class="NativeEmbedSample.App"> |
|
||||
<Application.Styles> |
|
||||
<StyleInclude Source="avares://Avalonia.Themes.Default/DefaultTheme.xaml"/> |
|
||||
<StyleInclude Source="avares://Avalonia.Themes.Default/Accents/BaseLight.xaml"/> |
|
||||
</Application.Styles> |
|
||||
</Application> |
|
||||
@ -1,22 +0,0 @@ |
|||||
using Avalonia; |
|
||||
using Avalonia.Controls.ApplicationLifetimes; |
|
||||
using Avalonia.Markup.Xaml; |
|
||||
|
|
||||
namespace NativeEmbedSample |
|
||||
{ |
|
||||
public class App : Application |
|
||||
{ |
|
||||
public override void Initialize() |
|
||||
{ |
|
||||
AvaloniaXamlLoader.Load(this); |
|
||||
} |
|
||||
|
|
||||
public override void OnFrameworkInitializationCompleted() |
|
||||
{ |
|
||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktopLifetime) |
|
||||
desktopLifetime.MainWindow = new MainWindow(); |
|
||||
|
|
||||
base.OnFrameworkInitializationCompleted(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,121 +0,0 @@ |
|||||
using System; |
|
||||
using System.Diagnostics; |
|
||||
using System.IO; |
|
||||
using System.Runtime.InteropServices; |
|
||||
using System.Text; |
|
||||
using Avalonia.Controls; |
|
||||
using Avalonia.Platform; |
|
||||
using Avalonia.Threading; |
|
||||
using MonoMac.AppKit; |
|
||||
using MonoMac.Foundation; |
|
||||
using MonoMac.WebKit; |
|
||||
using Encoding = SharpDX.Text.Encoding; |
|
||||
|
|
||||
namespace NativeEmbedSample |
|
||||
{ |
|
||||
public class EmbedSample : NativeControlHost |
|
||||
{ |
|
||||
public bool IsSecond { get; set; } |
|
||||
private Process _mplayer; |
|
||||
|
|
||||
IPlatformHandle CreateLinux(IPlatformHandle parent) |
|
||||
{ |
|
||||
if (IsSecond) |
|
||||
{ |
|
||||
var chooser = GtkHelper.CreateGtkFileChooser(parent.Handle); |
|
||||
if (chooser != null) |
|
||||
return chooser; |
|
||||
} |
|
||||
|
|
||||
var control = base.CreateNativeControlCore(parent); |
|
||||
var nodes = Path.GetFullPath(Path.Combine(typeof(EmbedSample).Assembly.GetModules()[0].FullyQualifiedName, |
|
||||
"..", |
|
||||
"nodes.mp4")); |
|
||||
_mplayer = Process.Start(new ProcessStartInfo("mplayer", |
|
||||
$"-vo x11 -zoom -loop 0 -wid {control.Handle.ToInt64()} \"{nodes}\"") |
|
||||
{ |
|
||||
UseShellExecute = false, |
|
||||
|
|
||||
}); |
|
||||
return control; |
|
||||
} |
|
||||
|
|
||||
void DestroyLinux(IPlatformHandle handle) |
|
||||
{ |
|
||||
_mplayer?.Kill(); |
|
||||
_mplayer = null; |
|
||||
base.DestroyNativeControlCore(handle); |
|
||||
} |
|
||||
|
|
||||
private const string RichText = |
|
||||
@"{\rtf1\ansi\ansicpg1251\deff0\nouicompat\deflang1049{\fonttbl{\f0\fnil\fcharset0 Calibri;}}
|
|
||||
{\colortbl ;\red255\green0\blue0;\red0\green77\blue187;\red0\green176\blue80;\red155\green0\blue211;\red247\green150\blue70;\red75\green172\blue198;} |
|
||||
{\*\generator Riched20 6.3.9600}\viewkind4\uc1 |
|
||||
\pard\sa200\sl276\slmult1\f0\fs22\lang9 <PREFIX>I \i am\i0 a \cf1\b Rich Text \cf0\b0\fs24 control\cf2\fs28 !\cf3\fs32 !\cf4\fs36 !\cf1\fs40 !\cf5\fs44 !\cf6\fs48 !\cf0\fs44\par |
|
||||
}";
|
|
||||
|
|
||||
IPlatformHandle CreateWin32(IPlatformHandle parent) |
|
||||
{ |
|
||||
WinApi.LoadLibrary("Msftedit.dll"); |
|
||||
var handle = WinApi.CreateWindowEx(0, "RICHEDIT50W", |
|
||||
@"Rich Edit", |
|
||||
0x800000 | 0x10000000 | 0x40000000 | 0x800000 | 0x10000 | 0x0004, 0, 0, 1, 1, parent.Handle, |
|
||||
IntPtr.Zero, WinApi.GetModuleHandle(null), IntPtr.Zero); |
|
||||
var st = new WinApi.SETTEXTEX { Codepage = 65001, Flags = 0x00000008 }; |
|
||||
var text = RichText.Replace("<PREFIX>", IsSecond ? "\\qr " : ""); |
|
||||
var bytes = Encoding.UTF8.GetBytes(text); |
|
||||
WinApi.SendMessage(handle, 0x0400 + 97, ref st, bytes); |
|
||||
return new PlatformHandle(handle, "HWND"); |
|
||||
|
|
||||
} |
|
||||
|
|
||||
void DestroyWin32(IPlatformHandle handle) |
|
||||
{ |
|
||||
WinApi.DestroyWindow(handle.Handle); |
|
||||
} |
|
||||
|
|
||||
IPlatformHandle CreateOSX(IPlatformHandle parent) |
|
||||
{ |
|
||||
// Note: We are using MonoMac for example purposes
|
|
||||
// It shouldn't be used in production apps
|
|
||||
MacHelper.EnsureInitialized(); |
|
||||
|
|
||||
var webView = new WebView(); |
|
||||
Dispatcher.UIThread.Post(() => |
|
||||
{ |
|
||||
webView.MainFrame.LoadRequest(new NSUrlRequest(new NSUrl( |
|
||||
IsSecond ? "https://bing.com": "https://google.com/"))); |
|
||||
}); |
|
||||
return new MacOSViewHandle(webView); |
|
||||
|
|
||||
} |
|
||||
|
|
||||
void DestroyOSX(IPlatformHandle handle) |
|
||||
{ |
|
||||
((MacOSViewHandle)handle).Dispose(); |
|
||||
} |
|
||||
|
|
||||
protected override IPlatformHandle CreateNativeControlCore(IPlatformHandle parent) |
|
||||
{ |
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) |
|
||||
return CreateLinux(parent); |
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) |
|
||||
return CreateWin32(parent); |
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) |
|
||||
return CreateOSX(parent); |
|
||||
return base.CreateNativeControlCore(parent); |
|
||||
} |
|
||||
|
|
||||
protected override void DestroyNativeControlCore(IPlatformHandle control) |
|
||||
{ |
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) |
|
||||
DestroyLinux(control); |
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) |
|
||||
DestroyWin32(control); |
|
||||
else if(RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) |
|
||||
DestroyOSX(control); |
|
||||
else |
|
||||
base.DestroyNativeControlCore(control); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,58 +0,0 @@ |
|||||
using System; |
|
||||
using System.Threading.Tasks; |
|
||||
using Avalonia.Controls.Platform; |
|
||||
using Avalonia.Platform; |
|
||||
using Avalonia.Platform.Interop; |
|
||||
using Avalonia.X11.NativeDialogs; |
|
||||
using static Avalonia.X11.NativeDialogs.Gtk; |
|
||||
using static Avalonia.X11.NativeDialogs.Glib; |
|
||||
namespace NativeEmbedSample |
|
||||
{ |
|
||||
public class GtkHelper |
|
||||
{ |
|
||||
private static Task<bool> s_gtkTask; |
|
||||
class FileChooser : INativeControlHostDestroyableControlHandle |
|
||||
{ |
|
||||
private readonly IntPtr _widget; |
|
||||
|
|
||||
public FileChooser(IntPtr widget, IntPtr xid) |
|
||||
{ |
|
||||
_widget = widget; |
|
||||
Handle = xid; |
|
||||
} |
|
||||
|
|
||||
public IntPtr Handle { get; } |
|
||||
public string HandleDescriptor => "XID"; |
|
||||
public void Destroy() |
|
||||
{ |
|
||||
RunOnGlibThread(() => |
|
||||
{ |
|
||||
gtk_widget_destroy(_widget); |
|
||||
return 0; |
|
||||
}).Wait(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
|
|
||||
|
|
||||
public static IPlatformHandle CreateGtkFileChooser(IntPtr parentXid) |
|
||||
{ |
|
||||
if (s_gtkTask == null) |
|
||||
s_gtkTask = StartGtk(); |
|
||||
if (!s_gtkTask.Result) |
|
||||
return null; |
|
||||
return RunOnGlibThread(() => |
|
||||
{ |
|
||||
using (var title = new Utf8Buffer("Embedded")) |
|
||||
{ |
|
||||
var widget = gtk_file_chooser_dialog_new(title, IntPtr.Zero, GtkFileChooserAction.SelectFolder, |
|
||||
IntPtr.Zero); |
|
||||
gtk_widget_realize(widget); |
|
||||
var xid = gdk_x11_window_get_xid(gtk_widget_get_window(widget)); |
|
||||
gtk_window_present(widget); |
|
||||
return new FileChooser(widget, xid); |
|
||||
} |
|
||||
}).Result; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,39 +0,0 @@ |
|||||
using System; |
|
||||
using Avalonia.Platform; |
|
||||
using MonoMac.AppKit; |
|
||||
|
|
||||
namespace NativeEmbedSample |
|
||||
{ |
|
||||
public class MacHelper |
|
||||
{ |
|
||||
private static bool _isInitialized; |
|
||||
|
|
||||
public static void EnsureInitialized() |
|
||||
{ |
|
||||
if (_isInitialized) |
|
||||
return; |
|
||||
_isInitialized = true; |
|
||||
NSApplication.Init(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
class MacOSViewHandle : IPlatformHandle, IDisposable |
|
||||
{ |
|
||||
private NSView _view; |
|
||||
|
|
||||
public MacOSViewHandle(NSView view) |
|
||||
{ |
|
||||
_view = view; |
|
||||
} |
|
||||
|
|
||||
public IntPtr Handle => _view?.Handle ?? IntPtr.Zero; |
|
||||
public string HandleDescriptor => "NSView"; |
|
||||
|
|
||||
public void Dispose() |
|
||||
{ |
|
||||
_view.Dispose(); |
|
||||
_view = null; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
@ -1,52 +0,0 @@ |
|||||
<Window xmlns="https://github.com/avaloniaui" MinWidth="500" MinHeight="300" |
|
||||
Width="1024" Height="800" |
|
||||
Title="Native embedding sample" |
|
||||
xmlns:local="clr-namespace:NativeEmbedSample" |
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
|
||||
x:Class="NativeEmbedSample.MainWindow"> |
|
||||
<DockPanel> |
|
||||
<Menu DockPanel.Dock="Top"> |
|
||||
<MenuItem Header="Test"> |
|
||||
<MenuItem Header="SubMenu"> |
|
||||
<MenuItem Header="Item 1"/> |
|
||||
<MenuItem Header="Item 2"/> |
|
||||
<MenuItem Header="Item 3"/> |
|
||||
</MenuItem> |
|
||||
<MenuItem Header="Item 1"/> |
|
||||
<MenuItem Header="Item 2"/> |
|
||||
<MenuItem Header="Item 3"/> |
|
||||
</MenuItem> |
|
||||
</Menu> |
|
||||
<DockPanel DockPanel.Dock="Top"> |
|
||||
<Button DockPanel.Dock="Right" Click="ShowPopupDelay">Show popup (delay)</Button> |
|
||||
<Button DockPanel.Dock="Right" Click="ShowPopup">Show popup</Button> |
|
||||
<Border DockPanel.Dock="Right" Background="#c0c0c0"> |
|
||||
<ToolTip.Tip> |
|
||||
<ToolTip> |
|
||||
<TextBlock>Text</TextBlock> |
|
||||
</ToolTip> |
|
||||
</ToolTip.Tip> |
|
||||
<TextBlock>Tooltip</TextBlock> |
|
||||
</Border> |
|
||||
<TextBox Text="Lorem ipsum dolor sit amet"/> |
|
||||
|
|
||||
</DockPanel> |
|
||||
<Grid ColumnDefinitions="*,5,*"> |
|
||||
<DockPanel> |
|
||||
<StackPanel Orientation="Horizontal" DockPanel.Dock="Top"> |
|
||||
<CheckBox x:Name="firstVisible" IsChecked="True"/> |
|
||||
<TextBlock>Visible</TextBlock> |
|
||||
</StackPanel> |
|
||||
<local:EmbedSample IsVisible="{Binding #firstVisible.IsChecked}"/> |
|
||||
</DockPanel> |
|
||||
<GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Stretch" /> |
|
||||
<DockPanel Grid.Column="2"> |
|
||||
<StackPanel Orientation="Horizontal" DockPanel.Dock="Top"> |
|
||||
<CheckBox x:Name="secondVisible" IsChecked="True"/> |
|
||||
<TextBlock>Visible</TextBlock> |
|
||||
</StackPanel> |
|
||||
<local:EmbedSample IsSecond="True" IsVisible="{Binding #secondVisible.IsChecked}"/> |
|
||||
</DockPanel> |
|
||||
</Grid> |
|
||||
</DockPanel> |
|
||||
</Window> |
|
||||
@ -1,36 +0,0 @@ |
|||||
using System.Collections.Generic; |
|
||||
using System.Threading.Tasks; |
|
||||
using Avalonia; |
|
||||
using Avalonia.Controls; |
|
||||
using Avalonia.Interactivity; |
|
||||
using Avalonia.Markup.Xaml; |
|
||||
|
|
||||
namespace NativeEmbedSample |
|
||||
{ |
|
||||
public class MainWindow : Window |
|
||||
{ |
|
||||
public MainWindow() |
|
||||
{ |
|
||||
AvaloniaXamlLoader.Load(this); |
|
||||
this.AttachDevTools(); |
|
||||
} |
|
||||
|
|
||||
public async void ShowPopupDelay(object sender, RoutedEventArgs args) |
|
||||
{ |
|
||||
await Task.Delay(3000); |
|
||||
ShowPopup(sender, args); |
|
||||
} |
|
||||
|
|
||||
public void ShowPopup(object sender, RoutedEventArgs args) |
|
||||
{ |
|
||||
|
|
||||
new ContextMenu() |
|
||||
{ |
|
||||
Items = new List<MenuItem> |
|
||||
{ |
|
||||
new MenuItem() { Header = "Test" }, new MenuItem() { Header = "Test" } |
|
||||
} |
|
||||
}.Open((Control)sender); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,31 +0,0 @@ |
|||||
<Project Sdk="Microsoft.NET.Sdk"> |
|
||||
|
|
||||
<PropertyGroup> |
|
||||
<OutputType>Exe</OutputType> |
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework> |
|
||||
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch> |
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> |
|
||||
</PropertyGroup> |
|
||||
|
|
||||
<ItemGroup> |
|
||||
<PackageReference Include="MonoMac.NetStandard" Version="0.0.4" /> |
|
||||
<ProjectReference Include="..\..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" /> |
|
||||
<ProjectReference Include="..\..\..\src\Windows\Avalonia.Direct2D1\Avalonia.Direct2D1.csproj" /> |
|
||||
<ProjectReference Include="..\..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" /> |
|
||||
<ProjectReference Include="..\..\..\src\Avalonia.X11\Avalonia.X11.csproj" /> |
|
||||
<PackageReference Include="Avalonia.Angle.Windows.Natives" Version="2.1.0.2020091801" /> |
|
||||
|
|
||||
<AvaloniaResource Include="**\*.xaml"> |
|
||||
<SubType>Designer</SubType> |
|
||||
</AvaloniaResource> |
|
||||
<None Remove="nodes.mp4" /> |
|
||||
<Content Include="nodes.mp4"> |
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> |
|
||||
</Content> |
|
||||
<Compile Include="..\..\..\src\Avalonia.X11\NativeDialogs\Gtk.cs" /> |
|
||||
</ItemGroup> |
|
||||
|
|
||||
<Import Project="..\..\..\build\SampleApp.props" /> |
|
||||
<Import Project="..\..\..\build\BuildTargets.targets" /> |
|
||||
<Import Project="..\..\..\build\ReferenceCoreLibraries.props" /> |
|
||||
</Project> |
|
||||
@ -1,17 +0,0 @@ |
|||||
using Avalonia; |
|
||||
|
|
||||
namespace NativeEmbedSample |
|
||||
{ |
|
||||
class Program |
|
||||
{ |
|
||||
static int Main(string[] args) => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); |
|
||||
|
|
||||
public static AppBuilder BuildAvaloniaApp() |
|
||||
=> AppBuilder.Configure<App>() |
|
||||
.With(new AvaloniaNativePlatformOptions() |
|
||||
{ |
|
||||
}) |
|
||||
.UsePlatformDetect(); |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
@ -1,74 +0,0 @@ |
|||||
using System; |
|
||||
using System.Runtime.InteropServices; |
|
||||
|
|
||||
namespace NativeEmbedSample |
|
||||
{ |
|
||||
public unsafe class WinApi |
|
||||
{ |
|
||||
public enum CommonControls : uint |
|
||||
{ |
|
||||
ICC_LISTVIEW_CLASSES = 0x00000001, // listview, header
|
|
||||
ICC_TREEVIEW_CLASSES = 0x00000002, // treeview, tooltips
|
|
||||
ICC_BAR_CLASSES = 0x00000004, // toolbar, statusbar, trackbar, tooltips
|
|
||||
ICC_TAB_CLASSES = 0x00000008, // tab, tooltips
|
|
||||
ICC_UPDOWN_CLASS = 0x00000010, // updown
|
|
||||
ICC_PROGRESS_CLASS = 0x00000020, // progress
|
|
||||
ICC_HOTKEY_CLASS = 0x00000040, // hotkey
|
|
||||
ICC_ANIMATE_CLASS = 0x00000080, // animate
|
|
||||
ICC_WIN95_CLASSES = 0x000000FF, |
|
||||
ICC_DATE_CLASSES = 0x00000100, // month picker, date picker, time picker, updown
|
|
||||
ICC_USEREX_CLASSES = 0x00000200, // comboex
|
|
||||
ICC_COOL_CLASSES = 0x00000400, // rebar (coolbar) control
|
|
||||
ICC_INTERNET_CLASSES = 0x00000800, |
|
||||
ICC_PAGESCROLLER_CLASS = 0x00001000, // page scroller
|
|
||||
ICC_NATIVEFNTCTL_CLASS = 0x00002000, // native font control
|
|
||||
ICC_STANDARD_CLASSES = 0x00004000, |
|
||||
ICC_LINK_CLASS = 0x00008000 |
|
||||
} |
|
||||
|
|
||||
[StructLayout(LayoutKind.Sequential)] |
|
||||
public struct INITCOMMONCONTROLSEX |
|
||||
{ |
|
||||
public int dwSize; |
|
||||
public uint dwICC; |
|
||||
} |
|
||||
|
|
||||
[DllImport("Comctl32.dll")] |
|
||||
public static extern void InitCommonControlsEx(ref INITCOMMONCONTROLSEX init); |
|
||||
|
|
||||
[DllImport("user32.dll", SetLastError = true)] |
|
||||
public static extern bool DestroyWindow(IntPtr hwnd); |
|
||||
|
|
||||
[DllImport("kernel32.dll")] |
|
||||
public static extern IntPtr LoadLibrary(string lib); |
|
||||
|
|
||||
|
|
||||
[DllImport("kernel32.dll")] |
|
||||
public static extern IntPtr GetModuleHandle(string lpModuleName); |
|
||||
|
|
||||
[DllImport("user32.dll", SetLastError = true)] |
|
||||
public static extern IntPtr CreateWindowEx( |
|
||||
int dwExStyle, |
|
||||
string lpClassName, |
|
||||
string lpWindowName, |
|
||||
uint dwStyle, |
|
||||
int x, |
|
||||
int y, |
|
||||
int nWidth, |
|
||||
int nHeight, |
|
||||
IntPtr hWndParent, |
|
||||
IntPtr hMenu, |
|
||||
IntPtr hInstance, |
|
||||
IntPtr lpParam); |
|
||||
|
|
||||
[StructLayout(LayoutKind.Sequential)] |
|
||||
public struct SETTEXTEX |
|
||||
{ |
|
||||
public uint Flags; |
|
||||
public uint Codepage; |
|
||||
} |
|
||||
|
|
||||
[DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "SendMessageW")] |
|
||||
public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, ref SETTEXTEX wParam, byte[] lParam); |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,32 @@ |
|||||
|
#nullable enable |
||||
|
|
||||
|
using System; |
||||
|
|
||||
|
using Android.Views; |
||||
|
|
||||
|
using Avalonia.Controls.Platform; |
||||
|
using Avalonia.Platform; |
||||
|
|
||||
|
namespace Avalonia.Android |
||||
|
{ |
||||
|
public class AndroidViewControlHandle : INativeControlHostDestroyableControlHandle |
||||
|
{ |
||||
|
internal const string AndroidDescriptor = "JavaObjectHandle"; |
||||
|
|
||||
|
public AndroidViewControlHandle(View view) |
||||
|
{ |
||||
|
View = view; |
||||
|
} |
||||
|
|
||||
|
public View View { get; } |
||||
|
|
||||
|
public string HandleDescriptor => AndroidDescriptor; |
||||
|
|
||||
|
IntPtr IPlatformHandle.Handle => View.Handle; |
||||
|
|
||||
|
public void Destroy() |
||||
|
{ |
||||
|
View?.Dispose(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,139 @@ |
|||||
|
#nullable enable |
||||
|
|
||||
|
using System; |
||||
|
using System.Diagnostics.CodeAnalysis; |
||||
|
|
||||
|
using Android.Views; |
||||
|
using Android.Widget; |
||||
|
|
||||
|
using Avalonia.Controls.Platform; |
||||
|
using Avalonia.Platform; |
||||
|
|
||||
|
namespace Avalonia.Android.Platform |
||||
|
{ |
||||
|
internal class AndroidNativeControlHostImpl : INativeControlHostImpl |
||||
|
{ |
||||
|
private readonly AvaloniaView _avaloniaView; |
||||
|
|
||||
|
public AndroidNativeControlHostImpl(AvaloniaView avaloniaView) |
||||
|
{ |
||||
|
_avaloniaView = avaloniaView; |
||||
|
} |
||||
|
|
||||
|
public INativeControlHostDestroyableControlHandle CreateDefaultChild(IPlatformHandle parent) |
||||
|
{ |
||||
|
return new AndroidViewControlHandle(new FrameLayout(_avaloniaView.Context!)); |
||||
|
} |
||||
|
|
||||
|
public INativeControlHostControlTopLevelAttachment CreateNewAttachment(Func<IPlatformHandle, IPlatformHandle> create) |
||||
|
{ |
||||
|
var parent = new AndroidViewControlHandle(_avaloniaView); |
||||
|
AndroidNativeControlAttachment? attachment = null; |
||||
|
try |
||||
|
{ |
||||
|
var child = create(parent); |
||||
|
// It has to be assigned to the variable before property setter is called so we dispose it on exception
|
||||
|
#pragma warning disable IDE0017 // Simplify object initialization
|
||||
|
attachment = new AndroidNativeControlAttachment(child); |
||||
|
#pragma warning restore IDE0017 // Simplify object initialization
|
||||
|
attachment.AttachedTo = this; |
||||
|
return attachment; |
||||
|
} |
||||
|
catch |
||||
|
{ |
||||
|
attachment?.Dispose(); |
||||
|
throw; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public INativeControlHostControlTopLevelAttachment CreateNewAttachment(IPlatformHandle handle) |
||||
|
{ |
||||
|
return new AndroidNativeControlAttachment(handle) |
||||
|
{ |
||||
|
AttachedTo = this |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
public bool IsCompatibleWith(IPlatformHandle handle) => handle.HandleDescriptor == AndroidViewControlHandle.AndroidDescriptor; |
||||
|
|
||||
|
private class AndroidNativeControlAttachment : INativeControlHostControlTopLevelAttachment |
||||
|
{ |
||||
|
private View? _view; |
||||
|
private AndroidNativeControlHostImpl? _attachedTo; |
||||
|
|
||||
|
public AndroidNativeControlAttachment(IPlatformHandle child) |
||||
|
{ |
||||
|
_view = (child as AndroidViewControlHandle)?.View |
||||
|
?? Java.Lang.Object.GetObject<View>(child.Handle, global::Android.Runtime.JniHandleOwnership.DoNotTransfer); |
||||
|
} |
||||
|
|
||||
|
[MemberNotNull(nameof(_view))] |
||||
|
private void CheckDisposed() |
||||
|
{ |
||||
|
if (_view == null) |
||||
|
throw new ObjectDisposedException(nameof(AndroidNativeControlAttachment)); |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
if (_view != null && _attachedTo?._avaloniaView is ViewGroup parent) |
||||
|
{ |
||||
|
parent.RemoveView(_view); |
||||
|
} |
||||
|
_attachedTo = null; |
||||
|
_view?.Dispose(); |
||||
|
_view = null; |
||||
|
} |
||||
|
|
||||
|
public INativeControlHostImpl? AttachedTo |
||||
|
{ |
||||
|
get => _attachedTo; |
||||
|
set |
||||
|
{ |
||||
|
CheckDisposed(); |
||||
|
|
||||
|
var oldAttachedTo = _attachedTo; |
||||
|
_attachedTo = (AndroidNativeControlHostImpl?)value; |
||||
|
if (_attachedTo == null) |
||||
|
{ |
||||
|
oldAttachedTo?._avaloniaView.RemoveView(_view); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
_attachedTo._avaloniaView.AddView(_view); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public bool IsCompatibleWith(INativeControlHostImpl host) => host is AndroidNativeControlHostImpl; |
||||
|
|
||||
|
public void HideWithSize(Size size) |
||||
|
{ |
||||
|
CheckDisposed(); |
||||
|
if (_attachedTo == null) |
||||
|
return; |
||||
|
|
||||
|
size *= _attachedTo._avaloniaView.TopLevelImpl.RenderScaling; |
||||
|
_view.Visibility = ViewStates.Gone; |
||||
|
_view.LayoutParameters = new FrameLayout.LayoutParams(Math.Max(1, (int)size.Width), Math.Max(1, (int)size.Height)); |
||||
|
_view.RequestLayout(); |
||||
|
} |
||||
|
|
||||
|
public void ShowInBounds(Rect bounds) |
||||
|
{ |
||||
|
CheckDisposed(); |
||||
|
if (_attachedTo == null) |
||||
|
throw new InvalidOperationException("The control isn't currently attached to a toplevel"); |
||||
|
|
||||
|
bounds *= _attachedTo._avaloniaView.TopLevelImpl.RenderScaling; |
||||
|
_view.Visibility = ViewStates.Visible; |
||||
|
_view.LayoutParameters = new FrameLayout.LayoutParams(Math.Max(1, (int)bounds.Width), Math.Max(1, (int)bounds.Height)) |
||||
|
{ |
||||
|
LeftMargin = (int)bounds.X, |
||||
|
TopMargin = (int)bounds.Y |
||||
|
}; |
||||
|
_view.RequestLayout(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,152 @@ |
|||||
|
#nullable enable |
||||
|
using System.Diagnostics.CodeAnalysis; |
||||
|
|
||||
|
using Avalonia.Controls.Platform; |
||||
|
using Avalonia.Platform; |
||||
|
|
||||
|
using Microsoft.AspNetCore.Components; |
||||
|
using Microsoft.JSInterop; |
||||
|
|
||||
|
namespace Avalonia.Web.Blazor.Interop |
||||
|
{ |
||||
|
|
||||
|
internal class NativeControlHostInterop : JSModuleInterop, INativeControlHostImpl |
||||
|
{ |
||||
|
private const string JsFilename = "./_content/Avalonia.Web.Blazor/NativeControlHost.js"; |
||||
|
private const string CreateDefaultChildSymbol = "NativeControlHost.CreateDefaultChild"; |
||||
|
private const string CreateAttachmentSymbol = "NativeControlHost.CreateAttachment"; |
||||
|
private const string GetReferenceSymbol = "NativeControlHost.GetReference"; |
||||
|
|
||||
|
private readonly ElementReference hostElement; |
||||
|
|
||||
|
public static async Task<NativeControlHostInterop> ImportAsync(IJSRuntime js, ElementReference element) |
||||
|
{ |
||||
|
var interop = new NativeControlHostInterop(js, element); |
||||
|
await interop.ImportAsync(); |
||||
|
return interop; |
||||
|
} |
||||
|
|
||||
|
public NativeControlHostInterop(IJSRuntime js, ElementReference element) |
||||
|
: base(js, JsFilename) |
||||
|
{ |
||||
|
hostElement = element; |
||||
|
} |
||||
|
|
||||
|
public INativeControlHostDestroyableControlHandle CreateDefaultChild(IPlatformHandle parent) |
||||
|
{ |
||||
|
var element = Invoke<IJSInProcessObjectReference>(CreateDefaultChildSymbol); |
||||
|
return new JSObjectControlHandle(element); |
||||
|
} |
||||
|
|
||||
|
public INativeControlHostControlTopLevelAttachment CreateNewAttachment(Func<IPlatformHandle, IPlatformHandle> create) |
||||
|
{ |
||||
|
Attachment? a = null; |
||||
|
try |
||||
|
{ |
||||
|
using var hostElementJsReference = Invoke<IJSInProcessObjectReference>(GetReferenceSymbol, hostElement); |
||||
|
var child = create(new JSObjectControlHandle(hostElementJsReference)); |
||||
|
var attachmenetReference = Invoke<IJSInProcessObjectReference>(CreateAttachmentSymbol); |
||||
|
// It has to be assigned to the variable before property setter is called so we dispose it on exception
|
||||
|
#pragma warning disable IDE0017 // Simplify object initialization
|
||||
|
a = new Attachment(attachmenetReference, child); |
||||
|
#pragma warning restore IDE0017 // Simplify object initialization
|
||||
|
a.AttachedTo = this; |
||||
|
return a; |
||||
|
} |
||||
|
catch |
||||
|
{ |
||||
|
a?.Dispose(); |
||||
|
throw; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public INativeControlHostControlTopLevelAttachment CreateNewAttachment(IPlatformHandle handle) |
||||
|
{ |
||||
|
var attachmenetReference = Invoke<IJSInProcessObjectReference>(CreateAttachmentSymbol); |
||||
|
var a = new Attachment(attachmenetReference, handle); |
||||
|
a.AttachedTo = this; |
||||
|
return a; |
||||
|
} |
||||
|
|
||||
|
public bool IsCompatibleWith(IPlatformHandle handle) => handle is JSObjectControlHandle; |
||||
|
|
||||
|
class Attachment : INativeControlHostControlTopLevelAttachment |
||||
|
{ |
||||
|
private const string InitializeWithChildHandleSymbol = "InitializeWithChildHandle"; |
||||
|
private const string AttachToSymbol = "AttachTo"; |
||||
|
private const string ShowInBoundsSymbol = "ShowInBounds"; |
||||
|
private const string HideWithSizeSymbol = "HideWithSize"; |
||||
|
private const string ReleaseChildSymbol = "ReleaseChild"; |
||||
|
|
||||
|
private IJSInProcessObjectReference? _native; |
||||
|
private NativeControlHostInterop? _attachedTo; |
||||
|
|
||||
|
public Attachment(IJSInProcessObjectReference native, IPlatformHandle handle) |
||||
|
{ |
||||
|
_native = native; |
||||
|
_native.InvokeVoid(InitializeWithChildHandleSymbol, ((JSObjectControlHandle)handle).Object); |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
if (_native != null) |
||||
|
{ |
||||
|
_native.InvokeVoid(ReleaseChildSymbol); |
||||
|
_native.Dispose(); |
||||
|
_native = null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public INativeControlHostImpl? AttachedTo |
||||
|
{ |
||||
|
get => _attachedTo!; |
||||
|
set |
||||
|
{ |
||||
|
CheckDisposed(); |
||||
|
|
||||
|
var host = (NativeControlHostInterop?)value; |
||||
|
if (host == null) |
||||
|
{ |
||||
|
_native.InvokeVoid(AttachToSymbol); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
_native.InvokeVoid(AttachToSymbol, host.hostElement); |
||||
|
} |
||||
|
_attachedTo = host; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public bool IsCompatibleWith(INativeControlHostImpl host) => host is NativeControlHostInterop; |
||||
|
|
||||
|
public void HideWithSize(Size size) |
||||
|
{ |
||||
|
CheckDisposed(); |
||||
|
if (_attachedTo == null) |
||||
|
return; |
||||
|
|
||||
|
_native.InvokeVoid(HideWithSizeSymbol, Math.Max(1, (float)size.Width), Math.Max(1, (float)size.Height)); |
||||
|
} |
||||
|
|
||||
|
public void ShowInBounds(Rect bounds) |
||||
|
{ |
||||
|
CheckDisposed(); |
||||
|
|
||||
|
if (_attachedTo == null) |
||||
|
throw new InvalidOperationException("Native control isn't attached to a toplevel"); |
||||
|
|
||||
|
bounds = new Rect(bounds.X, bounds.Y, Math.Max(1, bounds.Width), |
||||
|
Math.Max(1, bounds.Height)); |
||||
|
|
||||
|
_native.InvokeVoid(ShowInBoundsSymbol, (float)bounds.X, (float)bounds.Y, (float)bounds.Width, (float)bounds.Height); |
||||
|
} |
||||
|
|
||||
|
[MemberNotNull(nameof(_native))] |
||||
|
private void CheckDisposed() |
||||
|
{ |
||||
|
if (_native == null) |
||||
|
throw new ObjectDisposedException(nameof(Attachment)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,56 @@ |
|||||
|
export class NativeControlHost { |
||||
|
public static CreateDefaultChild(parent: HTMLElement): HTMLElement { |
||||
|
return document.createElement("div"); |
||||
|
} |
||||
|
|
||||
|
// Used to convert ElementReference to JSObjectReference.
|
||||
|
// Is there a better way?
|
||||
|
public static GetReference(element: Element): Element { |
||||
|
return element; |
||||
|
} |
||||
|
|
||||
|
public static CreateAttachment(): NativeControlHostTopLevelAttachment { |
||||
|
return new NativeControlHostTopLevelAttachment(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
class NativeControlHostTopLevelAttachment |
||||
|
{ |
||||
|
_child: HTMLElement; |
||||
|
_host: HTMLElement; |
||||
|
|
||||
|
InitializeWithChildHandle(child: HTMLElement) { |
||||
|
this._child = child; |
||||
|
this._child.style.position = "absolute"; |
||||
|
} |
||||
|
|
||||
|
AttachTo(host: HTMLElement): void { |
||||
|
if (this._host) { |
||||
|
this._host.removeChild(this._child); |
||||
|
} |
||||
|
|
||||
|
this._host = host; |
||||
|
|
||||
|
if (this._host) { |
||||
|
this._host.appendChild(this._child); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
ShowInBounds(x: number, y: number, width: number, height: number): void { |
||||
|
this._child.style.top = y + "px"; |
||||
|
this._child.style.left = x + "px"; |
||||
|
this._child.style.width = width + "px"; |
||||
|
this._child.style.height = height + "px"; |
||||
|
this._child.style.display = "block"; |
||||
|
} |
||||
|
|
||||
|
HideWithSize(width: number, height: number): void { |
||||
|
this._child.style.width = width + "px"; |
||||
|
this._child.style.height = height + "px"; |
||||
|
this._child.style.display = "none"; |
||||
|
} |
||||
|
|
||||
|
ReleaseChild(): void { |
||||
|
this._child = null; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,35 @@ |
|||||
|
#nullable enable |
||||
|
using Avalonia.Controls.Platform; |
||||
|
|
||||
|
using Microsoft.JSInterop; |
||||
|
|
||||
|
namespace Avalonia.Web.Blazor |
||||
|
{ |
||||
|
public class JSObjectControlHandle : INativeControlHostDestroyableControlHandle |
||||
|
{ |
||||
|
internal const string ElementReferenceDescriptor = "JSObjectReference"; |
||||
|
|
||||
|
public JSObjectControlHandle(IJSObjectReference reference) |
||||
|
{ |
||||
|
Object = reference; |
||||
|
} |
||||
|
|
||||
|
public IJSObjectReference Object { get; } |
||||
|
|
||||
|
public IntPtr Handle => throw new NotSupportedException(); |
||||
|
|
||||
|
public string? HandleDescriptor => ElementReferenceDescriptor; |
||||
|
|
||||
|
public void Destroy() |
||||
|
{ |
||||
|
if (Object is IJSInProcessObjectReference inProcess) |
||||
|
{ |
||||
|
inProcess.Dispose(); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
_ = Object.DisposeAsync(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,160 @@ |
|||||
|
#nullable enable |
||||
|
|
||||
|
using System; |
||||
|
using System.Diagnostics.CodeAnalysis; |
||||
|
using Avalonia.Controls.Platform; |
||||
|
using Avalonia.Platform; |
||||
|
using CoreGraphics; |
||||
|
using ObjCRuntime; |
||||
|
using UIKit; |
||||
|
|
||||
|
namespace Avalonia.iOS |
||||
|
{ |
||||
|
internal class NativeControlHostImpl : INativeControlHostImpl |
||||
|
{ |
||||
|
private readonly AvaloniaView _avaloniaView; |
||||
|
|
||||
|
public NativeControlHostImpl(AvaloniaView avaloniaView) |
||||
|
{ |
||||
|
_avaloniaView = avaloniaView; |
||||
|
} |
||||
|
|
||||
|
public INativeControlHostDestroyableControlHandle CreateDefaultChild(IPlatformHandle parent) |
||||
|
{ |
||||
|
return new UIViewControlHandle(new UIView()); |
||||
|
} |
||||
|
|
||||
|
public INativeControlHostControlTopLevelAttachment CreateNewAttachment(Func<IPlatformHandle, IPlatformHandle> create) |
||||
|
{ |
||||
|
var parent = new UIViewControlHandle(_avaloniaView); |
||||
|
NativeControlAttachment? attachment = null; |
||||
|
try |
||||
|
{ |
||||
|
var child = create(parent); |
||||
|
// It has to be assigned to the variable before property setter is called so we dispose it on exception
|
||||
|
#pragma warning disable IDE0017 // Simplify object initialization
|
||||
|
attachment = new NativeControlAttachment(child); |
||||
|
#pragma warning restore IDE0017 // Simplify object initialization
|
||||
|
attachment.AttachedTo = this; |
||||
|
return attachment; |
||||
|
} |
||||
|
catch |
||||
|
{ |
||||
|
attachment?.Dispose(); |
||||
|
throw; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public INativeControlHostControlTopLevelAttachment CreateNewAttachment(IPlatformHandle handle) |
||||
|
{ |
||||
|
return new NativeControlAttachment(handle) |
||||
|
{ |
||||
|
AttachedTo = this |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
public bool IsCompatibleWith(IPlatformHandle handle) => handle.HandleDescriptor == UIViewControlHandle.UIViewDescriptor; |
||||
|
|
||||
|
private class ViewHolder : UIView |
||||
|
{ |
||||
|
public ViewHolder(IntPtr handle) : base(new NativeHandle(handle)) |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private class NativeControlAttachment : INativeControlHostControlTopLevelAttachment |
||||
|
{ |
||||
|
// ReSharper disable once NotAccessedField.Local (keep GC reference)
|
||||
|
private IPlatformHandle? _child; |
||||
|
private UIView? _view; |
||||
|
private NativeControlHostImpl? _attachedTo; |
||||
|
|
||||
|
public NativeControlAttachment(IPlatformHandle child) |
||||
|
{ |
||||
|
_child = child; |
||||
|
|
||||
|
_view = (child as UIViewControlHandle)?.View ?? new ViewHolder(child.Handle); |
||||
|
} |
||||
|
|
||||
|
[MemberNotNull(nameof(_view))] |
||||
|
private void CheckDisposed() |
||||
|
{ |
||||
|
if (_view == null) |
||||
|
throw new ObjectDisposedException(nameof(NativeControlAttachment)); |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
_view?.RemoveFromSuperview(); |
||||
|
_child = null; |
||||
|
_attachedTo = null; |
||||
|
_view?.Dispose(); |
||||
|
_view = null; |
||||
|
} |
||||
|
|
||||
|
public INativeControlHostImpl? AttachedTo |
||||
|
{ |
||||
|
get => _attachedTo; |
||||
|
set |
||||
|
{ |
||||
|
CheckDisposed(); |
||||
|
|
||||
|
_attachedTo = (NativeControlHostImpl?)value; |
||||
|
if (_attachedTo == null) |
||||
|
{ |
||||
|
_view.RemoveFromSuperview(); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
_attachedTo._avaloniaView.AddSubview(_view); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public bool IsCompatibleWith(INativeControlHostImpl host) => host is NativeControlHostImpl; |
||||
|
|
||||
|
public void HideWithSize(Size size) |
||||
|
{ |
||||
|
CheckDisposed(); |
||||
|
if (_attachedTo == null) |
||||
|
return; |
||||
|
|
||||
|
_view.Hidden = true; |
||||
|
_view.Frame = new CGRect(0d, 0d, Math.Max(1d, size.Width), Math.Max(1d, size.Height)); |
||||
|
} |
||||
|
|
||||
|
public void ShowInBounds(Rect bounds) |
||||
|
{ |
||||
|
CheckDisposed(); |
||||
|
if (_attachedTo == null) |
||||
|
throw new InvalidOperationException("The control isn't currently attached to a toplevel"); |
||||
|
|
||||
|
_view.Frame = new CGRect(bounds.X, bounds.Y, Math.Max(1d, bounds.Width), Math.Max(1d, bounds.Height)); |
||||
|
_view.Hidden = false; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class UIViewControlHandle : INativeControlHostDestroyableControlHandle |
||||
|
{ |
||||
|
internal const string UIViewDescriptor = "UIView"; |
||||
|
|
||||
|
|
||||
|
public UIViewControlHandle(UIView view) |
||||
|
{ |
||||
|
View = view; |
||||
|
} |
||||
|
|
||||
|
public UIView View { get; } |
||||
|
|
||||
|
public string HandleDescriptor => UIViewDescriptor; |
||||
|
|
||||
|
IntPtr IPlatformHandle.Handle => View.Handle.Handle; |
||||
|
|
||||
|
public void Destroy() |
||||
|
{ |
||||
|
View.Dispose(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue