Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QLineEdit Undo / Redo Shortcut Text Disappearing
Qt 6.11 is out! See what's new in the release blog

QLineEdit Undo / Redo Shortcut Text Disappearing

Scheduled Pinned Locked Moved Solved General and Desktop
8 Posts 3 Posters 215 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • Z Offline
    Z Offline
    zenzero-2001
    wrote last edited by
    #1

    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_H
    

    mainwindow.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

    JonBJ 1 Reply Last reply
    0
    • I IgKh

      It is indeed a very peculiar behavior, unique only to the context menus of QLineEdit, QTextEdit and QPlainTextEdit.

      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 custom ACCEL_KEY macro 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 4 Qt3 (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)

      Z Offline
      Z Offline
      zenzero-2001
      wrote last edited by
      #7

      @IgKh @JonB

      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.

      JonBJ 1 Reply Last reply
      2
      • Z zenzero-2001

        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_H
        

        mainwindow.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

        JonBJ Offline
        JonBJ Offline
        JonB
        wrote last edited by
        #2

        @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.

        1 Reply Last reply
        0
        • Z Offline
          Z Offline
          zenzero-2001
          wrote last edited by
          #3

          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

          JonBJ 1 Reply Last reply
          0
          • Z zenzero-2001

            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

            JonBJ Offline
            JonBJ Offline
            JonB
            wrote last edited by
            #4

            @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?

            1 Reply Last reply
            1
            • I Offline
              I Offline
              IgKh
              wrote last edited by IgKh
              #5

              It is indeed a very peculiar behavior, unique only to the context menus of QLineEdit, QTextEdit and QPlainTextEdit.

              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 custom ACCEL_KEY macro 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 4 Qt3 (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)

              Z 1 Reply Last reply
              4
              • Z Offline
                Z Offline
                zenzero-2001
                wrote last edited by
                #6

                Thanks for the help @JonB and @IgKh . Glad it is not me.

                I will look into it further this evening and in the coming days. Have to do some real work now :-(

                1 Reply Last reply
                0
                • I IgKh

                  It is indeed a very peculiar behavior, unique only to the context menus of QLineEdit, QTextEdit and QPlainTextEdit.

                  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 custom ACCEL_KEY macro 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 4 Qt3 (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)

                  Z Offline
                  Z Offline
                  zenzero-2001
                  wrote last edited by
                  #7

                  @IgKh @JonB

                  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.

                  JonBJ 1 Reply Last reply
                  2
                  • Z zenzero-2001

                    @IgKh @JonB

                    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.

                    JonBJ Offline
                    JonBJ Offline
                    JonB
                    wrote last edited by JonB
                    #8

                    @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.

                    1 Reply Last reply
                    0
                    • Z zenzero-2001 has marked this topic as solved

                    • Login

                    • Login or register to search.
                    • First post
                      Last post
                    0
                    • Categories
                    • Recent
                    • Tags
                    • Popular
                    • Users
                    • Groups
                    • Search
                    • Get Qt Extensions
                    • Unsolved