Wasm apparently doesn't handle keyboard shortcuts
-
I almost got it right, the reason why the 2 actions worked is because I changed their Shortcut Context parameter value to "Application shortcut". Now I changed that for all other shortcut, and they work as well. But I still need to have the menubar selected to have them work, maybe do I need to change the QAction parent to the QGraphicsScene ?
I finally found a workaround by removing the application menu bar and instead adding toolbars at the top of the 3 widgets I'm using.
-
G Gilboonet has marked this topic as solved on
-
I almost got it right, the reason why the 2 actions worked is because I changed their Shortcut Context parameter value to "Application shortcut". Now I changed that for all other shortcut, and they work as well. But I still need to have the menubar selected to have them work, maybe do I need to change the QAction parent to the QGraphicsScene ?
I finally found a workaround by removing the application menu bar and instead adding toolbars at the top of the 3 widgets I'm using.
@Gilboonet I would create a bug regarding the menu bar shortcuts if you think they are not work properly as they should in webassembly. That would likely fix the issue for everyone.
-
@Gilboonet I would create a bug regarding the menu bar shortcuts if you think they are not work properly as they should in webassembly. That would likely fix the issue for everyone.
@RandomGuy For the moment I'm still investigating.
- With Application Windows MenuBar populated, my GraphicsView never receive keyboard shortcuts nor keyboard events event when I click on it to give it the focus.
- Without MenuBar and with ToolBar as first widget of my GraphicsView's parent layout, it works only if I only use simple ToolButtons, and even then, if I simply hover over one of those ToolButtons and let the tooltip appear, the GraphicsView stop receiving key events.
- If I try to add a ToolButton with its popupmode set and add a menu to it, WA crashes
- With a ComboBox instead of the ToolBar, I can have a menu remplacement but my GraphicsView doesn't receive keyboard shortcuts not keyboard events (I redefined a customGraphicsView to handle key events for that).
I'm open for any solution where a main menu (I have file options , 3d options, and 2d options and need menus, drop down buttons or combo for the user to be able to select them) is usable while the user is able to use the keyboard to move things (on the 3d view but also the 2d), and for the moment I didn't find a way to do so on WA.
-
G Gilboonet has marked this topic as unsolved on
-
Hello, I now have more details about this behaviour. First, I got rid of the menubar and shortcuts and I'm only using keyboard events from a keyPressEvent on my MainWindow class. But the problem is remaining, there are some widget interactions that stops those keyboard events to he handled even if the focus is on my 3d view. The concerned widgets are a dialog (QColorDialog) and clicked comboboxes, and anything that showed a tooltip. After their interaction I always need to press "TAB" (and also click into the 3d view, a subclassed QGraphicsView, after the dialog closes) to be able to use the keyboard. I tried to use releaseKeyboard() on those widgets grabKeyboard() on my 3d view, but it didn't work. So there is maybe a WA bug when interacting with widgets that showed a tooltip as that works fine with Desktop version.

-
For the moment I removed the QColorDialog and each time the "+" button is pressed I add a new QColor from a preloaded list, it helps keeping the editing flow fluid, but there are still moments where I still need to press TAB to be able to use the keyboard to turn the 3d model. I will maybe replace the widgets that show a tooltip by a QGraphicsScene.
-
Here's how I create my MainWindow. There's no .ui file. I create a splitter, then 4 widgets that are added to the splitter. I create 4 VBoxLayouts that are each parented with one of the 4 widgets. The first VBoxLayout is for the main menu that is a toolbar, so I add create a toolbar that I add to it as widget, the others ones have not only a toolbar but also another widget, so I create a toolbar for each of them and set it with setMenuBar(). The widgets are : for the second VBoxLayout, a TableWidget, and for the third and the fourth ones a subclassed QGraphicsView, one for 3d view and one for 2d view. All the keyboard events are handled into the MainWindow KeyPressEvent() handler. That's it. My problem is that each time the mouse hovers some widget that has a tooltip, I need to press TAB key to be able to have my keystrokes handled. What are the things I can do to get rid of that compulsory TAB press ?
-
While waiting for next update of WebAssembly, I have now compiled my application both on Windows, Linux and Mac OS, and they all run fine, no loss of keyboard handling after a tooltip has shown or a dialog opened (and closed). I also compiled and deployed WebAssembly with Windows and Mac Os and the problem appear exactly the same. I'm about to try to port my application using Dear ImGui to see if its generated WebAssembly has the same behaviour but finally I may simply port it to javascript/html trying to use my C++ code as much as possible.
-
I am having a similar issue too. In one of my views (mixed Qml and C++) I have a Node editor, where nodes can be added and connected. I used to handle the
Keys.onPressedevent and delete selected nodes and connection when Delete is pressed. This worked very well with Qt 6.6.1 in Webassembly (I use Linux). Now, when I switched to 6.8.1,Keys.onPressedevent is never generated in Webassempbly (works well on desktop). My scene receives the active focus (I am loggingonActiveFocusChanged) but not the keyboard events. -
I'm currently working on debugging a similar problem. It seems that with webassembly ( at least in versions 6.9 and 6.10, I'm not sure when this started) there is an issue (unclear if it's a feature or a bug) where the HTML document's "activeElement" is not set correctly to send keyboard events to your embedded Qt Webassembly module. Thus even if the object has focus according to Qt, the entire webassembly module does NOT have focus according to your browser, so no keyboard events are received.
To receive keyboard events in Qt, the document's activeElement needs to be the div whose id is "qt-shadow-container". (I'm not actually sure where/when this div gets created, as i can find no reference to it in the html or js files generated by em++, or in any Qt documentation.)
Clicking on QWidgets (which sets their focus in Qt) does NOT change the document's activeElement to be this div. Instead, it actually unsets the activeElement (i.e. if you call document.activeElement from javascript it will return the BODY element, not any div), so nothing receives keyboard events! An exception to this rule is if you click on an explicit text-entry widget (such as a QLineEdit). In that case, I think it creates a new INPUT element on the fly and it becomes the activeElement (and subsequently receives keyboard events as it should). But when you finish with that INPUT (such as by clicking away from it), the activeElement is again unset.
As noted above, pressing "tab" when there is no activeElement WILL cycle the focus to the qt-shadow-container, so you will then be able to use keypressevents. But many subsequent actions you can take (such as clicking) will cause the focus to again become unset.
I'm working on a fix that involves adding javascript functions in the .html file that detect focusOut events and always give focus back to the qt-shadow-container div. I'll post it if/when i get it working.
-
I figured out some more stuff, including a workaround for Qt 6.9.0 that doesnt require rebuilding the webassembly module from source. I'm able to reproduce this "bug" using just the calendarwidget example, so it at seems to happen in relatively simple situations.
Hopefully this is helpful to other people with this same problem. I suspect there are easier ways to accomplish all of this...
First off, i discovered that qt-shadow-container is defined in the file qwasmwindow.cpp which you can find in \Src\qtbase\src\plugins\platforms\wasm. From looking at that file I figured out that this div is used to display a "shadow dom" that contains the real Qt content. The importance of this is that when an element in the shadow dom has focus, document.activeElement shows the shadow root (qt-shadow-container) as the activeElement even though it isn't really. So, in fact, it's not true that qt-shadow-container has/needs focus in order for you to receive keyboard events. I discovered that when you press "Tab", the focus ACTUALLY goes to an invisible button element whose class is "hidden-visually-read-by-screen-reader". I think this is actually something of a coincidence: buttons are inherently able to receive tab focus and so this just happens to be what receives the focus when you press tab. And it's part of the shadow dom hierarchy in such a way that, when it has focus, Qt will process the keypress events that it receives.
So, what does this all mean?
Long story short, you can use javascript to programmatically force the "hidden-visually-read-by-screen-reader" to always have focus, which will ensure that Qt processes all of your keypress events.
First problem: when you open your website, no object has focus, so we need an initial action that sets the focus. (Note, i think in Qt 6.10 it DOES initially have focus so you may not need this step) You can do this by pressing "Tab", or you can set it programmatically with javascript. It's a little difficult because the button we want to focus on doesn't exist when you first open the page (it doesnt exist until Qt creates it). So you need to delay focusing on it until you're sure it exists. I solved this by modifying the "onLoaded" callback function in generated html file to look like this:
onLoaded: () => { showUi(screen); setTimeout(function() { console.log('setting initial focus'); const shadowContainer = document.getElementById('qt-shadow-container'); if(shadowContainer) { const shadowRoot = document.getElementById('qt-shadow-container').shadowRoot; if (shadowRoot) { console.log('shadow root found'); console.log(document.getElementById('qt-shadow-container')); const targetElement = shadowRoot.querySelector('.hidden-visually-read-by-screen-reader'); console.log(targetElement); targetElement.focus(); } else { console.log('Shadow Root not found or is closed.'); } } }, 1000); },So, it waits 1 second and then sets the focus. If I dont have it wait then it fails to find the required objects.
Second problem: Restore focus to this object any time no object has focus. For this I added another script to the html file that looks like this:
<script> document.addEventListener('focusout', function (event) { console.log('Element lost focus:', event.target); console.log('Element gaining focus:', event.relatedTarget); if (event.relatedTarget === null) { const shadowRoot = document.getElementById('qt-shadow-container').shadowRoot; if (shadowRoot) { const targetElement = shadowRoot.querySelector('.hidden-visually-read-by-screen-reader'); console.log(targetElement); targetElement.focus(); } else { console.log('Shadow Root not found or is closed.'); } } }); </script>So, basically the same function but this time it runs when an element loses focus and no new element is gaining focus.
Third problem: This one is probably a lot more obscure, but i was running into problems implementing a custom "createEditor" function on QAbstractItemDelegate where the html INPUT object corresponding to the editor never lost focus, even once "destroyEditor" ran (notably, my destroyEditor function did not delete the editor but only hid it). To solve, this, I implemented a javascript function to be called by Qt to force the input to lose focus:
#ifdef Q_OS_WASM #include "emscripten.h" EM_JS(void, resetFocus, (), { console.log('removing focus from INPUT'); document.activeElement.blur(); //This takes focus away from the active element, which may be an INPUT, and returns it to the BODY element }); #endif...then you can just call resetFocus() in any situation where you find the INPUT is keeping focus. This will trigger a focusout even and then the above function takes over.
(How do you know if INPUT is keeping focus? FOr debugging i set up a QTimer that runs another javascript function that just does "console.log(document.activeElement)" every few seconds. Then you can watch the console to see what object in the DOM has keyboard focus as you test)