Browse Source

Reuse module initialization and make it configurable + some fixes

pull/9142/head
Max Katz 3 years ago
parent
commit
897f7c9706
  1. 5
      samples/ControlCatalog.Web/App.razor.cs
  2. 3
      src/Web/Avalonia.Web.Blazor/AvaloniaView.cs
  3. 21
      src/Web/Avalonia.Web.Blazor/BlazorSingleViewLifetime.cs
  4. 8
      src/Web/Avalonia.Web/Avalonia.Web.csproj
  5. 3
      src/Web/Avalonia.Web/AvaloniaView.cs
  6. 79
      src/Web/Avalonia.Web/BrowserSingleViewLifetime.cs
  7. 22
      src/Web/Avalonia.Web/Interop/AvaloniaModule.cs
  8. 6
      src/Web/Avalonia.Web/Interop/CanvasHelper.cs
  9. 8
      src/Web/Avalonia.Web/Interop/DomHelper.cs
  10. 24
      src/Web/Avalonia.Web/Interop/InputHelper.cs
  11. 14
      src/Web/Avalonia.Web/Interop/NativeControlHostHelper.cs
  12. 30
      src/Web/Avalonia.Web/Interop/StorageHelper.cs
  13. 16
      src/Web/Avalonia.Web/Interop/StreamHelper.cs
  14. 2
      src/Web/Avalonia.Web/Storage/BlobReadableStream.cs
  15. 12
      src/Web/Avalonia.Web/Storage/BrowserStorageProvider.cs
  16. 2
      src/Web/Avalonia.Web/Storage/WriteableStream.cs
  17. 14
      src/Web/Avalonia.Web/webapp/modules/avalonia.ts
  18. 5
      src/Web/Avalonia.Web/webapp/modules/avalonia/dom.ts

5
samples/ControlCatalog.Web/App.razor.cs

@ -7,8 +7,9 @@ public partial class App
{
protected override void OnParametersSet()
{
WebAppBuilder.Configure<ControlCatalog.App>()
.With(new SkiaOptions { CustomGpuFactory = null }) // uncomment to disable GPU/GL rendering
AppBuilder.Configure<ControlCatalog.App>()
.UseBlazor()
// .With(new SkiaOptions { CustomGpuFactory = null }) // uncomment to disable GPU/GL rendering
.SetupWithSingleViewLifetime();
base.OnParametersSet();

3
src/Web/Avalonia.Web.Blazor/AvaloniaView.cs

@ -24,6 +24,7 @@ public class AvaloniaView : ComponentBase
{
builder.OpenElement(0, "div");
builder.AddAttribute(1, "id", _containerId);
builder.AddAttribute(2, "style", "width:100vw;height:100vh");
builder.CloseElement();
}
@ -31,7 +32,7 @@ public class AvaloniaView : ComponentBase
{
if (OperatingSystem.IsBrowser())
{
_ = await JSHost.ImportAsync("avalonia", "/_content/Avalonia.Web.Blazor/avalonia.js");
await Avalonia.Web.Interop.AvaloniaModule.ImportMain();
_browserView = new BrowserView(_containerId);
if (Application.Current?.ApplicationLifetime is ISingleViewApplicationLifetime lifetime)

21
src/Web/Avalonia.Web.Blazor/BlazorSingleViewLifetime.cs

@ -1,8 +1,11 @@
using Avalonia.Controls;
using System.Runtime.Versioning;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
namespace Avalonia.Web.Blazor;
[SupportedOSPlatform("browser")]
public static class WebAppBuilder
{
public static T SetupWithSingleViewLifetime<T>(
@ -12,13 +15,21 @@ public static class WebAppBuilder
return builder.SetupWithLifetime(new BlazorSingleViewLifetime());
}
public static T UseBlazor<T>(this T builder) where T : AppBuilderBase<T>, new()
{
return builder
.UseBrowser()
.With(new BrowserPlatformOptions
{
FrameworkAssetPathResolver = new(filePath => $"/_content/Avalonia.Web.Blazor/{filePath}")
});
}
public static AppBuilder Configure<TApp>()
where TApp : Application, new()
{
var builder = AppBuilder.Configure<TApp>()
.UseBrowser();
return builder;
return AppBuilder.Configure<TApp>()
.UseBlazor();
}
internal class BlazorSingleViewLifetime : ISingleViewApplicationLifetime

8
src/Web/Avalonia.Web/Avalonia.Web.csproj

@ -39,10 +39,6 @@
</Content>
</ItemGroup>
<ItemGroup>
<Folder Include="wwwroot\" />
</ItemGroup>
<Target Name="NpmInstall" Inputs="webapp/package.json" Outputs="webapp/node_modules/.install-stamp">
<Exec Command="npm install" WorkingDirectory="webapp" />
<!-- Write the stamp file, so incremental builds work -->
@ -52,4 +48,8 @@
<Exec Command="npm run build" WorkingDirectory="webapp" />
</Target>
<ItemGroup Label="InternalsVisibleTo">
<InternalsVisibleTo Include="Avalonia.Web.Blazor, PublicKey=$(AvaloniaPublicKey)" />
</ItemGroup>
</Project>

3
src/Web/Avalonia.Web/AvaloniaView.cs

@ -37,7 +37,6 @@ namespace Avalonia.Web
private bool _useGL;
private ITextInputMethodClient? _client;
private static int _canvasCount;
public AvaloniaView(string divId)
: this(DomHelper.GetElementById(divId) ?? throw new Exception($"Element with id {divId} was not found in the html document."))
@ -63,8 +62,6 @@ namespace Avalonia.Web
_splash = DomHelper.GetElementById("avalonia-splash");
_canvas.SetProperty("id", $"avaloniaCanvas{_canvasCount++}");
_topLevelImpl = new BrowserTopLevelImpl(this);
_topLevel = new WebEmbeddableControlRoot(_topLevelImpl, () =>

79
src/Web/Avalonia.Web/BrowserSingleViewLifetime.cs

@ -1,51 +1,54 @@
using System.Runtime.InteropServices.JavaScript;
using System;
using System;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Media;
using Avalonia.Web.Skia;
using System.Runtime.Versioning;
namespace Avalonia.Web
namespace Avalonia.Web;
[SupportedOSPlatform("browser")]
public class BrowserSingleViewLifetime : ISingleViewApplicationLifetime
{
public AvaloniaView? View;
public Control? MainView
{
get => View!.Content;
set => View!.Content = value;
}
}
public class BrowserPlatformOptions
{
public Func<string, string> FrameworkAssetPathResolver { get; set; } = new(fileName => $"./{fileName}");
}
[SupportedOSPlatform("browser")]
public static class WebAppBuilder
{
[SupportedOSPlatform("browser")]
public class BrowserSingleViewLifetime : ISingleViewApplicationLifetime
public static T SetupBrowserApp<T>(
this T builder, string mainDivId)
where T : AppBuilderBase<T>, new()
{
public AvaloniaView? View;
var lifetime = new BrowserSingleViewLifetime();
public Control? MainView
{
get => View!.Content;
set => View!.Content = value;
}
return builder
.UseBrowser()
.AfterSetup(b =>
{
lifetime.View = new AvaloniaView(mainDivId);
})
.SetupWithLifetime(lifetime);
}
[SupportedOSPlatform("browser")]
public static partial class WebAppBuilder
public static T UseBrowser<T>(
this T builder)
where T : AppBuilderBase<T>, new()
{
public static T SetupBrowserApp<T>(
this T builder, string mainDivId)
where T : AppBuilderBase<T>, new()
{
var lifetime = new BrowserSingleViewLifetime();
return builder
.UseBrowser()
.AfterSetup(b =>
{
lifetime.View = new AvaloniaView(mainDivId);
})
.SetupWithLifetime(lifetime);
}
public static T UseBrowser<T>(
this T builder)
where T : AppBuilderBase<T>, new()
{
return builder
.UseWindowingSubsystem(BrowserWindowingPlatform.Register)
.UseSkia()
.With(new SkiaOptions { CustomGpuFactory = () => new BrowserSkiaGpu() });
}
return builder
.UseWindowingSubsystem(BrowserWindowingPlatform.Register)
.UseSkia()
.With(new SkiaOptions { CustomGpuFactory = () => new BrowserSkiaGpu() });
}
}

22
src/Web/Avalonia.Web/Interop/AvaloniaModule.cs

@ -0,0 +1,22 @@
using System.Runtime.InteropServices.JavaScript;
using System.Threading.Tasks;
namespace Avalonia.Web.Interop;
internal static class AvaloniaModule
{
public const string MainModuleName = "avalonia";
public const string StorageModuleName = "storage";
public static Task ImportMain()
{
var options = AvaloniaLocator.Current.GetService<BrowserPlatformOptions>() ?? new BrowserPlatformOptions();
return JSHost.ImportAsync(MainModuleName, options.FrameworkAssetPathResolver("avalonia.js"));
}
public static Task ImportStorage()
{
var options = AvaloniaLocator.Current.GetService<BrowserPlatformOptions>() ?? new BrowserPlatformOptions();
return JSHost.ImportAsync(StorageModuleName, options.FrameworkAssetPathResolver("storage.js"));
}
}

6
src/Web/Avalonia.Web/Interop/CanvasHelper.cs

@ -29,13 +29,13 @@ internal static partial class CanvasHelper
return glInfo;
}
[JSImport("Canvas.requestAnimationFrame", "avalonia")]
[JSImport("Canvas.requestAnimationFrame", AvaloniaModule.MainModuleName)]
public static partial void RequestAnimationFrame(JSObject canvas, bool renderLoop);
[JSImport("Canvas.setCanvasSize", "avalonia")]
[JSImport("Canvas.setCanvasSize", AvaloniaModule.MainModuleName)]
public static partial void SetCanvasSize(JSObject canvas, int height, int width);
[JSImport("Canvas.initGL", "avalonia")]
[JSImport("Canvas.initGL", AvaloniaModule.MainModuleName)]
private static partial JSObject InitGL(
JSObject canvas,
string canvasId,

8
src/Web/Avalonia.Web/Interop/DomHelper.cs

@ -8,20 +8,20 @@ internal static partial class DomHelper
[JSImport("globalThis.document.getElementById")]
internal static partial JSObject? GetElementById(string id);
[JSImport("AvaloniaDOM.createAvaloniaHost", "avalonia")]
[JSImport("AvaloniaDOM.createAvaloniaHost", AvaloniaModule.MainModuleName)]
public static partial JSObject CreateAvaloniaHost(JSObject element);
[JSImport("AvaloniaDOM.addClass", "avalonia")]
[JSImport("AvaloniaDOM.addClass", AvaloniaModule.MainModuleName)]
public static partial void AddCssClass(JSObject element, string className);
[JSImport("SizeWatcher.observe", "avalonia")]
[JSImport("SizeWatcher.observe", AvaloniaModule.MainModuleName)]
public static partial JSObject ObserveSize(
JSObject canvas,
string? canvasId,
[JSMarshalAs<JSType.Function<JSType.Number, JSType.Number>>]
Action<int, int> onSizeChanged);
[JSImport("DpiWatcher.start", "avalonia")]
[JSImport("DpiWatcher.start", AvaloniaModule.MainModuleName)]
public static partial double ObserveDpi(
[JSMarshalAs<JSType.Function<JSType.Number, JSType.Number>>]
Action<double, double> onDpiChanged);

24
src/Web/Avalonia.Web/Interop/InputHelper.cs

@ -6,7 +6,7 @@ namespace Avalonia.Web.Interop;
internal static partial class InputHelper
{
[JSImport("InputHelper.subscribeKeyEvents", "avalonia")]
[JSImport("InputHelper.subscribeKeyEvents", AvaloniaModule.MainModuleName)]
public static partial void SubscribeKeyEvents(
JSObject htmlElement,
[JSMarshalAs<JSType.Function<JSType.String, JSType.String, JSType.Number, JSType.Boolean>>]
@ -14,7 +14,7 @@ internal static partial class InputHelper
[JSMarshalAs<JSType.Function<JSType.String, JSType.String, JSType.Number, JSType.Boolean>>]
Func<string, string, int, bool> keyUp);
[JSImport("InputHelper.subscribeTextEvents", "avalonia")]
[JSImport("InputHelper.subscribeTextEvents", AvaloniaModule.MainModuleName)]
public static partial void SubscribeTextEvents(
JSObject htmlElement,
[JSMarshalAs<JSType.Function<JSType.String, JSType.String, JSType.Boolean>>]
@ -26,7 +26,7 @@ internal static partial class InputHelper
[JSMarshalAs<JSType.Function<JSType.Object, JSType.Boolean>>]
Func<JSObject, bool> onCompositionEnd);
[JSImport("InputHelper.subscribePointerEvents", "avalonia")]
[JSImport("InputHelper.subscribePointerEvents", AvaloniaModule.MainModuleName)]
public static partial void SubscribePointerEvents(
JSObject htmlElement,
[JSMarshalAs<JSType.Function<JSType.Object, JSType.Boolean>>]
@ -39,35 +39,35 @@ internal static partial class InputHelper
Func<JSObject, bool> wheel);
[JSImport("InputHelper.subscribeInputEvents", "avalonia")]
[JSImport("InputHelper.subscribeInputEvents", AvaloniaModule.MainModuleName)]
public static partial void SubscribeInputEvents(
JSObject htmlElement,
[JSMarshalAs<JSType.Function<JSType.String, JSType.Boolean>>]
Func<string, bool> input);
[JSImport("InputHelper.clearInput", "avalonia")]
[JSImport("InputHelper.clearInput", AvaloniaModule.MainModuleName)]
public static partial void ClearInputElement(JSObject htmlElement);
[JSImport("InputHelper.isInputElement", "avalonia")]
[JSImport("InputHelper.isInputElement", AvaloniaModule.MainModuleName)]
public static partial void IsInputElement(JSObject htmlElement);
[JSImport("InputHelper.focusElement", "avalonia")]
[JSImport("InputHelper.focusElement", AvaloniaModule.MainModuleName)]
public static partial void FocusElement(JSObject htmlElement);
[JSImport("InputHelper.setCursor", "avalonia")]
[JSImport("InputHelper.setCursor", AvaloniaModule.MainModuleName)]
public static partial void SetCursor(JSObject htmlElement, string kind);
[JSImport("InputHelper.hide", "avalonia")]
[JSImport("InputHelper.hide", AvaloniaModule.MainModuleName)]
public static partial void HideElement(JSObject htmlElement);
[JSImport("InputHelper.show", "avalonia")]
[JSImport("InputHelper.show", AvaloniaModule.MainModuleName)]
public static partial void ShowElement(JSObject htmlElement);
[JSImport("InputHelper.setSurroundingText", "avalonia")]
[JSImport("InputHelper.setSurroundingText", AvaloniaModule.MainModuleName)]
public static partial void SetSurroundingText(JSObject htmlElement, string text, int start, int end);
[JSImport("InputHelper.setBounds", "avalonia")]
[JSImport("InputHelper.setBounds", AvaloniaModule.MainModuleName)]
public static partial void SetBounds(JSObject htmlElement, int x, int y, int width, int height, int caret);
[JSImport("globalThis.navigator.clipboard.readText")]

14
src/Web/Avalonia.Web/Interop/NativeControlHostHelper.cs

@ -5,24 +5,24 @@ namespace Avalonia.Web.Interop;
internal static partial class NativeControlHostHelper
{
[JSImport("NativeControlHost.createDefaultChild", "avalonia")]
[JSImport("NativeControlHost.createDefaultChild", AvaloniaModule.MainModuleName)]
internal static partial JSObject CreateDefaultChild(JSObject? parent);
[JSImport("NativeControlHost.createAttachment", "avalonia")]
[JSImport("NativeControlHost.createAttachment", AvaloniaModule.MainModuleName)]
internal static partial JSObject CreateAttachment();
[JSImport("NativeControlHost.initializeWithChildHandle", "avalonia")]
[JSImport("NativeControlHost.initializeWithChildHandle", AvaloniaModule.MainModuleName)]
internal static partial void InitializeWithChildHandle(JSObject element, JSObject child);
[JSImport("NativeControlHost.attachTo", "avalonia")]
[JSImport("NativeControlHost.attachTo", AvaloniaModule.MainModuleName)]
internal static partial void AttachTo(JSObject element, JSObject? host);
[JSImport("NativeControlHost.showInBounds", "avalonia")]
[JSImport("NativeControlHost.showInBounds", AvaloniaModule.MainModuleName)]
internal static partial void ShowInBounds(JSObject element, double x, double y, double width, double height);
[JSImport("NativeControlHost.hideWithSize", "avalonia")]
[JSImport("NativeControlHost.hideWithSize", AvaloniaModule.MainModuleName)]
internal static partial void HideWithSize(JSObject element, double width, double height);
[JSImport("NativeControlHost.releaseChild", "avalonia")]
[JSImport("NativeControlHost.releaseChild", AvaloniaModule.MainModuleName)]
internal static partial void ReleaseChild(JSObject element);
}

30
src/Web/Avalonia.Web/Interop/StorageHelper.cs

@ -5,51 +5,51 @@ namespace Avalonia.Web.Interop;
internal static partial class StorageHelper
{
[JSImport("Caniuse.canShowOpenFilePicker", "avalonia")]
[JSImport("Caniuse.canShowOpenFilePicker", AvaloniaModule.MainModuleName)]
public static partial bool CanShowOpenFilePicker();
[JSImport("Caniuse.canShowSaveFilePicker", "avalonia")]
[JSImport("Caniuse.canShowSaveFilePicker", AvaloniaModule.MainModuleName)]
public static partial bool CanShowSaveFilePicker();
[JSImport("Caniuse.canShowDirectoryPicker", "avalonia")]
[JSImport("Caniuse.canShowDirectoryPicker", AvaloniaModule.MainModuleName)]
public static partial bool CanShowDirectoryPicker();
[JSImport("StorageProvider.selectFolderDialog", "storage")]
[JSImport("StorageProvider.selectFolderDialog", AvaloniaModule.StorageModuleName)]
public static partial Task<JSObject?> SelectFolderDialog(JSObject? startIn);
[JSImport("StorageProvider.openFileDialog", "storage")]
[JSImport("StorageProvider.openFileDialog", AvaloniaModule.StorageModuleName)]
public static partial Task<JSObject?> OpenFileDialog(JSObject? startIn, bool multiple,
[JSMarshalAs<JSType.Array<JSType.Any>>] object[]? types, bool excludeAcceptAllOption);
[JSImport("StorageProvider.saveFileDialog", "storage")]
[JSImport("StorageProvider.saveFileDialog", AvaloniaModule.StorageModuleName)]
public static partial Task<JSObject?> SaveFileDialog(JSObject? startIn, string? suggestedName,
[JSMarshalAs<JSType.Array<JSType.Any>>] object[]? types, bool excludeAcceptAllOption);
[JSImport("StorageProvider.openBookmark", "storage")]
[JSImport("StorageProvider.openBookmark", AvaloniaModule.StorageModuleName)]
public static partial Task<JSObject?> OpenBookmark(string key);
[JSImport("StorageItem.saveBookmark", "storage")]
[JSImport("StorageItem.saveBookmark", AvaloniaModule.StorageModuleName)]
public static partial Task<string?> SaveBookmark(JSObject item);
[JSImport("StorageItem.deleteBookmark", "storage")]
[JSImport("StorageItem.deleteBookmark", AvaloniaModule.StorageModuleName)]
public static partial Task DeleteBookmark(JSObject item);
[JSImport("StorageItem.getProperties", "storage")]
[JSImport("StorageItem.getProperties", AvaloniaModule.StorageModuleName)]
public static partial Task<JSObject?> GetProperties(JSObject item);
[JSImport("StorageItem.openWrite", "storage")]
[JSImport("StorageItem.openWrite", AvaloniaModule.StorageModuleName)]
public static partial Task<JSObject> OpenWrite(JSObject item);
[JSImport("StorageItem.openRead", "storage")]
[JSImport("StorageItem.openRead", AvaloniaModule.StorageModuleName)]
public static partial Task<JSObject> OpenRead(JSObject item);
[JSImport("StorageItem.getItems", "storage")]
[JSImport("StorageItem.getItems", AvaloniaModule.StorageModuleName)]
[return: JSMarshalAs<JSType.Promise<JSType.Object>>]
public static partial Task<JSObject> GetItems(JSObject item);
[JSImport("StorageItems.itemsArray", "storage")]
[JSImport("StorageItems.itemsArray", AvaloniaModule.StorageModuleName)]
public static partial JSObject[] ItemsArray(JSObject item);
[JSImport("StorageProvider.createAcceptType", "storage")]
[JSImport("StorageProvider.createAcceptType", AvaloniaModule.StorageModuleName)]
public static partial JSObject CreateAcceptType(string description, string[] mimeTypes);
}

16
src/Web/Avalonia.Web/Interop/StreamHelper.cs

@ -2,33 +2,33 @@
using System.Runtime.InteropServices.JavaScript;
using System.Threading.Tasks;
namespace Avalonia.Web.Storage;
namespace Avalonia.Web.Interop;
/// <summary>
/// Set of FileSystemWritableFileStream and Blob methods.
/// </summary>
internal static partial class StreamHelper
{
[JSImport("StreamHelper.seek", "avalonia")]
[JSImport("StreamHelper.seek", AvaloniaModule.MainModuleName)]
public static partial void Seek(JSObject stream, [JSMarshalAs<JSType.Number>] long position);
[JSImport("StreamHelper.truncate", "avalonia")]
[JSImport("StreamHelper.truncate", AvaloniaModule.MainModuleName)]
public static partial void Truncate(JSObject stream, [JSMarshalAs<JSType.Number>] long size);
[JSImport("StreamHelper.write", "avalonia")]
[JSImport("StreamHelper.write", AvaloniaModule.MainModuleName)]
public static partial Task WriteAsync(JSObject stream, [JSMarshalAs<JSType.MemoryView>] ArraySegment<byte> data);
[JSImport("StreamHelper.close", "avalonia")]
[JSImport("StreamHelper.close", AvaloniaModule.MainModuleName)]
public static partial Task CloseAsync(JSObject stream);
[JSImport("StreamHelper.byteLength", "avalonia")]
[JSImport("StreamHelper.byteLength", AvaloniaModule.MainModuleName)]
[return: JSMarshalAs<JSType.Number>]
public static partial long ByteLength(JSObject stream);
[JSImport("StreamHelper.sliceArrayBuffer", "avalonia")]
[JSImport("StreamHelper.sliceArrayBuffer", AvaloniaModule.MainModuleName)]
private static partial Task<JSObject> SliceToArrayBuffer(JSObject stream, [JSMarshalAs<JSType.Number>] long offset, int count);
[JSImport("StreamHelper.toMemoryView", "avalonia")]
[JSImport("StreamHelper.toMemoryView", AvaloniaModule.MainModuleName)]
[return: JSMarshalAs<JSType.Array<JSType.Number>>]
private static partial byte[] ArrayBufferToMemoryView(JSObject stream);

2
src/Web/Avalonia.Web/Storage/BlobReadableStream.cs

@ -4,6 +4,8 @@ using System.Runtime.InteropServices.JavaScript;
using System.Threading;
using System.Threading.Tasks;
using Avalonia.Web.Interop;
namespace Avalonia.Web.Storage;
[System.Runtime.Versioning.SupportedOSPlatform("browser")]

12
src/Web/Avalonia.Web/Storage/BrowserStorageProvider.cs

@ -20,7 +20,7 @@ internal class BrowserStorageProvider : IStorageProvider
internal const string PickerCancelMessage = "The user aborted a request";
internal const string NoPermissionsMessage = "Permissions denied";
private readonly Lazy<Task<JSObject>> _lazyModule = new(() => JSHost.ImportAsync("storage", "./storage.js"));
private readonly Lazy<Task> _lazyModule = new(() => AvaloniaModule.ImportStorage());
public bool CanOpen => StorageHelper.CanShowOpenFilePicker();
public bool CanSave => StorageHelper.CanShowSaveFilePicker();
@ -28,7 +28,7 @@ internal class BrowserStorageProvider : IStorageProvider
public async Task<IReadOnlyList<IStorageFile>> OpenFilePickerAsync(FilePickerOpenOptions options)
{
_ = await _lazyModule.Value;
await _lazyModule.Value;
var startIn = (options.SuggestedStartLocation as JSStorageItem)?.FileHandle;
var (types, exludeAll) = ConvertFileTypes(options.FileTypeFilter);
@ -62,7 +62,7 @@ internal class BrowserStorageProvider : IStorageProvider
public async Task<IStorageFile?> SaveFilePickerAsync(FilePickerSaveOptions options)
{
_ = await _lazyModule.Value;
await _lazyModule.Value;
var startIn = (options.SuggestedStartLocation as JSStorageItem)?.FileHandle;
var (types, exludeAll) = ConvertFileTypes(options.FileTypeChoices);
@ -90,7 +90,7 @@ internal class BrowserStorageProvider : IStorageProvider
public async Task<IReadOnlyList<IStorageFolder>> OpenFolderPickerAsync(FolderPickerOpenOptions options)
{
_ = await _lazyModule.Value;
await _lazyModule.Value;
var startIn = (options.SuggestedStartLocation as JSStorageItem)?.FileHandle;
try
@ -106,14 +106,14 @@ internal class BrowserStorageProvider : IStorageProvider
public async Task<IStorageBookmarkFile?> OpenFileBookmarkAsync(string bookmark)
{
_ = await _lazyModule.Value;
await _lazyModule.Value;
var item = await StorageHelper.OpenBookmark(bookmark);
return item is not null ? new JSStorageFile(item) : null;
}
public async Task<IStorageBookmarkFolder?> OpenFolderBookmarkAsync(string bookmark)
{
_ = await _lazyModule.Value;
await _lazyModule.Value;
var item = await StorageHelper.OpenBookmark(bookmark);
return item is not null ? new JSStorageFolder(item) : null;
}

2
src/Web/Avalonia.Web/Storage/WriteableStream.cs

@ -4,6 +4,8 @@ using System.Runtime.InteropServices.JavaScript;
using System.Threading;
using System.Threading.Tasks;
using Avalonia.Web.Interop;
namespace Avalonia.Web.Storage;
[System.Runtime.Versioning.SupportedOSPlatform("browser")]

14
src/Web/Avalonia.Web/webapp/modules/avalonia.ts

@ -7,10 +7,18 @@ import { StreamHelper } from "./avalonia/stream";
import { NativeControlHost } from "./avalonia/nativeControlHost";
async function registerAvaloniaModule(api: RuntimeAPI): Promise<void> {
api.setModuleImports("avalonia", avaloniaModule);
api.setModuleImports("avalonia", {
Caniuse,
Canvas,
InputHelper,
SizeWatcher,
DpiWatcher,
AvaloniaDOM,
StreamHelper,
NativeControlHost
});
}
export const avaloniaModule = {
export {
Caniuse,
Canvas,
InputHelper,

5
src/Web/Avalonia.Web/webapp/modules/avalonia/dom.ts

@ -4,6 +4,8 @@ export class AvaloniaDOM {
}
static createAvaloniaHost(host: HTMLElement) {
const randomIdPart = Math.random().toString(36).replace(/[^a-z]+/g, "").substr(2, 10);
// Root element
host.classList.add("avalonia-container");
host.tabIndex = 0;
@ -11,6 +13,7 @@ export class AvaloniaDOM {
// Rendering target canvas
const canvas = document.createElement("canvas");
canvas.id = `canvas${randomIdPart}`;
canvas.classList.add("avalonia-canvas");
canvas.style.backgroundColor = "#ccc";
canvas.style.width = "100%";
@ -19,6 +22,7 @@ export class AvaloniaDOM {
// Native controls host
const nativeHost = document.createElement("div");
canvas.id = `nativeHost${randomIdPart}`;
nativeHost.classList.add("avalonia-native-host");
nativeHost.style.left = "0px";
nativeHost.style.top = "0px";
@ -28,6 +32,7 @@ export class AvaloniaDOM {
// IME
const inputElement = document.createElement("input");
canvas.id = `input${randomIdPart}`;
inputElement.classList.add("avalonia-input-element");
inputElement.autocapitalize = "none";
inputElement.type = "text";

Loading…
Cancel
Save