mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-25 01:37:42 +00:00
Merge pull request #5661 from keymanapp/refactor/web/osk-activation-and-visibility
refactor(web): osk activation and visibility modeling ✂️
This commit is contained in:
commit
b726ddac41
9 changed files with 516 additions and 387 deletions
|
|
@ -596,7 +596,7 @@ namespace com.keyman.dom {
|
|||
prevTarget.hideCaret();
|
||||
}
|
||||
|
||||
this.keyman.domManager.activeElement=target;
|
||||
this.keyman.domManager.activeElement = target;
|
||||
// The issue here is that touching a DIV does not actually set the focus for iOS, even when enabled to accept focus (by setting tabIndex=0)
|
||||
// We must explicitly set the focus in order to remove focus from any non-KMW input
|
||||
target.focus(); //Android native browsers may not like this, but it is needed for Chrome, Safari
|
||||
|
|
@ -718,7 +718,7 @@ namespace com.keyman.dom {
|
|||
* Close OSK and remove simulated caret on losing focus
|
||||
*/
|
||||
cancelInput(): void {
|
||||
this.keyman.domManager.activeElement=null;
|
||||
this.keyman.domManager.activeElement = null;
|
||||
this.keyman.domManager.lastActiveElement = null;
|
||||
this.keyman.osk.hideNow();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -458,7 +458,7 @@ namespace com.keyman.dom {
|
|||
this.activeElement = null;
|
||||
}
|
||||
this.lastActiveElement = null;
|
||||
this.keyman.osk._Hide(false);
|
||||
this.keyman.osk.startHide(false);
|
||||
}
|
||||
|
||||
return;
|
||||
|
|
@ -1257,8 +1257,14 @@ namespace com.keyman.dom {
|
|||
set lastActiveElement(Pelem: HTMLElement) {
|
||||
DOMEventHandlers.states._lastActiveElement = Pelem;
|
||||
|
||||
if(this.lastActiveElement == null && this.activeElement == null) {
|
||||
this.keyman.osk.hideNow(); // originally from a different one, seemed to serve the same role?
|
||||
const osk = this.keyman.osk;
|
||||
if(osk) {
|
||||
if(this.lastActiveElement == null && this.activeElement == null) {
|
||||
// Assigning to the property does have side-effects.
|
||||
// If the property is already unset, it's best to not unset it again.
|
||||
osk.activeTarget = null;
|
||||
this.keyman.osk.hideNow(); // originally from a different one, seemed to serve the same role?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1280,28 +1286,13 @@ namespace com.keyman.dom {
|
|||
|
||||
// Hide the OSK when the control is blurred, unless the UI is being temporarily selected
|
||||
const osk = this.keyman.osk;
|
||||
const device = this.keyman.util.device;
|
||||
if(this.keyman.osk) {
|
||||
if(!Pelem) {
|
||||
if(this.keyman.osk && !isActivating) {
|
||||
this.keyman.osk._Hide(false);
|
||||
}
|
||||
} else {
|
||||
// Force display of OSK for touch input device, or if a CJK keyboard, to ensure visibility of pick list
|
||||
if(device.touchable) {
|
||||
osk._Enabled = true;
|
||||
osk._Show();
|
||||
} else {
|
||||
// Conditionally show the OSK when control receives the focus
|
||||
if(this.keyman.isCJK()) {
|
||||
osk._Enabled = true;
|
||||
}
|
||||
if(osk._Enabled) {
|
||||
osk._Show();
|
||||
} else {
|
||||
osk._Hide(false);
|
||||
}
|
||||
}
|
||||
// const device = this.keyman.util.device;
|
||||
|
||||
if(osk) {
|
||||
const target = Pelem?._kmwAttachment?.interface || null;
|
||||
if(osk && (target || !isActivating)) {
|
||||
// Do not unset the field if the UI is activated.
|
||||
osk.activeTarget = target;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1829,7 +1820,7 @@ namespace com.keyman.dom {
|
|||
this.keyman.ui['initialize']();
|
||||
// Display the OSK (again) if enabled, in order to set its position correctly after
|
||||
// adding the UI to the page
|
||||
this.keyman.osk._Show();
|
||||
this.keyman.osk.present();
|
||||
} else if(this.keyman.isEmbedded) {
|
||||
// UI modules aren't utilized in embedded mode. There's nothing to init, so we simply
|
||||
// return instead of waiting for a UI module that will never come.
|
||||
|
|
|
|||
|
|
@ -577,7 +577,7 @@ namespace com.keyman.keyboards {
|
|||
|
||||
// Hide OSK and do not update keyboard list if using internal keyboard (desktops)
|
||||
if(PInternalName == '') {
|
||||
osk._Hide(false);
|
||||
osk.startHide(false);
|
||||
|
||||
if(!this.keymanweb.isEmbedded) {
|
||||
util.wait(false);
|
||||
|
|
@ -614,7 +614,7 @@ namespace com.keyman.keyboards {
|
|||
&& ((this.keyboardStubs[Ln]['KLC'] == PLgCode) || (PLgCode == '---'))) {
|
||||
// Force OSK display for CJK keyboards (keyboards using a pick list)
|
||||
if(this.isCJK(this.keyboardStubs[Ln]) || util.device.touchable) {
|
||||
osk._Enabled = true;
|
||||
osk.displayIfActive = true;
|
||||
}
|
||||
|
||||
// Create a script to load from the server - when it finishes loading, it will register itself,
|
||||
|
|
@ -624,7 +624,7 @@ namespace com.keyman.keyboards {
|
|||
if(!this.keyboardStubs[Ln].asyncLoader) {
|
||||
// Always (temporarily) hide the OSK when loading a new keyboard, to ensure that a failure to load doesn't leave the current OSK displayed
|
||||
if(osk) {
|
||||
osk._Hide(false);
|
||||
osk.startHide(false);
|
||||
}
|
||||
|
||||
var loadingStub = this.keyboardStubs[Ln];
|
||||
|
|
|
|||
|
|
@ -32,6 +32,11 @@ namespace com.keyman.osk {
|
|||
super(modeledDevice);
|
||||
|
||||
document.body.appendChild(this._Box);
|
||||
|
||||
let keymanweb = com.keyman.singleton;
|
||||
if(keymanweb.isEmbedded) {
|
||||
this.activationMode == ActivationMode.manual;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -66,8 +71,8 @@ namespace com.keyman.osk {
|
|||
this._Box.onmouseover = this._VKbdMouseOver;
|
||||
this._Box.onmouseout = this._VKbdMouseOut;
|
||||
|
||||
if(this._Enabled) {
|
||||
this._Show();
|
||||
if(this.displayIfActive) {
|
||||
this.present();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -202,30 +207,8 @@ namespace com.keyman.osk {
|
|||
return; // I3363 (Build 301)
|
||||
}
|
||||
|
||||
/**
|
||||
* Display KMW OSK at specified position (returns nothing)
|
||||
*
|
||||
* @param {number=} Px x-coordinate for OSK rectangle
|
||||
* @param {number=} Py y-coordinate for OSK rectangle
|
||||
*/
|
||||
_Show(Px?: number, Py?: number) {
|
||||
// Do not try to display OSK if no active element
|
||||
if(!this.activeTarget) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.makeVisible();
|
||||
|
||||
var Ls = this._Box.style;
|
||||
|
||||
/* In case it's still '0' from a hide() operation.
|
||||
* Happens when _Show is called before the transitionend events are processed,
|
||||
* which can happen in bulk-rendering contexts.
|
||||
*
|
||||
* (Opacity is only modified when device.touchable = true, though a couple of extra
|
||||
* conditions may apply.)
|
||||
*/
|
||||
Ls.opacity='1';
|
||||
protected setDisplayPositioning() {
|
||||
let Ls = this._Box.style;
|
||||
|
||||
// The following code will always be executed except for externally created OSK such as EuroLatin
|
||||
if(this.vkbd) {
|
||||
|
|
@ -233,86 +216,6 @@ namespace com.keyman.osk {
|
|||
Ls.left=Ls.bottom='0px';
|
||||
Ls.border='none';
|
||||
Ls.borderTop='1px solid gray';
|
||||
|
||||
this._Enabled=true;
|
||||
this._Visible=true; // I3363 (Build 301)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide Keymanweb On Screen Keyboard
|
||||
*
|
||||
* @param {boolean} hiddenByUser Distinguish between hiding on loss of focus and explicit hiding by user
|
||||
*/
|
||||
_Hide(hiddenByUser: boolean) {
|
||||
let keymanweb = com.keyman.singleton;
|
||||
let device = keymanweb.util.device;
|
||||
// The test for CJK languages is necessary to prevent a picklist (displayed in the OSK) from being hidden by the user
|
||||
// Once picklist functionality is separated out, this will no longer be needed.
|
||||
// Logic is: execute always if hidden on lost focus, but if requested by user, only if not CJK
|
||||
|
||||
if(keymanweb.isEmbedded) {
|
||||
// We never hide the keyboard in embedded mode
|
||||
return;
|
||||
}
|
||||
|
||||
// Save current size if visible
|
||||
const priorDisplayStyle = this._Box.style.display;
|
||||
this.makeHidden(hiddenByUser);
|
||||
|
||||
if(hiddenByUser) {
|
||||
//osk.loadCookie(); // preserve current offset and userlocated state
|
||||
this._Enabled = ((keymanweb.isCJK() || device.touchable)? true : false); // I3363 (Build 301)
|
||||
} else if(device.formFactor == 'desktop') {
|
||||
//Allow desktop OSK to remain visible on blur if body class set
|
||||
if(document.body.className.indexOf('osk-always-visible') >= 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this._Visible = false;
|
||||
if(this._Box && device.touchable && this._Box.offsetHeight > 0) { // I3363 (Build 301)
|
||||
var os=this._Box.style;
|
||||
// Prevent insta-hide behavior; we want an animated fadeout here.
|
||||
os.display = priorDisplayStyle;
|
||||
|
||||
//Firefox doesn't transition opacity if start delay is explicitly set to 0!
|
||||
if(typeof(os.MozBoxSizing) == 'string') {
|
||||
os.transition='opacity 0.8s linear';
|
||||
} else {
|
||||
os.transition=os.msTransition=os.WebkitTransition='opacity 0.5s linear 0';
|
||||
}
|
||||
|
||||
// Cannot hide the OSK smoothly using a transitioned drop, since for
|
||||
// position:fixed elements transitioning is incompatible with translate3d(),
|
||||
// and also does not work with top, bottom or height styles.
|
||||
// Opacity can be transitioned and is probably the simplest alternative.
|
||||
// We must condition on osk._Visible in case focus has since been moved to another
|
||||
// input (in which case osk._Visible will be non-zero)
|
||||
window.setTimeout(function(this: AnchoredOSKView) {
|
||||
var os=this._Box.style;
|
||||
if(this._Visible) {
|
||||
// Leave opacity alone and clear transition if another element activated
|
||||
os.transition=os.msTransition=os.MozTransition=os.WebkitTransition='';
|
||||
} else {
|
||||
// Set opacity to zero, should decrease smoothly
|
||||
os.opacity='0';
|
||||
|
||||
// Actually hide the OSK at the end of the transition
|
||||
this._Box.addEventListener('transitionend', this.hideNow, false);
|
||||
this._Box.addEventListener('webkitTransitionEnd', this.hideNow, false);
|
||||
}
|
||||
}.bind(this), 200); // Wait a bit before starting, to allow for moving to another element
|
||||
}
|
||||
|
||||
// Allow UI to execute code when hiding the OSK
|
||||
var p={};
|
||||
p['HiddenByUser']=hiddenByUser;
|
||||
this.doHide(p);
|
||||
|
||||
// If hidden by the UI, be sure to restore the focus
|
||||
if(hiddenByUser) {
|
||||
this.lastActiveTarget.focus();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ namespace com.keyman.osk {
|
|||
|
||||
// OSK positioning fields
|
||||
userPositioned: boolean = false;
|
||||
specifiedPosition: boolean = false;
|
||||
x: number;
|
||||
y: number;
|
||||
noDrag: boolean = false;
|
||||
|
|
@ -88,10 +89,11 @@ namespace com.keyman.osk {
|
|||
this.footerView = null;
|
||||
}
|
||||
|
||||
this.loadCookie();
|
||||
this.setNeedsLayout();
|
||||
|
||||
if(this._Enabled) {
|
||||
this._Show();
|
||||
if(this.displayIfActive) {
|
||||
this.present();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -104,8 +106,8 @@ namespace com.keyman.osk {
|
|||
*/
|
||||
['restorePosition']: (keepDefaultPosition?: boolean) => void = function(this: FloatingOSKView, keepDefaultPosition?: boolean) {
|
||||
let isVisible = this._Visible;
|
||||
if(isVisible) {
|
||||
this.lastActiveTarget.focus(); // I2036 - OSK does not unpin to correct location
|
||||
if(isVisible && this.activeTarget instanceof dom.targets.OutputTarget) {
|
||||
this.activeTarget?.focus(); // I2036 - OSK does not unpin to correct location
|
||||
}
|
||||
|
||||
this.loadCookie();
|
||||
|
|
@ -117,7 +119,7 @@ namespace com.keyman.osk {
|
|||
this.saveCookie();
|
||||
|
||||
if(isVisible) {
|
||||
this._Show();
|
||||
this.present();
|
||||
}
|
||||
|
||||
this.doResizeMove(); //allow the UI to respond to OSK movements
|
||||
|
|
@ -131,7 +133,7 @@ namespace com.keyman.osk {
|
|||
* Description Test if KMW OSK is enabled
|
||||
*/
|
||||
['isEnabled'](): boolean {
|
||||
return this._Enabled;
|
||||
return this.displayIfActive;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -174,7 +176,7 @@ namespace com.keyman.osk {
|
|||
var c = util.loadCookie('KeymanWeb_OnScreenKeyboard');
|
||||
var p = this.getPos();
|
||||
|
||||
c['visible'] = this._Enabled ? 1 : 0;
|
||||
c['visible'] = this.displayIfActive ? 1 : 0;
|
||||
c['userSet'] = this.userPositioned ? 1 : 0;
|
||||
c['left'] = p.left;
|
||||
c['top'] = p.top;
|
||||
|
|
@ -198,7 +200,7 @@ namespace com.keyman.osk {
|
|||
|
||||
var c = util.loadCookie('KeymanWeb_OnScreenKeyboard');
|
||||
|
||||
this._Enabled = util.toNumber(c['visible'], 1) == 1;
|
||||
this.displayIfActive = util.toNumber(c['visible'], 1) == 1;
|
||||
this.userPositioned = util.toNumber(c['userSet'], 0) == 1;
|
||||
this.x = util.toNumber(c['left'],-1);
|
||||
this.y = util.toNumber(c['top'],-1);
|
||||
|
|
@ -471,63 +473,70 @@ namespace com.keyman.osk {
|
|||
}
|
||||
}
|
||||
|
||||
public setDisplayPositioning() {
|
||||
var Ls = this._Box.style;
|
||||
|
||||
Ls.position='absolute'; Ls.display='block'; //Ls.visibility='visible';
|
||||
Ls.left='0px';
|
||||
if(this.specifiedPosition || this.userPositioned) {
|
||||
Ls.left = this.x+'px';
|
||||
Ls.top = this.y+'px';
|
||||
} else {
|
||||
let el: HTMLElement = null;
|
||||
if(this.activeTarget instanceof dom.targets.OutputTarget) {
|
||||
el = this.activeTarget?.getElement();
|
||||
}
|
||||
|
||||
if(this.dfltX) {
|
||||
Ls.left=this.dfltX;
|
||||
} else if(typeof el != 'undefined' && el != null) {
|
||||
Ls.left=dom.Utils.getAbsoluteX(el) + 'px';
|
||||
}
|
||||
|
||||
if(this.dfltY) {
|
||||
Ls.top=this.dfltY;
|
||||
} else if(typeof el != 'undefined' && el != null) {
|
||||
Ls.top=(dom.Utils.getAbsoluteY(el) + el.offsetHeight)+'px';
|
||||
}
|
||||
}
|
||||
|
||||
// Unset the flag, keeping 'specified position' specific to single
|
||||
// presentAtPosition calls.
|
||||
this.specifiedPosition = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display KMW OSK at specified position (returns nothing)
|
||||
*
|
||||
* @param {number=} Px x-coordinate for OSK rectangle
|
||||
* @param {number=} Py y-coordinate for OSK rectangle
|
||||
*/
|
||||
_Show(Px?: number, Py?: number) {
|
||||
// Do not try to display OSK if no active element
|
||||
if(!this.activeTarget) {
|
||||
presentAtPosition(Px?: number, Py?: number) {
|
||||
if(!this.mayShow()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Never display the OSK for desktop browsers unless KMW element is focused, and a keyboard selected
|
||||
if(this.activeKeyboard == null || !this._Enabled) {
|
||||
this.specifiedPosition = Px >= 0 || Py >= 0; //probably never happens, legacy support only
|
||||
if(this.specifiedPosition) {
|
||||
this.x = Px;
|
||||
this.y = Py;
|
||||
}
|
||||
|
||||
// Combines the two paths with set positioning.
|
||||
this.specifiedPosition = this.specifiedPosition || this.userPositioned;
|
||||
|
||||
this.present();
|
||||
}
|
||||
|
||||
present() {
|
||||
if(!this.mayShow()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.makeVisible();
|
||||
|
||||
var Ls = this._Box.style;
|
||||
|
||||
Ls.position='absolute'; Ls.display='block'; //Ls.visibility='visible';
|
||||
Ls.left='0px';
|
||||
this.loadCookie();
|
||||
if(Px >= 0) { //probably never happens, legacy support only
|
||||
Ls.left = Px + 'px'; Ls.top = Py + 'px';
|
||||
} else {
|
||||
if(this.userPositioned) {
|
||||
Ls.left=this.x+'px';
|
||||
Ls.top=this.y+'px';
|
||||
} else {
|
||||
var el = this.activeTarget?.getElement();
|
||||
|
||||
if(this.dfltX) {
|
||||
Ls.left=this.dfltX;
|
||||
} else if(typeof el != 'undefined' && el != null) {
|
||||
Ls.left=dom.Utils.getAbsoluteX(el) + 'px';
|
||||
}
|
||||
|
||||
if(this.dfltY) {
|
||||
Ls.top=this.dfltY;
|
||||
} else if(typeof el != 'undefined' && el != null) {
|
||||
Ls.top=(dom.Utils.getAbsoluteY(el) + el.offsetHeight)+'px';
|
||||
}
|
||||
}
|
||||
}
|
||||
this._Enabled=true;
|
||||
this._Visible=true;
|
||||
|
||||
if(this.vkbd) {
|
||||
this.vkbd.refit();
|
||||
}
|
||||
|
||||
this.saveCookie();
|
||||
|
||||
this.desktopLayout.titleBar.showPin(this.userPositioned);
|
||||
|
||||
super.present();
|
||||
|
||||
// Allow desktop UI to execute code when showing the OSK
|
||||
var Lpos={};
|
||||
Lpos['x']=this._Box.offsetLeft;
|
||||
|
|
@ -536,99 +545,12 @@ namespace com.keyman.osk {
|
|||
this.doShow(Lpos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide Keymanweb On Screen Keyboard
|
||||
*
|
||||
* @param {boolean} hiddenByUser Distinguish between hiding on loss of focus and explicit hiding by user
|
||||
*/
|
||||
_Hide(hiddenByUser: boolean) {
|
||||
let keymanweb = com.keyman.singleton;
|
||||
let device = keymanweb.util.device;
|
||||
// The test for CJK languages is necessary to prevent a picklist (displayed in the OSK) from being hidden by the user
|
||||
// Once picklist functionality is separated out, this will no longer be needed.
|
||||
// Logic is: execute always if hidden on lost focus, but if requested by user, only if not CJK
|
||||
|
||||
// Save current size if visible
|
||||
const priorDisplayStyle = this._Box.style.display;
|
||||
this.makeHidden(hiddenByUser);
|
||||
public startHide(hiddenByUser: boolean) {
|
||||
super.startHide(hiddenByUser);
|
||||
|
||||
if(hiddenByUser) {
|
||||
//osk.loadCookie(); // preserve current offset and userlocated state
|
||||
this._Enabled = ((keymanweb.isCJK() || device.touchable)? true : false); // I3363 (Build 301)
|
||||
this.saveCookie(); // Save current OSK state, size and position (desktop only)
|
||||
} else if(device.formFactor == 'desktop') {
|
||||
//Allow desktop OSK to remain visible on blur if body class set
|
||||
if(document.body.className.indexOf('osk-always-visible') >= 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this._Visible = false;
|
||||
if(this._Box && device.touchable && this._Box.offsetHeight > 0) { // I3363 (Build 301)
|
||||
var os=this._Box.style;
|
||||
// Prevent insta-hide behavior; we want an animated fadeout here.
|
||||
os.display = priorDisplayStyle;
|
||||
|
||||
//Firefox doesn't transition opacity if start delay is explicitly set to 0!
|
||||
if(typeof(os.MozBoxSizing) == 'string') {
|
||||
os.transition='opacity 0.8s linear';
|
||||
} else {
|
||||
os.transition=os.msTransition=os.WebkitTransition='opacity 0.5s linear 0';
|
||||
}
|
||||
|
||||
// Cannot hide the OSK smoothly using a transitioned drop, since for
|
||||
// position:fixed elements transitioning is incompatible with translate3d(),
|
||||
// and also does not work with top, bottom or height styles.
|
||||
// Opacity can be transitioned and is probably the simplest alternative.
|
||||
// We must condition on osk._Visible in case focus has since been moved to another
|
||||
// input (in which case osk._Visible will be non-zero)
|
||||
window.setTimeout(function(this: AnchoredOSKView) {
|
||||
var os=this._Box.style;
|
||||
if(this._Visible) {
|
||||
// Leave opacity alone and clear transition if another element activated
|
||||
os.transition=os.msTransition=os.MozTransition=os.WebkitTransition='';
|
||||
} else {
|
||||
// Set opacity to zero, should decrease smoothly
|
||||
os.opacity='0';
|
||||
|
||||
// Actually hide the OSK at the end of the transition
|
||||
this._Box.addEventListener('transitionend', this.hideNow, false);
|
||||
this._Box.addEventListener('webkitTransitionEnd', this.hideNow, false);
|
||||
}
|
||||
}.bind(this), 200); // Wait a bit before starting, to allow for moving to another element
|
||||
}
|
||||
|
||||
// Allow UI to execute code when hiding the OSK
|
||||
var p={};
|
||||
p['HiddenByUser']=hiddenByUser;
|
||||
this.doHide(p);
|
||||
|
||||
// If hidden by the UI, be sure to restore the focus
|
||||
if(hiddenByUser) {
|
||||
this.lastActiveTarget.focus();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow UI to respond to OSK being shown (passing position and properties)
|
||||
*
|
||||
* @param {Object=} p object with coordinates and userdefined flag
|
||||
* @return {boolean}
|
||||
*
|
||||
*/
|
||||
doShow(p) {
|
||||
return com.keyman.singleton.util.callEvent('osk.show',p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow UI to update respond to OSK being hidden
|
||||
*
|
||||
* @param {Object=} p object with coordinates and userdefined flag
|
||||
* @return {boolean}
|
||||
*
|
||||
*/
|
||||
doHide(p) {
|
||||
return com.keyman.singleton.util.callEvent('osk.hide',p);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ namespace com.keyman.osk.layouts {
|
|||
}
|
||||
|
||||
this._closeButton.onclick = function () {
|
||||
osk._Hide(true);
|
||||
osk.startHide(true);
|
||||
return false;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,12 @@ namespace com.keyman.osk {
|
|||
'nomove'?: boolean
|
||||
};
|
||||
|
||||
export enum ActivationMode {
|
||||
static = "static", // For use by documentation keyboards, eventually.
|
||||
manual = "manual",
|
||||
automatic = "automatic"
|
||||
}
|
||||
|
||||
export abstract class OSKView {
|
||||
_Box: HTMLDivElement;
|
||||
|
||||
|
|
@ -25,13 +31,16 @@ namespace com.keyman.osk {
|
|||
keyboardView: KeyboardView; // Which implements OSKViewComponent
|
||||
footerView: OSKViewComponent;
|
||||
|
||||
protected device: com.keyman.utils.DeviceSpec;
|
||||
protected readonly device: com.keyman.utils.DeviceSpec;
|
||||
protected readonly hostDevice: com.keyman.utils.DeviceSpec;
|
||||
|
||||
private _boxBaseMouseDown: (e: MouseEvent) => boolean;
|
||||
private _boxBaseTouchStart: (e: TouchEvent) => boolean;
|
||||
private _boxBaseTouchEventCancel: (e: TouchEvent) => boolean;
|
||||
|
||||
private keyboard: keyboards.Keyboard;
|
||||
|
||||
private _target: text.OutputTarget;
|
||||
|
||||
/**
|
||||
* The configured width for this OSKManager. May be `undefined` or `null`
|
||||
|
|
@ -65,9 +74,21 @@ namespace com.keyman.osk {
|
|||
|
||||
private needsLayout: boolean = true;
|
||||
|
||||
constructor(deviceSpec: com.keyman.utils.DeviceSpec) {
|
||||
//
|
||||
private _activationMode: ActivationMode = ActivationMode.automatic;
|
||||
private _displayIfActive: boolean = true;
|
||||
|
||||
private _animatedHideTimeout: number;
|
||||
private _animatedHideResolver: () => void;
|
||||
|
||||
constructor(deviceSpec: com.keyman.utils.DeviceSpec, hostDevice?: com.keyman.utils.DeviceSpec) {
|
||||
this.device = deviceSpec;
|
||||
|
||||
if(!hostDevice) {
|
||||
hostDevice = deviceSpec;
|
||||
}
|
||||
this.hostDevice = hostDevice;
|
||||
|
||||
// OSK initialization - create DIV and set default styles
|
||||
this._Box = document.createElement('div'); // Container for OSK (Help DIV, displayed when user clicks Help icon)
|
||||
this._Box.style.userSelect = 'none';
|
||||
|
|
@ -158,14 +179,111 @@ namespace com.keyman.osk {
|
|||
this._boxBaseTouchStart = null;
|
||||
}
|
||||
|
||||
public get activeTarget(): dom.targets.OutputTarget {
|
||||
const el=com.keyman.singleton.domManager.activeElement;
|
||||
return dom.Utils.getOutputTarget(el);
|
||||
/**
|
||||
* Gets and sets the IME-like interface (`OutputTarget`) to be affected by events from
|
||||
* the OSK.
|
||||
*
|
||||
* If `activationMode` is `'conditional'`, this property's state controls the visibility
|
||||
* of the OSKView.
|
||||
*/
|
||||
public get activeTarget(): text.OutputTarget {
|
||||
return this._target;
|
||||
}
|
||||
|
||||
public get lastActiveTarget(): dom.targets.OutputTarget {
|
||||
const el = com.keyman.singleton.domManager.lastActiveElement;
|
||||
return dom.Utils.getOutputTarget(el);
|
||||
public set activeTarget(targ: text.OutputTarget) {
|
||||
// If already null & set to null again, take no action.
|
||||
if(this._target == null && targ == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._target = targ;
|
||||
this.commonCheckAndDisplay();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the activation state model used to control presentation of the OSK.
|
||||
* - `'conditional'`: Only displays if `activeTarget` is non-null - if there is an active
|
||||
* target that can receive the OSK's context-manipulation events.
|
||||
* - `'manual'`: Display is directly controlled by manipulating the value of `displayIfActive`.
|
||||
* It may be displayed while `activeTarget` is `null`.
|
||||
* - `'static'`: The OSK should be permanently displayed and may never be hidden.
|
||||
*/
|
||||
get activationMode(): ActivationMode {
|
||||
if(!this._activationMode) {
|
||||
this._activationMode = ActivationMode.automatic;
|
||||
}
|
||||
|
||||
return this._activationMode;
|
||||
}
|
||||
|
||||
set activationMode(mode: ActivationMode) {
|
||||
this._activationMode = mode;
|
||||
this.commonCheckAndDisplay();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of the activation modeling described in the documentation for
|
||||
* `activationMode`.
|
||||
*/
|
||||
protected get activationConditionsMet(): boolean {
|
||||
switch(this.activationMode) {
|
||||
case 'manual':
|
||||
return true;
|
||||
case 'static':
|
||||
return true;
|
||||
case 'automatic':
|
||||
return !!this.activeTarget;
|
||||
default:
|
||||
console.error("Unexpected activation mode set for the OSK.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A property denoting whether or not the OSK should be presented if it meets its
|
||||
* activation conditions.
|
||||
*
|
||||
* When `activationMode == 'manual'`, `displayIfActive == true` is the lone
|
||||
* activation condition.
|
||||
*
|
||||
* Note: cannot be set to `false` if `activationMode == 'static'`.
|
||||
*/
|
||||
get displayIfActive(): boolean {
|
||||
return this._displayIfActive;
|
||||
}
|
||||
|
||||
set displayIfActive(flag: boolean) {
|
||||
if(this.displayIfActive == flag) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if is touch device or is CJK keyboard, this.displayIfActive must remain true.
|
||||
if(this.keyboard?.isCJK && !flag) {
|
||||
console.warn("Cannot hide display of OSK for CJK keyboards.");
|
||||
flag = true;
|
||||
} else if(this.hostDevice.touchable && !flag) {
|
||||
console.warn("Cannot hide display of OSK when hosted on touch-based devices.");
|
||||
flag = true;
|
||||
} else if(this.activationMode == 'static') {
|
||||
// Silently fail; it's a documentation keyboard.
|
||||
// This is the primary difference between 'manual' and 'static'.
|
||||
flag = true;
|
||||
}
|
||||
|
||||
this._displayIfActive = flag;
|
||||
this.commonCheckAndDisplay();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by the activation & visibility properties as a common helper; all of their
|
||||
* setters rely on this function to manage presentation (showing / hiding) of the OSK.
|
||||
*/
|
||||
private commonCheckAndDisplay() {
|
||||
if(this.activationConditionsMet && this.displayIfActive) {
|
||||
this.present();
|
||||
} else {
|
||||
this.startHide(false);
|
||||
}
|
||||
}
|
||||
|
||||
public get vkbd(): VisualKeyboard {
|
||||
|
|
@ -271,6 +389,10 @@ namespace com.keyman.osk {
|
|||
public set activeKeyboard(keyboard: keyboards.Keyboard) {
|
||||
this.keyboard = keyboard;
|
||||
this.loadActiveKeyboard();
|
||||
|
||||
if(this.keyboard?.isCJK) {
|
||||
this.displayIfActive = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* private */ computeFrameHeight(): number {
|
||||
|
|
@ -327,6 +449,13 @@ namespace com.keyman.osk {
|
|||
this.needsLayout = false;
|
||||
|
||||
// Step 3: perform layout operations.
|
||||
if(!this._baseFontSize && this.parsedBaseFontSize) {
|
||||
// Make sure to initialize the default font size if it hasn't already been set!
|
||||
this.banner.element.style.fontSize = this.baseFontSize;
|
||||
if(this.vkbd) {
|
||||
this.vkbd.fontSize = this.parsedBaseFontSize;
|
||||
}
|
||||
}
|
||||
if(!pending) {
|
||||
this.headerView?.refreshLayout();
|
||||
this.bannerView.refreshLayout();
|
||||
|
|
@ -396,13 +525,9 @@ namespace com.keyman.osk {
|
|||
|
||||
// Add suggestion banner bar to OSK
|
||||
this._Box.appendChild(this.banner.element);
|
||||
this.banner.element.style.fontSize = this.baseFontSize;
|
||||
|
||||
let kbdView: KeyboardView = this.keyboardView = this._GenerateKeyboardView(this.activeKeyboard);
|
||||
this._Box.appendChild(kbdView.element);
|
||||
if(kbdView instanceof VisualKeyboard) {
|
||||
kbdView.fontSize = this.parsedBaseFontSize;
|
||||
}
|
||||
kbdView.postInsert();
|
||||
|
||||
// Add footer element to OSK only for desktop browsers
|
||||
|
|
@ -515,11 +640,17 @@ namespace com.keyman.osk {
|
|||
return vkbd;
|
||||
}
|
||||
|
||||
// Corresponds to the desktop OSK's _Show, but acts as a core, common method
|
||||
// usable by all display patterns.
|
||||
protected makeVisible() {
|
||||
// Do not try to display/render the OSK if undefined or no keyboard is loaded.
|
||||
if(!this._Box || !this.keyboardView) {
|
||||
/**
|
||||
* The main function for presenting the OSKView.
|
||||
*
|
||||
* This includes:
|
||||
* - refreshing its layout
|
||||
* - displaying it
|
||||
* - positioning it
|
||||
*/
|
||||
public present(): void {
|
||||
// Do not try to display OSK if no active element
|
||||
if(!this.mayShow()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -535,6 +666,15 @@ namespace com.keyman.osk {
|
|||
this.keyboardView.showLanguage();
|
||||
}
|
||||
|
||||
this._Visible=true;
|
||||
|
||||
/* In case it's still '0' from a hide() operation.
|
||||
*
|
||||
* (Opacity is only modified when device.touchable = true,
|
||||
* though a couple of extra conditions may apply.)
|
||||
*/
|
||||
this._Box.style.opacity = '1';
|
||||
|
||||
// If OSK still hidden, make visible only after all calculation finished
|
||||
if(this._Box.style.visibility == 'hidden') {
|
||||
let _this = this;
|
||||
|
|
@ -542,16 +682,222 @@ namespace com.keyman.osk {
|
|||
_this._Box.style.visibility = 'visible';
|
||||
}, 0);
|
||||
}
|
||||
|
||||
this.setDisplayPositioning();
|
||||
}
|
||||
|
||||
protected makeHidden(hiddenByUser: boolean) {
|
||||
// Save current size if visible
|
||||
if(this._Box && this._Box.style.display == 'block' && this.keyboardView instanceof VisualKeyboard) {
|
||||
this.keyboardView.refit();
|
||||
/**
|
||||
* Method usable by subclasses of OSKView to control that OSKView type's
|
||||
* positioning behavior when needed by the present() method.
|
||||
*/
|
||||
protected abstract setDisplayPositioning();
|
||||
|
||||
/**
|
||||
* Method used to start a potentially-asynchronous hide of the OSK.
|
||||
* @param hiddenByUser `true` if this hide operation was directly requested by the user.
|
||||
*/
|
||||
public startHide(hiddenByUser: boolean): void {
|
||||
if(!this.mayHide(hiddenByUser)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(hiddenByUser) {
|
||||
// The one location outside of the `displayIfActive` property that bypasses the setter.
|
||||
// Avoids needless recursion that could be triggered by it, as we're already in the
|
||||
// process of hiding the OSK anyway.
|
||||
this._displayIfActive = ((this.keyboard.isCJK || this.hostDevice.touchable)? true : false); // I3363 (Build 301)
|
||||
}
|
||||
|
||||
let promise: Promise<boolean> = null;
|
||||
if(this._Box && this.hostDevice.touchable && !(this.keyboardView instanceof EmptyView)) {
|
||||
/**
|
||||
* Note: this refactored code appears to reflect a currently-dead code path. 14.0's
|
||||
* equivalent is either extremely niche or is actually inaccessible.
|
||||
*/
|
||||
promise = this.useHideAnimation();
|
||||
} else {
|
||||
promise = Promise.resolve(true);
|
||||
}
|
||||
|
||||
const _this = this;
|
||||
promise.then(function(shouldHide: boolean) {
|
||||
if(shouldHide) {
|
||||
_this.finalizeHide();
|
||||
}
|
||||
});
|
||||
|
||||
// Allow UI to execute code when hiding the OSK
|
||||
var p={};
|
||||
p['HiddenByUser']=hiddenByUser;
|
||||
this.doHide(p);
|
||||
|
||||
// If hidden by the UI, be sure to restore the focus
|
||||
if(hiddenByUser && this.activeTarget instanceof dom.targets.OutputTarget) {
|
||||
this.activeTarget?.focus();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the _actual_ logic and functionality involved in hiding the OSK.
|
||||
*/
|
||||
protected finalizeHide() {
|
||||
if(document.body.className.indexOf('osk-always-visible') >= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(this._Box) {
|
||||
this._Box.style.display = 'none';
|
||||
let bs=this._Box.style;
|
||||
bs.display = 'none';
|
||||
bs.transition = '';
|
||||
bs.opacity = '1';
|
||||
this._Visible=false;
|
||||
}
|
||||
|
||||
if(this.vkbd) {
|
||||
this.vkbd.onHide();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns `false` if the OSK is in an invalid state for being presented to the user.
|
||||
*/
|
||||
protected mayShow(): boolean {
|
||||
if(!this.activationConditionsMet) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Never display the OSK for desktop browsers unless KMW element is focused, and a keyboard selected
|
||||
if(!this.keyboardView || this.keyboardView instanceof EmptyView || !this.displayIfActive) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!this._Box) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param hiddenByUser
|
||||
* @returns `false` if the OSK is in an invalid state for being hidden from the user.
|
||||
*/
|
||||
protected mayHide(hiddenByUser: boolean): boolean {
|
||||
if(this.activationMode != 'automatic' && this.displayIfActive) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!hiddenByUser && this.hostDevice.formFactor == 'desktop') {
|
||||
//Allow desktop OSK to remain visible on blur if body class set
|
||||
if(document.body.className.indexOf('osk-always-visible') >= 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies CSS styling and handling needed to perform a fade animation when
|
||||
* hiding the OSK.
|
||||
*
|
||||
* Note: currently reflects an effectively-dead code path, though this is
|
||||
* likely not intentional. Other parts of the KMW engine seem to call hideNow()
|
||||
* synchronously after each and every part of the engine that calls this function,
|
||||
* cancelling the Promise.
|
||||
*
|
||||
* @returns A Promise denoting either cancellation of the hide (`false`) or
|
||||
* completion of the hide & its animation (`true`)
|
||||
*/
|
||||
protected useHideAnimation(): Promise<boolean> {
|
||||
const os = this._Box.style;
|
||||
const _this = this;
|
||||
const promise = new Promise<void>(function(resolve) {
|
||||
os.transition='opacity 0.5s linear 0';
|
||||
|
||||
_this._animatedHideResolver = resolve;
|
||||
|
||||
// Cannot hide the OSK smoothly using a transitioned drop, since for
|
||||
// position:fixed elements transitioning is incompatible with translate3d(),
|
||||
// and also does not work with top, bottom or height styles.
|
||||
// Opacity can be transitioned and is probably the simplest alternative.
|
||||
// We must condition on osk._Visible in case focus has since been moved to another
|
||||
// input (in which case osk._Visible will be non-zero)
|
||||
_this._animatedHideTimeout = window.setTimeout(function(this: AnchoredOSKView) {
|
||||
if(_this._animatedHideResolver) {
|
||||
_this._animatedHideResolver();
|
||||
}
|
||||
|
||||
_this._animatedHideTimeout = 0;
|
||||
_this._animatedHideResolver = null;
|
||||
}.bind(_this), 200); // Wait a bit before starting, to allow for moving to another element
|
||||
});
|
||||
|
||||
return promise.then(function() {
|
||||
// Repro for passing this condition:
|
||||
// 1. Touch an input element of the page
|
||||
// 2. Within a second (before the focusing timer expires), touch the base page.
|
||||
// See domEventHandlers.ts, `focusTimer` / `setFocusTimer`.
|
||||
// 3. After the timer expires, touch the base page again.
|
||||
if(_this._Visible && _this.activationConditionsMet) {
|
||||
// Leave opacity alone and clear transition if another element activated
|
||||
os.transition='';
|
||||
return false;
|
||||
} else {
|
||||
// Set opacity to zero, should decrease smoothly. Starts the actual animation.
|
||||
os.opacity='0';
|
||||
|
||||
// Listen for the animation's end.
|
||||
return new Promise<void>(function(resolve) {
|
||||
_this._animatedHideResolver = resolve;
|
||||
// Actually hide the OSK at the end of the transition
|
||||
_this._Box.addEventListener('transitionend', _this._animatedHideResolver, false);
|
||||
_this._Box.addEventListener('webkitTransitionEnd', _this._animatedHideResolver, false);
|
||||
}).then(function() {
|
||||
// Remove the promise's resolver method from future handling.
|
||||
_this._Box.removeEventListener('transitionend', _this._animatedHideResolver, false);
|
||||
_this._Box.removeEventListener('webkitTransitionEnd', _this._animatedHideResolver, false);
|
||||
|
||||
// The hide animation is considered complete now.
|
||||
return true;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to synchronously hide the OSK, cancelling any async hide animations that have
|
||||
* not started and immediately completing the hide of any hide ops pending completion
|
||||
* of their animation.
|
||||
*/
|
||||
public hideNow() {
|
||||
if(!this._Box) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Two possible uses for _animatedHideResolver:
|
||||
// - _animatedHideTimeout is set: animation is waiting to start
|
||||
// - _animatedHideTimeout is null: animation has already started.
|
||||
|
||||
// Was an animated hide waiting to start? Just cancel it.
|
||||
if(this._animatedHideTimeout) {
|
||||
window.clearTimeout(this._animatedHideTimeout);
|
||||
this._animatedHideResolver = null;
|
||||
this._animatedHideTimeout = 0;
|
||||
}
|
||||
|
||||
// Was an animated hide already in progress? If so, just trigger it early.
|
||||
if(this._animatedHideResolver) {
|
||||
this._animatedHideResolver(); // also triggers finalizeHide().
|
||||
this._animatedHideResolver = null;
|
||||
} else {
|
||||
const os = this._Box.style;
|
||||
os.transition='';
|
||||
os.opacity='0';
|
||||
|
||||
this.finalizeHide();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -595,24 +941,6 @@ namespace com.keyman.osk {
|
|||
* The simplest way forward is to maintain them, then resolve them independently,
|
||||
* one at a time.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Display KMW OSK at specified position (returns nothing)
|
||||
*
|
||||
* The positioning parameters only make sense for the FloatingOSKView type;
|
||||
* other implementations should use no parameters whatsoever.
|
||||
*
|
||||
* @param {number=} Px x-coordinate for OSK rectangle
|
||||
* @param {number=} Py y-coordinate for OSK rectangle
|
||||
*/
|
||||
public abstract _Show(Px?: number, Py?: number);
|
||||
|
||||
/**
|
||||
* Hide Keymanweb On Screen Keyboard
|
||||
*
|
||||
* @param {boolean} hiddenByUser Distinguish between hiding on loss of focus and explicit hiding by user
|
||||
*/
|
||||
public abstract _Hide(hiddenByUser: boolean);
|
||||
|
||||
/**
|
||||
* Display build number
|
||||
|
|
@ -640,49 +968,10 @@ namespace com.keyman.osk {
|
|||
menu.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Function hideNow
|
||||
* Scope Private
|
||||
* Description Hide the OSK unconditionally and immediately, cancel any pending transition
|
||||
*
|
||||
* Usages:
|
||||
* - during rotations to temporarily hide the OSK during layout ops
|
||||
* - when controls lose focus (N/A to embedded mode)
|
||||
*
|
||||
* Somewhat conflated with the _Show / _Hide methods, which often serve more as an
|
||||
* "enable" vs "disable" feature on the OSK - though that distinction isn't super-clear.
|
||||
*
|
||||
* Definitely needs clearer design & modeling, at the least.
|
||||
*/
|
||||
hideNow: () => void = function(this: OSKView) { // I3363 (Build 301)
|
||||
if(!this._Box) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._Box.removeEventListener('transitionend', this.hideNow, false);
|
||||
this._Box.removeEventListener('webkitTransitionEnd', this.hideNow, false);
|
||||
|
||||
if(document.body.className.indexOf('osk-always-visible') >= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var os=this._Box.style;
|
||||
os.display='none';
|
||||
os.opacity='1';
|
||||
this._Visible=false;
|
||||
os.transition=os.msTransition=os.MozTransition=os.WebkitTransition='';
|
||||
|
||||
if(this.vkbd) {
|
||||
this.vkbd.onHide();
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
// OSK state fields
|
||||
// OSK state fields & events
|
||||
//
|
||||
// They're not very well defined or encapsulated; there's definitely room for more
|
||||
// "polish" here.
|
||||
// These are relatively stable and may be preserved as they are.
|
||||
_Visible: boolean = false;
|
||||
_Enabled: boolean = true;
|
||||
|
||||
/**
|
||||
* Function enabled
|
||||
|
|
@ -691,7 +980,7 @@ namespace com.keyman.osk {
|
|||
* Description Test if KMW OSK is enabled
|
||||
*/
|
||||
['isEnabled'](): boolean {
|
||||
return this._Enabled;
|
||||
return this.displayIfActive;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -711,8 +1000,8 @@ namespace com.keyman.osk {
|
|||
* Description Prevent display of OSK window on focus
|
||||
*/
|
||||
['hide']() {
|
||||
this._Enabled = false;
|
||||
this._Hide(true);
|
||||
this.displayIfActive = false;
|
||||
this.startHide(true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -723,21 +1012,36 @@ namespace com.keyman.osk {
|
|||
*/
|
||||
['show'](bShow: boolean) {
|
||||
if(arguments.length > 0) {
|
||||
this._Enabled=bShow;
|
||||
if(bShow) {
|
||||
this._Show();
|
||||
} else {
|
||||
this._Hide(true);
|
||||
}
|
||||
this.displayIfActive = bShow;
|
||||
} else {
|
||||
if(this._Visible) {
|
||||
this._Hide(true);
|
||||
} else {
|
||||
this._Show();
|
||||
if(this.activationConditionsMet) {
|
||||
this.displayIfActive = !this.displayIfActive;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow UI to respond to OSK being shown (passing position and properties)
|
||||
*
|
||||
* @param {Object=} p object with coordinates and userdefined flag
|
||||
* @return {boolean}
|
||||
*
|
||||
*/
|
||||
doShow(p) {
|
||||
return com.keyman.singleton.util.callEvent('osk.show',p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow UI to update respond to OSK being hidden
|
||||
*
|
||||
* @param {Object=} p object with coordinates and userdefined flag
|
||||
* @return {boolean}
|
||||
*
|
||||
*/
|
||||
doHide(p) {
|
||||
return com.keyman.singleton.util.callEvent('osk.hide',p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function addEventListener
|
||||
* Scope Public
|
||||
|
|
|
|||
|
|
@ -308,15 +308,6 @@ namespace com.keyman.osk {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by OSKManager after resize operations in order to determine the final
|
||||
* size actually used by the visual keyboard.
|
||||
*/
|
||||
public refit() { // TODO: Should probably remove; VisualKeyboard shouldn't be responsible for it.
|
||||
this._width = this.kbdDiv.offsetWidth;
|
||||
this._height = this.kbdDiv.offsetHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default properties for a key object, used to construct
|
||||
* both a base keyboard key and popup keys
|
||||
|
|
@ -1175,7 +1166,8 @@ namespace com.keyman.osk {
|
|||
layerElement.style.display='block';
|
||||
//b[n].style.visibility='visible';
|
||||
|
||||
// If osk._Show has been called, there's probably been a change in modifier or state key state. Keep it updated!
|
||||
// Most functions that call this one often indicate a change in modifier
|
||||
// or state key state. Keep it updated!
|
||||
this._UpdateVKShiftStyle();
|
||||
} else {
|
||||
layerElement.style.display='none';
|
||||
|
|
@ -1591,7 +1583,7 @@ namespace com.keyman.osk {
|
|||
oskManager.showLanguageMenu();
|
||||
} else if(keyName.indexOf('K_ROPT') >= 0) {
|
||||
keyman.uiManager.setActivatingUI(false);
|
||||
oskManager._Hide(true);
|
||||
oskManager.startHide(true);
|
||||
let active = keyman.domManager.activeElement;
|
||||
if(dom.Utils.instanceof(active, "TouchAliasElement")) {
|
||||
(active as dom.TouchAliasElement).hideCaret();
|
||||
|
|
|
|||
|
|
@ -101,20 +101,37 @@ namespace com.keyman.text {
|
|||
//The following entry points are defined but should not normally be used in a keyboard, as OSK display is no longer determined by the keyboard
|
||||
KeyboardInterface.prototype['hideHelp'] = function(): void {
|
||||
let keyman = com.keyman.singleton;
|
||||
keyman.osk._Hide(true);
|
||||
keyman.osk.startHide(true);
|
||||
}
|
||||
|
||||
KeyboardInterface.prototype['showHelp'] = function(Px: number, Py: number): void {
|
||||
let keyman = com.keyman.singleton;
|
||||
keyman.osk._Show(Px,Py);
|
||||
if(keyman.osk instanceof osk.FloatingOSKView) {
|
||||
keyman.osk.presentAtPosition(Px,Py);
|
||||
} else {
|
||||
keyman.osk.present();
|
||||
}
|
||||
}
|
||||
|
||||
KeyboardInterface.prototype['showPinnedHelp'] = function(): void {
|
||||
let keyman = com.keyman.singleton;
|
||||
if(keyman.osk instanceof osk.FloatingOSKView) {
|
||||
keyman.osk.userPositioned=true;
|
||||
// An old KMW bug previously auto-unset the affected field when this function was
|
||||
// used by CJK keyboards during rule processing. As a result, we need to condition
|
||||
// on whether or not:
|
||||
// 1. The active keyboard is CJK
|
||||
// 2. A keyboard rule is actively processing.
|
||||
//
|
||||
// If BOTH are true, we do NOT mutate keyman.osk.userPositioned.
|
||||
// Otherwise, not all conditions are met, so we still allow OSK pinning.
|
||||
if(!keyman.core.activeKeyboard.isCJK || !this.ruleBehavior) {
|
||||
keyman.osk.userPositioned=true;
|
||||
}
|
||||
}
|
||||
keyman.osk._Show(-1,-1);
|
||||
// Automatically reuses previously-set positioning.
|
||||
// Other OSK API functions must have previously been used to set the
|
||||
// pinned position.
|
||||
keyman.osk.present();
|
||||
}
|
||||
|
||||
// Also needed for some legacy CJK keyboards.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue