From 142684dbe7e5c9d5c5fc1baa00b1027367a45213 Mon Sep 17 00:00:00 2001
From: Guillaume Dieleman <gdi@odoo.com>
Date: Tue, 24 Jan 2023 15:00:23 +0000
Subject: [PATCH] [IMP] web_editor: add touchscreen support for the editor

Before this commit, website and email editing was very limited on
devices that have a touchscreen. The blocks tab, the we-lists, ... were
unusable.
Example of issues (on a device that has a touchscreen):
- The users cannot drag & drop snippets in the page.
- The users cannot reorder the values of a multiple checkboxes field.
- ...

This commit allows to handle touchscreen events by translating them
into mouse events to allow users to fully use the editor.

opw-3195487

closes odoo/odoo#114485

Signed-off-by: Quentin Smetz (qsm) <qsm@odoo.com>
---
 .../static/src/js/editor/snippets.editor.js   | 38 +++++++++++++++++++
 1 file changed, 38 insertions(+)

diff --git a/addons/web_editor/static/src/js/editor/snippets.editor.js b/addons/web_editor/static/src/js/editor/snippets.editor.js
index 855a85aaaafe..06ecb1f9a6e4 100644
--- a/addons/web_editor/static/src/js/editor/snippets.editor.js
+++ b/addons/web_editor/static/src/js/editor/snippets.editor.js
@@ -1861,6 +1861,12 @@ var SnippetsMenu = Widget.extend({
         // own window and not on the top window lest jquery behave unexpectedly.
         this.$el = this.window.$(this.$el);
         this.$el.data('snippetMenu', this);
+        // We need to activate the touch events to be able to drag and drop
+        // snippets on devices with a touch screen.
+        this.__onTouchEvent = this._onTouchEvent.bind(this);
+        document.addEventListener("touchstart", this.__onTouchEvent, true);
+        document.addEventListener("touchmove", this.__onTouchEvent, true);
+        document.addEventListener("touchend", this.__onTouchEvent, true);
 
         this.customizePanel = document.createElement('div');
         this.customizePanel.classList.add('o_we_customize_panel', 'd-none');
@@ -2063,6 +2069,10 @@ var SnippetsMenu = Widget.extend({
      */
     destroy: function () {
         this._super.apply(this, arguments);
+        // Remove listeners for touch events.
+        document.removeEventListener("touchstart", this.__onTouchEvent, true);
+        document.removeEventListener("touchmove", this.__onTouchEvent, true);
+        document.removeEventListener("touchend", this.__onTouchEvent, true);
         if (this.$window) {
             if (this.$snippetEditorArea) {
                 this.$snippetEditorArea.remove();
@@ -3816,6 +3826,34 @@ var SnippetsMenu = Widget.extend({
         // snippets could have changed on the page.
         await this._updateInvisibleDOM();
     },
+    /**
+     * Transforms an event coming from a touch screen into a mouse event.
+     *
+     * @private
+     * @param {Event} ev - a touch event
+     */
+    _onTouchEvent(ev) {
+        if (ev.touches.length > 1) {
+            // Ignore multi-touch events.
+            return;
+        }
+        const touch = ev.changedTouches[0];
+        const touchToMouse = {
+            touchstart: "mousedown",
+            touchmove: "mousemove",
+            touchend: "mouseup"
+        };
+        const simulatedEvent = new MouseEvent(touchToMouse[ev.type], {
+            screenX: touch.screenX,
+            screenY: touch.screenY,
+            clientX: touch.clientX,
+            clientY: touch.clientY,
+            button: 0, // left mouse button
+            bubbles: true,
+            cancelable: true,
+        });
+        touch.target.dispatchEvent(simulatedEvent);
+    },
     /**
      * Returns the droppable snippet from which a dropped snippet originates.
      *
-- 
GitLab