spiegel-keyman/web/source/dom/targets/touchAlias.ts
Marc Durdin cacbbee5cc fix(web): properly handle selection
Relates to #5853 and others.

Selection management was not working properly with the various
OutputTargets:

1. When there is a non-empty selection, rules have no context -- it's
   like new text.
2. Backspace over a selection deletes just the selection.
3. Typing a character replaces the selection, of course, and collapses
   the caret to the end of the new text.
4. `hasSelection` is a very strange name for `OutputTarget` descendants.
   It doesn't mean "has an active selection" but rather, kinda means
   "supports selection internally".
5. Added `isSelectionEmpty` which is used for some of the new selection
   rules above.

Note that the `touchAlias` OutputTarget class does not currently support
selection. I hope we can deprecate `touchAlias` with the use of
`inputMode` (#3030) in the future, rather than adding support for
selection.
2022-02-23 15:12:58 +11:00

93 lines
No EOL
2.6 KiB
TypeScript

// Defines the TouchAliasElement merged type.
/// <reference path="../touchAliasElement.ts" />
namespace com.keyman.dom.targets {
export class TouchAlias extends OutputTarget {
root: TouchAliasElement;
constructor(e: TouchAliasElement) {
super();
this.root = e;
}
getElement(): HTMLDivElement {
return this.root;
}
clearSelection(): void {
// Touch-alias elements do not currently support selections.
return;
}
invalidateSelection(): void {
// Touch-alias elements do not currently support selections.
return;
}
isSelectionEmpty(): boolean {
// Touch-alias elements do not currently support selections.
return true;
}
hasSelection(): boolean {
// Always has an internal caret position.
return true;
}
getDeadkeyCaret(): number {
return this.root.getTextCaret();
}
getTextBeforeCaret(): string {
return this.root.getTextBeforeCaret();
}
getTextAfterCaret(): string {
return this.root.getTextAfterCaret();
}
getText(): string {
return this.root.getText();
}
deleteCharsBeforeCaret(dn: number): void {
if(dn > 0) {
let curText = this.getTextBeforeCaret();
if(this.getDeadkeyCaret() < dn) {
dn = this.getDeadkeyCaret();
}
this.adjustDeadkeys(-dn);
this.root.setTextBeforeCaret(curText.kmwSubstring(0, this.root.getTextCaret() - dn));
}
}
insertTextBeforeCaret(s: string): void {
this.adjustDeadkeys(s._kmwLength());
this.root.setTextBeforeCaret(this.root.getTextBeforeCaret() + s);
}
handleNewlineAtCaret(): void {
// Insert new line in text area fields
if(this.root.base.nodeName == 'TEXTAREA') {
// As the TouchAliasElement was implemented long before OutputTargets, it already has
// built-in newline handling.
this.insertTextBeforeCaret('\n');
} else if(dom.Utils.instanceof(this.root.base, "HTMLInputElement")) {
// HTMLInputElements do not permit newlines; they instead have DOM-specific behaviors.
this.root.hideCaret();
Input.newlineHandler(this.root.base as HTMLInputElement);
} else {
console.warn("TouchAlias OutputTarget cannot output newlines for unexpected base element types!");
}
}
protected setTextAfterCaret(s: string) {
this.root.setText(this.getTextBeforeCaret() + s, this.getTextBeforeCaret()._kmwLength());
}
doInputEvent() {
// Dispatch the event on the aliased element, not the TouchAliasElement itself.
this.dispatchInputEventOn(this.root.base);
}
}
}