mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-08 18:05:32 +00:00
Merge pull request #8806 from keymanapp/chore/web/page-integration-handlers
chore(web): modularization of Web's page-integration handlers 🧩
This commit is contained in:
commit
112cea00ea
6 changed files with 238 additions and 159 deletions
209
web/src/app/browser/src/context/pageIntegrationHandlers.ts
Normal file
209
web/src/app/browser/src/context/pageIntegrationHandlers.ts
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
import { DomEventTracker } from 'keyman/engine/events';
|
||||
|
||||
import { KeymanEngine } from "../keymanEngine.js";
|
||||
import { FocusAssistant } from './focusAssistant.js';
|
||||
|
||||
// Note: in the future, it'd probably be best to have an instance per iframe window as
|
||||
// well as the top-level window. This was not done in or before KMW 16.0 though, so
|
||||
// we'll leave that out for now within the initial modular form of app/browser KMW in 17.0.
|
||||
export class PageIntegrationHandlers {
|
||||
private readonly window: Window;
|
||||
private readonly engine: KeymanEngine;
|
||||
private readonly domEventTracker = new DomEventTracker();
|
||||
|
||||
/**
|
||||
* Used together with `deactivateOnRelease` to determine the distance of vertical scrolls;
|
||||
* if sufficiently far at any point, we avoid deactivating the current context when it ends.
|
||||
*/
|
||||
private touchY: number;
|
||||
|
||||
/**
|
||||
* Used together with `touchY` to determine the distance of vertical scrolls;
|
||||
* if sufficiently far at any point, we avoid deactivating the current context when it ends.
|
||||
*/
|
||||
private deactivateOnRelease: boolean;
|
||||
|
||||
/**
|
||||
* Used on certain browser/OS combinations (e.g. Chrome on Android) to prevent odd behaviors
|
||||
* that arise when URL bars scroll into view during an ongoing scroll, as this can impede
|
||||
* proper / smooth positioning of the OSK. (Deactivating the active target also hides the OSK.)
|
||||
*/
|
||||
private deactivateOnScroll: boolean;
|
||||
|
||||
constructor(window: Window, engine: KeymanEngine) {
|
||||
this.window = window;
|
||||
this.engine = engine;
|
||||
|
||||
this.attachHandlers();
|
||||
}
|
||||
|
||||
private get focusAssistant(): FocusAssistant {
|
||||
return this.engine.contextManager.focusAssistant;
|
||||
}
|
||||
|
||||
private suppressFocusCheck: (e: FocusEvent) => boolean = (e) => {
|
||||
if(this.focusAssistant._IgnoreBlurFocus) {
|
||||
// Prevent triggering other blur-handling events (as possible)
|
||||
e.stopPropagation();
|
||||
e.cancelBubble = true;
|
||||
}
|
||||
// But DO perform default event behavior (actually blurring & focusing the affected element)
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset context when entering or exiting the active element.
|
||||
* Will also trigger OSK shift state / layer reset.
|
||||
**/
|
||||
private pageFocusHandler: (e: FocusEvent) => boolean = () => {
|
||||
if(!this.focusAssistant.maintainingFocus && this.engine.osk?.vkbd) {
|
||||
this.engine.contextManager.deactivateCurrentTarget();
|
||||
this.engine.contextManager.resetContext();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Sets up page-default touch-based handling for activation-state management.
|
||||
// These always trigger for the page, wherever a touch may occur. Does not
|
||||
// prevent element-specific or OSK-key-specific handling from triggering.
|
||||
|
||||
private touchStartActivationHandler: (e: TouchEvent) => boolean = (e) => {
|
||||
const osk = this.engine.osk;
|
||||
if(!osk) {
|
||||
return false;
|
||||
}
|
||||
const device = this.engine.config.hostDevice;
|
||||
|
||||
this.deactivateOnRelease=true;
|
||||
this.touchY=e.touches[0].screenY;
|
||||
|
||||
// On Chrome, scrolling up or down causes the URL bar to be shown or hidden
|
||||
// according to whether or not the document is at the top of the screen.
|
||||
// But when doing that, each OSK row top and height gets modified by Chrome
|
||||
// looking very ugly. It would be best to hide the OSK then show it again
|
||||
// when the user scroll finishes, but Chrome has no way to reliably report
|
||||
// the touch end event after a move. c.f. http://code.google.com/p/chromium/issues/detail?id=152913
|
||||
// The best compromise behaviour is simply to hide the OSK whenever any
|
||||
// non-input and non-OSK element is touched.
|
||||
this.deactivateOnScroll=false;
|
||||
if(device.OS == 'android' && device.browser == 'chrome') {
|
||||
// this.deactivateOnScroll has the inverse of the 'true' default,
|
||||
// but that fact actually facilitates the following conditional logic.
|
||||
if(typeof(osk._Box) == 'undefined') return false;
|
||||
if(typeof(osk._Box.style) == 'undefined') return false;
|
||||
|
||||
// The following tests are needed to prevent the OSK from being hidden during normal input!
|
||||
let p=(e.target as HTMLElement).parentElement;
|
||||
if(typeof(p) != 'undefined' && p != null) {
|
||||
if(p.className.indexOf('kmw-key-') >= 0) return false;
|
||||
if(typeof(p.parentElement) != 'undefined' && p.parentElement != null) {
|
||||
p=p.parentElement;
|
||||
if(p.className.indexOf('kmw-key-') >= 0) return false;
|
||||
}
|
||||
}
|
||||
|
||||
this.deactivateOnScroll = true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
private touchMoveActivationHandler: (e: TouchEvent) => boolean = (e) => {
|
||||
if(this.deactivateOnScroll) { // Android / Chrone case.
|
||||
this.focusAssistant.focusing = false;
|
||||
this.engine.contextManager.deactivateCurrentTarget();
|
||||
}
|
||||
|
||||
const y = e.touches[0].screenY;
|
||||
const y0 = this.touchY;
|
||||
if(y-y0 > 5 || y0-y < 5) {
|
||||
this.deactivateOnRelease = false;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
private touchEndActivationHandler: (e: TouchEvent) => boolean = (e) => {
|
||||
// Should not hide OSK if simply closing the language menu (30/4/15)
|
||||
// or if the focusing timer (focusAssistant.setFocusTimer) is still active.
|
||||
if(this.deactivateOnRelease && !osk['lgList'] && !this.focusAssistant.focusing) {
|
||||
this.engine.contextManager.deactivateCurrentTarget();
|
||||
}
|
||||
this.deactivateOnRelease=false;
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
private _WindowLoad: (e: Event) => void = () => {
|
||||
// Always return to top of page after a page reload
|
||||
document.body.scrollTop=0;
|
||||
if(typeof document.documentElement != 'undefined') {
|
||||
document.documentElement.scrollTop=0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function _WindowUnload
|
||||
* Scope Private
|
||||
* Description Remove handlers before detaching KMW window
|
||||
*/
|
||||
private _WindowUnload: () => void = () => {
|
||||
// Future note: should restrict this to anything for the corresponding document if on a
|
||||
// child iframe, not the whole engine.
|
||||
this.engine.shutdown();
|
||||
}
|
||||
|
||||
private attachHandlers() {
|
||||
const eventTracker = this.domEventTracker;
|
||||
const device = this.engine.config.hostDevice;
|
||||
const docBody = this.window.document.body;
|
||||
|
||||
eventTracker.attachDOMEvent(this.window, 'focus', this.pageFocusHandler, false);
|
||||
eventTracker.attachDOMEvent(this.window, 'blur', this.pageFocusHandler, false);
|
||||
|
||||
/*
|
||||
* To prevent propagation of focus & blur events from the input-scroll workaround,
|
||||
* we attach top-level capturing listeners to the focus & blur events. They prevent propagation
|
||||
* but NOT default behavior, allowing the scroll to complete while preventing nearly all
|
||||
* possible event 'noise' that could result from the workaround.
|
||||
*/
|
||||
eventTracker.attachDOMEvent(docBody, 'focus', this.suppressFocusCheck, true);
|
||||
eventTracker.attachDOMEvent(docBody, 'blur', this.suppressFocusCheck, true);
|
||||
|
||||
if(device.touchable) {
|
||||
eventTracker.attachDOMEvent(docBody, 'touchstart', this.touchStartActivationHandler,false);
|
||||
eventTracker.attachDOMEvent(docBody, 'touchmove', this.touchMoveActivationHandler, false);
|
||||
eventTracker.attachDOMEvent(docBody, 'touchend', this.touchEndActivationHandler, false);
|
||||
}
|
||||
|
||||
eventTracker.attachDOMEvent(window, 'load', this._WindowLoad, false);
|
||||
eventTracker.attachDOMEvent(window, 'unload', this._WindowUnload,false);
|
||||
|
||||
// TODO: Hotkey module stuff. Is not yet modularized.
|
||||
// eventTracker.attachDOMEvent(document, 'keyup', this.engine.hotkeyManager._Process, false);
|
||||
}
|
||||
|
||||
public shutdown() {
|
||||
const eventTracker = this.domEventTracker;
|
||||
const device = this.engine.config.hostDevice;
|
||||
const docBody = this.window.document.body;
|
||||
|
||||
// See `attachHandlers` for the purpose behind all handlers listed here.
|
||||
|
||||
eventTracker.detachDOMEvent(this.window, 'focus', this.pageFocusHandler, false);
|
||||
eventTracker.detachDOMEvent(this.window, 'blur', this.pageFocusHandler, false);
|
||||
|
||||
eventTracker.detachDOMEvent(docBody, 'focus', this.suppressFocusCheck, true);
|
||||
eventTracker.detachDOMEvent(docBody, 'blur', this.suppressFocusCheck, true);
|
||||
|
||||
if(device.touchable) {
|
||||
eventTracker.detachDOMEvent(docBody, 'touchstart', this.touchStartActivationHandler,false);
|
||||
eventTracker.detachDOMEvent(docBody, 'touchmove', this.touchMoveActivationHandler, false);
|
||||
eventTracker.detachDOMEvent(docBody, 'touchend', this.touchEndActivationHandler, false);
|
||||
}
|
||||
|
||||
eventTracker.detachDOMEvent(window, 'load', this._WindowLoad, false);
|
||||
eventTracker.detachDOMEvent(window, 'unload', this._WindowUnload,false);
|
||||
|
||||
// TODO: Hotkey module stuff.
|
||||
// eventTracker.detachDOMEvent(document, 'keyup', this.engine.hotkeyManager._Process, false);
|
||||
}
|
||||
}
|
||||
|
|
@ -46,7 +46,6 @@ export default class ContextManager extends ContextManagerBase<BrowserConfigurat
|
|||
private cookieManager = new CookieSerializer<KeyboardCookie>('KeymanWeb_Keyboard');
|
||||
readonly focusAssistant = new FocusAssistant();
|
||||
readonly page: PageContextAttachment;
|
||||
|
||||
private mostRecentTarget: OutputTarget<any>;
|
||||
private currentTarget: OutputTarget<any>;
|
||||
|
||||
|
|
@ -81,6 +80,7 @@ export default class ContextManager extends ContextManagerBase<BrowserConfigurat
|
|||
|
||||
this.engineConfig.deferForInitialization.then(() => {
|
||||
const device = this.engineConfig.hostDevice;
|
||||
|
||||
const noPropagation = (event: Event) => event.stopPropagation()
|
||||
|
||||
// For any elements being attached, or being enabled after having been disabled...
|
||||
|
|
|
|||
|
|
@ -3,19 +3,21 @@ import { Device as DeviceDetector } from 'keyman/engine/device-detect';
|
|||
import { getAbsoluteY } from 'keyman/engine/dom-utils';
|
||||
import { OutputTarget } from 'keyman/engine/element-wrappers';
|
||||
import { AnchoredOSKView, FloatingOSKView, FloatingOSKViewConfiguration, OSKView } from 'keyman/engine/osk';
|
||||
import { DeviceSpec, ProcessorInitOptions } from "@keymanapp/keyboard-processor";
|
||||
import { DeviceSpec, ProcessorInitOptions, extendString } from "@keymanapp/keyboard-processor";
|
||||
|
||||
import { BrowserConfiguration, BrowserInitOptionDefaults, BrowserInitOptionSpec } from './configuration.js';
|
||||
import ContextManager from './contextManager.js';
|
||||
import DefaultBrowserRules from './defaultBrowserRules.js';
|
||||
import KeyEventKeyboard from './keyEventKeyboard.js';
|
||||
import { FocusStateAPIObject } from './context/focusAssistant.js';
|
||||
import { PageIntegrationHandlers } from './context/pageIntegrationHandlers.js';
|
||||
import { setupOskListeners } from './oskConfiguration.js';
|
||||
|
||||
export class KeymanEngine extends KeymanEngineBase<ContextManager, KeyEventKeyboard> {
|
||||
keyEventRefocus = () => {
|
||||
this.contextManager.restoreLastActiveTarget();
|
||||
}
|
||||
private pageIntegration: PageIntegrationHandlers;
|
||||
|
||||
constructor(worker: Worker, sourceUri: string) {
|
||||
const config = new BrowserConfiguration(sourceUri); // currently set to perform device auto-detect.
|
||||
|
|
@ -91,6 +93,12 @@ export class KeymanEngine extends KeymanEngineBase<ContextManager, KeyEventKeybo
|
|||
|
||||
setupOskListeners(this, this.osk, this.contextManager);
|
||||
|
||||
// Automatically performs related handler setup & maintains references
|
||||
// needed for related cleanup / shutdown.
|
||||
this.pageIntegration = new PageIntegrationHandlers(window, this);
|
||||
|
||||
// Initialize supplementary plane string extensions
|
||||
String.kmwEnableSupplementaryPlane(true);
|
||||
this.config.finalizeInit();
|
||||
}
|
||||
|
||||
|
|
@ -149,4 +157,19 @@ export class KeymanEngine extends KeymanEngineBase<ContextManager, KeyEventKeybo
|
|||
|
||||
this.contextManager.setKeyboardForTarget(Pelem._kmwAttachment.interface, Pkbd, Plc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detaches all KMW event handlers attached by this instance of the engine and releases
|
||||
* other related resources as appropriate.
|
||||
*
|
||||
* The primary use of this method is to facilitate a clean transition between engine
|
||||
* instances during integration testing. The goal is to prevent interactions intended
|
||||
* for the 'current' instance from being accidentally intercepted by a discarded one.
|
||||
*/
|
||||
shutdown() {
|
||||
this.pageIntegration.shutdown();
|
||||
this.contextManager.shutdown();
|
||||
this.osk?.shutdown();
|
||||
this.core.languageProcessor.shutdown();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -159,37 +159,6 @@ namespace com.keyman.dom {
|
|||
this._BeepObjects = [];
|
||||
}
|
||||
|
||||
/* ------------- Page and document-level management events ------------------ */
|
||||
|
||||
_WindowLoad: (e: Event) => void = function(e: Event) {
|
||||
//keymanweb.completeInitialization();
|
||||
// Always return to top of page after a page reload
|
||||
document.body.scrollTop=0;
|
||||
if(typeof document.documentElement != 'undefined') {
|
||||
document.documentElement.scrollTop=0;
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
/**
|
||||
* Function _WindowUnload
|
||||
* Scope Private
|
||||
* Description Remove handlers before detaching KMW window
|
||||
*/
|
||||
_WindowUnload: () => void = function(this: DOMManager) {
|
||||
// Allow the UI to release its own resources
|
||||
this.keyman.uiManager.doUnload();
|
||||
|
||||
// Allow the OSK to release its own resources
|
||||
if(this.keyman.osk) {
|
||||
this.keyman.osk.shutdown();
|
||||
if(this.keyman.osk['_Unload']) {
|
||||
this.keyman.osk['_Unload'](); // I3363 (Build 301)
|
||||
}
|
||||
}
|
||||
|
||||
this.lastActiveElement = null;
|
||||
}.bind(this);
|
||||
|
||||
/* ------ Defines independent, per-control keyboard setting behavior for the API. ------ */
|
||||
|
||||
/**
|
||||
|
|
@ -442,6 +411,8 @@ namespace com.keyman.dom {
|
|||
this.keyman.setInitialized(1);
|
||||
|
||||
// Finish keymanweb and initialize the OSK once all necessary resources are available
|
||||
// OSK type selection is already modularized... but the ordering related to the parts
|
||||
// afterward, which are not yet modularized, may be important.
|
||||
if(device.touchable) {
|
||||
this.keyman.osk = new com.keyman.osk.AnchoredOSKView(device.coreSpec);
|
||||
} else {
|
||||
|
|
@ -458,12 +429,6 @@ namespace com.keyman.dom {
|
|||
// Initialize the desktop UI
|
||||
this.initializeUI();
|
||||
|
||||
// Exit initialization here if we're using an embedded code path.
|
||||
if(this.keyman.isEmbedded) {
|
||||
this.keyman.keyboardManager.setDefaultKeyboard();
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// Determine the default font for mapped elements
|
||||
this.keyman.appliedFont=this.keyman.baseFont=this.getBaseFont();
|
||||
|
||||
|
|
@ -499,70 +464,6 @@ namespace com.keyman.dom {
|
|||
ds.width='100%';
|
||||
ds.height=(screen.width/2)+'px';
|
||||
document.body.appendChild(dTrailer);
|
||||
|
||||
// Sets up page-default touch-based handling for activation-state management.
|
||||
// These always trigger for the page, wherever a touch may occur. Does not
|
||||
// prevent element-specific or OSK-key-specific handling from triggering.
|
||||
const _this = this;
|
||||
this.touchStartActivationHandler=function(e) {
|
||||
_this.deactivateOnRelease=true;
|
||||
_this.touchY=e.touches[0].screenY;
|
||||
|
||||
// On Chrome, scrolling up or down causes the URL bar to be shown or hidden
|
||||
// according to whether or not the document is at the top of the screen.
|
||||
// But when doing that, each OSK row top and height gets modified by Chrome
|
||||
// looking very ugly. It would be best to hide the OSK then show it again
|
||||
// when the user scroll finishes, but Chrome has no way to reliably report
|
||||
// the touch end event after a move. c.f. http://code.google.com/p/chromium/issues/detail?id=152913
|
||||
// The best compromise behaviour is simply to hide the OSK whenever any
|
||||
// non-input and non-OSK element is touched.
|
||||
_this.deactivateOnScroll=false;
|
||||
if(device.OS == 'Android' && navigator.userAgent.indexOf('Chrome') > 0) {
|
||||
// _this.deactivateOnScroll has the inverse of the 'true' default,
|
||||
// but that fact actually facilitates the following conditional logic.
|
||||
if(typeof(osk._Box) == 'undefined') return false;
|
||||
if(typeof(osk._Box.style) == 'undefined') return false;
|
||||
|
||||
// The following tests are needed to prevent the OSK from being hidden during normal input!
|
||||
let p=(e.target as HTMLElement).parentElement;
|
||||
if(typeof(p) != 'undefined' && p != null) {
|
||||
if(p.className.indexOf('kmw-key-') >= 0) return false;
|
||||
if(typeof(p.parentElement) != 'undefined' && p.parentElement != null) {
|
||||
p=p.parentElement;
|
||||
if(p.className.indexOf('kmw-key-') >= 0) return false;
|
||||
}
|
||||
}
|
||||
|
||||
_this.deactivateOnScroll = true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
this.touchMoveActivationHandler = function(e) {
|
||||
if(_this.deactivateOnScroll) { // Android / Chrone case.
|
||||
DOMEventHandlers.states.focusing = false;
|
||||
_this.activeElement = null;
|
||||
}
|
||||
|
||||
const y = e.touches[0].screenY;
|
||||
const y0 = _this.touchY;
|
||||
if(y-y0 > 5 || y0-y < 5) {
|
||||
_this.deactivateOnRelease = false;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
this.touchEndActivationHandler = function() {
|
||||
// Should not hide OSK if simply closing the language menu (30/4/15)
|
||||
// or if the focusing timer (setFocusTimer) is still active.
|
||||
if(_this.deactivateOnRelease && !osk['lgList'] && !DOMEventHandlers.states.focusing) {
|
||||
_this.activeElement = null;
|
||||
}
|
||||
_this.deactivateOnRelease=false;
|
||||
return false;
|
||||
};
|
||||
|
||||
this.keyman.util.attachDOMEvent(document.body, 'touchstart', this.touchStartActivationHandler,false);
|
||||
this.keyman.util.attachDOMEvent(document.body, 'touchmove', this.touchMoveActivationHandler, false);
|
||||
this.keyman.util.attachDOMEvent(document.body, 'touchend', this.touchEndActivationHandler, false);
|
||||
}
|
||||
|
||||
//document.body.appendChild(keymanweb._StyleBlock);
|
||||
|
|
@ -572,13 +473,8 @@ namespace com.keyman.dom {
|
|||
|
||||
// Set exposed initialization flag to 2 to indicate deferred initialization also complete
|
||||
|
||||
/* To prevent propagation of focus & blur events from the input-scroll workaround,
|
||||
* we attach top-level capturing listeners to the focus & blur events. They prevent propagation
|
||||
* but NOT default behavior, allowing the scroll to complete while preventing nearly all
|
||||
* possible event 'noise' that could result from the workaround.
|
||||
*/
|
||||
this.keyman.util.attachDOMEvent(document.body, 'focus', DOMManager.suppressFocusCheck, true);
|
||||
this.keyman.util.attachDOMEvent(document.body, 'blur', DOMManager.suppressFocusCheck, true);
|
||||
// Other initialization details after this point have already been modularized:
|
||||
// within app/browser KeymanEngine.init, see `setupOskListeners` call and after.
|
||||
|
||||
this.keyman.setInitialized(2);
|
||||
return Promise.resolve();
|
||||
|
|
@ -648,15 +544,5 @@ namespace com.keyman.dom {
|
|||
window.setTimeout(this.initializeUI.bind(this),1000);
|
||||
}
|
||||
}
|
||||
|
||||
static suppressFocusCheck(e: Event) {
|
||||
if(DOMEventHandlers.states._IgnoreBlurFocus) {
|
||||
// Prevent triggering other blur-handling events (as possible)
|
||||
e.stopPropagation();
|
||||
e.cancelBubble = true;
|
||||
}
|
||||
// But DO perform default event behavior (actually blurring & focusing the affected element)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -103,26 +103,11 @@ if(!window['keyman']['initialized']) {
|
|||
keymanweb.debugElement=null;
|
||||
var dbg=keymanweb.debug;
|
||||
|
||||
keymanweb.delayedInit();
|
||||
|
||||
//TODO: find all references to next three routines and disambiguate!!
|
||||
|
||||
// Complete page initialization only after the page is fully loaded, including any embedded fonts
|
||||
// This avoids the need to use a timer to test for the fonts
|
||||
|
||||
util.attachDOMEvent(window, 'load', keymanweb.domManager._WindowLoad,false);
|
||||
util.attachDOMEvent(window, 'unload', keymanweb.domManager._WindowUnload,false); // added fourth argument (default value)
|
||||
|
||||
// *** I3319 Supplementary Plane modifications - end new code
|
||||
|
||||
util.attachDOMEvent(document, 'keyup', keymanweb.hotkeyManager._Process, false);
|
||||
|
||||
// We need to track this handler, as it causes... interesting... interactions during testing in certain browsers.
|
||||
util.attachDOMEvent(window, 'focus', keymanweb.pageFocusHandler, false); // I775
|
||||
util.attachDOMEvent(window, 'blur', keymanweb.pageFocusHandler, false); // I775
|
||||
|
||||
// Initialize supplementary plane string extensions
|
||||
String.kmwEnableSupplementaryPlane(true);
|
||||
|
||||
})();
|
||||
}
|
||||
|
|
@ -176,22 +176,6 @@ namespace com.keyman {
|
|||
this['loaded'] = true;
|
||||
}
|
||||
|
||||
delayedInit() {
|
||||
// Track the selected Event-handling object.
|
||||
this.touchAliasing = this.util.device.touchable ? this.domManager.touchHandlers : this.domManager.nonTouchHandlers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset context when entering or exiting the active element.
|
||||
* Will also trigger OSK shift state / layer reset.
|
||||
**/
|
||||
pageFocusHandler = () => {
|
||||
if(!focusAssistant.maintainingFocus && this.osk?.vkbd) {
|
||||
this.core.resetContext(null);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers a KeymanWeb engine shutdown to facilitate a full system reset.
|
||||
* This function is designed for use with KMW unit-testing, which reloads KMW
|
||||
|
|
@ -199,14 +183,6 @@ namespace com.keyman {
|
|||
*/
|
||||
['shutdown']() {
|
||||
// Disable page focus/blur events, which can sometimes trigger and cause parallel KMW instances in testing.
|
||||
this.util.detachDOMEvent(window, 'focus', this.pageFocusHandler, false);
|
||||
this.util.detachDOMEvent(window, 'blur', this.pageFocusHandler, false);
|
||||
|
||||
this.domManager.shutdown();
|
||||
this.osk.shutdown();
|
||||
this.util.shutdown();
|
||||
this.keyboardManager.shutdown();
|
||||
this.core.languageProcessor.shutdown();
|
||||
|
||||
if(this.ui && this.ui.shutdown) {
|
||||
this.ui.shutdown();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue