diff --git a/web/source/dom/TouchAliasElement.ts b/web/source/dom/TouchAliasElement.ts index 282f1c975e..da75179082 100644 --- a/web/source/dom/TouchAliasElement.ts +++ b/web/source/dom/TouchAliasElement.ts @@ -48,14 +48,15 @@ namespace com.keyman.dom { */ class TouchAliasData { ['base']: HTMLElement = null; // NOT undefined; we can use this distinction for 'type-checking'. - __caretSpan: HTMLSpanElement; __preCaret: HTMLSpanElement; __postCaret: HTMLSpanElement; __scrollDiv: HTMLDivElement; __scrollBar: HTMLDivElement; + __caretSpan: HTMLSpanElement; __caretDiv: HTMLDivElement; __caretTimerId: number; + __activeCaret: boolean = false; __resizeHandler: () => void; @@ -310,12 +311,16 @@ namespace com.keyman.dom { getTextBeforeCaret() { return this.__preCaret.textContent; } - - setTextBeforeCaret(e: HTMLElement, t: string): void { + + getTextAfterCaret() { + return this.__postCaret.textContent; + } + + setTextBeforeCaret(t: string): void { var tLen=0; // Collapse (trailing) whitespace to a single space for INPUT fields (also prevents wrapping) - if(e.base.nodeName != 'TEXTAREA') { + if(this['base'].nodeName != 'TEXTAREA') { t=t.replace(/\s+$/,' '); } this.__preCaret.textContent=t; @@ -328,7 +333,7 @@ namespace com.keyman.dom { this.scrollInput(); } - getTextCaret(e: HTMLElement): number { + getTextCaret(): number { return this.getTextBeforeCaret()._kmwLength(); } @@ -360,8 +365,10 @@ namespace com.keyman.dom { } flashCaret: () => void = function(this: TouchAliasData): void { - // TODO: How to detect if this is the active Touch Alias w/o referencing KMW code? - if(/*this.keyman.util.device.touchable &&*/ DOMEventHandlers.states.activeElement != null) { + // Significant change - each element manages its own caret, and its activation is managed through show/hideCaret() + // without referencing core KMW code. (KMW must thus check if the active element is a TouchAliasElement, then use these + // methods as appropriate.) + if(this.__activeCaret) { var cs=this.__caretDiv.style; cs.visibility = cs.visibility != 'visible' ? 'visible' : 'hidden'; } @@ -382,6 +389,7 @@ namespace com.keyman.dom { cs.top=sp2.offsetTop+'px'; cs.height=(sp2.offsetHeight-1)+'px'; cs.visibility='hidden'; // best to wait for timer to display caret + this.__activeCaret = true; // Scroll into view if required this.scrollBody(); @@ -395,7 +403,7 @@ namespace com.keyman.dom { // Always copy text back to underlying field on blur if(e.base instanceof e.base.ownerDocument.defaultView.HTMLTextAreaElement - ||e.base instanceof e.base.ownerDocument.defaultView.HTMLInputElement) { + || e.base instanceof e.base.ownerDocument.defaultView.HTMLInputElement) { e.base.value = this.getText(); } @@ -403,12 +411,12 @@ namespace com.keyman.dom { this.setText(null, 100000); // Set the element scroll to zero (or max for RTL INPUT) - var ss=(e.firstChild as HTMLElement).style; + var ss=this.__scrollDiv.style; if(e.base.nodeName == 'TEXTAREA') { ss.top='0'; } else { if(e.base.dir == 'rtl') { - ss.left=(e.offsetWidth-(e.firstChild as HTMLElement).offsetWidth-8)+'px'; + ss.left=(e.offsetWidth - this.__scrollDiv.offsetWidth-8)+'px'; } else { ss.left='0'; } @@ -421,9 +429,9 @@ namespace com.keyman.dom { } this.__caretDiv.style.visibility='hidden'; - if(e.childNodes.length > 1 ) { - (e.childNodes[1] as HTMLElement).style.visibility='hidden'; - } + this.__scrollBar.style.visibility='hidden'; + + this.__activeCaret = false; } getText(): string { diff --git a/web/source/dom/editableElement.ts b/web/source/dom/editableElement.ts index b163c542ad..a17037a418 100644 --- a/web/source/dom/editableElement.ts +++ b/web/source/dom/editableElement.ts @@ -8,6 +8,8 @@ /// // Defines a basic design-mode IFrame wrapper. /// +// Defines a basic touch-alias element wrapper. +/// // Makes TS aware of Window-based type prototypes /// @@ -21,7 +23,7 @@ namespace com.keyman.dom { } else if(Utils.instanceof(e, "HTMLTextAreaElement")) { return new TextArea( e); } else if(Utils.instanceof(e, "TouchAliasElement")) { - // Touch-alias mode! + return new TouchAlias( e); } else if(Utils.instanceof(e, "HTMLIFrameElement")) { let iframe = e; diff --git a/web/source/dom/touchAlias.ts b/web/source/dom/touchAlias.ts new file mode 100644 index 0000000000..c5eb8d0f31 --- /dev/null +++ b/web/source/dom/touchAlias.ts @@ -0,0 +1,54 @@ +// Defines the TouchAliasElement merged type. +/// + +namespace com.keyman.dom { + export class TouchAlias implements EditableElement { + root: TouchAliasElement; + + constructor(e: TouchAliasElement) { + 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; + } + + hasSelection(): boolean { + // Always has an internal caret position. + return true; + } + + 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(); + this.root.setTextBeforeCaret(curText.kmwSubstring(0, this.root.getTextCaret() - dn)); + } + } + + insertTextBeforeCaret(s: string): void { + this.root.setTextBeforeCaret(this.root.getTextBeforeCaret() + s); + } + } +} \ No newline at end of file