Add WebLoginDialog for Windows Chardonnay
This commit is contained in:
parent
df2936e0b6
commit
4456432116
2
.github/workflows/build-windows.yml
vendored
2
.github/workflows/build-windows.yml
vendored
@ -69,7 +69,7 @@ jobs:
|
|||||||
LoadByOS/${{ matrix.os }}ConfigApp/${{ matrix.os }}ConfigApp.csproj `
|
LoadByOS/${{ matrix.os }}ConfigApp/${{ matrix.os }}ConfigApp.csproj `
|
||||||
--configuration ${{ env.DOTNET_CONFIGURATION }} `
|
--configuration ${{ env.DOTNET_CONFIGURATION }} `
|
||||||
--output bin/Publish/${{ matrix.os }}-${{ matrix.release_name }} `
|
--output bin/Publish/${{ matrix.os }}-${{ matrix.release_name }} `
|
||||||
-p:PublishProfile=LoadByOS/Properties/${{ matrix.os }}ConfigApp/PublishProfiles/${{ matrix.os }}Profile.pubxml
|
-p:PublishProfile=LoadByOS/${{ matrix.os }}ConfigApp/PublishProfiles/${{ matrix.os }}Profile.pubxml
|
||||||
dotnet publish `
|
dotnet publish `
|
||||||
LibationCli/LibationCli.csproj `
|
LibationCli/LibationCli.csproj `
|
||||||
--configuration ${{ env.DOTNET_CONFIGURATION }} `
|
--configuration ${{ env.DOTNET_CONFIGURATION }} `
|
||||||
|
|||||||
176
Source/LibationAvalonia/Controls/NativeWebView.cs
Normal file
176
Source/LibationAvalonia/Controls/NativeWebView.cs
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Input;
|
||||||
|
using Avalonia.Platform;
|
||||||
|
using Avalonia;
|
||||||
|
using LibationFileManager;
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace LibationAvalonia.Controls;
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
public class NativeWebView : NativeControlHost, IWebView
|
||||||
|
{
|
||||||
|
private IWebViewAdapter? _webViewAdapter;
|
||||||
|
private Uri? _delayedSource;
|
||||||
|
private TaskCompletionSource _webViewReadyCompletion = new();
|
||||||
|
|
||||||
|
public event EventHandler<WebViewNavigationEventArgs>? NavigationCompleted;
|
||||||
|
|
||||||
|
public event EventHandler<WebViewNavigationEventArgs>? NavigationStarted;
|
||||||
|
public event EventHandler? DOMContentLoaded;
|
||||||
|
|
||||||
|
public bool CanGoBack => _webViewAdapter?.CanGoBack ?? false;
|
||||||
|
|
||||||
|
public bool CanGoForward => _webViewAdapter?.CanGoForward ?? false;
|
||||||
|
|
||||||
|
public Uri? Source
|
||||||
|
{
|
||||||
|
get => _webViewAdapter?.Source ?? throw new InvalidOperationException("Control was not initialized");
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_webViewAdapter is null)
|
||||||
|
{
|
||||||
|
_delayedSource = value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_webViewAdapter.Source = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public bool GoBack()
|
||||||
|
{
|
||||||
|
return _webViewAdapter?.GoBack() ?? throw new InvalidOperationException("Control was not initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool GoForward()
|
||||||
|
{
|
||||||
|
return _webViewAdapter?.GoForward() ?? throw new InvalidOperationException("Control was not initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<string?> InvokeScriptAsync(string scriptName)
|
||||||
|
{
|
||||||
|
return _webViewAdapter is null
|
||||||
|
? throw new InvalidOperationException("Control was not initialized")
|
||||||
|
: _webViewAdapter.InvokeScriptAsync(scriptName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Navigate(Uri url)
|
||||||
|
{
|
||||||
|
(_webViewAdapter ?? throw new InvalidOperationException("Control was not initialized"))
|
||||||
|
.Navigate(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task NavigateToString(string text)
|
||||||
|
{
|
||||||
|
return (_webViewAdapter ?? throw new InvalidOperationException("Control was not initialized"))
|
||||||
|
.NavigateToString(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Refresh()
|
||||||
|
{
|
||||||
|
(_webViewAdapter ?? throw new InvalidOperationException("Control was not initialized"))
|
||||||
|
.Refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
(_webViewAdapter ?? throw new InvalidOperationException("Control was not initialized"))
|
||||||
|
.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task WaitForNativeHost()
|
||||||
|
{
|
||||||
|
return _webViewReadyCompletion.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class PlatformHandle : IPlatformHandle
|
||||||
|
{
|
||||||
|
public nint Handle { get; init; }
|
||||||
|
|
||||||
|
public string? HandleDescriptor { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override IPlatformHandle CreateNativeControlCore(IPlatformHandle parent)
|
||||||
|
{
|
||||||
|
_webViewAdapter = InteropFactory.Create().CreateWebViewAdapter();
|
||||||
|
|
||||||
|
if (_webViewAdapter is null)
|
||||||
|
return base.CreateNativeControlCore(parent);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SubscribeOnEvents();
|
||||||
|
var handle = new PlatformHandle
|
||||||
|
{
|
||||||
|
Handle = _webViewAdapter.PlatformHandle.Handle,
|
||||||
|
HandleDescriptor = _webViewAdapter.PlatformHandle.HandleDescriptor
|
||||||
|
};
|
||||||
|
|
||||||
|
if (_delayedSource is not null)
|
||||||
|
{
|
||||||
|
_webViewAdapter.Source = _delayedSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
_webViewReadyCompletion.TrySetResult();
|
||||||
|
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SubscribeOnEvents()
|
||||||
|
{
|
||||||
|
if (_webViewAdapter is not null)
|
||||||
|
{
|
||||||
|
_webViewAdapter.NavigationStarted += WebViewAdapterOnNavigationStarted;
|
||||||
|
_webViewAdapter.NavigationCompleted += WebViewAdapterOnNavigationCompleted;
|
||||||
|
_webViewAdapter.DOMContentLoaded += _webViewAdapter_DOMContentLoaded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void _webViewAdapter_DOMContentLoaded(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
DOMContentLoaded?.Invoke(this, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WebViewAdapterOnNavigationStarted(object? sender, WebViewNavigationEventArgs e)
|
||||||
|
{
|
||||||
|
NavigationStarted?.Invoke(this, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WebViewAdapterOnNavigationCompleted(object? sender, WebViewNavigationEventArgs e)
|
||||||
|
{
|
||||||
|
NavigationCompleted?.Invoke(this, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
||||||
|
{
|
||||||
|
base.OnPropertyChanged(change);
|
||||||
|
if (change.Property == BoundsProperty && change.NewValue is Rect rect)
|
||||||
|
{
|
||||||
|
var scaling = (float)(VisualRoot?.RenderScaling ?? 1.0f);
|
||||||
|
_webViewAdapter?.HandleResize((int)(rect.Width * scaling), (int)(rect.Height * scaling), scaling);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnKeyDown(KeyEventArgs e)
|
||||||
|
{
|
||||||
|
if (_webViewAdapter != null)
|
||||||
|
{
|
||||||
|
e.Handled = _webViewAdapter.HandleKeyDown((uint)e.Key, (uint)e.KeyModifiers);
|
||||||
|
}
|
||||||
|
|
||||||
|
base.OnKeyDown(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void DestroyNativeControlCore(IPlatformHandle control)
|
||||||
|
{
|
||||||
|
if (_webViewAdapter is not null)
|
||||||
|
{
|
||||||
|
_webViewReadyCompletion = new TaskCompletionSource();
|
||||||
|
_webViewAdapter.NavigationStarted -= WebViewAdapterOnNavigationStarted;
|
||||||
|
_webViewAdapter.NavigationCompleted -= WebViewAdapterOnNavigationCompleted;
|
||||||
|
(_webViewAdapter as IDisposable)?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,6 +6,7 @@
|
|||||||
MinWidth="240" MinHeight="140"
|
MinWidth="240" MinHeight="140"
|
||||||
MaxWidth="240" MaxHeight="140"
|
MaxWidth="240" MaxHeight="140"
|
||||||
Width="240" Height="140"
|
Width="240" Height="140"
|
||||||
|
WindowStartupLocation="CenterOwner"
|
||||||
x:Class="LibationAvalonia.Dialogs.Login.ApprovalNeededDialog"
|
x:Class="LibationAvalonia.Dialogs.Login.ApprovalNeededDialog"
|
||||||
Title="Approval Alert Detected"
|
Title="Approval Alert Detected"
|
||||||
Icon="/Assets/libation.ico">
|
Icon="/Assets/libation.ico">
|
||||||
|
|||||||
@ -4,7 +4,7 @@ namespace LibationAvalonia.Dialogs.Login
|
|||||||
{
|
{
|
||||||
public partial class ApprovalNeededDialog : DialogWindow
|
public partial class ApprovalNeededDialog : DialogWindow
|
||||||
{
|
{
|
||||||
public ApprovalNeededDialog()
|
public ApprovalNeededDialog() : base(saveAndRestorePosition: false)
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
using AudibleApi;
|
using AudibleApi;
|
||||||
using AudibleUtilities;
|
using AudibleUtilities;
|
||||||
|
using Avalonia.Threading;
|
||||||
|
using LibationFileManager;
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@ -23,6 +25,20 @@ namespace LibationAvalonia.Dialogs.Login
|
|||||||
|
|
||||||
public async Task<ChoiceOut> StartAsync(ChoiceIn choiceIn)
|
public async Task<ChoiceOut> StartAsync(ChoiceIn choiceIn)
|
||||||
{
|
{
|
||||||
|
if (Configuration.IsWindows && Environment.OSVersion.Version.Major >= 10)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var weblogin = new WebLoginDialog(_account.AccountId, choiceIn.LoginUrl);
|
||||||
|
if (await weblogin.ShowDialog<DialogResult>(App.MainWindow) is DialogResult.OK)
|
||||||
|
return ChoiceOut.External(weblogin.ResponseUrl);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Serilog.Log.Logger.Error(ex, $"Failed to run {nameof(WebLoginDialog)}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var dialog = new LoginChoiceEagerDialog(_account);
|
var dialog = new LoginChoiceEagerDialog(_account);
|
||||||
|
|
||||||
if (await dialog.ShowDialogAsync() is not DialogResult.OK ||
|
if (await dialog.ShowDialogAsync() is not DialogResult.OK ||
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
MinWidth="220" MinHeight="250"
|
MinWidth="220" MinHeight="250"
|
||||||
MaxWidth="220" MaxHeight="250"
|
MaxWidth="220" MaxHeight="250"
|
||||||
Width="220" Height="250"
|
Width="220" Height="250"
|
||||||
|
WindowStartupLocation="CenterOwner"
|
||||||
x:Class="LibationAvalonia.Dialogs.Login.CaptchaDialog"
|
x:Class="LibationAvalonia.Dialogs.Login.CaptchaDialog"
|
||||||
Title="CAPTCHA"
|
Title="CAPTCHA"
|
||||||
Icon="/Assets/libation.ico">
|
Icon="/Assets/libation.ico">
|
||||||
|
|||||||
@ -13,7 +13,7 @@ namespace LibationAvalonia.Dialogs.Login
|
|||||||
public string Answer => _viewModel.Answer;
|
public string Answer => _viewModel.Answer;
|
||||||
|
|
||||||
private readonly CaptchaDialogViewModel _viewModel;
|
private readonly CaptchaDialogViewModel _viewModel;
|
||||||
public CaptchaDialog()
|
public CaptchaDialog() : base(saveAndRestorePosition: false)
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
passwordBox = this.FindControl<TextBox>(nameof(passwordBox));
|
passwordBox = this.FindControl<TextBox>(nameof(passwordBox));
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
mc:Ignorable="d" d:DesignWidth="300" d:DesignHeight="120"
|
mc:Ignorable="d" d:DesignWidth="300" d:DesignHeight="120"
|
||||||
MinWidth="300" MinHeight="120"
|
MinWidth="300" MinHeight="120"
|
||||||
Width="300" Height="120"
|
Width="300" Height="120"
|
||||||
|
WindowStartupLocation="CenterOwner"
|
||||||
x:Class="LibationAvalonia.Dialogs.Login.LoginCallbackDialog"
|
x:Class="LibationAvalonia.Dialogs.Login.LoginCallbackDialog"
|
||||||
Title="Audible Login"
|
Title="Audible Login"
|
||||||
Icon="/Assets/libation.ico">
|
Icon="/Assets/libation.ico">
|
||||||
|
|||||||
@ -11,7 +11,7 @@ namespace LibationAvalonia.Dialogs.Login
|
|||||||
public Account Account { get; }
|
public Account Account { get; }
|
||||||
public string Password { get; set; }
|
public string Password { get; set; }
|
||||||
|
|
||||||
public LoginCallbackDialog()
|
public LoginCallbackDialog() : base(saveAndRestorePosition: false)
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
|
|||||||
@ -35,7 +35,7 @@
|
|||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Margin="0,5,0,5"
|
Margin="0,5,0,5"
|
||||||
ColumnDefinitions="Auto,*">
|
ColumnDefinitions="Auto,*,Auto">
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
@ -46,6 +46,12 @@
|
|||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
PasswordChar="*"
|
PasswordChar="*"
|
||||||
Text="{Binding Password, Mode=TwoWay}" />
|
Text="{Binding Password, Mode=TwoWay}" />
|
||||||
|
<Button
|
||||||
|
Margin="5,0"
|
||||||
|
Grid.Column="2"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
Content="Submit"
|
||||||
|
Command="{Binding SaveAndCloseAsync}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<StackPanel
|
<StackPanel
|
||||||
|
|||||||
@ -12,7 +12,7 @@ namespace LibationAvalonia.Dialogs.Login
|
|||||||
public string Password { get; set; }
|
public string Password { get; set; }
|
||||||
public LoginMethod LoginMethod { get; private set; }
|
public LoginMethod LoginMethod { get; private set; }
|
||||||
|
|
||||||
public LoginChoiceEagerDialog()
|
public LoginChoiceEagerDialog() : base(saveAndRestorePosition: false)
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
|
|||||||
@ -14,7 +14,7 @@ namespace LibationAvalonia.Dialogs.Login
|
|||||||
public string ExternalLoginUrl { get; }
|
public string ExternalLoginUrl { get; }
|
||||||
public string ResponseUrl { get; set; }
|
public string ResponseUrl { get; set; }
|
||||||
|
|
||||||
public LoginExternalDialog()
|
public LoginExternalDialog() : base(saveAndRestorePosition: false)
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
MinWidth="400" MinHeight="200"
|
MinWidth="400" MinHeight="200"
|
||||||
MaxWidth="400" MaxHeight="400"
|
MaxWidth="400" MaxHeight="400"
|
||||||
Width="400" Height="200"
|
Width="400" Height="200"
|
||||||
|
WindowStartupLocation="CenterOwner"
|
||||||
x:Class="LibationAvalonia.Dialogs.Login.MfaDialog"
|
x:Class="LibationAvalonia.Dialogs.Login.MfaDialog"
|
||||||
Title="Two-Step Verification"
|
Title="Two-Step Verification"
|
||||||
Icon="/Assets/libation.ico">
|
Icon="/Assets/libation.ico">
|
||||||
|
|||||||
@ -14,7 +14,7 @@ namespace LibationAvalonia.Dialogs.Login
|
|||||||
public string SelectedValue { get; private set; }
|
public string SelectedValue { get; private set; }
|
||||||
private RbValues Values { get; } = new();
|
private RbValues Values { get; } = new();
|
||||||
|
|
||||||
public MfaDialog()
|
public MfaDialog() : base(saveAndRestorePosition: false)
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
|
|||||||
13
Source/LibationAvalonia/Dialogs/Login/WebLoginDialog.axaml
Normal file
13
Source/LibationAvalonia/Dialogs/Login/WebLoginDialog.axaml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<Window 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"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
|
||||||
|
x:Class="LibationAvalonia.Dialogs.Login.WebLoginDialog"
|
||||||
|
Width="500" Height="800"
|
||||||
|
WindowStartupLocation="CenterOwner"
|
||||||
|
Icon="/Assets/libation.ico"
|
||||||
|
Title="Audible Login">
|
||||||
|
<controls:NativeWebView Name="webView" />
|
||||||
|
</Window>
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
using Dinah.Core;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace LibationAvalonia.Dialogs.Login
|
||||||
|
{
|
||||||
|
public partial class WebLoginDialog : Window
|
||||||
|
{
|
||||||
|
public string ResponseUrl { get; private set; }
|
||||||
|
private readonly string accountID;
|
||||||
|
|
||||||
|
public WebLoginDialog()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
webView.NavigationStarted += WebView_NavigationStarted;
|
||||||
|
webView.DOMContentLoaded += WebView_NavigationCompleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WebLoginDialog(string accountID, string loginUrl) : this()
|
||||||
|
{
|
||||||
|
this.accountID = ArgumentValidator.EnsureNotNullOrWhiteSpace(accountID, nameof(accountID));
|
||||||
|
webView.Source = new Uri(ArgumentValidator.EnsureNotNullOrWhiteSpace(loginUrl, nameof(loginUrl)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WebView_NavigationStarted(object sender, LibationFileManager.WebViewNavigationEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Request?.AbsolutePath.Contains("/ap/maplanding") is true)
|
||||||
|
{
|
||||||
|
ResponseUrl = e.Request.ToString();
|
||||||
|
Close(DialogResult.OK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void WebView_NavigationCompleted(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
await webView.InvokeScriptAsync(getScript(accountID));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string getScript(string accountID) => $$"""
|
||||||
|
(function() {
|
||||||
|
var inputs = document.getElementsByTagName('input');
|
||||||
|
for (index = 0; index < inputs.length; ++index) {
|
||||||
|
if (inputs[index].name.includes('email')) {
|
||||||
|
inputs[index].value = '{{accountID}}';
|
||||||
|
}
|
||||||
|
if (inputs[index].name.includes('password')) {
|
||||||
|
inputs[index].focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
""";
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,6 +6,7 @@
|
|||||||
MinWidth="200" MinHeight="200"
|
MinWidth="200" MinHeight="200"
|
||||||
MaxWidth="200" MaxHeight="200"
|
MaxWidth="200" MaxHeight="200"
|
||||||
Width="200" Height="200"
|
Width="200" Height="200"
|
||||||
|
WindowStartupLocation="CenterOwner"
|
||||||
x:Class="LibationAvalonia.Dialogs.Login._2faCodeDialog"
|
x:Class="LibationAvalonia.Dialogs.Login._2faCodeDialog"
|
||||||
Title="2FA Code"
|
Title="2FA Code"
|
||||||
Icon="/Assets/libation.ico">
|
Icon="/Assets/libation.ico">
|
||||||
|
|||||||
@ -9,7 +9,7 @@ namespace LibationAvalonia.Dialogs.Login
|
|||||||
public string Prompt { get; } = "For added security, please enter the One Time Password (OTP) generated by your Authenticator App";
|
public string Prompt { get; } = "For added security, please enter the One Time Password (OTP) generated by your Authenticator App";
|
||||||
|
|
||||||
|
|
||||||
public _2faCodeDialog()
|
public _2faCodeDialog() : base(saveAndRestorePosition: false)
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
_2FABox = this.FindControl<TextBox>(nameof(_2FABox));
|
_2FABox = this.FindControl<TextBox>(nameof(_2FABox));
|
||||||
|
|||||||
@ -3,7 +3,8 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net7.0-windows</TargetFramework>
|
||||||
|
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||||
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
|
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
|
||||||
<ApplicationIcon>Assets/libation.ico</ApplicationIcon>
|
<ApplicationIcon>Assets/libation.ico</ApplicationIcon>
|
||||||
<AssemblyName>Libation</AssemblyName>
|
<AssemblyName>Libation</AssemblyName>
|
||||||
@ -16,6 +17,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
|
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
|
||||||
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
|||||||
@ -14,6 +14,7 @@ namespace LibationAvalonia
|
|||||||
{
|
{
|
||||||
static class Program
|
static class Program
|
||||||
{
|
{
|
||||||
|
[STAThread]
|
||||||
static void Main(string[] args)
|
static void Main(string[] args)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
|||||||
<Platform>Any CPU</Platform>
|
<Platform>Any CPU</Platform>
|
||||||
<PublishDir>..\bin\Publish\Windows-chardonnay</PublishDir>
|
<PublishDir>..\bin\Publish\Windows-chardonnay</PublishDir>
|
||||||
<PublishProtocol>FileSystem</PublishProtocol>
|
<PublishProtocol>FileSystem</PublishProtocol>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net7.0-windows</TargetFramework>
|
||||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||||
<SelfContained>true</SelfContained>
|
<SelfContained>true</SelfContained>
|
||||||
<PublishSingleFile>false</PublishSingleFile>
|
<PublishSingleFile>false</PublishSingleFile>
|
||||||
|
|||||||
79
Source/LibationAvalonia/app.manifest
Normal file
79
Source/LibationAvalonia/app.manifest
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<?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="MyApplication.app"/>
|
||||||
|
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||||
|
<security>
|
||||||
|
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<!-- UAC Manifest Options
|
||||||
|
If you want to change the Windows User Account Control level replace the
|
||||||
|
requestedExecutionLevel node with one of the following.
|
||||||
|
|
||||||
|
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||||
|
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
|
||||||
|
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
|
||||||
|
|
||||||
|
Specifying requestedExecutionLevel element will disable file and registry virtualization.
|
||||||
|
Remove this element if your application requires this virtualization for backwards
|
||||||
|
compatibility.
|
||||||
|
-->
|
||||||
|
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||||
|
</requestedPrivileges>
|
||||||
|
</security>
|
||||||
|
</trustInfo>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<!-- Indicates that the application is DPI-aware and will not be automatically scaled by Windows at higher
|
||||||
|
DPIs. Windows Presentation Foundation (WPF) applications are automatically DPI-aware and do not need
|
||||||
|
to opt in. Windows Forms applications targeting .NET Framework 4.6 that opt into this setting, should
|
||||||
|
also set the 'EnableWindowsFormsHighDpiAutoResizing' setting to 'true' in their app.config.
|
||||||
|
|
||||||
|
Makes the application long-path aware. See https://docs.microsoft.com/windows/win32/fileio/maximum-file-path-limitation -->
|
||||||
|
<!--
|
||||||
|
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<windowsSettings>
|
||||||
|
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
|
||||||
|
<longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
|
||||||
|
</windowsSettings>
|
||||||
|
</application>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- Enable themes for Windows common controls and dialogs (Windows XP and later) -->
|
||||||
|
<!--
|
||||||
|
<dependency>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity
|
||||||
|
type="win32"
|
||||||
|
name="Microsoft.Windows.Common-Controls"
|
||||||
|
version="6.0.0.0"
|
||||||
|
processorArchitecture="*"
|
||||||
|
publicKeyToken="6595b64144ccf1df"
|
||||||
|
language="*"
|
||||||
|
/>
|
||||||
|
</dependentAssembly>
|
||||||
|
</dependency>
|
||||||
|
-->
|
||||||
|
|
||||||
|
</assembly>
|
||||||
@ -1,14 +1,56 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace LibationFileManager
|
namespace LibationFileManager
|
||||||
{
|
{
|
||||||
public interface IInteropFunctions
|
#nullable enable
|
||||||
|
public interface IInteropFunctions
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Implementation of native web view control https://github.com/maxkatz6/AvaloniaWebView
|
||||||
|
/// </summary>
|
||||||
|
IWebViewAdapter? CreateWebViewAdapter();
|
||||||
void SetFolderIcon(string image, string directory);
|
void SetFolderIcon(string image, string directory);
|
||||||
void DeleteFolderIcon(string directory);
|
void DeleteFolderIcon(string directory);
|
||||||
Process RunAsRoot(string exe, string args);
|
Process RunAsRoot(string exe, string args);
|
||||||
void InstallUpgrade(string upgradeBundle);
|
void InstallUpgrade(string upgradeBundle);
|
||||||
bool CanUpgrade { get; }
|
bool CanUpgrade { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class WebViewNavigationEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public Uri? Request { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IWebView
|
||||||
|
{
|
||||||
|
event EventHandler<WebViewNavigationEventArgs>? NavigationCompleted;
|
||||||
|
event EventHandler<WebViewNavigationEventArgs>? NavigationStarted;
|
||||||
|
event EventHandler? DOMContentLoaded;
|
||||||
|
bool CanGoBack { get; }
|
||||||
|
bool CanGoForward { get; }
|
||||||
|
Uri? Source { get; set; }
|
||||||
|
bool GoBack();
|
||||||
|
bool GoForward();
|
||||||
|
Task<string?> InvokeScriptAsync(string scriptName);
|
||||||
|
void Navigate(Uri url);
|
||||||
|
Task NavigateToString(string text);
|
||||||
|
void Refresh();
|
||||||
|
void Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IWebViewAdapter : IWebView
|
||||||
|
{
|
||||||
|
object NativeWebView { get; }
|
||||||
|
IPlatformHandle2 PlatformHandle { get; }
|
||||||
|
void HandleResize(int width, int height, float zoom);
|
||||||
|
bool HandleKeyDown(uint key, uint keyModifiers);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IPlatformHandle2
|
||||||
|
{
|
||||||
|
IntPtr Handle { get; }
|
||||||
|
string? HandleDescriptor { get; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
namespace LibationFileManager
|
namespace LibationFileManager
|
||||||
{
|
{
|
||||||
public class NullInteropFunctions : IInteropFunctions
|
public class NullInteropFunctions : IInteropFunctions
|
||||||
@ -9,7 +11,8 @@ namespace LibationFileManager
|
|||||||
public NullInteropFunctions() { }
|
public NullInteropFunctions() { }
|
||||||
public NullInteropFunctions(params object[] values) { }
|
public NullInteropFunctions(params object[] values) { }
|
||||||
|
|
||||||
public void SetFolderIcon(string image, string directory) => throw new PlatformNotSupportedException();
|
public IWebViewAdapter? CreateWebViewAdapter() => throw new PlatformNotSupportedException();
|
||||||
|
public void SetFolderIcon(string image, string directory) => throw new PlatformNotSupportedException();
|
||||||
public void DeleteFolderIcon(string directory) => throw new PlatformNotSupportedException();
|
public void DeleteFolderIcon(string directory) => throw new PlatformNotSupportedException();
|
||||||
public bool CanUpgrade => throw new PlatformNotSupportedException();
|
public bool CanUpgrade => throw new PlatformNotSupportedException();
|
||||||
public Process RunAsRoot(string exe, string args) => throw new PlatformNotSupportedException();
|
public Process RunAsRoot(string exe, string args) => throw new PlatformNotSupportedException();
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
using Dinah.Core;
|
using Dinah.Core;
|
||||||
using Microsoft.Web.WebView2.WinForms;
|
using LibationFileManager;
|
||||||
using System;
|
using System;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
|
||||||
@ -8,35 +8,41 @@ namespace LibationWinForms.Login
|
|||||||
public partial class WebLoginDialog : Form
|
public partial class WebLoginDialog : Form
|
||||||
{
|
{
|
||||||
public string ResponseUrl { get; private set; }
|
public string ResponseUrl { get; private set; }
|
||||||
private readonly string loginUrl;
|
|
||||||
private readonly string accountID;
|
private readonly string accountID;
|
||||||
private readonly WebView2 webView = new();
|
private readonly IWebViewAdapter webView;
|
||||||
public WebLoginDialog()
|
public WebLoginDialog()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
webView.Dock = DockStyle.Fill;
|
webView = InteropFactory.Create().CreateWebViewAdapter();
|
||||||
Controls.Add(webView);
|
|
||||||
Shown += WebLoginDialog_Shown;
|
var webViewControl = webView.NativeWebView as Control;
|
||||||
|
webViewControl.Dock = DockStyle.Fill;
|
||||||
|
Controls.Add(webViewControl);
|
||||||
|
|
||||||
|
webView.NavigationStarted += WebView_NavigationStarted;
|
||||||
|
webView.DOMContentLoaded += WebView_DOMContentLoaded;
|
||||||
this.SetLibationIcon();
|
this.SetLibationIcon();
|
||||||
}
|
}
|
||||||
|
|
||||||
public WebLoginDialog(string accountID, string loginUrl) : this()
|
public WebLoginDialog(string accountID, string loginUrl) : this()
|
||||||
{
|
{
|
||||||
this.accountID = ArgumentValidator.EnsureNotNullOrWhiteSpace(accountID, nameof(accountID));
|
this.accountID = ArgumentValidator.EnsureNotNullOrWhiteSpace(accountID, nameof(accountID));
|
||||||
this.loginUrl = ArgumentValidator.EnsureNotNullOrWhiteSpace(loginUrl, nameof(loginUrl));
|
webView.Source = new Uri(ArgumentValidator.EnsureNotNullOrWhiteSpace(loginUrl, nameof(loginUrl)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void WebLoginDialog_Shown(object sender, EventArgs e)
|
private void WebView_NavigationStarted(object sender, WebViewNavigationEventArgs e)
|
||||||
{
|
{
|
||||||
await webView.EnsureCoreWebView2Async();
|
if (e.Request?.AbsolutePath.Contains("/ap/maplanding") is true)
|
||||||
webView.CoreWebView2.NavigationStarting += CoreWebView2_NavigationStarting;
|
{
|
||||||
webView.CoreWebView2.DOMContentLoaded += CoreWebView2_DOMContentLoaded;
|
ResponseUrl = e.Request.ToString();
|
||||||
webView.CoreWebView2.Navigate(loginUrl);
|
DialogResult = DialogResult.OK;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void CoreWebView2_DOMContentLoaded(object sender, Microsoft.Web.WebView2.Core.CoreWebView2DOMContentLoadedEventArgs e)
|
private async void WebView_DOMContentLoaded(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
await webView.CoreWebView2.ExecuteScriptAsync(getScript(accountID));
|
await webView.InvokeScriptAsync(getScript(accountID));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string getScript(string accountID) => $$"""
|
private static string getScript(string accountID) => $$"""
|
||||||
@ -52,16 +58,5 @@ namespace LibationWinForms.Login
|
|||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
""";
|
""";
|
||||||
|
|
||||||
private void CoreWebView2_NavigationStarting(object sender, Microsoft.Web.WebView2.Core.CoreWebView2NavigationStartingEventArgs e)
|
|
||||||
{
|
|
||||||
if (new Uri(e.Uri).AbsolutePath.Contains("/ap/maplanding"))
|
|
||||||
{
|
|
||||||
ResponseUrl = e.Uri;
|
|
||||||
e.Cancel = true;
|
|
||||||
DialogResult = DialogResult.OK;
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,10 +29,16 @@ namespace LibationWinForms.Login
|
|||||||
{
|
{
|
||||||
if (Environment.OSVersion.Version.Major >= 10)
|
if (Environment.OSVersion.Version.Major >= 10)
|
||||||
{
|
{
|
||||||
using var browserLogin = new WebLoginDialog(_account.AccountId, choiceIn.LoginUrl);
|
try
|
||||||
|
{
|
||||||
if (ShowDialog(browserLogin))
|
using var weblogin = new WebLoginDialog(_account.AccountId, choiceIn.LoginUrl);
|
||||||
return Task.FromResult(ChoiceOut.External(browserLogin.ResponseUrl));
|
if (ShowDialog(weblogin))
|
||||||
|
return Task.FromResult(ChoiceOut.External(weblogin.ResponseUrl));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Serilog.Log.Logger.Error(ex, $"Failed to run {nameof(WebLoginDialog)}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
using var dialog = new LoginChoiceEagerDialog(_account);
|
using var dialog = new LoginChoiceEagerDialog(_account);
|
||||||
|
|||||||
@ -38,7 +38,6 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Dinah.Core.WindowsDesktop" Version="7.2.2.1" />
|
<PackageReference Include="Dinah.Core.WindowsDesktop" Version="7.2.2.1" />
|
||||||
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.1777-prerelease" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@ -20,6 +20,7 @@ namespace LinuxConfigApp
|
|||||||
public LinuxInterop() { }
|
public LinuxInterop() { }
|
||||||
public LinuxInterop(params object[] values) { }
|
public LinuxInterop(params object[] values) { }
|
||||||
|
|
||||||
|
public IWebViewAdapter CreateWebViewAdapter() => null;
|
||||||
public void SetFolderIcon(string image, string directory) => throw new PlatformNotSupportedException();
|
public void SetFolderIcon(string image, string directory) => throw new PlatformNotSupportedException();
|
||||||
public void DeleteFolderIcon(string directory) => throw new PlatformNotSupportedException();
|
public void DeleteFolderIcon(string directory) => throw new PlatformNotSupportedException();
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
|
<!--<TargetFramework>net7.0-macos</TargetFramework>-->
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<PublishReadyToRun>true</PublishReadyToRun>
|
<PublishReadyToRun>true</PublishReadyToRun>
|
||||||
<RuntimeIdentifier>osx-x64</RuntimeIdentifier>
|
<RuntimeIdentifier>osx-x64</RuntimeIdentifier>
|
||||||
|
|||||||
@ -10,6 +10,7 @@ namespace MacOSConfigApp
|
|||||||
public MacOSInterop() { }
|
public MacOSInterop() { }
|
||||||
public MacOSInterop(params object[] values) { }
|
public MacOSInterop(params object[] values) { }
|
||||||
|
|
||||||
|
public IWebViewAdapter CreateWebViewAdapter() => null;
|
||||||
public void SetFolderIcon(string image, string directory)
|
public void SetFolderIcon(string image, string directory)
|
||||||
{
|
{
|
||||||
Process.Start("fileicon", $"set {directory.SurroundWithQuotes()} {image.SurroundWithQuotes()}").WaitForExit();
|
Process.Start("fileicon", $"set {directory.SurroundWithQuotes()} {image.SurroundWithQuotes()}").WaitForExit();
|
||||||
|
|||||||
134
Source/LoadByOS/MacOSConfigApp/MacWebViewAdapter.cs
Normal file
134
Source/LoadByOS/MacOSConfigApp/MacWebViewAdapter.cs
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
/* Work-in-progress
|
||||||
|
*
|
||||||
|
*
|
||||||
|
using LibationFileManager;
|
||||||
|
using ObjCRuntime;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using WebKit;
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
|
namespace MacOSConfigApp;
|
||||||
|
|
||||||
|
internal class WKNavigationDelegate1 : WKNavigationDelegate
|
||||||
|
{
|
||||||
|
public override void DidStartProvisionalNavigation(WKWebView webView, WKNavigation navigation)
|
||||||
|
{
|
||||||
|
base.DidStartProvisionalNavigation(webView, navigation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal class MacWebViewAdapter : IWebViewAdapter, IDisposable
|
||||||
|
{
|
||||||
|
private readonly WKWebView _webView;
|
||||||
|
public IPlatformHandle2 PlatformHandle { get; }
|
||||||
|
|
||||||
|
public bool CanGoBack => _webView.CanGoBack;
|
||||||
|
|
||||||
|
public bool CanGoForward => _webView.CanGoForward;
|
||||||
|
|
||||||
|
public Uri? Source { get => _webView?.Url; set => throw new NotImplementedException(); }
|
||||||
|
|
||||||
|
public object NativeWebView { get; }
|
||||||
|
|
||||||
|
public event EventHandler<WebViewNavigationEventArgs>? NavigationCompleted;
|
||||||
|
public event EventHandler<WebViewNavigationEventArgs>? NavigationStarted;
|
||||||
|
public event EventHandler? DOMContentLoaded;
|
||||||
|
|
||||||
|
WKNavigationDelegate1 navDelegate;
|
||||||
|
public MacWebViewAdapter()
|
||||||
|
{
|
||||||
|
var frame = new CGRect(0, 0, 500, 800);
|
||||||
|
NativeWebView = _webView = new WKWebView(frame, new WKWebViewConfiguration());
|
||||||
|
_webView.NavigationDelegate = navDelegate = new WKNavigationDelegate1();
|
||||||
|
PlatformHandle = new MacViewHandle(_webView.Handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_webView?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool GoBack()
|
||||||
|
{
|
||||||
|
if (_webView.CanGoBack)
|
||||||
|
{
|
||||||
|
_webView.GoBack();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool GoForward()
|
||||||
|
{
|
||||||
|
if (_webView.CanGoForward)
|
||||||
|
{
|
||||||
|
_webView.GoForward();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HandleKeyDown(uint key, uint keyModifiers)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void HandleResize(int width, int height, float zoom)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string?> InvokeScriptAsync(string scriptName)
|
||||||
|
{
|
||||||
|
var result = await _webView.EvaluateJavaScriptAsync(scriptName);
|
||||||
|
|
||||||
|
return result.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Navigate(Uri url)
|
||||||
|
{
|
||||||
|
NSUrl? nsurl = url;
|
||||||
|
if (nsurl is null) return;
|
||||||
|
|
||||||
|
var request = new NSUrlRequest(nsurl);
|
||||||
|
|
||||||
|
_webView.LoadRequest(request);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Task NavigateToString(string text)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Refresh()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal class MacViewHandle : IPlatformHandle2
|
||||||
|
{
|
||||||
|
private NativeHandle? _view;
|
||||||
|
|
||||||
|
public MacViewHandle(NativeHandle view)
|
||||||
|
{
|
||||||
|
_view = view;
|
||||||
|
}
|
||||||
|
|
||||||
|
public nint Handle => _view?.Handle ?? 0;
|
||||||
|
public string HandleDescriptor => "NativeHandle";
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
@ -12,7 +12,10 @@ namespace WindowsConfigApp
|
|||||||
public WinInterop() { }
|
public WinInterop() { }
|
||||||
public WinInterop(params object[] values) { }
|
public WinInterop(params object[] values) { }
|
||||||
|
|
||||||
public void SetFolderIcon(string image, string directory)
|
#nullable enable
|
||||||
|
public IWebViewAdapter? CreateWebViewAdapter() => new WindowsWebView2Adapter();
|
||||||
|
#nullable disable
|
||||||
|
public void SetFolderIcon(string image, string directory)
|
||||||
{
|
{
|
||||||
var icon = Image.Load(image).ToIcon();
|
var icon = Image.Load(image).ToIcon();
|
||||||
new DirectoryInfo(directory)?.SetIcon(icon, "Music");
|
new DirectoryInfo(directory)?.SetIcon(icon, "Music");
|
||||||
@ -50,5 +53,5 @@ namespace WindowsConfigApp
|
|||||||
|
|
||||||
return Process.Start(psi);
|
return Process.Start(psi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,8 +2,10 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net7.0-windows</TargetFramework>
|
||||||
<EnableWindowsTargeting>true</EnableWindowsTargeting>
|
<UseWindowsForms>true</UseWindowsForms>
|
||||||
|
<EnableWindowsTargeting>true</EnableWindowsTargeting>
|
||||||
|
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||||
<PublishReadyToRun>true</PublishReadyToRun>
|
<PublishReadyToRun>true</PublishReadyToRun>
|
||||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||||
@ -23,6 +25,10 @@
|
|||||||
<DebugType>embedded</DebugType>
|
<DebugType>embedded</DebugType>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.1722.45" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\LibationUiBase\LibationUiBase.csproj" />
|
<ProjectReference Include="..\..\LibationUiBase\LibationUiBase.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
114
Source/LoadByOS/WindowsConfigApp/WindowsWebView2Adapter.cs
Normal file
114
Source/LoadByOS/WindowsConfigApp/WindowsWebView2Adapter.cs
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
using LibationFileManager;
|
||||||
|
using Microsoft.Web.WebView2.WinForms;
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
namespace WindowsConfigApp;
|
||||||
|
|
||||||
|
internal class WindowsWebView2Adapter : IWebViewAdapter, IDisposable
|
||||||
|
{
|
||||||
|
public object NativeWebView { get; }
|
||||||
|
private readonly WebView2 _webView;
|
||||||
|
|
||||||
|
public WindowsWebView2Adapter()
|
||||||
|
{
|
||||||
|
NativeWebView = _webView = new WebView2();
|
||||||
|
PlatformHandle = new WebView2Handle { Handle = _webView.Handle };
|
||||||
|
|
||||||
|
_webView.CoreWebView2InitializationCompleted += _webView_CoreWebView2InitializationCompleted;
|
||||||
|
|
||||||
|
_webView.NavigationStarting += (s, a) =>
|
||||||
|
{
|
||||||
|
NavigationStarted?.Invoke(this, new WebViewNavigationEventArgs { Request = new Uri(a.Uri) });
|
||||||
|
};
|
||||||
|
_webView.NavigationCompleted += (s, a) =>
|
||||||
|
{
|
||||||
|
NavigationCompleted?.Invoke(this, new WebViewNavigationEventArgs { Request = _webView.Source });
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void _webView_CoreWebView2InitializationCompleted(object? sender, Microsoft.Web.WebView2.Core.CoreWebView2InitializationCompletedEventArgs e)
|
||||||
|
{
|
||||||
|
_webView.CoreWebView2.DOMContentLoaded -= CoreWebView2_DOMContentLoaded;
|
||||||
|
_webView.CoreWebView2.DOMContentLoaded += CoreWebView2_DOMContentLoaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CoreWebView2_DOMContentLoaded(object? sender, Microsoft.Web.WebView2.Core.CoreWebView2DOMContentLoadedEventArgs e)
|
||||||
|
=> DOMContentLoaded?.Invoke(this, e);
|
||||||
|
|
||||||
|
public IPlatformHandle2 PlatformHandle { get; }
|
||||||
|
|
||||||
|
public bool CanGoBack => _webView.CanGoBack;
|
||||||
|
|
||||||
|
public bool CanGoForward => _webView.CanGoForward;
|
||||||
|
|
||||||
|
public Uri? Source
|
||||||
|
{
|
||||||
|
get => _webView.Source;
|
||||||
|
set => _webView.Source = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler<WebViewNavigationEventArgs>? NavigationStarted;
|
||||||
|
public event EventHandler<WebViewNavigationEventArgs>? NavigationCompleted;
|
||||||
|
public event EventHandler? DOMContentLoaded;
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_webView.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool GoBack()
|
||||||
|
{
|
||||||
|
_webView.GoBack();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool GoForward()
|
||||||
|
{
|
||||||
|
_webView.GoForward();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string?> InvokeScriptAsync(string scriptName)
|
||||||
|
{
|
||||||
|
return await _webView.ExecuteScriptAsync(scriptName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Navigate(Uri url)
|
||||||
|
{
|
||||||
|
_webView.Source = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task NavigateToString(string text)
|
||||||
|
{
|
||||||
|
await _webView.EnsureCoreWebView2Async();
|
||||||
|
|
||||||
|
_webView.NavigateToString(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Refresh()
|
||||||
|
{
|
||||||
|
_webView.Refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
_webView.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void HandleResize(int width, int height, float zoom)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HandleKeyDown(uint key, uint keyModifiers)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class WebView2Handle : IPlatformHandle2
|
||||||
|
{
|
||||||
|
public IntPtr Handle { get; init; }
|
||||||
|
public string HandleDescriptor => "HWND";
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user