QLineEdit Undo / Redo Shortcut Text Disappearing
-
Hello
I have had a strange bug in my program for a long time. Recently, I have managed to narrow it down and create a minimal example illustrating the problem.
The problem is: If I have a menu with Undo and Redo options that have the same keyboard shortcuts as the Undo and Redo keyboard shortcuts in a QLineEdit context menu, then the text for the shortcut in the QLineEdit menu disappears when the corresponding action in the menu is enabled. The functionality of Undo and Redo in the QLineEdit works correctly, however.
main.cpp :
#include "mainwindow.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); return QCoreApplication::exec(); }mainwindow.h :
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> class QAction; QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget* parent = nullptr); ~MainWindow() override; private: Ui::MainWindow* ui; QAction* undoAction; QAction* redoAction; private slots: void toggleUndo(bool); void toggleRedo(bool); }; #endif // MAINWINDOW_Hmainwindow.cpp :
#include "mainwindow.h" #include "ui_mainwindow.h" #include <QAction> #include <QPushButton> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); undoAction = new QAction(this); redoAction = new QAction(this); undoAction->setEnabled(false); redoAction->setEnabled(false); undoAction->setText("&Undo"); redoAction->setText("&Redo"); undoAction->setShortcut(QKeySequence::Undo); redoAction->setShortcut(QKeySequence::Redo); connect(ui->undoButton, &QPushButton::clicked, this, &MainWindow::toggleUndo); connect(ui->redoButton, &QPushButton::clicked, this, &MainWindow::toggleRedo); // Menu bar QMenu* menu = new QMenu(ui->menubar); menu->setTitle("&Menu"); menu->addAction(undoAction); menu->addAction(redoAction); ui->menubar->addAction(menu->menuAction()); } MainWindow::~MainWindow() { delete ui; } void MainWindow::toggleUndo(bool checked) { undoAction->setEnabled(checked); } void MainWindow::toggleRedo(bool checked) { redoAction->setEnabled(checked); }mainwindow.ui :
<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>MainWindow</class> <widget class="QMainWindow" name="MainWindow"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>800</width> <height>600</height> </rect> </property> <property name="windowTitle"> <string>MainWindow</string> </property> <widget class="QWidget" name="centralwidget"> <layout class="QGridLayout" name="gridLayout"> <item row="0" column="0" colspan="2"> <widget class="QLineEdit" name="lineEdit"/> </item> <item row="1" column="0"> <widget class="QPushButton" name="undoButton"> <property name="text"> <string>Toggle Undo Action</string> </property> <property name="checkable"> <bool>true</bool> </property> </widget> </item> <item row="1" column="1"> <widget class="QPushButton" name="redoButton"> <property name="text"> <string>Toggle Redo Action</string> </property> <property name="checkable"> <bool>true</bool> </property> </widget> </item> </layout> </widget> <widget class="QMenuBar" name="menubar"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>800</width> <height>25</height> </rect> </property> </widget> </widget> <resources/> <connections/> </ui>Clicking the "Toggle Undo Action" button toggles the Undo action. When it is enabled the text for Undo in the context menu of the QLineEdit disappears. The expected behaviour is that it should still be visible. The "Toggle Redo Action" exhibits the same problem but for Redo.
Is this a Qt bug or (more likely) have I made a mistake?
Many thanks
-
It is indeed a very peculiar behavior, unique only to the context menus of
QLineEdit,QTextEditandQPlainTextEdit.If you look at the Qt code (e.g for
QLineEdit::createStandardContextMenu), you can see that they don't use the normal action shortcut function, but a customACCEL_KEYmacro that includes explicit code to check if anything else has the same shortcut and not display it if so.So this is very intentional, but I can't tell you the reason. The code is like that since
early Qt 4Qt3 (before the event horizon of the current version control), but can only speculate. Perhaps you can file a ticket in the issue tracker (https://qt-project.atlassian.net) if one isn't already open, for reconsideration(@Christian-Ehrlicher this may be of interest to you)
Had a quick play around in my lunch break. Sub-classing QLineEdit and reimplementing contextMenuEvent appears to workaround the problem:
void MyLineEdit::contextMenuEvent(QContextMenuEvent* event) { QMenu* menu = createStandardContextMenu(); for (QAction* action : menu->actions()) { if (action->text().contains("Undo")) { action->setShortcut(QKeySequence::Undo); } else if (action->text().contains("Redo")) { action->setShortcut(QKeySequence::Redo); } } menu->exec(event->globalPos()); menu->deleteLater(); }I asked duck.ai and it came up with three suggestions, the other two suggestions were not acceptable. Amusingly, it references this thread when formulating its reply :-)
Many thanks for pointing me in the right direction.
-
Hello
I have had a strange bug in my program for a long time. Recently, I have managed to narrow it down and create a minimal example illustrating the problem.
The problem is: If I have a menu with Undo and Redo options that have the same keyboard shortcuts as the Undo and Redo keyboard shortcuts in a QLineEdit context menu, then the text for the shortcut in the QLineEdit menu disappears when the corresponding action in the menu is enabled. The functionality of Undo and Redo in the QLineEdit works correctly, however.
main.cpp :
#include "mainwindow.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); return QCoreApplication::exec(); }mainwindow.h :
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> class QAction; QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget* parent = nullptr); ~MainWindow() override; private: Ui::MainWindow* ui; QAction* undoAction; QAction* redoAction; private slots: void toggleUndo(bool); void toggleRedo(bool); }; #endif // MAINWINDOW_Hmainwindow.cpp :
#include "mainwindow.h" #include "ui_mainwindow.h" #include <QAction> #include <QPushButton> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); undoAction = new QAction(this); redoAction = new QAction(this); undoAction->setEnabled(false); redoAction->setEnabled(false); undoAction->setText("&Undo"); redoAction->setText("&Redo"); undoAction->setShortcut(QKeySequence::Undo); redoAction->setShortcut(QKeySequence::Redo); connect(ui->undoButton, &QPushButton::clicked, this, &MainWindow::toggleUndo); connect(ui->redoButton, &QPushButton::clicked, this, &MainWindow::toggleRedo); // Menu bar QMenu* menu = new QMenu(ui->menubar); menu->setTitle("&Menu"); menu->addAction(undoAction); menu->addAction(redoAction); ui->menubar->addAction(menu->menuAction()); } MainWindow::~MainWindow() { delete ui; } void MainWindow::toggleUndo(bool checked) { undoAction->setEnabled(checked); } void MainWindow::toggleRedo(bool checked) { redoAction->setEnabled(checked); }mainwindow.ui :
<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>MainWindow</class> <widget class="QMainWindow" name="MainWindow"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>800</width> <height>600</height> </rect> </property> <property name="windowTitle"> <string>MainWindow</string> </property> <widget class="QWidget" name="centralwidget"> <layout class="QGridLayout" name="gridLayout"> <item row="0" column="0" colspan="2"> <widget class="QLineEdit" name="lineEdit"/> </item> <item row="1" column="0"> <widget class="QPushButton" name="undoButton"> <property name="text"> <string>Toggle Undo Action</string> </property> <property name="checkable"> <bool>true</bool> </property> </widget> </item> <item row="1" column="1"> <widget class="QPushButton" name="redoButton"> <property name="text"> <string>Toggle Redo Action</string> </property> <property name="checkable"> <bool>true</bool> </property> </widget> </item> </layout> </widget> <widget class="QMenuBar" name="menubar"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>800</width> <height>25</height> </rect> </property> </widget> </widget> <resources/> <connections/> </ui>Clicking the "Toggle Undo Action" button toggles the Undo action. When it is enabled the text for Undo in the context menu of the QLineEdit disappears. The expected behaviour is that it should still be visible. The "Toggle Redo Action" exhibits the same problem but for Redo.
Is this a Qt bug or (more likely) have I made a mistake?
Many thanks
@zenzero-2001
You should state your Qt version and platform.I am Linux Ubuntu 24.04, Xcb/Xorg GNOME desktop, Qt 6.4.2. Copying and pasting your code verbatim, no matter what buttons I press/press again I always find the Undo/Redo options on the
QLineEdit's right-click context menu are and stay enabled if there is anything to undo/redo, so I don't think I see your issue. -
Hi
I am using Manjaro Linux, Wayland KDE, Qt 6.11.0. The problem has occurred in my own program with Windows and Linux (including Ubuntu) over many different versions of Qt. I have not yet tested the minimal example in Windows yet, will do that today.
The problem is that the shortcut text (e.g. Ctrl+Z) disappears, not the options themselves. The functionality of the shortcut in the QLineEdit (and whether it is enabled or not) is correct.
The Undo / Redo options in the menubar have the same shortcuts as the QLineEdit, but they should not interfere. If I do not add the Undo / Redo actions to the mainwindow menubar (by commenting out the two lines) then the shortcut text in the QLineEdit functions correctly.
Many thanks
-
Hi
I am using Manjaro Linux, Wayland KDE, Qt 6.11.0. The problem has occurred in my own program with Windows and Linux (including Ubuntu) over many different versions of Qt. I have not yet tested the minimal example in Windows yet, will do that today.
The problem is that the shortcut text (e.g. Ctrl+Z) disappears, not the options themselves. The functionality of the shortcut in the QLineEdit (and whether it is enabled or not) is correct.
The Undo / Redo options in the menubar have the same shortcuts as the QLineEdit, but they should not interfere. If I do not add the Undo / Redo actions to the mainwindow menubar (by commenting out the two lines) then the shortcut text in the QLineEdit functions correctly.
Many thanks
@zenzero-2001
Ah, I see what you mean, I had not noticed that.Something about the way that context menu is in-built.
You may have to use void QLineEdit::contextMenuEvent(QContextMenuEvent *event) to either create your own context menu or try
QMenu *menu = createStandardContextMenu();from there and find the action and access it there. I don't know whether that would help or whether the behaviour of "don't show shortcut key when that key is in use as a shortcut elsewhere" would still apply, perhaps that is always the case when there is another shortcut. It's a bit of work to set it up so I haven't investigated but I could if you want me to look at it or you could? -
It is indeed a very peculiar behavior, unique only to the context menus of
QLineEdit,QTextEditandQPlainTextEdit.If you look at the Qt code (e.g for
QLineEdit::createStandardContextMenu), you can see that they don't use the normal action shortcut function, but a customACCEL_KEYmacro that includes explicit code to check if anything else has the same shortcut and not display it if so.So this is very intentional, but I can't tell you the reason. The code is like that since
early Qt 4Qt3 (before the event horizon of the current version control), but can only speculate. Perhaps you can file a ticket in the issue tracker (https://qt-project.atlassian.net) if one isn't already open, for reconsideration(@Christian-Ehrlicher this may be of interest to you)
-
It is indeed a very peculiar behavior, unique only to the context menus of
QLineEdit,QTextEditandQPlainTextEdit.If you look at the Qt code (e.g for
QLineEdit::createStandardContextMenu), you can see that they don't use the normal action shortcut function, but a customACCEL_KEYmacro that includes explicit code to check if anything else has the same shortcut and not display it if so.So this is very intentional, but I can't tell you the reason. The code is like that since
early Qt 4Qt3 (before the event horizon of the current version control), but can only speculate. Perhaps you can file a ticket in the issue tracker (https://qt-project.atlassian.net) if one isn't already open, for reconsideration(@Christian-Ehrlicher this may be of interest to you)
Had a quick play around in my lunch break. Sub-classing QLineEdit and reimplementing contextMenuEvent appears to workaround the problem:
void MyLineEdit::contextMenuEvent(QContextMenuEvent* event) { QMenu* menu = createStandardContextMenu(); for (QAction* action : menu->actions()) { if (action->text().contains("Undo")) { action->setShortcut(QKeySequence::Undo); } else if (action->text().contains("Redo")) { action->setShortcut(QKeySequence::Redo); } } menu->exec(event->globalPos()); menu->deleteLater(); }I asked duck.ai and it came up with three suggestions, the other two suggestions were not acceptable. Amusingly, it references this thread when formulating its reply :-)
Many thanks for pointing me in the right direction.
-
Had a quick play around in my lunch break. Sub-classing QLineEdit and reimplementing contextMenuEvent appears to workaround the problem:
void MyLineEdit::contextMenuEvent(QContextMenuEvent* event) { QMenu* menu = createStandardContextMenu(); for (QAction* action : menu->actions()) { if (action->text().contains("Undo")) { action->setShortcut(QKeySequence::Undo); } else if (action->text().contains("Redo")) { action->setShortcut(QKeySequence::Redo); } } menu->exec(event->globalPos()); menu->deleteLater(); }I asked duck.ai and it came up with three suggestions, the other two suggestions were not acceptable. Amusingly, it references this thread when formulating its reply :-)
Many thanks for pointing me in the right direction.
@zenzero-2001 Good stuff. You should mark (from the vertical
...item at bottom right of the post) your own last post as the "correct" solution to this issue, for others. -
Z zenzero-2001 has marked this topic as solved