Browse Source

Merge branch 'features/web' of https://github.com/AvaloniaUI/Avalonia into features/web

pull/7029/head
Benedikt Stebner 4 years ago
parent
commit
b2c076911e
  1. 2
      .gitignore
  2. 1
      src/Avalonia.Controls/Window.cs
  3. 28
      src/Web/Avalonia.Web.Blazor/Interop/Typescript/DpiWatcher.js
  4. 163
      src/Web/Avalonia.Web.Blazor/Interop/Typescript/SKHtmlCanvas.js
  5. 42
      src/Web/Avalonia.Web.Blazor/Interop/Typescript/SizeWatcher.js
  6. 1
      src/Web/Avalonia.Web.Blazor/wwwroot/.gitignore

2
.gitignore

@ -211,3 +211,5 @@ coc-settings.json
.ccls-cache
.ccls
*.map
src/Web/Avalonia.Web.Blazor/wwwroot/*.js
src/Web/Avalonia.Web.Blazor/Interop/Typescript/*.js

1
src/Avalonia.Controls/Window.cs

@ -994,6 +994,7 @@ namespace Avalonia.Controls
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
{
base.OnPropertyChanged(change);
if (change.Property == SystemDecorationsProperty)
{
var typedNewValue = change.NewValue.GetValueOrDefault<SystemDecorations>();

28
src/Web/Avalonia.Web.Blazor/Interop/Typescript/DpiWatcher.js

@ -1,28 +0,0 @@
export class DpiWatcher {
static getDpi() {
return window.devicePixelRatio;
}
static start(callback) {
//console.info(`Starting DPI watcher with callback ${callback._id}...`);
DpiWatcher.lastDpi = window.devicePixelRatio;
DpiWatcher.timerId = window.setInterval(DpiWatcher.update, 1000);
DpiWatcher.callback = callback;
return DpiWatcher.lastDpi;
}
static stop() {
//console.info(`Stopping DPI watcher with callback ${DpiWatcher.callback._id}...`);
window.clearInterval(DpiWatcher.timerId);
DpiWatcher.callback = undefined;
}
static update() {
if (!DpiWatcher.callback)
return;
const currentDpi = window.devicePixelRatio;
const lastDpi = DpiWatcher.lastDpi;
DpiWatcher.lastDpi = currentDpi;
if (Math.abs(lastDpi - currentDpi) > 0.001) {
DpiWatcher.callback.invokeMethod('Invoke', lastDpi, currentDpi);
}
}
}
//# sourceMappingURL=DpiWatcher.js.map

163
src/Web/Avalonia.Web.Blazor/Interop/Typescript/SKHtmlCanvas.js

@ -1,163 +0,0 @@
export class SKHtmlCanvas {
constructor(useGL, element, callback) {
this.renderLoopEnabled = false;
this.renderLoopRequest = 0;
this.htmlCanvas = element;
this.renderFrameCallback = callback;
if (useGL) {
const ctx = SKHtmlCanvas.createWebGLContext(this.htmlCanvas);
if (!ctx) {
console.error(`Failed to create WebGL context: err ${ctx}`);
return null;
}
// make current
GL.makeContextCurrent(ctx);
// read values
const fbo = GLctx.getParameter(GLctx.FRAMEBUFFER_BINDING);
this.glInfo = {
context: ctx,
fboId: fbo ? fbo.id : 0,
stencil: GLctx.getParameter(GLctx.STENCIL_BITS),
sample: 0,
depth: GLctx.getParameter(GLctx.DEPTH_BITS),
};
}
}
static initGL(element, elementId, callback) {
var view = SKHtmlCanvas.init(true, element, elementId, callback);
if (!view || !view.glInfo)
return null;
return view.glInfo;
}
static initRaster(element, elementId, callback) {
var view = SKHtmlCanvas.init(false, element, elementId, callback);
if (!view)
return false;
return true;
}
static init(useGL, element, elementId, callback) {
var htmlCanvas = element;
if (!htmlCanvas) {
console.error(`No canvas element was provided.`);
return null;
}
if (!SKHtmlCanvas.elements)
SKHtmlCanvas.elements = new Map();
SKHtmlCanvas.elements[elementId] = element;
const view = new SKHtmlCanvas(useGL, element, callback);
htmlCanvas.SKHtmlCanvas = view;
return view;
}
static deinit(elementId) {
if (!elementId)
return;
const element = SKHtmlCanvas.elements[elementId];
SKHtmlCanvas.elements.delete(elementId);
const htmlCanvas = element;
if (!htmlCanvas || !htmlCanvas.SKHtmlCanvas)
return;
htmlCanvas.SKHtmlCanvas.deinit();
htmlCanvas.SKHtmlCanvas = undefined;
}
static requestAnimationFrame(element, renderLoop, width, height) {
const htmlCanvas = element;
if (!htmlCanvas || !htmlCanvas.SKHtmlCanvas)
return;
htmlCanvas.SKHtmlCanvas.requestAnimationFrame(renderLoop, width, height);
}
static setEnableRenderLoop(element, enable) {
const htmlCanvas = element;
if (!htmlCanvas || !htmlCanvas.SKHtmlCanvas)
return;
htmlCanvas.SKHtmlCanvas.setEnableRenderLoop(enable);
}
static putImageData(element, pData, width, height) {
const htmlCanvas = element;
if (!htmlCanvas || !htmlCanvas.SKHtmlCanvas)
return;
htmlCanvas.SKHtmlCanvas.putImageData(pData, width, height);
}
deinit() {
this.setEnableRenderLoop(false);
}
requestAnimationFrame(renderLoop, width, height) {
// optionally update the render loop
if (renderLoop !== undefined && this.renderLoopEnabled !== renderLoop)
this.setEnableRenderLoop(renderLoop);
// make sure the canvas is scaled correctly for the drawing
if (width && height) {
this.htmlCanvas.width = width;
this.htmlCanvas.height = height;
}
// skip because we have a render loop
if (this.renderLoopRequest !== 0)
return;
// add the draw to the next frame
this.renderLoopRequest = window.requestAnimationFrame(() => {
if (this.glInfo) {
// make current
GL.makeContextCurrent(this.glInfo.context);
}
this.renderFrameCallback.invokeMethod('Invoke');
this.renderLoopRequest = 0;
// we may want to draw the next frame
if (this.renderLoopEnabled)
this.requestAnimationFrame();
});
}
setEnableRenderLoop(enable) {
this.renderLoopEnabled = enable;
// either start the new frame or cancel the existing one
if (enable) {
//console.info(`Enabling render loop with callback ${this.renderFrameCallback._id}...`);
this.requestAnimationFrame();
}
else if (this.renderLoopRequest !== 0) {
window.cancelAnimationFrame(this.renderLoopRequest);
this.renderLoopRequest = 0;
}
}
putImageData(pData, width, height) {
if (this.glInfo || !pData || width <= 0 || width <= 0)
return false;
var ctx = this.htmlCanvas.getContext('2d');
if (!ctx) {
console.error(`Failed to obtain 2D canvas context.`);
return false;
}
// make sure the canvas is scaled correctly for the drawing
this.htmlCanvas.width = width;
this.htmlCanvas.height = height;
// set the canvas to be the bytes
var buffer = new Uint8ClampedArray(Module.HEAPU8.buffer, pData, width * height * 4);
var imageData = new ImageData(buffer, width, height);
ctx.putImageData(imageData, 0, 0);
return true;
}
static createWebGLContext(htmlCanvas) {
const contextAttributes = {
alpha: 1,
depth: 1,
stencil: 8,
antialias: 1,
premultipliedAlpha: 1,
preserveDrawingBuffer: 0,
preferLowPowerToHighPerformance: 0,
failIfMajorPerformanceCaveat: 0,
majorVersion: 2,
minorVersion: 0,
enableExtensionsByDefault: 1,
explicitSwapControl: 0,
renderViaOffscreenBackBuffer: 0,
};
let ctx = GL.createContext(htmlCanvas, contextAttributes);
if (!ctx && contextAttributes.majorVersion > 1) {
console.warn('Falling back to WebGL 1.0');
contextAttributes.majorVersion = 1;
contextAttributes.minorVersion = 0;
ctx = GL.createContext(htmlCanvas, contextAttributes);
}
return ctx;
}
}
//# sourceMappingURL=SKHtmlCanvas.js.map

42
src/Web/Avalonia.Web.Blazor/Interop/Typescript/SizeWatcher.js

@ -1,42 +0,0 @@
export class SizeWatcher {
static observe(element, elementId, callback) {
if (!element || !callback)
return;
//console.info(`Adding size watcher observation with callback ${callback._id}...`);
SizeWatcher.init();
const watcherElement = element;
watcherElement.SizeWatcher = {
callback: callback
};
SizeWatcher.elements[elementId] = element;
SizeWatcher.observer.observe(element);
SizeWatcher.invoke(element);
}
static unobserve(elementId) {
if (!elementId || !SizeWatcher.observer)
return;
//console.info('Removing size watcher observation...');
const element = SizeWatcher.elements[elementId];
SizeWatcher.elements.delete(elementId);
SizeWatcher.observer.unobserve(element);
}
static init() {
if (SizeWatcher.observer)
return;
//console.info('Starting size watcher...');
SizeWatcher.elements = new Map();
SizeWatcher.observer = new ResizeObserver((entries) => {
for (let entry of entries) {
SizeWatcher.invoke(entry.target);
}
});
}
static invoke(element) {
const watcherElement = element;
const instance = watcherElement.SizeWatcher;
if (!instance || !instance.callback)
return;
return instance.callback.invokeMethod('Invoke', element.clientWidth, element.clientHeight);
}
}
//# sourceMappingURL=SizeWatcher.js.map

1
src/Web/Avalonia.Web.Blazor/wwwroot/.gitignore

@ -1 +0,0 @@
*.js
Loading…
Cancel
Save