Merge pull request #2045 from keymanapp/web-1460-osk-default-keycaps

[Web] Removes default key cap text behavior
This commit is contained in:
Joshua Horton 2019-09-06 12:20:32 +07:00 • committed by GitHub
commit a2bf4d53ee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 171 additions and 8 deletions

View file

@ -3,6 +3,9 @@
## 13.0 alpha
* Start version 13.0
## 2019-09-06 12.0.81 beta
* For keyboards compiled with Developer 12.0 onward, default key caps will no longer be provided. (#2045)
## 2019-09-04 12.0.80 beta
* Fixed issue with 'pt' and unitless font size specifications for keyboard layouts (#2033)
* Slightly enhanced error logging for internal events (#2037)

View file

@ -3,6 +3,8 @@
Copyright 2017 SIL International
***/
///<reference path="../utils/version.ts"/>
namespace com.keyman.osk {
let Codes = com.keyman.text.Codes;
@ -109,12 +111,13 @@ namespace com.keyman.osk {
/**
* Build a default layout for keyboards with no explicit layout
*
* @param {Object} PVK keyboard object (as loaded)
* @param {number} kbdBitmask keyboard modifier bitmask
* @param {Object} PVK keyboard object (as loaded)
* @param {Object} kbdDevVersion object representing the version of Developer that compiled the keyboard
* @param {number} kbdBitmask keyboard modifier bitmask
* @param {string} formFactor
* @return {Object}
*/
static buildDefaultLayout(PVK, kbdBitmask: number, formFactor: string): LayoutFormFactor {
static buildDefaultLayout(PVK, kbdDevVersion: utils.Version, kbdBitmask: number, formFactor: string): LayoutFormFactor {
let keyman = com.keyman.singleton;
let util = keyman.util;
@ -248,9 +251,9 @@ namespace com.keyman.osk {
if(kx >= 0 && kx < layerSpec.length) key['text']=layerSpec[kx];
}
// Fall back to US English keycap text as default for the base two layers if not otherwise defined.
// (Any 'ghost' keys must be explicitly defined in layout for these layers.)
if(isDefault) {
// Legacy (pre 12.0) behavior: fall back to US English keycap text as default for the base two layers
// if a key cap is not otherwise defined. (Any intentional 'ghost' keys must be explicitly defined.)
if(isDefault && kbdDevVersion.precedes(utils.Version.NO_DEFAULT_KEYCAPS)) {
if(key['id'] != 'K_SPACE' && kx+65 * isShift < Layouts.dfltText.length && key['text'] !== null) {
key['text'] = key['text'] || Layouts.dfltText[kx+65*isShift];
}

View file

@ -1,4 +1,5 @@
/// <reference path="activeLayout.ts" />
/// <reference path="../utils/version.ts" />
namespace com.keyman.osk {
let Codes = com.keyman.text.Codes;
@ -591,7 +592,8 @@ namespace com.keyman.osk {
// Build a layout using the default for the device
if(typeof layout != 'object' || layout == null) {
layout=Layouts.buildDefaultLayout(PVK,kbdBitmask, formFactor);
let kbdDevVersion = new utils.Version(activeKeyboard['KVER']);
layout=Layouts.buildDefaultLayout(PVK, kbdDevVersion, kbdBitmask, formFactor);
}
// Create the collection of HTML elements from the device-dependent layout object
@ -2093,7 +2095,8 @@ namespace com.keyman.osk {
// Else get a default layout for the device for this keyboard
if(layout == null && PVK != null) {
layout=Layouts.buildDefaultLayout(PVK,keymanweb.keyboardManager.getKeyboardModifierBitmask(PKbd),formFactor);
let kbdDevVersion = new utils.Version(PKbd['KVER']);
layout=Layouts.buildDefaultLayout(PVK, kbdDevVersion, keymanweb.keyboardManager.getKeyboardModifierBitmask(PKbd),formFactor);
}
// Cannot create an OSK if no layout defined, just return empty DIV

101
web/source/utils/version.ts Normal file
View file

@ -0,0 +1,101 @@
namespace com.keyman.utils {
// Dotted-decimal version
export class Version {
// Represents a default version value for keyboards compiled before this was compiled into keyboards.
// The exact version is unknown at this point, but the value is "good enough" for what we need.
public static readonly DEVELOPER_VERSION_FALLBACK = new Version([9, 0, 0]);
// For 12.0, the old default behavior of adding missing keycaps to the default layers was removed,
// as it results in unexpected, bug-like behavior for keyboard designers when it is unwanted.
public static readonly NO_DEFAULT_KEYCAPS = new Version([12, 0]);
private readonly components: number[]
/**
* Parses version information, preparing it for use in comparisons.
* @param text Either a string representing a version number (ex: "9.0.0") or an array representing
* its components (ex: [9, 0, 0]).
*/
constructor(text: String | number[]) {
// If a keyboard doesn't specify a version, use the DEVELOPER_VERSION_FALLBACK values.
if(text === undefined || text === null) {
this.components = [].concat(Version.DEVELOPER_VERSION_FALLBACK.components);
return;
}
if(Array.isArray(text)) {
let components = text as number[];
if(components.length < 2) {
throw new Error("Version string must have at least a major and minor component!");
} else {
this.components = [].concat(components);
return;
}
}
// else, standard constructor path.
let parts = text.split('.');
let componentArray: number[] = [];
if(parts.length < 2) {
throw new Error("Version string must have at least a major and minor component!");
}
for(let i=0; i < parts.length; i++) {
let value = parseInt(parts[i], 10);
if(isNaN(value)) {
throw new Error("Version string components must be numerical!");
}
componentArray.push(value);
}
this.components = componentArray;
}
get major(): number {
return this.components[0];
}
get minor(): number {
return this.components[1];
}
toString(): string {
return this.components.join('.');
}
equals(other: Version): boolean {
return this.compareTo(other) == 0;
}
precedes(other: Version): boolean {
return this.compareTo(other) < 0;
}
compareTo(other: Version): number {
// If the version info depth differs, we need a flag to indicate which instance is shorter.
var isShorter: boolean = this.components.length < other.components.length;
var maxDepth: number = (this.components.length < other.components.length) ? this.components.length : other.components.length;
var i: number;
for(i = 0; i < maxDepth; i++) {
let delta = this.components[i] - other.components[i];
if(delta != 0) {
return delta;
}
}
var longList = isShorter ? other.components : this.components;
do {
if(longList[i] > 0) {
return isShorter ? -1 : 1;
}
i++;
} while (i < longList.length);
// Equal.
return 0;
}
}
}

View file

@ -0,0 +1,53 @@
var assert = chai.assert;
describe('Version Logic', function() {
this.timeout(kmwconfig.timeouts.standard);
before(function(done) {
this.timeout(kmwconfig.timeouts.scriptLoad);
setupKMW(null, done, kmwconfig.timeouts.scriptLoad);
});
after(function() {
teardownKMW();
});
it('Should provide a default, fallback value when nothing is specified', function() {
var fallback = new com.keyman.utils.Version(undefined);
assert.isTrue(fallback.equals(com.keyman.utils.Version.DEVELOPER_VERSION_FALLBACK));
});
it('Should properly process a simple major.minor version string.', function() {
var version = new com.keyman.utils.Version("1.2");
assert.equal(version.major, 1);
assert.equal(version.minor, 2);
});
it('Should handle long/deep version specifications.', function() {
var version = new com.keyman.utils.Version("1.2.3.4.5.6");
assert.equal(version.components.length, 6);
assert.equal(version.major, 1);
assert.equal(version.minor, 2);
});
it('Should properly compare two versions.', function() {
var v9_0_1 = new com.keyman.utils.Version("9.0.1");
var v9_1_0 = new com.keyman.utils.Version("9.1.0");
var v10_0 = new com.keyman.utils.Version("10.0");
var v10_0_0 = new com.keyman.utils.Version("10.0.0");
// "Precede" checks
assert.equal(v9_0_1.compareTo(v9_1_0), -1);
assert.equal(v9_1_0.compareTo(v10_0_0), -1);
assert.equal(v9_0_1.compareTo(v10_0_0), -1);
// Equality checks
assert.equal(v9_0_1.compareTo(v9_0_1), 0);
// Tests equal versions where one omits the build number.
assert.equal( v10_0.compareTo(v10_0_0), 0);
// Ensures the first "precede" check's return is flipped when the order's flipped.
assert.equal(v9_1_0.compareTo(v9_0_1), 1);
});
});