mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-09 18:35:32 +00:00
chore(web): modularization of blur, focus, and text-change events
This commit is contained in:
parent
629b8c8d64
commit
e9fa472f69
8 changed files with 76 additions and 57 deletions
|
|
@ -40,7 +40,7 @@ export class BrowserConfiguration extends EngineConfiguration {
|
|||
const ruleTransform = ruleBehavior.transcription.transform;
|
||||
if(!isEmptyTransform(ruleTransform)) {
|
||||
if(outputTarget instanceof DOMOutputTarget) {
|
||||
dom.DOMEventHandlers.states.changed = true;
|
||||
outputTarget.changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,12 @@ import { type Keyboard, Mock } from '@keymanapp/keyboard-processor';
|
|||
import { type KeyboardStub } from 'keyman/engine/package-cache';
|
||||
import { CookieSerializer } from 'keyman/engine/dom-utils';
|
||||
import { PageContextAttachment } from 'keyman/engine/attachment';
|
||||
import { LegacyEventEmitter } from 'keyman/engine/events';
|
||||
import { DesignIFrame, OutputTarget, nestedInstanceOf } from 'keyman/engine/element-wrappers';
|
||||
import {
|
||||
ContextManagerBase,
|
||||
type KeyboardInterface
|
||||
type KeyboardInterface,
|
||||
LegacyAPIEvents
|
||||
} from 'keyman/engine/main';
|
||||
import { BrowserConfiguration } from './configuration.js';
|
||||
import { FocusAssistant } from './context/focusAssistant.js';
|
||||
|
|
@ -102,6 +104,16 @@ export default class ContextManager extends ContextManagerBase<BrowserConfigurat
|
|||
|
||||
private globalKeyboard: {keyboard: Keyboard, metadata: KeyboardStub};
|
||||
|
||||
private _eventsObj: () => LegacyEventEmitter<LegacyAPIEvents>;
|
||||
|
||||
constructor(engineConfig: BrowserConfiguration, eventsClosure: () => LegacyEventEmitter<LegacyAPIEvents>) {
|
||||
super(engineConfig);
|
||||
}
|
||||
|
||||
get apiEvents(): LegacyEventEmitter<LegacyAPIEvents> {
|
||||
return this._eventsObj();
|
||||
}
|
||||
|
||||
initialize(): void {
|
||||
this.on('keyboardasyncload', (stub, completion) => {
|
||||
// TODO: app/browser - display the loader UI if configured?
|
||||
|
|
@ -416,13 +428,19 @@ export default class ContextManager extends ContextManagerBase<BrowserConfigurat
|
|||
// Ltarg=Ltarg.contentWindow.document.body; // And we only care about Ltarg b/c of finding the OutputTarget.
|
||||
// }
|
||||
|
||||
// Step 2: Make it active.
|
||||
// Save it for the event in step 3... but now, before we mutate the field's value!
|
||||
const previousTarget = this.lastActiveTarget;
|
||||
|
||||
// Step 2: Make the newly-focused control the active control, and thus the active context.
|
||||
this.setActiveTarget(target);
|
||||
|
||||
// Step 3: related events
|
||||
|
||||
// //Execute external (UI) code needed on focus if required
|
||||
// this.doControlFocused(LfocusTarg, this.keyman.domManager.lastActiveElement);
|
||||
this.apiEvents.callEvent('controlfocused', {
|
||||
target: target.getElement(),
|
||||
activeControl: previousTarget.getElement()
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -450,8 +468,8 @@ export default class ContextManager extends ContextManagerBase<BrowserConfigurat
|
|||
}
|
||||
|
||||
// Step 1: determine the corresponding OutputTarget instance.
|
||||
let Ltarg = eventOutputTarget(e);
|
||||
if (Ltarg == null) {
|
||||
let target = eventOutputTarget(e);
|
||||
if (target == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -465,8 +483,9 @@ export default class ContextManager extends ContextManagerBase<BrowserConfigurat
|
|||
|
||||
// Step 3: Now that we've handled all prior-element maintenance, update the active and 'last-active element'.
|
||||
// (The "context target" state fields)
|
||||
const previousTarget = this.activeTarget;
|
||||
this.currentTarget = null; // I3363 (Build 301)
|
||||
this.mostRecentTarget = Ltarg;
|
||||
this.mostRecentTarget = target;
|
||||
|
||||
// Step 4: any and all related events
|
||||
/* If the KeymanWeb UI is active as a user changes controls, all UI-based effects
|
||||
|
|
@ -478,13 +497,30 @@ export default class ContextManager extends ContextManagerBase<BrowserConfigurat
|
|||
let activeKeyboard = this.activeKeyboard;
|
||||
const maintainingFocus = this.focusAssistant.maintainingFocus;
|
||||
if(!maintainingFocus && activeKeyboard) {
|
||||
activeKeyboard.keyboard.notify(0, Ltarg, 0); // I2187
|
||||
activeKeyboard.keyboard.notify(0, target, 0); // I2187
|
||||
}
|
||||
if(previousTarget && !this.activeTarget) {
|
||||
this.emit('targetchange', null);
|
||||
}
|
||||
|
||||
// TODO: these related events.
|
||||
// this.doControlBlurred(Ltarg, e, maintainingFocus);
|
||||
// this.doChangeEvent(Ltarg);
|
||||
this.apiEvents.callEvent('controlblurred', {
|
||||
target: target.getElement(),
|
||||
event: e,
|
||||
isActivating: maintainingFocus
|
||||
});
|
||||
|
||||
// Is not an "API event"; it models a native browser event instead.
|
||||
this.doChangeEvent(target);
|
||||
this.resetContext();
|
||||
return true;
|
||||
}
|
||||
|
||||
doChangeEvent(target: OutputTarget<any>) {
|
||||
if(target.changed) {
|
||||
let event = new Event('change', {"bubbles": true, "cancelable": false});
|
||||
target.getElement().dispatchEvent(event);
|
||||
}
|
||||
|
||||
target.changed = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -17,8 +17,10 @@ export class KeymanEngine extends KeymanEngineBase<ContextManager, KeyEventKeybo
|
|||
this.contextManager.restoreLastActiveTarget();
|
||||
}
|
||||
|
||||
constructor(worker: Worker, config: BrowserConfiguration) {
|
||||
super(worker, config, new ContextManager(config));
|
||||
constructor(worker: Worker, sourceUri: string) {
|
||||
const config = new BrowserConfiguration(sourceUri); // currently set to perform device auto-detect.
|
||||
|
||||
super(worker, config, new ContextManager(config, () => this.legacyAPIEvents));
|
||||
|
||||
// Scrolls the document-body to ensure that a focused element remains visible after the OSK appears.
|
||||
this.contextManager.on('targetchange', (target: OutputTarget<any>) => {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,12 @@ export default abstract class OutputTarget<EventMap extends EventEmitter.ValidEv
|
|||
// JS/TS can't do true multiple inheritance, so we maintain class events on a readonly field.
|
||||
public readonly events: EventEmitter<EventMap, this> = new EventEmitter<EventMap, this>();
|
||||
|
||||
/**
|
||||
* A field that may be used to track whether or not the represented context has changed over an
|
||||
* arbitrary period of time.
|
||||
*/
|
||||
public changed = false;
|
||||
|
||||
/**
|
||||
* Returns the underlying element / document modeled by the wrapper.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -3,4 +3,5 @@ export { ContextManagerBase, ContextManagerConfiguration } from './contextManage
|
|||
export { default as HardKeyboard } from './hardKeyboard.js';
|
||||
export { default as KeyboardInterface } from './keyboardInterface.js';
|
||||
export { default as KeymanEngine } from './keymanEngine.js';
|
||||
export { LegacyAPIEvents } from './legacyAPIEvents.js';
|
||||
export { VariableStoreCookieSerializer } from './variableStoreCookieSerializer.js';
|
||||
|
|
@ -24,7 +24,7 @@ export default class KeymanEngine<
|
|||
readonly keyboardRequisitioner: KeyboardRequisitioner;
|
||||
readonly modelCache: ModelCache;
|
||||
|
||||
private legacyAPIEvents = new LegacyEventEmitter<LegacyAPIEvents>();
|
||||
protected legacyAPIEvents = new LegacyEventEmitter<LegacyAPIEvents>();
|
||||
private _hardKeyboard: HardKeyboard;
|
||||
private _osk: OSKView;
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,23 @@ export interface LegacyAPIEvents extends LegacyEventMap {
|
|||
languageCode: string
|
||||
}) => boolean;
|
||||
|
||||
'controlfocused': (p: {
|
||||
/**
|
||||
* The element gaining focus
|
||||
*/
|
||||
target: HTMLElement,
|
||||
/**
|
||||
* The previous element to have focus
|
||||
*/
|
||||
activeControl: HTMLElement
|
||||
}) => boolean;
|
||||
|
||||
'controlblurred': (p: {
|
||||
target: HTMLElement,
|
||||
event: FocusEvent,
|
||||
isActivating: boolean
|
||||
}) => boolean;
|
||||
|
||||
// TODO: more of the documented API events. Note that any remaining events not seen here
|
||||
// yet go unused within the mobile apps.
|
||||
}
|
||||
|
|
@ -54,49 +54,6 @@ namespace com.keyman.dom {
|
|||
|
||||
// Universal DOM event handlers (both desktop + touch)
|
||||
|
||||
/**
|
||||
* Function doControlFocused
|
||||
* Scope Private
|
||||
* @param {Object} _target element gaining focus
|
||||
* @param {Object} _activeControl currently active control
|
||||
* @return {boolean}
|
||||
* Description Execute external (UI) code needed on focus
|
||||
*/
|
||||
doControlFocused(_target: HTMLElement, _activeControl: HTMLElement): boolean {
|
||||
var p={};
|
||||
p['target']=_target;
|
||||
p['activeControl']=_activeControl;
|
||||
|
||||
return this.keyman.util.callEvent('kmw.controlfocused',p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function doControlBlurred
|
||||
* Scope Private
|
||||
* @param {Object} _target element losing focus
|
||||
* @param {Event} _event event object
|
||||
* @param {(boolean|number)} _isActivating activation state
|
||||
* @return {boolean}
|
||||
* Description Execute external (UI) code needed on blur
|
||||
*/
|
||||
doControlBlurred(_target: HTMLElement, _event: Event, _isActivating: boolean|number): boolean {
|
||||
var p={};
|
||||
p['target']=_target;
|
||||
p['event']=_event;
|
||||
p['isActivating']=_isActivating;
|
||||
|
||||
return this.keyman.util.callEvent('kmw.controlblurred',p);
|
||||
}
|
||||
|
||||
doChangeEvent(_target: HTMLElement) {
|
||||
if(DOMEventHandlers.states.changed) {
|
||||
let event = new Event('change', {"bubbles": true, "cancelable": false});
|
||||
_target.dispatchEvent(event);
|
||||
}
|
||||
|
||||
DOMEventHandlers.states.changed = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function _KeyDown
|
||||
* Scope Private
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue