detect/WebViewControl.Avalonia/WebView.Avalonia.cs
2024-11-27 15:20:42 +08:00

110 lines
3.7 KiB
C#

using System;
using System.Runtime.ExceptionServices;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Media;
using Avalonia.Threading;
namespace WebViewControl {
partial class WebView : BaseControl {
private bool IsInDesignMode => false;
public static readonly StyledProperty<string> AddressProperty =
AvaloniaProperty.Register<WebView, string>(nameof(Address), defaultBindingMode: BindingMode.TwoWay);
public string Address {
get => GetValue(AddressProperty);
set => SetValue(AddressProperty, value);
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) {
base.OnPropertyChanged(change);
if (change.Property == AddressProperty) {
InternalAddress = Address;
}
}
partial void ExtraInitialize() {
VisualChildren.Add(chromium);
chromium[!FocusableProperty] = this[!FocusableProperty];
chromium.AddressChanged += (o, address) => ExecuteInUI(() => Address = address);
}
protected override void OnKeyDown(KeyEventArgs e) {
if (AllowDeveloperTools && e.Key == Key.F12) {
ToggleDeveloperTools();
e.Handled = true;
}
}
protected override void OnGotFocus(GotFocusEventArgs e) {
if (!e.Handled) {
e.Handled = true;
base.OnGotFocus(e);
// use async call to avoid reentrancy, otherwise the webview will fight to get the focus
Dispatcher.UIThread.Post(() => {
if (IsFocused) {
chromium.Focus();
}
}, DispatcherPriority.Background);
}
}
protected override void InternalDispose() => Dispose();
private void ForwardException(ExceptionDispatchInfo exceptionInfo) {
// TODO
}
private T ExecuteInUI<T>(Func<T> action) {
if (Dispatcher.UIThread.CheckAccess()) {
return action();
}
try
{
return Dispatcher.UIThread.InvokeAsync<T>(action).Result;
}
catch
{
return default;
}
}
private void AsyncExecuteInUI(Action action) {
if (isDisposing) {
return;
}
// use async call to avoid dead-locks, otherwise if the source action tries to to evaluate js it would block
Dispatcher.UIThread.InvokeAsync(
() => {
if (!isDisposing) {
ExecuteWithAsyncErrorHandling(action);
}
},
DispatcherPriority.Normal);
}
internal void InitializeBrowser(int initialWidth, int initialHeight) {
chromium.CreateBrowser(initialWidth, initialHeight);
}
/// <summary>
/// Called when the webview is requesting focus. Return false to allow the
/// focus to be set or true to cancel setting the focus.
/// <paramref name="isSystemEvent">True if is a system focus event, or false if is a navigation</paramref>
/// </summary>
protected virtual bool OnSetFocus(bool isSystemEvent) {
// VisualRoot can be null when webview is not yet added to the Visual tree
var focusedElement = TopLevel.GetTopLevel(this)?.FocusManager.GetFocusedElement();
return !(focusedElement == chromium || focusedElement == this);
}
}
}