[DesignerHost/HTML]: improve high-dpi screen behavior (#19996)
* DesignerHost: improve high-dpi screen behavior handle previewer dpi changed event * fix test --------- Co-authored-by: ijklam <43789618+Tangent-90@users.noreply.github.com>
This commit is contained in:
@@ -11,6 +11,7 @@ interface PreviewerPresenterProps {
|
||||
|
||||
export class PreviewerPresenter extends React.Component<PreviewerPresenterProps> {
|
||||
private canvasRef: React.RefObject<HTMLCanvasElement>;
|
||||
private scale: number = 1;
|
||||
|
||||
constructor(props: PreviewerPresenterProps) {
|
||||
super(props);
|
||||
@@ -28,6 +29,23 @@ export class PreviewerPresenter extends React.Component<PreviewerPresenterProps>
|
||||
|
||||
componentDidMount(): void {
|
||||
this.updateCanvas(this.canvasRef.current, this.props.conn.currentFrame);
|
||||
|
||||
if (parent != null && parent !== window)
|
||||
{
|
||||
window.addEventListener('message', (event) => {
|
||||
if (!event.origin.startsWith('vscode-webview://')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle previewer dpi changed event
|
||||
if (event.data.type === 'scaling') {
|
||||
const scale = Number(event.data.scale);
|
||||
if (Number.isFinite(scale) && scale > 0) {
|
||||
this.scale = event.data.scale;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Readonly<PreviewerPresenterProps>, prevState: Readonly<{}>, snapshot?: any): void {
|
||||
@@ -48,39 +66,59 @@ export class PreviewerPresenter extends React.Component<PreviewerPresenterProps>
|
||||
updateCanvas(canvas: HTMLCanvasElement | null, frame: PreviewerFrame | null) {
|
||||
if (!canvas)
|
||||
return;
|
||||
|
||||
const oldWidth = canvas.style.width;
|
||||
const oldHeight = canvas.style.height;
|
||||
|
||||
if (frame == null){
|
||||
canvas.width = canvas.height = 1;
|
||||
canvas.style.width = canvas.style.height = `1px`;
|
||||
canvas.getContext('2d')!.clearRect(0,0,1,1);
|
||||
}
|
||||
else {
|
||||
canvas.width = frame.data.width;
|
||||
canvas.height = frame.data.height;
|
||||
|
||||
// To prevent blurry images on high-DPI screens
|
||||
canvas.style.width = `${frame.data.width / window.devicePixelRatio}px`;
|
||||
canvas.style.height = `${frame.data.height / window.devicePixelRatio}px`;
|
||||
|
||||
const ctx = canvas.getContext('2d')!;
|
||||
ctx.putImageData(frame.data, 0,0);
|
||||
}
|
||||
|
||||
if (oldWidth !== canvas.style.width || oldHeight !== canvas.style.height)
|
||||
{
|
||||
// Inform the parent page (a VSCode extention) the canvas size has changed
|
||||
parent?.postMessage({
|
||||
type: 'resize',
|
||||
width: canvas.width / window.devicePixelRatio,
|
||||
height: canvas.height / window.devicePixelRatio,
|
||||
}, '*');
|
||||
}
|
||||
}
|
||||
|
||||
handleMouseDown(e: React.MouseEvent) {
|
||||
e.preventDefault();
|
||||
const pointerPressedEventMessage = new PointerPressedEventMessage(e);
|
||||
const pointerPressedEventMessage = new PointerPressedEventMessage(e, this.scale / window.devicePixelRatio);
|
||||
this.props.conn.sendMouseEvent(pointerPressedEventMessage);
|
||||
}
|
||||
|
||||
handleMouseUp(e: React.MouseEvent) {
|
||||
e.preventDefault();
|
||||
const pointerReleasedEventMessage = new PointerReleasedEventMessage(e);
|
||||
const pointerReleasedEventMessage = new PointerReleasedEventMessage(e, this.scale / window.devicePixelRatio);
|
||||
this.props.conn.sendMouseEvent(pointerReleasedEventMessage);
|
||||
}
|
||||
|
||||
handleMouseMove(e: React.MouseEvent) {
|
||||
e.preventDefault();
|
||||
const pointerMovedEventMessage = new PointerMovedEventMessage(e);
|
||||
const pointerMovedEventMessage = new PointerMovedEventMessage(e, this.scale / window.devicePixelRatio);
|
||||
this.props.conn.sendMouseEvent(pointerMovedEventMessage);
|
||||
}
|
||||
|
||||
handleWheel(e: React.WheelEvent) {
|
||||
e.preventDefault();
|
||||
const scrollEventMessage = new ScrollEventMessage(e);
|
||||
const scrollEventMessage = new ScrollEventMessage(e, this.scale / window.devicePixelRatio);
|
||||
this.props.conn.sendMouseEvent(scrollEventMessage);
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -5,9 +5,9 @@ export abstract class PointerEventMessageBase extends InputEventMessageBase {
|
||||
public readonly x: number;
|
||||
public readonly y: number;
|
||||
|
||||
protected constructor(e: React.MouseEvent) {
|
||||
protected constructor(e: React.MouseEvent, scale: number) {
|
||||
super(e);
|
||||
this.x = e.clientX;
|
||||
this.y = e.clientY;
|
||||
this.x = e.clientX / scale;
|
||||
this.y = e.clientY / scale;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -2,8 +2,8 @@
|
||||
import {PointerEventMessageBase} from "./PointerEventMessageBase";
|
||||
|
||||
export class PointerMovedEventMessage extends PointerEventMessageBase {
|
||||
constructor(e: React.MouseEvent) {
|
||||
super(e);
|
||||
constructor(e: React.MouseEvent, scale: number) {
|
||||
super(e, scale);
|
||||
}
|
||||
|
||||
public toString = () : string => {
|
||||
|
||||
+2
-2
@@ -6,8 +6,8 @@ import {getMouseButton} from "./MouseEventHelpers";
|
||||
export class PointerPressedEventMessage extends PointerEventMessageBase {
|
||||
public readonly button: MouseButton
|
||||
|
||||
constructor(e: React.MouseEvent) {
|
||||
super(e);
|
||||
constructor(e: React.MouseEvent, scale: number) {
|
||||
super(e, scale);
|
||||
this.button = getMouseButton(e);
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -6,8 +6,8 @@ import {getMouseButton} from "./MouseEventHelpers";
|
||||
export class PointerReleasedEventMessage extends PointerEventMessageBase {
|
||||
public readonly button: MouseButton
|
||||
|
||||
constructor(e: React.MouseEvent) {
|
||||
super(e);
|
||||
constructor(e: React.MouseEvent, scale: number) {
|
||||
super(e, scale);
|
||||
this.button = getMouseButton(e);
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -5,8 +5,8 @@ export class ScrollEventMessage extends PointerEventMessageBase {
|
||||
public readonly deltaX: number;
|
||||
public readonly deltaY: number;
|
||||
|
||||
constructor(e: React.WheelEvent) {
|
||||
super(e);
|
||||
constructor(e: React.WheelEvent, scale: number) {
|
||||
super(e, scale);
|
||||
this.deltaX = -e.deltaX;
|
||||
this.deltaY = -e.deltaY;
|
||||
}
|
||||
|
||||
+6
@@ -6,6 +6,12 @@ export interface PreviewerFrame {
|
||||
dpiY: number;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
avaloniaPreviewerSecurityCookie: string;
|
||||
}
|
||||
}
|
||||
|
||||
export class PreviewerServerConnection {
|
||||
private nextFrame = {
|
||||
width: 0,
|
||||
|
||||
@@ -5,7 +5,11 @@
|
||||
<meta charset="UTF-8">
|
||||
<title>Avalonia XAML previewer web edition</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- Removes the default margin of the body element.
|
||||
Most browsers apply a default margin (typically 8px) to the body element
|
||||
Setting it to 0 makes the content flush against the browser window edges, eliminating white space -->
|
||||
<body style="margin: 0;">
|
||||
<div id="app">
|
||||
<center>Loading...</center>
|
||||
</div>
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
"noImplicitThis": true,
|
||||
"noImplicitAny": true,
|
||||
"strictNullChecks": true,
|
||||
"suppressImplicitAnyIndexErrors": true,
|
||||
"noUnusedLocals": false,
|
||||
"baseUrl": ".",
|
||||
"experimentalDecorators": true,
|
||||
|
||||
+4
-4
@@ -58,17 +58,17 @@ describe("Input event tests", () => {
|
||||
.object()
|
||||
|
||||
it("PointerMovedEventMessage", () => {
|
||||
const message = new PointerMovedEventMessage(mouseEvent)
|
||||
const message = new PointerMovedEventMessage(mouseEvent, 1)
|
||||
expect(message.toString())
|
||||
.equal(`pointer-moved:${modifiers}:${x}:${y}`)
|
||||
})
|
||||
it("PointerPressedEventMessage", () => {
|
||||
const message = new PointerPressedEventMessage(mouseEvent)
|
||||
const message = new PointerPressedEventMessage(mouseEvent, 1)
|
||||
expect(message.toString())
|
||||
.equal(`pointer-pressed:${modifiers}:${x}:${y}:${button}`)
|
||||
})
|
||||
it("PointerReleasedEventMessage", () => {
|
||||
const message = new PointerReleasedEventMessage(mouseEvent)
|
||||
const message = new PointerReleasedEventMessage(mouseEvent, 1)
|
||||
expect(message.toString())
|
||||
.equal(`pointer-released:${modifiers}:${x}:${y}:${button}`)
|
||||
})
|
||||
@@ -85,7 +85,7 @@ describe("Input event tests", () => {
|
||||
.setup(x => x.deltaX).returns(-deltaX)
|
||||
.setup(x => x.deltaY).returns(-deltaY)
|
||||
.object()
|
||||
const message = new ScrollEventMessage(wheelEvent)
|
||||
const message = new ScrollEventMessage(wheelEvent, 1)
|
||||
|
||||
expect(message.toString())
|
||||
.equal(`scroll:${modifiers}:${x}:${y}:${deltaX}:${deltaY}`)
|
||||
|
||||
Reference in New Issue
Block a user