From c37883f3c62809580db27d4231985845ef3e2cbe Mon Sep 17 00:00:00 2001 From: Caleb Hearon Date: Tue, 19 Nov 2024 21:47:26 -0500 Subject: [PATCH] ability to change the origin style (parent of ) I want to invisibly set the zoom to the devicePixelRatio in the demo site, but still allow people to see the effect of zoom on as they would in a normal browser. I don't think this will be useful for other purposes, and it effectively clears the style cache, so not documenting it. --- I've been thinking about how to optimize the use of the zoom property for devicePixelRatio for more than a month now. The way the API is now, you have to regenerate both a DOM and a layout tree if you want to change it. This could be bad for CellEngine since it can retain tens of thousands of layouts. I seriously considered having an element's style list be mutable. I mostly figured out how Firefox tracks style damage, restyle roots, lazy frame construction, etc. Avoiding recreating the DOM and box tree when you want to change styles would lower GC pressure and reduce CPU time. However, the CPU doesn't get off that easily since layout is far more expensive than DOM and box generation combined. It would also be a big API change because I would have to move generate and style calc into layout. Ultimatley I decided this isn't a problem to fix here because: 1. Server-side doesn't benefit from mutable DOM, and that's the primary focus for dropflow 2. CellEngine could use zoom: 1 for measurement and, when DPR changes, only regenerate in-viewport layouts (mark the rest dirty). Also worth noting is that CellEngine doesn't even need zoom since it only renders text. Who knows if it ever will. 3. CellEngine should probably not be retaining that many layouts. It would be better to page them in and out, only creating all of them when needed (measuring). --- src/api.ts | 4 ++-- src/dom.ts | 6 +++--- src/style.ts | 22 ++++++++++++++++++++-- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/api.ts b/src/api.ts index 70a24a6..10092f1 100644 --- a/src/api.ts +++ b/src/api.ts @@ -1,5 +1,5 @@ import {HTMLElement, TextNode} from './dom.js'; -import {DeclaredStyle, initialStyle, computeElementStyle} from './style.js'; +import {DeclaredStyle, getOriginStyle, computeElementStyle} from './style.js'; import {registerFont, unregisterFont, getFontUrls, RegisterFontOptions} from './text-font.js'; import {generateBlockContainer, layoutBlockBox, BlockFormattingContext, BlockContainer} from './layout-flow.js'; import HtmlPaintBackend from './paint-html.js'; @@ -18,7 +18,7 @@ export {createDeclaredStyle as style} from './style.js'; export {registerFont, unregisterFont}; export function generate(rootElement: HTMLElement): BlockContainer { - if (rootElement.style === initialStyle) { + if (rootElement.style === getOriginStyle()) { throw new Error( 'To use the hyperscript API, pass the element tree to dom() and use ' + 'the return value as the argument to generate().' diff --git a/src/dom.ts b/src/dom.ts index 32951f4..dc6a82a 100644 --- a/src/dom.ts +++ b/src/dom.ts @@ -1,6 +1,6 @@ import {Box} from './layout-box.js'; import {loggableText} from './util.js'; -import {Style, DeclaredStyle, initialStyle, EMPTY_STYLE} from './style.js'; +import {Style, DeclaredStyle, getOriginStyle, EMPTY_STYLE} from './style.js'; import {query, queryAll, Adapter} from './style-query.js'; export class TextNode { @@ -11,7 +11,7 @@ export class TextNode { constructor(id: string, text: string, parent: HTMLElement | null = null) { this.id = id; - this.style = initialStyle; + this.style = getOriginStyle(); this.text = text; this.parent = parent; } @@ -40,7 +40,7 @@ export class HTMLElement { ) { this.id = id; this.tagName = tagName; - this.style = initialStyle; + this.style = getOriginStyle(); this.declaredStyle = declaredStyle; this.parent = parent; this.attrs = attrs; diff --git a/src/style.ts b/src/style.ts index a8b3a5d..03786b1 100644 --- a/src/style.ts +++ b/src/style.ts @@ -667,7 +667,25 @@ const initialPlainStyle: ComputedStyle = Object.freeze({ overflow: 'visible' }); -export const initialStyle = new Style(initialPlainStyle); +let originStyle = new Style(initialPlainStyle); + +export function getOriginStyle() { + return originStyle; +} + +/** + * Set the style that the style inherits from + * + * Be careful calling this. It makes the inheritance style cache useless for any + * styles created after calling it. Using it incorrectly can hurt performance. + * + * Currently the only legitimately known usage is to set the zoom to a desired + * CSS-to-device pixel density (devicePixelRatio). As such, it should only be + * called when devicePixelRatio actually changes. + */ +export function setOriginStyle(style: Partial) { + originStyle = new Style({...initialPlainStyle, ...style}); +} type InheritedStyleDefinitions = {[K in keyof DeclaredStyleProperties]: boolean}; @@ -964,7 +982,7 @@ export function computeElementStyle(el: HTMLElement | TextNode) { el.style = createStyle(el.parent!.style, EMPTY_STYLE); } else { const styles = el.getDeclaredStyles(); - const parentStyle = el.parent ? el.parent.style : initialStyle; + const parentStyle = el.parent ? el.parent.style : originStyle; const uaDeclaredStyle = uaDeclaredStyles[el.tagName]; if (uaDeclaredStyle) styles.push(uaDeclaredStyle); if (!el.parent) styles.push(rootDeclaredStyle);