diff --git a/web/source/osk/lengthStyle.ts b/web/source/osk/lengthStyle.ts
new file mode 100644
index 0000000000..732b6f3745
--- /dev/null
+++ b/web/source/osk/lengthStyle.ts
@@ -0,0 +1,75 @@
+namespace com.keyman.osk {
+ export interface LengthStyle {
+ val: number,
+ absolute: boolean
+ };
+
+ export class ParsedLengthStyle implements LengthStyle {
+ public readonly val: number;
+ public readonly absolute: boolean;
+
+ public constructor(style: LengthStyle | string) {
+ if(typeof style == 'string') {
+ const parsed = ParsedLengthStyle.parseLengthStyle(style);
+ this.val = parsed.val;
+ this.absolute = parsed.absolute;
+ } else {
+ this.val = style.val;
+ this.absolute = style.absolute;
+ }
+ }
+
+ public get styleString(): string {
+ if(this.absolute) {
+ return this.val + 'px';
+ } else {
+ return this.absolute + '%';
+ }
+ }
+
+ public scaledBy(scalar: number): ParsedLengthStyle {
+ return new ParsedLengthStyle({
+ val: scalar * this.val,
+ absolute: this.absolute
+ });
+ }
+
+ public static inPixels(val: number): ParsedLengthStyle {
+ return new ParsedLengthStyle({val: val, absolute: true});
+ }
+
+ public static inPercent(val: number): ParsedLengthStyle {
+ return new ParsedLengthStyle({val: val/100, absolute: false});
+ }
+
+ public static forScalar(val: number): ParsedLengthStyle {
+ return new ParsedLengthStyle({val: val, absolute: false});
+ }
+
+ private static parseLengthStyle(spec: string): {val: number, absolute: boolean} {
+ var val: number;
+
+ if(spec.indexOf('px') != -1) {
+ val = parseFloat(spec.substr(0, spec.indexOf('px')));
+ return {val: val, absolute: true};
+ } else if(spec.indexOf('pt') != -1) {
+ // 16 px ~= 12 pt.
+ // Reference: https://kyleschaeffer.com/css-font-size-em-vs-px-vs-pt-vs-percent
+ val = parseFloat(spec.substr(0, spec.indexOf('pt')));
+ return {val: (4 * val / 3), absolute: true};
+ } else if(spec.indexOf('%') != -1) {
+ val = parseFloat(spec.substr(0, spec.indexOf('%')));
+ return {val: val/100, absolute: false};
+ } else if(!isNaN(val = Number(spec))) {
+ // Note: this one is NOT natively handled by browsers!
+ // We'll treat it as if it were 'pt', since that's likely the user's
+ // most familiar font size unit.
+ return {val: (4 * val / 3), absolute: true};
+ } else {
+ // Cannot parse.
+ console.error("Could not properly parse specified length style info: '" + spec + "'.");
+ return null;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/web/source/osk/oskKey.ts b/web/source/osk/oskKey.ts
index 71cad5132e..8efb933a12 100644
--- a/web/source/osk/oskKey.ts
+++ b/web/source/osk/oskKey.ts
@@ -346,7 +346,7 @@ namespace com.keyman.osk {
// approximation for that. `this.kbdDiv` is the element controlling the OSK's width, set in px.
// This is an approximation that tends to be a bit too large, but it's close enough to be useful.
- return Math.floor(vkbd.width * this.spec['widthpc'] / 100);
+ return Math.floor(vkbd.computedWidth * this.spec['widthpc'] / 100);
}
}
diff --git a/web/source/osk/oskLayer.ts b/web/source/osk/oskLayer.ts
index fe21da8171..b81b44a9b0 100644
--- a/web/source/osk/oskLayer.ts
+++ b/web/source/osk/oskLayer.ts
@@ -90,13 +90,16 @@ namespace com.keyman.osk {
public refreshLayout(vkbd: VisualKeyboard, paddedHeight: number, trueHeight: number) {
// Check the heights of each row, in case different layers have different row counts.
let nRows = this.rows.length;
- this.element.style.height=(paddedHeight)+'px';
-
let rowHeight = Math.floor(trueHeight/(nRows == 0 ? 1 : nRows));
- if(vkbd.device.OS == 'Android' && 'devicePixelRatio' in window) {
- this.element.style.height = this.element.style.maxHeight = paddedHeight + 'px';
- rowHeight /= window.devicePixelRatio;
+ if(vkbd.device.touchable) {
+ this.element.style.height=(paddedHeight)+'px';
+
+
+ if(vkbd.device.OS == 'Android' && 'devicePixelRatio' in window) {
+ this.element.style.height = this.element.style.maxHeight = paddedHeight + 'px';
+ rowHeight /= window.devicePixelRatio;
+ }
}
// Sets the layers to the correct height
@@ -105,8 +108,10 @@ namespace com.keyman.osk {
for(let nRow=0; nRow
+
namespace com.keyman.osk {
export function getFontSizeStyle(e: HTMLElement|string): {val: number, absolute: boolean} {
- var val: number;
var fs: string;
if(typeof e == 'string') {
@@ -13,28 +14,10 @@ namespace com.keyman.osk {
}
if(fs.indexOf('em') != -1) {
- val = parseFloat(fs.substr(0, fs.indexOf('em')));
- return {val: val, absolute: false};
- } else if(fs.indexOf('px') != -1) {
- val = parseFloat(fs.substr(0, fs.indexOf('px')));
- return {val: val, absolute: true};
- } else if(fs.indexOf('pt') != -1) {
- // 16 px ~= 12 pt.
- // Reference: https://kyleschaeffer.com/css-font-size-em-vs-px-vs-pt-vs-percent
- val = parseFloat(fs.substr(0, fs.indexOf('pt')));
- return {val: (4 * val / 3), absolute: true};
- } else if(fs.indexOf('%') != -1) {
- val = parseFloat(fs.substr(0, fs.indexOf('%')));
- return {val: val/100, absolute: false};
- } else if(!isNaN(val = Number(fs))) {
- // Note: this one is NOT natively handled by browsers!
- // We'll treat it as if it were 'pt', since that's likely the user's
- // most familiar font size unit.
- return {val: (4 * val / 3), absolute: true};
+ const val = parseFloat(fs);
+ return ParsedLengthStyle.forScalar(val);
} else {
- // Cannot parse.
- console.error("Could not properly parse specified fontsize info: '" + fs + "'.");
- return null;
+ return new ParsedLengthStyle(fs);
}
}
}
\ No newline at end of file
diff --git a/web/source/osk/visualKeyboard.ts b/web/source/osk/visualKeyboard.ts
index 30073460f5..988b4f5342 100644
--- a/web/source/osk/visualKeyboard.ts
+++ b/web/source/osk/visualKeyboard.ts
@@ -1,4 +1,5 @@
///
+///
///
///
///
@@ -41,13 +42,13 @@ namespace com.keyman.osk {
* The configured width for this VisualKeyboard. May be `undefined` or `null`
* to allow automatic width scaling.
*/
- private _width: number;
+ private _width: ParsedLengthStyle;
/**
* The configured height for this VisualKeyboard. May be `undefined` or `null`
* to allow automatic height scaling.
*/
- private _height: number;
+ private _height: ParsedLengthStyle;
/**
* The computed width for this VisualKeyboard. May be null if auto sizing
@@ -205,7 +206,7 @@ namespace com.keyman.osk {
* The configured width for this VisualKeyboard. May be `undefined` or `null`
* to allow automatic width scaling.
*/
- get width(): number {
+ get width(): LengthStyle {
return this._width;
}
@@ -213,7 +214,7 @@ namespace com.keyman.osk {
* The configured height for this VisualKeyboard. May be `undefined` or `null`
* to allow automatic height scaling.
*/
- get height(): number {
+ get height(): LengthStyle {
return this._height;
}
@@ -222,6 +223,12 @@ namespace com.keyman.osk {
* is allowed and the VisualKeyboard is not currently in the DOM hierarchy.
*/
get computedWidth(): number {
+ if(!this.kbdDiv) {
+ // Intermediate state - can be called during VisualKeyboard's constructor, before
+ // computedHeight can receive a value.
+ return undefined;
+ }
+
// Computed during layout operations; allows caching instead of continuous recomputation.
if(this.needsLayout) {
let osk = com.keyman.singleton.osk;
@@ -235,6 +242,12 @@ namespace com.keyman.osk {
* is allowed and the VisualKeyboard is not currently in the DOM hierarchy.
*/
get computedHeight(): number {
+ if(!this.kbdDiv) {
+ // Intermediate state - can be called during VisualKeyboard's constructor, before
+ // computedHeight can receive a value.
+ return undefined;
+ }
+
// Computed during layout operations; allows caching instead of continuous recomputation.
if(this.needsLayout) {
let osk = com.keyman.singleton.osk;
@@ -250,18 +263,27 @@ namespace com.keyman.osk {
* @param pending Set to `true` if called during a resizing interaction
*/
public setSize(width?: number, height?: number, pending?: boolean) {
- this._width = width;
- this._height = height;
+ this._width = ParsedLengthStyle.inPixels(width);
+ this._height = ParsedLengthStyle.inPixels(height);
if(!pending && this.kbdDiv) {
- this.kbdDiv.style.width = width ? this._width+'px' : '';
- this.kbdDiv.style.height = height ? this._height+'px' : '';
- this.kbdDiv.style.fontSize = height ? (this._height/8)+'px' : '';
+ this.kbdDiv.style.width = width ? this._width.styleString : '';
+ this.kbdDiv.style.height = height ? this._height.styleString : '';
+ this.kbdDiv.style.fontSize = height ? (this._height.scaledBy(1/8).styleString) : '';
+
+ let osk = com.keyman.singleton.osk;
+ this.refreshLayout(osk.getKeyboardHeight());
+ } else {
+ this.needsLayout = true;
}
}
+ public setNeedsLayout(): void {
+ this.needsLayout = true;
+ }
+
public defaultFontSize(): number {
- return this.height ? this.height / 8 : undefined;
+ return this.computedHeight ? this.computedHeight / 8 : undefined;
}
/**
@@ -269,8 +291,8 @@ namespace com.keyman.osk {
* size actually used by the visual keyboard.
*/
public refit() {
- this._width=this.kbdDiv.offsetWidth;
- this._height=this.kbdDiv.offsetHeight;
+ this._width = ParsedLengthStyle.inPixels(this.kbdDiv.offsetWidth);
+ this._height = ParsedLengthStyle.inPixels(this.kbdDiv.offsetHeight);
}
/**
@@ -550,7 +572,6 @@ namespace com.keyman.osk {
*
**/
moveOver: (e: TouchEvent) => void = function(this: VisualKeyboard, e: TouchEvent) {
- let keyman = com.keyman.singleton;
e.preventDefault();
e.cancelBubble=true;
@@ -603,14 +624,13 @@ namespace com.keyman.osk {
this.currentTarget = key1;
// _Box has (most of) the useful client values.
- let _Box = this.kbdDiv.parentElement ? this.kbdDiv.parentElement : keyman.osk._Box;
let height = this.kbdDiv.offsetHeight;
// We need to adjust the offset properties by any offsets related to the active banner.
// Determine the y-threshold at which touch-cancellation should automatically occur.
let rowCount = this.currentLayer.rows.length;
let yBufferThreshold = (0.333 * height / rowCount); // Allows vertical movement by 1/3 the height of a row.
- var yMin = (this.kbdDiv && _Box) ? Math.max(5, this.kbdDiv.offsetTop - yBufferThreshold) : 5;
+ var yMin = Math.max(5, this.kbdDiv.offsetTop - yBufferThreshold);
if(key0 && e.touches[0].pageY < yMin) {
this.highlightKey(key0,false);
this.showKeyTip(null,false);
@@ -1097,26 +1117,23 @@ namespace com.keyman.osk {
let b = this.kbdDiv.firstChild as HTMLElement;
let gs = this.kbdDiv.style;
let bs=b.style;
- // Sets the layer group to the correct height.
- gs.height=gs.maxHeight=paddedHeight+'px';
- bs.fontSize=fs+'em';
-
- for(const layerId in this.layerGroup.layers) {
- const layer = this.layerGroup.layers[layerId];
- layer.refreshLayout(this, paddedHeight, height);
+ if(device.touchable) {
+ // Sets the layer group to the correct height.
+ gs.height = gs.maxHeight = paddedHeight + 'px';
}
+ bs.fontSize=fs+'em';
// NEW CODE ------
// Step 1: have the necessary conditions been met?
- const fixedSize = this.width && this.height;
+ const fixedSize = this.width && this.height && this.width.absolute && this.height.absolute;
const computedStyle = getComputedStyle(this.kbdDiv);
const isInDOM = computedStyle.height != '' && computedStyle.height != 'auto';
// Step 2: determine basic layout geometry
if(fixedSize) {
- this._computedWidth = this.width;
- this._computedHeight = this.height;
+ this._computedWidth = this.width.val;
+ this._computedHeight = this.height.val;
} else if(isInDOM) {
this._computedWidth = parseInt(computedStyle.width, 10);
if(!this._computedWidth) {
@@ -1138,6 +1155,14 @@ namespace com.keyman.osk {
// - rescale key text
this.needsLayout = false;
+
+ // END NEW CODE -----------
+
+ // Needs the refreshed layout info to work correctly.
+ for(const layerId in this.layerGroup.layers) {
+ const layer = this.layerGroup.layers[layerId];
+ layer.refreshLayout(this, paddedHeight, height);
+ }
}
/*private*/ computedAdjustedOskHeight(allottedHeight: number): number {