letsfindaway / OpenBoard

I'm using this fork to contribute features and fixes to the upstream project. In order to create good pull requests, I'm rebasing my feature branches, squashing and reordering commits, etc. If you fork this repository be aware that my development branches may rewrite history without prior notice.
http://openboard.ch/
GNU General Public License v3.0
9 stars 0 forks source link

impossible to type a `^` in the input of a widget #142

Open kaamui opened 11 months ago

kaamui commented 11 months ago

Hi Martin,

I just found that it is impossible to type ^ in a widget ! Tested on Linux and Mac on the Wikipedia widget and a custom one I was working on.

Do you have any idea what could be possibly implied here ? (tested with 1.6.4 too and no issue)

Thanks in advance for your help.

letsfindaway commented 11 months ago

First test with Wikipedia widget: I can enter a ^ in the search field. Next try: an arbitrary web page with an input field as widget (https://www.binaryhexconverter.com/hex-to-binary-converter): it also accepts the ^ in the input field.

I am on the current dev/master with 1.7.0rc-231003.

Can you type this character at other places, i.e. in a text box or a file name field, e.g. for export or whatever else? So is the problem restricted to widgets?

letsfindaway commented 11 months ago

Might it be a problem with a French keyboard? Here in Germany the ^ key directly produces this character when pressed once. Is it some kind of accent character key in French? What about pressing this key twice or followed by a space?

letsfindaway commented 11 months ago

Ok, can reproduce now when I switch to a French keyboard layout. ^ works as expected in a text box, but not in a widget. I can now start to analyze this further.

kaamui commented 11 months ago

Yep sorry for my late reply. I obviously tested it outside widgets context but forgot to precise it.

Didn't tested it at home, but happens with my french-swiss keyboard (qwertz), where the ^ must be combined with a letter or a space, like in a french-french keyboard (azerty).

No issues on the web search bar, not either on a text box, or anywhere else, just widgets

kaamui commented 11 months ago

Same with ¨with the same behavior where you need to combine it with another key

kaamui commented 11 months ago

Also forgot to precise that copy-pasting the character works, so it seems to be related to combined keys, indeed

kaamui commented 11 months ago

Looks like in english these "combined" keys are called "dead keys", if it can help you on your search.

Also found this Qt-Bug that could potentially be related : https://bugreports.qt.io/browse/QTBUG-69652

letsfindaway commented 11 months ago

I don't think that this bug report is related. We have no Javascript expecting some specific dead key behavior. But still I have no clue what happens. I added debug output to the keyPressEvent and keyReleaseEvent handlers in UBGraphicsScene, UBGraphicsTextItem, UBGraphicsWidgetItem and UBWebEngineView with the following results:

Typing ^a in a text item:

=== Scene Key Release QKeyEvent(KeyRelease, Key_Dead_Circumflex) ""
=== Text Key Release QKeyEvent(KeyRelease, Key_Dead_Circumflex) ""
=== Scene Key Release QKeyEvent(KeyRelease, Key_A, text="a") "a"
=== Text Key Release QKeyEvent(KeyRelease, Key_A, text="a") "a"

Typing ^a in a widget:

=== Scene Key Press QKeyEvent(KeyPress, Key_Dead_Circumflex) ""
=== Widget Key Press QKeyEvent(KeyPress, Key_Dead_Circumflex) ""
=== Scene Key Release QKeyEvent(KeyRelease, Key_Dead_Circumflex) ""
=== Widget Key Release QKeyEvent(KeyRelease, Key_Dead_Circumflex) ""
=== Scene Key Press QKeyEvent(KeyPress, Key_A, text="a") "a"
=== Widget Key Press QKeyEvent(KeyPress, Key_A, text="a") "a"
=== Scene Key Release QKeyEvent(KeyRelease, Key_A, text="a") "a"
=== Widget Key Release QKeyEvent(KeyRelease, Key_A, text="a") "a"

Observations:

I also tried to set Qt::WA_InputMethodEnabled at various places, but with no effect.

kaamui commented 11 months ago

To make things even stranger, the issue doesn't occur on Windows ! (tested with the wiki widget)

letsfindaway commented 11 months ago

To make things even stranger, the issue doesn't occur on Windows !

Here we have probably a completely different handling of dead keys so this is not so strange to me.

letsfindaway commented 11 months ago

Here the log with the global event filter:

Web widget:

=== Key Event QKeyEvent(KeyPress, Key_Dead_Circumflex) on QWidgetWindow(0x2556830, name = "MainWindowWindow")
=== Key Event QKeyEvent(KeyPress, Key_Dead_Circumflex) on UBBoardView(0x2571940, name = "ControlView")
=== Key Event QKeyEvent(KeyPress, Key_Dead_Circumflex) on UBGraphicsScene(0x361c040, name = "BoardScene")
=== Scene Key QKeyEvent(KeyPress, Key_Dead_Circumflex) ""
=== Scene Key Press QKeyEvent(KeyPress, Key_Dead_Circumflex) ""
=== Key Event QKeyEvent(KeyPress, Key_Dead_Circumflex) on UBGraphicsW3CWidgetItem(0x36e53d0)
=== Widget Key QKeyEvent(KeyPress, Key_Dead_Circumflex) ""
=== Widget Key Press QKeyEvent(KeyPress, Key_Dead_Circumflex) ""
=== Key Event QKeyEvent(KeyPress, Key_Dead_Circumflex) on QtWebEngineCore::RenderWidgetHostViewQtDelegateWidget(0x37a4700)
=== Key Event QKeyEvent(KeyRelease, Key_Dead_Circumflex) on QWidgetWindow(0x2556830, name = "MainWindowWindow")
=== Key Event QKeyEvent(KeyRelease, Key_Dead_Circumflex) on UBBoardView(0x2571940, name = "ControlView")
=== Key Event QKeyEvent(KeyRelease, Key_Dead_Circumflex) on UBGraphicsScene(0x361c040, name = "BoardScene")
=== Scene Key QKeyEvent(KeyRelease, Key_Dead_Circumflex) ""
=== Scene Key Release QKeyEvent(KeyRelease, Key_Dead_Circumflex) ""
=== Key Event QKeyEvent(KeyRelease, Key_Dead_Circumflex) on UBGraphicsW3CWidgetItem(0x36e53d0)
=== Widget Key QKeyEvent(KeyRelease, Key_Dead_Circumflex) ""
=== Widget Key Release QKeyEvent(KeyRelease, Key_Dead_Circumflex) ""
=== Key Event QKeyEvent(KeyRelease, Key_Dead_Circumflex) on QtWebEngineCore::RenderWidgetHostViewQtDelegateWidget(0x37a4700)
=== Key Event QKeyEvent(KeyPress, Key_A, text="a") on QWidgetWindow(0x2556830, name = "MainWindowWindow")
=== Key Event QKeyEvent(KeyPress, Key_A, text="a") on UBBoardView(0x2571940, name = "ControlView")
=== Key Event QKeyEvent(KeyPress, Key_A, text="a") on UBGraphicsScene(0x361c040, name = "BoardScene")
=== Scene Key QKeyEvent(KeyPress, Key_A, text="a") "a"
=== Scene Key Press QKeyEvent(KeyPress, Key_A, text="a") "a"
=== Key Event QKeyEvent(KeyPress, Key_A, text="a") on UBGraphicsW3CWidgetItem(0x36e53d0)
=== Widget Key QKeyEvent(KeyPress, Key_A, text="a") "a"
=== Widget Key Press QKeyEvent(KeyPress, Key_A, text="a") "a"
=== Key Event QKeyEvent(KeyPress, Key_A, text="a") on QtWebEngineCore::RenderWidgetHostViewQtDelegateWidget(0x37a4700)
=== Key Event QKeyEvent(KeyRelease, Key_A, text="a") on QWidgetWindow(0x2556830, name = "MainWindowWindow")
=== Key Event QKeyEvent(KeyRelease, Key_A, text="a") on UBBoardView(0x2571940, name = "ControlView")
=== Key Event QKeyEvent(KeyRelease, Key_A, text="a") on UBGraphicsScene(0x361c040, name = "BoardScene")
=== Scene Key QKeyEvent(KeyRelease, Key_A, text="a") "a"
=== Scene Key Release QKeyEvent(KeyRelease, Key_A, text="a") "a"
=== Key Event QKeyEvent(KeyRelease, Key_A, text="a") on UBGraphicsW3CWidgetItem(0x36e53d0)
=== Widget Key QKeyEvent(KeyRelease, Key_A, text="a") "a"
=== Widget Key Release QKeyEvent(KeyRelease, Key_A, text="a") "a"
=== Key Event QKeyEvent(KeyRelease, Key_A, text="a") on QtWebEngineCore::RenderWidgetHostViewQtDelegateWidget(0x37a4700)

Browser

=== Key Event QKeyEvent(KeyRelease, Key_Dead_Circumflex) on QWidgetWindow(0x1e843f0, name = "MainWindowWindow")
=== Key Event QKeyEvent(KeyRelease, Key_Dead_Circumflex) on QtWebEngineCore::RenderWidgetHostViewQtDelegateWidget(0x3f2cf00)
=== Key Event QKeyEvent(KeyRelease, Key_Dead_Circumflex) on QStackedWidget(0x2e534f0, name = "qt_tabwidget_stackedwidget")
=== Key Event QKeyEvent(KeyRelease, Key_Dead_Circumflex) on TabWidget(0x2e40a70)
=== Key Event QKeyEvent(KeyRelease, Key_Dead_Circumflex) on BrowserWindow(0x2e82ae0)
=== Key Event QKeyEvent(KeyRelease, Key_Dead_Circumflex) on QWidget(0x1e6d570)
=== Key Event QKeyEvent(KeyRelease, Key_Dead_Circumflex) on UBMainWindow(0x18cde10, name = "MainWindow")
=== Key Event QKeyEvent(KeyRelease, Key_A, text="a") on QWidgetWindow(0x1e843f0, name = "MainWindowWindow")
=== Key Event QKeyEvent(KeyRelease, Key_A, text="a") on QtWebEngineCore::RenderWidgetHostViewQtDelegateWidget(0x3f2cf00)
=== Key Event QKeyEvent(KeyRelease, Key_A, text="a") on QStackedWidget(0x2e534f0, name = "qt_tabwidget_stackedwidget")
=== Key Event QKeyEvent(KeyRelease, Key_A, text="a") on TabWidget(0x2e40a70)
=== Key Event QKeyEvent(KeyRelease, Key_A, text="a") on BrowserWindow(0x2e82ae0)
=== Key Event QKeyEvent(KeyRelease, Key_A, text="a") on QWidget(0x1e6d570)
=== Key Event QKeyEvent(KeyRelease, Key_A, text="a") on UBMainWindow(0x18cde10, name = "MainWindow")

In both cases RenderWidgetHostViewQtDelegateWidget seems to be the target of the event, but no key press appears in the browser.

kaamui commented 11 months ago

I expect the issue to be located on the WebEngine implementation so it's strange to me that the issue is there for macOS and Linux but not for Windows. OK it must be very different for each OS but I was not expecting it to work anywhere.

letsfindaway commented 11 months ago

May be on Windows you never get the dead key events, but the OS is already handling this and only providing the final result? Or it is sending the dead key event, but when pressing "a", it delivers already the correct accented character as text()?

kaamui commented 11 months ago

On Windows, both events appear on the logs at the same time when I press p on the following example on the browser

QKeyEvent(KeyPress, Key_P, text="^")
QKeyEvent(KeyPress, Key_P, text="^")
QKeyEvent(KeyPress, 0, text="p")
QKeyEvent(KeyPress, 0, text="p")
letsfindaway commented 11 months ago

I assume the problem in the QGraphicsProxyWidget, because this makes the difference. But I have no clue why and where the key press events are eaten in the cases when it works. Why can I not even see them in a global event filter at application level?

kaamui commented 11 months ago

I am not sure to understand. I don't see any keyPress events when typing on the webengine browser on Ubuntu where QGraphicsProxyWidget is not involved. I did the same test as above on Ubuntu and I don't have any event (I also check in UBApplication::eventFilter).

I expected the KeyPress events to always show up, and that it would not work if not, not the opposite...

On the other hand, the webengine is a different process, so maybe it "eats them without notifying main thread" ?

letsfindaway commented 11 months ago

I am not sure to understand. I don't see any keyPress events when typing on the webengine browser on Ubuntu where QGraphicsProxyWidget is not involved. I did the same test as above on Ubuntu and I don't have any event (I also check in UBApplication::eventFilter).

That's what I also do not understand. I cannot see the KeyPress events anywhere. Tried also overriding QCoreApplication::notify where the documentation says:

Note that this function is called for all events sent to any object in any thread.

This is apparently not true.

I expected the KeyPress events to always show up, and that it would not work if not, not the opposite...

Same with me.

On the other hand, the webengine is a different process, so maybe it "eats them without notifying main thread" ?

No. This process does not have the focus so the OS will not deliver key events to it. They always go to the foreground process.

But my main question is: Why don't we see KeyPress in the cases when it works? Where are these events handled and how?

I could create the correct character if I implement my own dead key handler in UBGraphicsWidgetItem::keyPressEvent and exchange the text with the accented character. But that is no solution.

kaamui commented 11 months ago

Observations (WIP):

letsfindaway commented 11 months ago

See https://bugreports.qt.io/browse/QTBUG-79216

Edit: I now think that what is described in this QTBUG is expected behavior: dead keys don't create KeyPress events, but only KeyRelease events. An InputMethod event is used to pass the character constructed by the input method afterwards.

kaamui commented 11 months ago

What did you use to track the call stack until === Key Event QKeyEvent(KeyRelease, Key_A, text="a") on QtWebEngineCore::RenderWidgetHostViewQtDelegateWidget(0x3f2cf00) ?

kaamui commented 11 months ago

On Windows, the call stack when we end in UBGraphicsWidgetItem::keyPressEvent (only after typing the second key) :

1  UBGraphicsWidgetItem::keyPressEvent              UBGraphicsWidgetItem.cpp       579  0x7ff7e91ed0a1 
2  QGraphicsItem::sceneEvent                        qgraphicsitem.cpp              6961 0x7ffba9e07617 
3  QGraphicsWidget::sceneEvent                      qgraphicswidget.cpp            1238 0x7ffba9eba827 
4  QGraphicsScenePrivate::sendEvent                 qgraphicsscene.cpp             1259 0x7ffba9e54cec 
5  QGraphicsScene::keyPressEvent                    qgraphicsscene.cpp             4000 0x7ffba9e4d2c8 
6  QGraphicsScene::event                            qgraphicsscene.cpp             3411 0x7ffba9e4bbd8 
7  QApplicationPrivate::notify_helper               qapplication.cpp               3671 0x7ffba9831987 
8  QApplication::notify                             qapplication.cpp               3011 0x7ffba982bb47 
9  QCoreApplication::notifyInternal2                qcoreapplication.cpp           1061 0x7ffb9aa9ae9f 
10 QCoreApplication::sendEvent                      qcoreapplication.cpp           1457 0x7ffb9aa98f82 
11 UBBoardView::keyPressEvent                       UBBoardView.cpp                208  0x7ff7e90b6e49 
12 QWidget::event                                   qwidget.cpp                    8714 0x7ffba98928b7 
13 QFrame::event                                    qframe.cpp                     550  0x7ffba9a005cf 
14 QAbstractScrollArea::event                       qabstractscrollarea.cpp        1042 0x7ffba9a038dc 
15 QGraphicsView::event                             qgraphicsview.cpp              2885 0x7ffba9ea643f 
16 UBBoardView::event                               UBBoardView.cpp                333  0x7ff7e90b6cf6 
17 QApplicationPrivate::notify_helper               qapplication.cpp               3671 0x7ffba9831987 
18 QApplication::notify                             qapplication.cpp               3033 0x7ffba982bd05 
19 QCoreApplication::notifyInternal2                qcoreapplication.cpp           1061 0x7ffb9aa9ae9f 
20 QCoreApplication::forwardEvent                   qcoreapplication.cpp           1077 0x7ffb9aa9af3c 
21 QWidgetWindow::handleKeyEvent                    qwidgetwindow.cpp              716  0x7ffba98e2e0d 
22 QWidgetWindow::event                             qwidgetwindow.cpp              289  0x7ffba98e24b5 
23 QApplicationPrivate::notify_helper               qapplication.cpp               3671 0x7ffba9831987 
24 QApplication::notify                             qapplication.cpp               3011 0x7ffba982bb47 
25 QCoreApplication::notifyInternal2                qcoreapplication.cpp           1061 0x7ffb9aa9ae9f 
26 QCoreApplication::sendSpontaneousEvent           qcoreapplication.cpp           1469 0x7ffb9aa9ad3b 
27 QGuiApplicationPrivate::processKeyEvent          qguiapplication.cpp            2363 0x7ffba861ab88 
28 QGuiApplicationPrivate::processWindowSystemEvent qguiapplication.cpp            1953 0x7ffba861da5a 
29 QWindowSystemInterface::sendWindowSystemEvents   qwindowsysteminterface.cpp     1181 0x7ffba85e7d92 
30 QWindowsGuiEventDispatcher::sendPostedEvents     qwindowsguieventdispatcher.cpp 82   0x7ffba774a2f2 
31 QEventDispatcherWin32::processEvents             qeventdispatcher_win.cpp       527  0x7ffb9ab6e9b2 
32 QWindowsGuiEventDispatcher::processEvents        qwindowsguieventdispatcher.cpp 73   0x7ffba774a2a4 
33 QEventLoop::processEvents                        qeventloop.cpp                 140  0x7ffb9aa957db 
34 QEventLoop::exec                                 qeventloop.cpp                 232  0x7ffb9aa95a44 
35 QCoreApplication::exec                           qcoreapplication.cpp           1369 0x7ffb9aa98d09 
36 QGuiApplication::exec                            qguiapplication.cpp            1868 0x7ffba8616388 
37 QApplication::exec                               qapplication.cpp               2812 0x7ffba982b45a 
38 UBApplication::exec                              UBApplication.cpp              418  0x7ff7e90dc6bb 
39 main                                             main.cpp                       168  0x7ff7e90d8549 
40 WinMain                                          qtmain_win.cpp                 97   0x7ff7e96a6745 
41 invoke_main                                      exe_common.inl                 107  0x7ff7e95df762 
42 __scrt_common_main_seh                           exe_common.inl                 288  0x7ff7e95df64e 
43 __scrt_common_main                               exe_common.inl                 331  0x7ff7e95df50e 
44 WinMainCRTStartup                                exe_winmain.cpp                17   0x7ff7e95df7fe 
45 BaseThreadInitThunk                              KERNEL32                            0x7ffc4b397344 
46 RtlUserThreadStart                               ntdll                               0x7ffc4b8226b1 
letsfindaway commented 11 months ago

What did you use to track the call stack until === Key Event QKeyEvent(KeyRelease, Key_A, text="a") on QtWebEngineCore::RenderWidgetHostViewQtDelegateWidget(0x3f2cf00) ?

I was installing a global event filter in main.cpp:

class MyEventFilter : public QObject
{
public:
    MyEventFilter(QObject *parent = nullptr) : QObject(parent) {}

    bool eventFilter(QObject *target, QEvent *event) override
    {
        if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) {
            QKeyEvent *ke = static_cast<QKeyEvent *>(event);
            qDebug() << "=== Key Event" << ke << "on" << target;
        }

        // Continue processing the event
        return false;
    }
};

and later

    MyEventFilter filter;
    app.installEventFilter(&filter);

This shows the same event with multiple targets.

letsfindaway commented 11 months ago

Also a longer reading: https://www.kdab.com/qt-input-method-depth/

but I don't know whether this can help understanding our problem., as I was not able to create any inputMethodEvents.

Edit: This article explains in detail how a key press is processed when input methods are enabled. It also explains, why we don't receive and QKeyPressEvent in this case. It even explains why we cannot easily affect that programmatically: the final negotiation is between the focus object and the QPlatformInputContext, which is the interface to the OS specific input method. If the focus object wants to have InputMethodEvents, it gets them. Setting flags on the widgets is not sufficient. They are not the focus object.

kaamui commented 11 months ago
 bool eventFilter(QObject *target

Oh I didn't know the first argument was the target. On the already existing eventFilter of UBApplication it is called "obj", that is way more precise of course... 😄

kaamui commented 11 months ago

On Linux, the call stack when we end in UBGraphicsWidgetItem::keyPressEvent after we type ^ :

1  UBGraphicsWidgetItem::keyPressEvent              UBGraphicsWidgetItem.cpp   620  0x5555557673b6 
2  QGraphicsItem::sceneEvent                        qgraphicsitem.cpp          6960 0x7ffff52811b6 
**3  QGraphicsScene::keyReleaseEvent                  qgraphicsscene.cpp         4032 0x7ffff52a1e72**  weird
4  QGraphicsScene::event                            qgraphicsscene.cpp         3410 0x7ffff52b24ff 
5  QApplicationPrivate::notify_helper               qapplication.cpp           3671 0x7ffff4faf13c 
6  QApplication::notify                             qapplication.cpp           3417 0x7ffff4fb5d10 
7  QCoreApplication::notifyInternal2                qcoreapplication.cpp       1061 0x7fffe9f9f8f8 
8  UBBoardView::keyPressEvent                       UBBoardView.cpp            207  0x555555683eeb 
9  QWidget::event                                   qwidget.cpp                8686 0x7ffff4fedf47 
10 QFrame::event                                    qframe.cpp                 550  0x7ffff5095efe 
11 QAbstractScrollArea::event                       qabstractscrollarea.cpp    1042 0x7ffff5098bd3 
12 UBBoardView::event                               UBBoardView.cpp            331  0x555555684374 
13 QApplicationPrivate::notify_helper               qapplication.cpp           3671 0x7ffff4faf13c 
14 QApplication::notify                             qapplication.cpp           3033 0x7ffff4fb606b 
15 QCoreApplication::notifyInternal2                qcoreapplication.cpp       1061 0x7fffe9f9f8f8 
16 QWidgetWindow::event                             qwidgetwindow.cpp          288  0x7ffff5009b73 
17 QApplicationPrivate::notify_helper               qapplication.cpp           3671 0x7ffff4faf13c 
18 QApplication::notify                             qapplication.cpp           3417 0x7ffff4fb5d10 
19 QCoreApplication::notifyInternal2                qcoreapplication.cpp       1061 0x7fffe9f9f8f8 
20 QCoreApplication::sendSpontaneousEvent           qcoreapplication.cpp       1468 0x7fffe9f9fabe 
21 QGuiApplicationPrivate::processKeyEvent          qguiapplication.cpp        2346 0x7fffeb98763b 
22 QGuiApplicationPrivate::processWindowSystemEvent qguiapplication.cpp        1952 0x7fffeb98c385 
23 QWindowSystemInterface::sendWindowSystemEvents   qwindowsysteminterface.cpp 1175 0x7fffeb96817b 
24 xcbSourceDispatch                                qxcbeventdispatcher.cpp    105  0x7fffdd4ac3aa 
25 g_main_context_dispatch                                                          0x7fffe839c17d 
26 ??                                                                               0x7fffe839c400 
27 g_main_context_iteration                                                         0x7fffe839c4a3 
28 QEventDispatcherGlib::processEvents              qeventdispatcher_glib.cpp  423  0x7fffe9ffb11c 
29 QEventLoop::exec                                 qeventloop.cpp             232  0x7fffe9f9e30a 
30 QCoreApplication::exec                           qcoreapplication.cpp       1369 0x7fffe9fa72b3 
31 UBApplication::exec                              UBApplication.cpp          417  0x5555556a49a5 
32 main                                             main.cpp                   168  0x5555556a0e35 
kaamui commented 11 months ago

Maybe a workaround would be to store the last dead key the user typed, and when we recieve the next key, we create these events

QKeyEvent(KeyPress, Key_P, text="^")
QKeyEvent(KeyPress, Key_P, text="^")
QKeyEvent(KeyPress, 0, text="p")
QKeyEvent(KeyPress, 0, text="p")

send them to the proxy widget and then we reset the dead key data member

letsfindaway commented 11 months ago

This is what happens on the text widget when I also log the InputMethodEvents:

=== Key Event QKeyEvent(KeyRelease, Key_Dead_Circumflex) on QWidgetWindow(0x12aff40, name = "MainWindowWindow")
=== Key Event QKeyEvent(KeyRelease, Key_Dead_Circumflex) on UBBoardView(0x124de80, name = "ControlView")
=== Key Event QKeyEvent(KeyRelease, Key_Dead_Circumflex) on UBGraphicsScene(0x2584420, name = "BoardScene")
=== Scene Key Release QKeyEvent(KeyRelease, Key_Dead_Circumflex) ""
=== Key Event QKeyEvent(KeyRelease, Key_Dead_Circumflex) on UBGraphicsTextItem(0xf871b0)
=== Text Key Release QKeyEvent(KeyRelease, Key_Dead_Circumflex) ""
=== InputMethodEvent QInputMethodEvent(, commit="U+e2") on UBBoardView(0x124de80, name = "ControlView") 
=== InputMethodEvent QInputMethodEvent(, commit="U+e2") on UBGraphicsScene(0x2584420, name = "BoardScene") 
=== InputMethodEvent QInputMethodEvent(, commit="U+e2") on UBGraphicsTextItem(0xf871b0)
=== Key Event QKeyEvent(KeyRelease, Key_A, text="a") on QWidgetWindow(0x12aff40, name = "MainWindowWindow")
=== Key Event QKeyEvent(KeyRelease, Key_A, text="a") on UBBoardView(0x124de80, name = "ControlView")
=== Key Event QKeyEvent(KeyRelease, Key_A, text="a") on UBGraphicsScene(0x2584420, name = "BoardScene")
=== Scene Key Release QKeyEvent(KeyRelease, Key_A, text="a") "a"
=== Key Event QKeyEvent(KeyRelease, Key_A, text="a") on UBGraphicsTextItem(0xf871b0)
=== Text Key Release QKeyEvent(KeyRelease, Key_A, text="a") "a"

When I press "a" after "^", an InputMethodEvent is fired with commit="U+e2", which is the code of "â". The event is offered to the view, then the scene and finally the text widget.

I found however no way to produce this behavior when the focus is on any other widget than the text widget. Also when the focus is e.g. on a stroke, we get the key press AND release events. Only with the text widget we don't get the key press events, but instead the InputMethodEvent. There must be some magic in the QGraphicsTextItem.

letsfindaway commented 11 months ago

Maybe a workaround would be to store the last dead key the user typed, and when we recieve the next key, we create these events

QKeyEvent(KeyPress, Key_P, text="^")
QKeyEvent(KeyPress, Key_P, text="^")
QKeyEvent(KeyPress, 0, text="p")
QKeyEvent(KeyPress, 0, text="p")

send them to the proxy widget and then we reset the dead key data member

Did you test whether this could work? And why duplicating the events? Is it intended that the key and the text don't match in these events?

kaamui commented 11 months ago

First of all, I simply tested to send an event with ^ as text and the ^ appeared

Maybe a workaround would be to store the last dead key the user typed, and when we receive the next key, we create these events

QKeyEvent(KeyPress, Key_P, text="^")
QKeyEvent(KeyPress, Key_P, text="^")
QKeyEvent(KeyPress, 0, text="p")
QKeyEvent(KeyPress, 0, text="p")

send them to the proxy widget and then we reset the dead key data member

Did you test whether this could work? And why duplicating the events? Is it intended that the key and the text don't match in these events?

The events are duplicated in Windows logs, but I don't intend to reproduce that. I thought that sending both events QKeyEvent(KeyPress, Key_O, text="^"), QKeyEvent(KeyPress, 0, text="o") would maybe produce the same result (some magic would combine this as a unique accented letter in the widget) but it produced ^o instead of ô

The good news is that we still have a workaround possible, but quite heavy : sending an event with the dead key as text works, as the dead key is written in the input of the widget, so we could create a method dedicated to Not-Windows and the concerned keyboard layouts, where we would map any dead key with any compatible letter, and send an event with the resulting accented character as text.

So I started to search how to do so without having to detail all the cases, and it resulted on ... inputMethod events ^^

So I tried to make them work by overriding QGrqphicsProxyWidget::InputMethodEvent and QGrqphicsProxyWidget::inputMethodQuery and set WA_... to true, but nothing happens, it never passes through these methods and I don't understand why.

I also downloaded german language and layouts to compare the call stack, or to see if something would be different in the values of the differents objects, but everything looks exactly the same, expect the result (it works with german layout)...

source of my attempt on input methods : https://stackoverflow.com/questions/28793356/qt-and-dead-keys-in-a-custom-widget

edit : I also verified if our custom UBGraphicsWidgetItem::eventFilter could not be responsible for filtering such events but nope.

kaamui commented 11 months ago

I don't have more information or things to test. I'll start to implement the workaround.

I'm not sure how to know if the keyboard layout is concerned by this workaround. Any idea ?

InputMethod::locale() always return the same value, independently of the layout I choose, so I must be mistaking two things there.

kaamui commented 11 months ago

Hi Martin,

Can you please review and test this commit ? => https://github.com/OpenBoard-org/OpenBoard/commit/9c8b668b89b1417a86e6bd1eaec4ced496a47d95

Thank you !

kaamui commented 11 months ago

! Huge discovery while testing ! It is getting stranger 😄

Independently of the code I produced (e.g test on the dev branch or the 1.7.1-dev branch, it's the same) :

  1. drop the Wikipedia widget
  2. click on the input
  3. try to write on the input (as before, dead keys ignored)
  4. move the widget with the grey frame
  5. click on the input
  6. try to write on the input

result : dead keys aren't ignore anymore ! It is handled like it is in the underlying OS

Important Note : After step 6, when typing a dead keys and another key after, we simply never pass on the keyPressEvent of the widget, and we no longer pass through the global filter event ! Like in the web browser.

letsfindaway commented 11 months ago

Then we can probably use another workaround we already have implemented for another problem.

See https://github.com/letsfindaway/OpenBoard/blob/3251cc13c7287380c1157cfd20abb0d9b247cd2b/src/domain/UBGraphicsWidgetItem.cpp#L385-L392

called from https://github.com/letsfindaway/OpenBoard/blob/3251cc13c7287380c1157cfd20abb0d9b247cd2b/src/domain/UBGraphicsScene.cpp#L2393-L2405

and from various other places.

We could probably call this function after dropping the widget. Have not much time to test this today (it's my wife's birthday ;) but could you give it a try?

We needed this to update the knowledge about the widget position. You remember the problem with the drop down menus? So we call this when moving the widget. May be it also helps for this issue?

kaamui commented 11 months ago

Yep ! I'll try this ! Thank you to have taken the time to reply ! Have a great day and happy birthday to your wife !

letsfindaway commented 11 months ago

Yep ! I'll try this ! Thank you to have taken the time to reply ! Have a great day and happy birthday to your wife !

Thanks!

we have https://github.com/letsfindaway/OpenBoard/blob/3251cc13c7287380c1157cfd20abb0d9b247cd2b/src/domain/UBGraphicsWidgetItem.cpp#L743-L751, but this function is never called. May be we just have to activate that?

kaamui commented 11 months ago

there is a connect() with this function as a slot, so it may be more complicated, but I'll look at it.

letsfindaway commented 11 months ago

My observations:

Edit: it is probably not blocked. You just have to click again into the input field, even if it looks as if it would be selected and active.

kaamui commented 11 months ago

Thank you,

I did not find what's the root action that is making this change of behavior when moving the widget, and quickly moved to another feature I was working on before I found the issue. If you are out of ideas too, maybe we can leave it as-is for now.

when you have time, could you please tell me if the workaround is OK for you (code review but above all no regression with your keyboard layout) ?

letsfindaway commented 11 months ago

Quick check:

Will do a code review in the afternoon.

kaamui commented 11 months ago

Quick check:

  • so I have to delete the second character to get the desired effect. Other applications produce a single ^ in those cases.

Exact. I forgot this use case. I'll make an exception when second key is space

letsfindaway commented 11 months ago

Another observation:

Edit: it is probably not blocked. You just have to click again into the input field, even if it looks as if it would be selected and active.

letsfindaway commented 11 months ago

Quick check:

  • so I have to delete the second character to get the desired effect. Other applications produce a single ^ in those cases.

Exact. I forgot this use case. I'll make an exception when second key is space

... or the same dead key.

letsfindaway commented 11 months ago

I assume that even things like Chinese or Japanese input would work if the InputMethod would work. So your workaround is for Latin alphabets only. But better than nothing anyway!

Let's keep the issue in mind. Probably there will be a solution some day. Maybe I will also create a QTBUG for this and a minimal proof-of-failure.

letsfindaway commented 11 months ago

I will come up with a PR later today as I have some more proposals for changes.

kaamui commented 11 months ago

Thank you for your help.

Do you plan to create a proof-of-failure and a ticket in Qt-bugs ?

Also, what do you think of the following call stack ? I'm really surprised to see such a call here :

1  UBGraphicsWidgetItem::keyPressEvent              UBGraphicsWidgetItem.cpp   620  0x5555557673b6 
2  QGraphicsItem::sceneEvent                        qgraphicsitem.cpp          6960 0x7ffff52811b6 
**3  QGraphicsScene::keyReleaseEvent                  qgraphicsscene.cpp         4032 0x7ffff52a1e72**  weird
4  QGraphicsScene::event                            qgraphicsscene.cpp         3410 0x7ffff52b24ff 
5  QApplicationPrivate::notify_helper               qapplication.cpp           3671 0x7ffff4faf13c 
6  QApplication::notify                             qapplication.cpp           3417 0x7ffff4fb5d10 
7  QCoreApplication::notifyInternal2                qcoreapplication.cpp       1061 0x7fffe9f9f8f8 
letsfindaway commented 11 months ago

Thank you for your help.

You are welcome!

Do you plan to create a proof-of-failure and a ticket in Qt-bugs ?

Yes, during this week.

Also, what do you think of the following call stack ? I'm really surprised to see such a call here :

1  UBGraphicsWidgetItem::keyPressEvent              UBGraphicsWidgetItem.cpp   620  0x5555557673b6 
2  QGraphicsItem::sceneEvent                        qgraphicsitem.cpp          6960 0x7ffff52811b6 
**3  QGraphicsScene::keyReleaseEvent                  qgraphicsscene.cpp         4032 0x7ffff52a1e72**  weird
4  QGraphicsScene::event                            qgraphicsscene.cpp         3410 0x7ffff52b24ff 
5  QApplicationPrivate::notify_helper               qapplication.cpp           3671 0x7ffff4faf13c 
6  QApplication::notify                             qapplication.cpp           3417 0x7ffff4fb5d10 
7  QCoreApplication::notifyInternal2                qcoreapplication.cpp       1061 0x7fffe9f9f8f8 

See https://github.com/qt/qtbase/blob/b470da91073fab7a51a75d0d6cd3184e297b20cf/src/widgets/graphicsview/qgraphicsscene.cpp#L3871-L3926. The comment says that these two functions are identical. Probably this is already a first step in squashing these two functions into one. However I cannot see something like that in the calling code in QGraphicsScene::event.

Edit: But the compiler may be smart enough to recognize that!!! So I think it is the compiler optimization which generates only one code for these two functions.

letsfindaway commented 11 months ago

See https://bugreports.qt.io/browse/QTBUG-118136

kaamui commented 8 months ago

Hi Martin,

The issue is still occurring on MacOS, because the key code received is Qt::KeyUnknown. I tried to get more info using a native event filter, but I can't obtain more than the VirtualKeyCode, that changes upon keyboard layout, so no quick/global solution (I would have to handle manually each keyboard layout).

I just found another workaround, like moving the widget a bit : with the input on focus, going to next page and coming back to our page seems to resolve the issue too. So I can see a workaround with the following operation :

  1. focus on any input of type text
  2. reload or move the widget

So my question is : I know that your tests on moving programmatically the widget didn't produce the expected effect, but could we inject a script to give focus to any input on the page, before doing so (maybe even refresh the page in the js side could work ?) ? Do you think it could work (I'm starting to try this right now) or am I just going to loose time ?

Thanks in advance for your help.

edit : simply adding runScript("document.querySelector('input').focus()"); when mainFrame is loaded seems to do the trick in macOS ! It probably enables InputMethod

void UBGraphicsWidgetItem::mainFrameLoadFinished (bool ok)
{
    mInitialLoadDone = true;
    mLoadIsErronous = !ok;

    runScript("document.querySelector('input').focus()"); //placing it after the update and updatePosition calls produces the same effect

    // repaint when initial rendering is done
    update();
    updatePosition();
}
letsfindaway commented 8 months ago

edit : simply adding runScript("document.querySelector('input').focus()"); when mainFrame is loaded seems to do the trick in macOS ! It probably enables InputMethod

That means you have solved it now for MacOS? Would this also work for all other platforms?

And I wonder: You invoke this when loading is finished. Will it still work when the focus leaves the widget and re-enters it later without reloading? If yes, then this is a quite elegant workaround!

I don't wonder that sequence of execution does not affect this. runScript is asynchronous any way and you have no guarantee when the script is actually executed. So call sequence is not that much important.

kaamui commented 8 months ago

That means you have solved it now for MacOS? Would this also work for all other platforms?

just reverted the Linux code and tested it on Ubuntu 20.04 and it also workarounds the issue !

And now testing with this to avoid errors on pages with no input : const el = document.querySelector('input'); if (el) el.focus()