From 30ea81d0fbb2c88bdc40acf4da64a072fe2ad42f Mon Sep 17 00:00:00 2001 From: Meinzzzz Date: Mon, 8 Dec 2025 22:59:08 +0100 Subject: [PATCH] Improve virtual keyboard logic and fix Tab issues --- .../ckeditor5-math/src/ui/mainformview.ts | 28 +++++------ .../ckeditor5-math/src/ui/mathinputview.ts | 49 ++++++++++++++++--- 2 files changed, 55 insertions(+), 22 deletions(-) diff --git a/packages/ckeditor5-math/src/ui/mainformview.ts b/packages/ckeditor5-math/src/ui/mainformview.ts index cb96826a09..2c327517ce 100644 --- a/packages/ckeditor5-math/src/ui/mainformview.ts +++ b/packages/ckeditor5-math/src/ui/mainformview.ts @@ -134,42 +134,42 @@ export default class MainFormView extends View { public set equation( equation: string ) { const norm = equation.trim(); this.mathInputView.value = norm.length ? norm : null; - if ( this.mathView ) this.mathView.value = norm; + if ( this.mathView ) { + this.mathView.value = norm; + } } public focus(): void { this._focusCycler.focusFirst(); } - /** Handle delimiter stripping and preview updates. */ - private _setupSync(previewEnabled: boolean): void { - this.mathInputView.on('change:value', () => { - let eq = (this.mathInputView.value ?? '').trim(); + private _setupSync( previewEnabled: boolean ): void { + this.mathInputView.on( 'change:value', () => { + let eq = ( this.mathInputView.value ?? '' ).trim(); - // Strip delimiters if present (e.g. pasted content) - if (hasDelimiters(eq)) { - const params = extractDelimiters(eq); + if ( hasDelimiters( eq ) ) { + const params = extractDelimiters( eq ); eq = params.equation; this.displayButtonView.isOn = params.display; - // Update the input with stripped delimiters - if (this.mathInputView.value !== eq) { + if ( this.mathInputView.value !== eq ) { this.mathInputView.value = eq.length ? eq : null; } } - // Update preview - if (previewEnabled && this.mathView && this.mathView.value !== eq) { + if ( previewEnabled && this.mathView && this.mathView.value !== eq ) { this.mathView.value = eq; } - }); + } ); } private _createButton( label: string, icon: string, className: string, type?: 'submit' | 'button' ): ButtonView { const btn = new ButtonView( this.locale ); btn.set( { label, icon, tooltip: true } ); btn.extendTemplate( { attributes: { class: className } } ); - if (type) btn.type = type; + if ( type ) { + btn.type = type; + } return btn; } diff --git a/packages/ckeditor5-math/src/ui/mathinputview.ts b/packages/ckeditor5-math/src/ui/mathinputview.ts index 7df5c590df..b296c86aae 100644 --- a/packages/ckeditor5-math/src/ui/mathinputview.ts +++ b/packages/ckeditor5-math/src/ui/mathinputview.ts @@ -84,6 +84,11 @@ export default class MathInputView extends View { textarea.value = this.value ?? ''; textarea.readOnly = this.isReadOnly; + if ( this.mathfield ) { + this.mathfield.remove(); + this.mathfield = null; + } + textarea.addEventListener( 'input', () => { const val = textarea.value; if ( this.mathfield ) { @@ -92,11 +97,30 @@ export default class MathInputView extends View { this.value = val || null; } ); + this.on( 'change:value', ( _e, _n, val ) => { + const newVal = val ?? ''; + textarea.value = newVal; + if ( this.mathfield && this.mathfield.value !== newVal ) { + this.mathfield.setValue( newVal, { silenceNotifications: true } ); + } + } ); + this.on( 'change:isReadOnly', ( _e, _n, val ) => { textarea.readOnly = val; - if ( this.mathfield ) { this.mathfield.readOnly = val; } + if ( this.mathfield ) { + this.mathfield.readOnly = val; + } } ); + const vk = ( window as any ).mathVirtualKeyboard; + if ( vk ) { + vk.addEventListener( 'geometrychange', () => { + if ( vk.visible && document.activeElement === textarea && this.mathfield ) { + this.mathfield.focus(); + } + } ); + } + this._loadMathLive(); } @@ -111,17 +135,23 @@ export default class MathInputView extends View { MathfieldClass.plonkSound = null; } - if ( this.element ) { this._createMathField(); } + if ( this.element ) { + this._createMathField(); + } } catch ( e ) { console.error( 'MathLive load failed:', e ); const c = this.element?.querySelector( '.ck-mathlive-container' ); - if ( c ) { c.textContent = 'Math editor unavailable'; } + if ( c ) { + c.textContent = 'Math editor unavailable'; + } } } private _createMathField(): void { const container = this.element?.querySelector( '.ck-mathlive-container' ); - if ( !container ) { return; } + if ( !container ) { + return; + } const mf = document.createElement( 'math-field' ) as MathFieldElement; mf.mathVirtualKeyboardPolicy = 'auto'; @@ -131,8 +161,6 @@ export default class MathInputView extends View { mf.addEventListener( 'mount', () => { mf.inlineShortcuts = { ...mf.inlineShortcuts, dx: 'dx', dy: 'dy', dt: 'dt' }; - const btn = mf.shadowRoot?.querySelector( '[part="virtual-keyboard-toggle"]' ) as HTMLElement; - btn?.addEventListener( 'click', () => mf.focus() ); }, { once: true } ); mf.addEventListener( 'input', () => { @@ -153,13 +181,18 @@ export default class MathInputView extends View { public hideKeyboard(): void { const vk = ( window as any ).mathVirtualKeyboard; - if ( vk?.visible ) { vk.hide(); } + if ( vk?.visible ) { + vk.hide(); + } } public override destroy(): void { this.hideKeyboard(); if ( this.mathfield ) { - try { this.mathfield.blur(); this.mathfield.remove(); } catch { /* ignore */ } + try { + this.mathfield.blur(); + this.mathfield.remove(); + } catch { /* ignore */ } this.mathfield = null; } super.destroy();