diff --git a/packages/plugin-devtools/assets/DevTools.css b/packages/plugin-devtools/assets/DevTools.css
index 8c439fea8..cfb5a1813 100644
--- a/packages/plugin-devtools/assets/DevTools.css
+++ b/packages/plugin-devtools/assets/DevTools.css
@@ -360,3 +360,25 @@ devtools-info devtools-about devtools-id {
color: #76526c;
font-size: smaller;
}
+
+/* Keystrokes display */
+#keys_log {
+ opacity: 0;
+ padding: 5px;
+ z-index: 100000;
+ margin: 25px;
+ position: fixed;
+ top: 0px;
+ background-color: #875A7B;
+ color: white;
+ font-size: 25px;
+ border: 1px solid #68465f;
+ border-radius: 10px;
+ -webkit-box-shadow: 2px 2px 5px 0px rgba(0,0,0,0.42);
+ -moz-box-shadow: 2px 2px 5px 0px rgba(0,0,0,0.42);
+ box-shadow: 2px 2px 5px 0px rgba(0,0,0,0.42);
+}
+#keys_log.has-key {
+ transition: opacity .5s;
+ opacity: 1;
+}
diff --git a/packages/plugin-devtools/assets/DevTools.xml b/packages/plugin-devtools/assets/DevTools.xml
index f35acfb9a..1ae5a36be 100644
--- a/packages/plugin-devtools/assets/DevTools.xml
+++ b/packages/plugin-devtools/assets/DevTools.xml
@@ -494,7 +494,10 @@
Modes
- 🔍
+ 🔍
+ 🅺
diff --git a/packages/plugin-devtools/src/components/DevToolsComponent.ts b/packages/plugin-devtools/src/components/DevToolsComponent.ts
index ccfc8b84b..b93258f0e 100644
--- a/packages/plugin-devtools/src/components/DevToolsComponent.ts
+++ b/packages/plugin-devtools/src/components/DevToolsComponent.ts
@@ -7,14 +7,17 @@ import { OwlComponent } from '../../../plugin-owl/src/OwlComponent';
import { CommandIdentifier, CommandParams } from '../../../core/src/Dispatcher';
import { nodeName } from '../../../utils/src/utils';
import { hooks } from '@odoo/owl';
+import { ProcessKeydownParams } from '../../../plugin-dom-layout/src/DomLayout';
-////////////////////////////// todo: use API ///////////////////////////////////
+const keystrokeDiv = document.createElement('div');
+keystrokeDiv.id = 'keys_log';
interface DevToolsState {
closed: boolean; // Are the devtools open?
height: number; // In px
currentTab: string; // Name of the current tab
commands: Array<[CommandIdentifier, CommandParams]>;
+ displayKeystrokes: boolean;
}
export class DevToolsComponent extends OwlComponent {
@@ -33,18 +36,34 @@ export class DevToolsComponent extends OwlComponent {
currentTab: 'inspector',
height: 300,
commands: [], // Stack of all commands executed since init.
+ displayKeystrokes: false,
};
- localStorage = ['closed', 'currentTab', 'height'];
+ localStorage = ['closed', 'currentTab', 'height', 'displayKeystrokes'];
// For resizing/opening (see toggleClosed)
_heightOnLastMousedown: number;
async willStart(): Promise {
this.env.editor.dispatcher.registerCommandHook('*', this.addCommand.bind(this));
this.env.editor.dispatcher.registerCommandHook('@commit', this.render.bind(this));
+ this.env.editor.dispatcher.registerCommandHook(
+ '@layout-keydown',
+ this.displayKeystroke.bind(this),
+ );
return super.willStart();
}
+ async mounted(): Promise {
+ if (this.state.displayKeystrokes) {
+ if (!document.getElementById('keys_log')) {
+ document.body.appendChild(keystrokeDiv);
+ }
+ }
+ return super.mounted();
+ }
willUnmount(): void {
this.state.commands = [];
+ if (this.state.displayKeystrokes && keystrokeDiv.parentNode === document.body) {
+ document.body.removeChild(keystrokeDiv);
+ }
}
//--------------------------------------------------------------------------
@@ -69,6 +88,45 @@ export class DevToolsComponent extends OwlComponent {
this.state.currentTab = 'inspector';
(this.inspectorRef.comp as InspectorComponent)?.inspectDom();
}
+ /**
+ * Toggle display keystrokes.
+ */
+ toggleKeystrokes(): void {
+ this.state.displayKeystrokes = !this.state.displayKeystrokes;
+ if (this.state.displayKeystrokes) {
+ if (!document.getElementById('keys_log')) {
+ document.body.appendChild(keystrokeDiv);
+ }
+ } else if (keystrokeDiv.parentNode === document.body) {
+ document.body.removeChild(keystrokeDiv);
+ }
+ }
+ /**
+ * Display the key hit on the screen.
+ *
+ * @param params
+ */
+ displayKeystroke(params: ProcessKeydownParams): void {
+ if (!this.state.displayKeystrokes) return;
+ const ev = params.event;
+ keystrokeDiv.textContent = '';
+ keystrokeDiv.className = '';
+ if (['Control', 'Alt', 'Shift', 'Meta'].includes(ev.key)) {
+ keystrokeDiv.className = 'has-key';
+ keystrokeDiv.textContent = `'${ev.key}'`;
+ } else if (ev.key) {
+ keystrokeDiv.className = 'has-key';
+ const keyModifiers = [
+ ev.metaKey ? 'Meta+' : false,
+ ev.ctrlKey ? 'Ctrl+' : false,
+ ev.shiftKey ? 'Shift+' : false,
+ ev.altKey ? 'Alt+' : false,
+ ]
+ .filter(mod => mod)
+ .join('');
+ keystrokeDiv.textContent = `${keyModifiers}'${ev.key}'`;
+ }
+ }
/**
* Add the recent dispatching of the given command with the given arguments.
*/
diff --git a/packages/plugin-dom-layout/src/DomLayout.ts b/packages/plugin-dom-layout/src/DomLayout.ts
index 404d13472..e557c8a8b 100644
--- a/packages/plugin-dom-layout/src/DomLayout.ts
+++ b/packages/plugin-dom-layout/src/DomLayout.ts
@@ -15,7 +15,7 @@ import { ZoneXmlDomParser } from './ZoneXmlDomParser';
import { LayoutContainerDomObjectRenderer } from './LayoutContainerDomObjectRenderer';
import { ZoneIdentifier, ZoneNode } from '../../plugin-layout/src/ZoneNode';
import { Keymap } from '../../plugin-keymap/src/Keymap';
-import { CommandIdentifier } from '../../core/src/Dispatcher';
+import { CommandIdentifier, CommandParams } from '../../core/src/Dispatcher';
import { ActionableDomObjectRenderer } from './ActionableDomObjectRenderer';
import { ActionableGroupDomObjectRenderer } from './ActionableGroupDomObjectRenderer';
import { ActionableGroupSelectItemDomObjectRenderer } from './ActionableGroupSelectItemDomObjectRenderer';
@@ -33,6 +33,9 @@ export interface DomLayoutConfig extends JWPluginConfig {
componentZones?: [ComponentId, ZoneIdentifier[]][];
pressedActionablesClassName?: string;
}
+export interface ProcessKeydownParams extends CommandParams {
+ event: KeyboardEvent;
+}
export class DomLayout extends JWPlugin {
static dependencies = [DomObjectRenderer, Parser, Renderer, Layout, Keymap];
@@ -129,6 +132,9 @@ export class DomLayout extends JWPl
event: KeyboardEvent,
processingContext: ExecutionContext = this.editor,
): Promise {
+ await this.editor.dispatcher.dispatchHooks('@layout-keydown', {
+ event: event,
+ } as ProcessKeydownParams);
if (
this.focusedNode &&
['INPUT', 'SELECT', 'TEXTAREA'].includes(nodeName(this.focusedNode))