Skip to content

Commit

Permalink
fix(web): fixes kbd-help documentation for help.keyman.com
Browse files Browse the repository at this point in the history
  • Loading branch information
jahorton committed May 17, 2024
1 parent aa68c17 commit 74d6316
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 35 deletions.
2 changes: 1 addition & 1 deletion common/web/utils/src/managedPromise.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
type ResolveSignature<Type> = (value: Type | PromiseLike<Type>) => void;
type RejectSignature = (reason?: any) => void;

export default class ManagedPromise<Type> {
export default class ManagedPromise<Type = void> {
/**
* Calling this function will fulfill the Promise represented by this class.
*/
Expand Down
55 changes: 29 additions & 26 deletions web/src/engine/dom-utils/src/stylesheets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@ type FontFamilyStyleMap = {[family: string]: HTMLStyleElement};

export class StylesheetManager {
private fontStyleDefinitions: { [os: string]: FontFamilyStyleMap} = {};
private linkedSheets: HTMLStyleElement[] = [];
private linkedSheets: {
sheet: HTMLStyleElement,
load: ManagedPromise<void>
}[] = [];
private fontPromises: Promise<FontFace>[] = [];
private doCacheBusting: boolean;

public readonly linkNode: Node;

public get sheets(): readonly HTMLStyleElement[] {
return this.linkedSheets;
return this.linkedSheets.map((entry) => entry.sheet);
}

public constructor(linkNode?: Node, doCacheBusting?: boolean) {
Expand All @@ -28,12 +31,25 @@ export class StylesheetManager {
this.doCacheBusting = doCacheBusting || false;
}

linkStylesheet(sheet: HTMLStyleElement) {
linkStylesheet(sheet: HTMLStyleElement | HTMLLinkElement) {
if(!(sheet instanceof HTMLLinkElement) && !sheet.innerHTML) {
return;
}

this.linkedSheets.push(sheet);
const promise = new ManagedPromise();
if(sheet instanceof HTMLLinkElement) {
sheet.onload = () => promise.resolve();
} else {
// If it's an inline sheet, it's essentially already loaded.
// The microtask delay this induces (for type compat) also
// gives the browser time to apply the inlined-style.
promise.resolve();
}

this.linkedSheets.push({
sheet: sheet,
load: promise
});
this.linkNode.appendChild(sheet);
}

Expand All @@ -42,25 +58,7 @@ export class StylesheetManager {
* Any change to the set of linked sheets after the initial call will be ignored.
*/
async allLoadedPromise(): Promise<void> {
const promises: Promise<void>[] = [];

for(const sheetElem of this.linkedSheets) {
// Based on https://stackoverflow.com/a/21147238
if(sheetElem.sheet?.cssRules) {
promises.push(Promise.resolve());
} else if(sheetElem.innerHTML) {
// NOT at the StackOverflow link, but something I found experimentally.
// Needed for live-constructed sheets with no corresponding file.
promises.push(Promise.resolve());
} else {
const promise = new ManagedPromise<void>();
sheetElem.addEventListener('load', () => promise.resolve());
sheetElem.addEventListener('error', () => promise.reject());
promises.push(promise.corePromise);
}
}

const allPromises = promises.concat(this.fontPromises as Promise<any>[]);
const allPromises = this.linkedSheets.map((entry) => entry.load.corePromise);
if(Promise.allSettled) {
// allSettled - Chrome 76 / Safari 13
// Delays for settling (either then OR catch) for ALL promises.
Expand Down Expand Up @@ -222,9 +220,11 @@ export class StylesheetManager {
}

public unlink(stylesheet: HTMLStyleElement) {
const index = this.linkedSheets.indexOf(stylesheet);
const index = this.linkedSheets.findIndex((entry) => entry.sheet == stylesheet);
if(index > -1) {
this.linkedSheets.splice(index, 1);
const tuple = this.linkedSheets.splice(index, 1);
// Ensure we don't leave `await`s that were waiting on the stylesheet hanging.
tuple[0].load.resolve();
stylesheet.parentNode.removeChild(stylesheet);
return true;
}
Expand All @@ -233,10 +233,13 @@ export class StylesheetManager {
}

public unlinkAll() {
for(let sheet of this.linkedSheets) {
for(let tuple of this.linkedSheets) {
const sheet = tuple.sheet;
if(sheet.parentNode) {
sheet.parentNode.removeChild(sheet);
}
// Clear out any lingering `await`s.
tuple.load.resolve();
}

this.linkedSheets.splice(0, this.linkedSheets.length);
Expand Down
5 changes: 5 additions & 0 deletions web/src/engine/osk/src/keyboard-layout/oskLayerGroup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,11 @@ export default class OSKLayerGroup {
}

public refreshLayout(layoutParams: LayerLayoutParams) {
if(isNaN(layoutParams.keyboardWidth) || isNaN(layoutParams.keyboardHeight)) {
// We're not in the DOM yet; we'll refresh properly once that changes.
// Can be reached if the layerId is changed before the keyboard enters the DOM.
return;
}
// Set layer-group copies of relevant computed-size values; they are used by nearest-key
// detection.
this.computedWidth = layoutParams.keyboardWidth;
Expand Down
19 changes: 11 additions & 8 deletions web/src/engine/osk/src/visualKeyboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1267,7 +1267,7 @@ export default class VisualKeyboard extends EventEmitter<EventMap> implements Ke
// Step 1: have the necessary conditions been met?
const fixedSize = this.width && this.height;
const computedStyle = getComputedStyle(this.kbdDiv);
const groupStyle = getComputedStyle(this.kbdDiv.firstElementChild);
const groupStyle = getComputedStyle(this.layerGroup.element);

const isInDOM = computedStyle.height != '' && computedStyle.height != 'auto';

Expand Down Expand Up @@ -1372,17 +1372,15 @@ export default class VisualKeyboard extends EventEmitter<EventMap> implements Ke
var activeKeyboard = this.layoutKeyboard;
var activeStub = this.layoutKeyboardProperties;

// Do not do anything if a null stub
if (activeStub == null) {
return;
}

// First remove any existing keyboard style sheet
if (this.styleSheet && this.styleSheet.parentNode) {
this.styleSheet.parentNode.removeChild(this.styleSheet);
}

var kfd = activeStub.textFont, ofd = activeStub.oskFont;
// For help.keyman.com, sometimes we aren't given a stub for the keyboard.
// We can't get the keyboard's fonts correct in that case, but we can
// at least proceed safely.
var kfd = activeStub?.textFont, ofd = activeStub?.oskFont;

// Add and define style sheets for embedded fonts if necessary (each font-face style will only be added once)
this.styleSheetManager.addStyleSheetForFont(kfd, this.fontRootPath, this.device.OS);
Expand Down Expand Up @@ -1497,7 +1495,12 @@ export default class VisualKeyboard extends EventEmitter<EventMap> implements Ke
isStatic: true,
topContainer: null,
pathConfig: pathConfig,
styleSheetManager: null
styleSheetManager: null,
specialFont: {
family: 'SpecialOSK',
files: [`${pathConfig.resources}/osk/keymanweb-osk.ttf`],
path: '' // Not actually used.
}
});

kbdObj.layerGroup.element.className = kbdObj.kbdDiv.className; // may contain multiple classes
Expand Down

0 comments on commit 74d6316

Please sign in to comment.