Browse Source
# Conflicts: # tests/Avalonia.Markup.Xaml.UnitTests/Xaml/DataTemplateTests.cspull/8220/head
230 changed files with 4041 additions and 3564 deletions
File diff suppressed because it is too large
@ -1,7 +1,7 @@ |
|||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<ItemGroup> |
|||
<PackageReference Include="SkiaSharp" Version="2.88.0" /> |
|||
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="SkiaSharp.NativeAssets.Linux" Version="2.88.0" /> |
|||
<PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="SkiaSharp.NativeAssets.WebAssembly" Version="2.88.0" /> |
|||
<PackageReference Include="SkiaSharp" Version="2.88.1-preview.1" /> |
|||
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="SkiaSharp.NativeAssets.Linux" Version="2.88.1-preview.1" /> |
|||
<PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="SkiaSharp.NativeAssets.WebAssembly" Version="2.88.1-preview.1" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
|
|||
@ -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,86 @@ |
|||
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>
|
|||
/// <param name="parent"></param>
|
|||
/// <param name="createDefault"></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(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue