diff --git a/Avalonia.sln b/Avalonia.sln
index c8e513f94c..c3554a7447 100644
--- a/Avalonia.sln
+++ b/Avalonia.sln
@@ -97,6 +97,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1
build\DevAnalyzers.props = build\DevAnalyzers.props
build\EmbedXaml.props = build\EmbedXaml.props
build\HarfBuzzSharp.props = build\HarfBuzzSharp.props
+ build\ImageSharp.props = build\ImageSharp.props
build\JetBrains.Annotations.props = build\JetBrains.Annotations.props
build\JetBrains.dotMemoryUnit.props = build\JetBrains.dotMemoryUnit.props
build\Microsoft.CSharp.props = build\Microsoft.CSharp.props
@@ -117,7 +118,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1
build\System.Memory.props = build\System.Memory.props
build\UnitTests.NetFX.props = build\UnitTests.NetFX.props
build\XUnit.props = build\XUnit.props
- build\ImageSharp.props = build\ImageSharp.props
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Targets", "Targets", "{4D6FAF79-58B4-482F-9122-0668C346364C}"
@@ -179,8 +179,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.FreeDesktop", "src
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.DataGrid.UnitTests", "tests\Avalonia.Controls.DataGrid.UnitTests\Avalonia.Controls.DataGrid.UnitTests.csproj", "{351337F5-D66F-461B-A957-4EF60BDB4BA6}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NativeEmbedSample", "samples\interop\NativeEmbedSample\NativeEmbedSample.csproj", "{3C84E04B-36CF-4D0D-B965-C26DD649D1F3}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Themes.Fluent", "src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj", "{C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Headless", "src\Avalonia.Headless\Avalonia.Headless.csproj", "{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}"
@@ -1413,6 +1411,30 @@ Global
{AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Release|iPhone.Build.0 = Release|Any CPU
{AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|Any CPU.Build.0 = Debug|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|iPhone.Build.0 = Debug|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhone.Build.0 = Release|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
@@ -1509,30 +1531,6 @@ Global
{351337F5-D66F-461B-A957-4EF60BDB4BA6}.Release|iPhone.Build.0 = Release|Any CPU
{351337F5-D66F-461B-A957-4EF60BDB4BA6}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{351337F5-D66F-461B-A957-4EF60BDB4BA6}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
- {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
- {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
- {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
- {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
- {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
- {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
- {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
- {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.AppStore|Any CPU.Build.0 = Debug|Any CPU
- {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
- {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.AppStore|iPhone.Build.0 = Debug|Any CPU
- {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
- {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
- {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Debug|iPhone.ActiveCfg = Debug|Any CPU
- {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Debug|iPhone.Build.0 = Debug|Any CPU
- {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
- {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
- {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Release|Any CPU.Build.0 = Release|Any CPU
- {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Release|iPhone.ActiveCfg = Release|Any CPU
- {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Release|iPhone.Build.0 = Release|Any CPU
- {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
- {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
@@ -1965,30 +1963,6 @@ Global
{2B390431-288C-435C-BB6B-A374033BD8D1}.Release|iPhone.Build.0 = Release|Any CPU
{2B390431-288C-435C-BB6B-A374033BD8D1}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{2B390431-288C-435C-BB6B-A374033BD8D1}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
- {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
- {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
- {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
- {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
- {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
- {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
- {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
- {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|Any CPU.Build.0 = Debug|Any CPU
- {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
- {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|iPhone.Build.0 = Debug|Any CPU
- {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
- {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
- {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|iPhone.ActiveCfg = Debug|Any CPU
- {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|iPhone.Build.0 = Debug|Any CPU
- {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
- {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
- {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|Any CPU.Build.0 = Release|Any CPU
- {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhone.ActiveCfg = Release|Any CPU
- {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhone.Build.0 = Release|Any CPU
- {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
- {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -2035,7 +2009,6 @@ Global
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{AF915D5C-AB00-4EA0-B5E6-001F4AE84E68} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{351337F5-D66F-461B-A957-4EF60BDB4BA6} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
- {3C84E04B-36CF-4D0D-B965-C26DD649D1F3} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9}
{909A8CBD-7D0E-42FD-B841-022AD8925820} = {8B6A8209-894F-4BA1-B880-965FD453982C}
{11BE52AF-E2DD-4CF0-B19A-05285ACAF571} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{BC594FD5-4AF2-409E-A1E6-04123F54D7C5} = {9B9E3891-2366-4253-A952-D08BCEB71098}
diff --git a/build/SkiaSharp.props b/build/SkiaSharp.props
index 1ee4aa56a2..d54cffba08 100644
--- a/build/SkiaSharp.props
+++ b/build/SkiaSharp.props
@@ -1,7 +1,7 @@
-
-
-
+
+
+
diff --git a/samples/ControlCatalog.Android/EmbedSample.Android.cs b/samples/ControlCatalog.Android/EmbedSample.Android.cs
new file mode 100644
index 0000000000..250121fc53
--- /dev/null
+++ b/samples/ControlCatalog.Android/EmbedSample.Android.cs
@@ -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 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);
+ }
+ }
+}
diff --git a/samples/ControlCatalog.Android/MainActivity.cs b/samples/ControlCatalog.Android/MainActivity.cs
index 44290d9816..33ca511340 100644
--- a/samples/ControlCatalog.Android/MainActivity.cs
+++ b/samples/ControlCatalog.Android/MainActivity.cs
@@ -10,7 +10,11 @@ namespace ControlCatalog.Android
{
protected override AppBuilder CustomizeAppBuilder(AppBuilder builder)
{
- return base.CustomizeAppBuilder(builder);
+ return base.CustomizeAppBuilder(builder)
+ .AfterSetup(_ =>
+ {
+ Pages.EmbedSample.Implementation = new EmbedSampleAndroid();
+ });
}
}
}
diff --git a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
index 2b45ac1508..0667644643 100644
--- a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
+++ b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
@@ -4,6 +4,7 @@
WinExe
net6.0
true
+ true
@@ -12,6 +13,16 @@
7.0.0-*
+
+
+
+
+
+
+ PreserveNewest
+
+
+
@@ -20,6 +31,8 @@
+
+
@@ -32,6 +45,7 @@
en
+ app.manifest
diff --git a/samples/ControlCatalog.NetCore/NativeControls/Gtk/EmbedSample.Gtk.cs b/samples/ControlCatalog.NetCore/NativeControls/Gtk/EmbedSample.Gtk.cs
new file mode 100644
index 0000000000..521d3674eb
--- /dev/null
+++ b/samples/ControlCatalog.NetCore/NativeControls/Gtk/EmbedSample.Gtk.cs
@@ -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 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;
+ }
+}
diff --git a/samples/ControlCatalog.NetCore/NativeControls/Gtk/GtkHelper.cs b/samples/ControlCatalog.NetCore/NativeControls/Gtk/GtkHelper.cs
new file mode 100644
index 0000000000..456f77a44d
--- /dev/null
+++ b/samples/ControlCatalog.NetCore/NativeControls/Gtk/GtkHelper.cs
@@ -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 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;
+ }
+}
diff --git a/samples/interop/NativeEmbedSample/nodes-license.md b/samples/ControlCatalog.NetCore/NativeControls/Gtk/nodes-license.md
similarity index 100%
rename from samples/interop/NativeEmbedSample/nodes-license.md
rename to samples/ControlCatalog.NetCore/NativeControls/Gtk/nodes-license.md
diff --git a/samples/interop/NativeEmbedSample/nodes.mp4 b/samples/ControlCatalog.NetCore/NativeControls/Gtk/nodes.mp4
similarity index 100%
rename from samples/interop/NativeEmbedSample/nodes.mp4
rename to samples/ControlCatalog.NetCore/NativeControls/Gtk/nodes.mp4
diff --git a/samples/ControlCatalog.NetCore/NativeControls/Mac/EmbedSample.Mac.cs b/samples/ControlCatalog.NetCore/NativeControls/Mac/EmbedSample.Mac.cs
new file mode 100644
index 0000000000..7967c9c073
--- /dev/null
+++ b/samples/ControlCatalog.NetCore/NativeControls/Mac/EmbedSample.Mac.cs
@@ -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 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);
+ }
+}
diff --git a/samples/ControlCatalog.NetCore/NativeControls/Mac/MacHelper.cs b/samples/ControlCatalog.NetCore/NativeControls/Mac/MacHelper.cs
new file mode 100644
index 0000000000..5b3bc9abf1
--- /dev/null
+++ b/samples/ControlCatalog.NetCore/NativeControls/Mac/MacHelper.cs
@@ -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;
+ }
+}
diff --git a/samples/ControlCatalog.NetCore/NativeControls/Win/EmbedSample.Win.cs b/samples/ControlCatalog.NetCore/NativeControls/Win/EmbedSample.Win.cs
new file mode 100644
index 0000000000..77982db0ca
--- /dev/null
+++ b/samples/ControlCatalog.NetCore/NativeControls/Win/EmbedSample.Win.cs
@@ -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 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 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("", 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);
+ }
+}
diff --git a/samples/ControlCatalog.NetCore/NativeControls/Win/WinApi.cs b/samples/ControlCatalog.NetCore/NativeControls/Win/WinApi.cs
new file mode 100644
index 0000000000..47d368f7a4
--- /dev/null
+++ b/samples/ControlCatalog.NetCore/NativeControls/Win/WinApi.cs
@@ -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);
+}
diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs
index 4464413e63..fd080cfc5b 100644
--- a/samples/ControlCatalog.NetCore/Program.cs
+++ b/samples/ControlCatalog.NetCore/Program.cs
@@ -7,11 +7,12 @@ using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
-using Avalonia.Dialogs;
using Avalonia.Headless;
using Avalonia.LogicalTree;
using Avalonia.Threading;
+using ControlCatalog.Pages;
+
namespace ControlCatalog.NetCore
{
static class Program
@@ -123,6 +124,11 @@ namespace ControlCatalog.NetCore
{
StartupScreenIndex = 1,
});
+
+ EmbedSample.Implementation = OperatingSystem.IsWindows() ? (INativeDemoControl)new EmbedSampleWin()
+ : OperatingSystem.IsMacOS() ? new EmbedSampleMac()
+ : OperatingSystem.IsLinux() ? new EmbedSampleGtk()
+ : null;
})
.LogToTrace();
diff --git a/samples/ControlCatalog.NetCore/app.manifest b/samples/ControlCatalog.NetCore/app.manifest
new file mode 100644
index 0000000000..db90057191
--- /dev/null
+++ b/samples/ControlCatalog.NetCore/app.manifest
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/ControlCatalog.Web/App.razor.cs b/samples/ControlCatalog.Web/App.razor.cs
index a150824ac3..c0b7ddbe1e 100644
--- a/samples/ControlCatalog.Web/App.razor.cs
+++ b/samples/ControlCatalog.Web/App.razor.cs
@@ -7,6 +7,10 @@ public partial class App
protected override void OnParametersSet()
{
WebAppBuilder.Configure()
+ .AfterSetup(_ =>
+ {
+ ControlCatalog.Pages.EmbedSample.Implementation = new EmbedSampleWeb();
+ })
.SetupWithSingleViewLifetime();
base.OnParametersSet();
diff --git a/samples/ControlCatalog.Web/EmbedSample.Browser.cs b/samples/ControlCatalog.Web/EmbedSample.Browser.cs
new file mode 100644
index 0000000000..5fe14409de
--- /dev/null
+++ b/samples/ControlCatalog.Web/EmbedSample.Browser.cs
@@ -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 createDefault)
+ {
+ var runtime = AvaloniaLocator.Current.GetRequiredService();
+
+ if (isSecond)
+ {
+ var iframe = runtime.Invoke("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("window.createAppButton");
+
+ return new JSObjectControlHandle(button);
+ }
+ }
+}
diff --git a/samples/ControlCatalog.Web/Shared/MainLayout.razor.css b/samples/ControlCatalog.Web/Shared/MainLayout.razor.css
deleted file mode 100644
index 43c355a47a..0000000000
--- a/samples/ControlCatalog.Web/Shared/MainLayout.razor.css
+++ /dev/null
@@ -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;
- }
-}
diff --git a/samples/ControlCatalog.Web/wwwroot/css/app.css b/samples/ControlCatalog.Web/wwwroot/css/app.css
index d2a8dc525c..49ca14e162 100644
--- a/samples/ControlCatalog.Web/wwwroot/css/app.css
+++ b/samples/ControlCatalog.Web/wwwroot/css/app.css
@@ -44,47 +44,13 @@ a, .btn-link {
z-index: 1000;
}
- #blazor-error-ui .dismiss {
- cursor: pointer;
- position: absolute;
- right: 0.75rem;
- top: 0.5rem;
- }
-
-.canvas-container {
- opacity:1;
- background-color:#ccc;
- position:fixed;
- width:100%;
- height:100%;
- top:0px;
- left:0px;
- z-index:500;
-}
-
-canvas
-{
- opacity:1;
- background-color:#ccc;
- position:fixed;
- width:100%;
- height:100%;
- top:0px;
- left:0px;
- z-index:500;
+#blazor-error-ui .dismiss {
+ cursor: pointer;
+ position: absolute;
+ right: 0.75rem;
+ top: 0.5rem;
}
#app, .page {
height: 100%;
}
-
-.overlay{
- opacity:0.0;
- background-color:#ccc;
- position:fixed;
- width:100vw;
- height:100vh;
- top:0px;
- left:0px;
- z-index:1000;
-}
diff --git a/samples/ControlCatalog.Web/wwwroot/js/app.js b/samples/ControlCatalog.Web/wwwroot/js/app.js
index 5f282702bb..29697661a6 100644
--- a/samples/ControlCatalog.Web/wwwroot/js/app.js
+++ b/samples/ControlCatalog.Web/wwwroot/js/app.js
@@ -1 +1,10 @@
-
\ No newline at end of file
+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;
+}
diff --git a/samples/ControlCatalog.iOS/AppDelegate.cs b/samples/ControlCatalog.iOS/AppDelegate.cs
index f1c2241003..f8caffed14 100644
--- a/samples/ControlCatalog.iOS/AppDelegate.cs
+++ b/samples/ControlCatalog.iOS/AppDelegate.cs
@@ -13,6 +13,13 @@ namespace ControlCatalog
[Register("AppDelegate")]
public partial class AppDelegate : AvaloniaAppDelegate
{
-
+ protected override AppBuilder CustomizeAppBuilder(AppBuilder builder)
+ {
+ return base.CustomizeAppBuilder(builder)
+ .AfterSetup(_ =>
+ {
+ Pages.EmbedSample.Implementation = new EmbedSampleIOS();
+ });
+ }
}
}
diff --git a/samples/ControlCatalog.iOS/EmbedSample.iOS.cs b/samples/ControlCatalog.iOS/EmbedSample.iOS.cs
new file mode 100644
index 0000000000..ad86d2b578
--- /dev/null
+++ b/samples/ControlCatalog.iOS/EmbedSample.iOS.cs
@@ -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 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);
+ }
+ }
+}
diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj
index bce924a3f2..903c849834 100644
--- a/samples/ControlCatalog/ControlCatalog.csproj
+++ b/samples/ControlCatalog/ControlCatalog.csproj
@@ -14,6 +14,9 @@
+
+
+
@@ -32,5 +35,17 @@
+
+
+ MSBuild:Compile
+
+
+
+
+
+ %(Filename)
+
+
+
diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml
index 59d724db69..d8dc3bad2d 100644
--- a/samples/ControlCatalog/MainView.xaml
+++ b/samples/ControlCatalog/MainView.xaml
@@ -2,8 +2,8 @@
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:ControlSamples;assembly=ControlSamples"
- xmlns:pages="clr-namespace:ControlCatalog.Pages"
- xmlns:models="clr-namespace:ControlCatalog.Models">
+ xmlns:models="clr-namespace:ControlCatalog.Models"
+ xmlns:pages="clr-namespace:ControlCatalog.Pages">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/ControlCatalog/Pages/NativeEmbedPage.xaml.cs b/samples/ControlCatalog/Pages/NativeEmbedPage.xaml.cs
new file mode 100644
index 0000000000..14310500ab
--- /dev/null
+++ b/samples/ControlCatalog/Pages/NativeEmbedPage.xaml.cs
@@ -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