Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

How to override Paste or catch the moment before Paste happens in QLineEdit?



  • I need to change the behavior of "Paste" (completely override, make it act in a different way) in the standard context menu of the QLineEdit.
    Or at least somehow catch the moment before Paste is about to happen in QLineEdit and do some preparations (clean the text line).
    How can I do it for both the context menu and shortcut ctr+v?

    I have something like this:

    MyLineEdit::MyLineEdit(QWidget* parent)
    	: QLineEdit(parent)
    {
    	setContextMenuPolicy(Qt::CustomContextMenu);
    	connect(this, &MyLineEdit::customContextMenuRequested, this, &MyLineEdit::ShowContextMenu);
    }
    
    void MyLineEdit::ShowContextMenu(const QPoint& pos)
    {
    	QScopedPointer<QMenu> menu(createStandardContextMenu());
    	QAction* defaultPaste = menu->actions()[5];
    
    	// How can I override default Paste functionality here?
    	// Or at least add some functionality before QLineEdit::paste() slot will be executed?
    
    	menu->exec(mapToGlobal(pos));
    }
    

    It's not necessary to use this code/this way tho. Any better/proper way is appreciated.



  • @Rian-Firth said in How to override Paste or catch the moment before Paste happens in QLineEdit?:

    catch the moment before Paste is about to happen in QLineEdit and do some preparations

    The problem is that is is not done within QLineEdit class.

    See here
    The QLineEdit::paste() just triggers the internal paste function (here), which takes the content from clipboard and inserts it to your widget. And because it's internal, you cant just access or intercept it without changing the Qt source.

    You could try to grab the keyEvent and check if the pressed key(-sequence) is QKeySequence::Paste (https://doc.qt.io/qt-5/qkeysequence.html#standard-shortcuts) and then do your stuff.



  • Isn't there a way to do something with QLineEdit::paste() slot?

    You could try to grab the keyEvent and check if the pressed key(-sequence) is QKeySequence::Paste (https://doc.qt.io/qt-5/qkeysequence.html#standard-shortcuts) and then do your stuff.

    But what about context menu Paste action?
    It'll change shortcut behavior but if you'll click Paste in the context menu it'll be still the same.



  • @Rian-Firth said in How to override Paste or catch the moment before Paste happens in QLineEdit?:

    It'll change shortcut behavior but if you'll click Paste in the context menu it'll be still the same.

    You have access to your context menu and its QActions, dont you?!

    There you can add a connection to do your custom stuff before you call the QLineEdit::paste() slot.

    Maybe you have to disconnect everything from your standard Paste-QAction first, because I don't know if the connection is made, when creating the "standard context menu". Then the paste would happen before you can do your stuff. So delete the default connection, connect to your slot and then call paste() when you are ready to insert the text.

    -> defaultPaste->disconnect() // Disconnects all slots from object

    @Rian-Firth said in How to override Paste or catch the moment before Paste happens in QLineEdit?:

    menu->actions()[5]

    Make sure that 5 is always the paste action. The order might change, esp. when you add more QActions to your menu.



  • So delete the default connection, connect to your slot and then call paste() when you are ready to insert the text.

    Oh, this does the trick!
    So I do this for the context menu and I also should do what you said earlier about keyEvent to override whole paste functionality?
    You either ctrl+v or click Paste in the context menu, there's nothing else I should be worried about?
    There's no other way to paste text in QLineEdit?

    Make sure that 5 is always the paste action. The order might change, esp. when you add more QActions to your menu.

    I'd really like to use findChild<QAction*>("action_name") but objectName's are not set for created actions: https://code.woboq.org/qt5/qtbase/src/widgets/widgets/qlineedit.cpp.html#2210



  • @Rian-Firth said in How to override Paste or catch the moment before Paste happens in QLineEdit?:

    You either ctrl+v or click Paste in the context menu, there's nothing else I should be worried about?

    How else you want to paste from clipboard? I only know about keyboard shortcuts (Ctrl + V or Insert key) and menus.

    @Pl45m4 said in How to override Paste or catch the moment before Paste happens in QLineEdit?:

    because I don't know if the connection is made, when creating the "standard context menu"

    I can confirm that now :)
    (createStandardContextMenu() Code)

    @Rian-Firth said in How to override Paste or catch the moment before Paste happens in QLineEdit?:

    I'd really like to use findChild<QAction*>("action_name") but objectName's are not set for created actions:

    I guess you can work with the shortcut... (unless you don't change it manually!)

    action->shortcut() returns a QKeySequence. And you know it has to be QKeySequence::Paste You only need to check which the primary KeySequence is (what shortcut()returns... Ctrl + V or "Paste" or whatever), since shortcut() returns the primary only.
    Otherwise you could use shortcuts(), but this will return a list with all shortcuts for this widget... so you would have to check and compare even more...



  • @Pl45m4 said in How to override Paste or catch the moment before Paste happens in QLineEdit?:

    You could try to grab the keyEvent and check if the pressed key(-sequence) is QKeySequence::Paste (https://doc.qt.io/qt-5/qkeysequence.html#standard-shortcuts) and then do your stuff.

    Did you mean something like this?
    It works but feels so wrong.

    MyLineEdit::MyLineEdit(QWidget * parent)
    	: QLineEdit(parent)
    {
    	setContextMenuPolicy(Qt::CustomContextMenu);
    	connect(this, &MyLineEdit::customContextMenuRequested, this, &MyLineEdit::ShowContextMenu);
    
    	QShortcut* shortcut = new QShortcut(QKeySequence::Paste, this); // It works only with these two lines.
    	qApp->installEventFilter(this);                                 // And I've no idea how it works.
    }
    
    void MyLineEdit::MyPaste()
    {
    	clear();
    	paste();
    }
    
    void MyLineEdit::ShowContextMenu(const QPoint& pos)
    {
    	QScopedPointer<QMenu> menu(createStandardContextMenu());
    	QAction* defaultPaste = menu->actions()[5];
    	defaultPaste->disconnect(); // Remove connect(action, SIGNAL(triggered()), SLOT(paste()));
    
    	// Because of QShortcut* shortcut = new QShortcut(QKeySequence::Paste, this); in constructor
    	// Paste doesn't have shortcut in the context menu, setting it to default one again
    	defaultPaste->setShortcut(QKeySequence::Paste);
    
    	connect(defaultPaste, &QAction::triggered, [this]() {
    		MyPaste(); // Override the context menu Paste's functionality
    	});
    
    	menu->exec(mapToGlobal(pos));
    }
    
    bool MyLineEdit::eventFilter(QObject *watched, QEvent *event)
    {
    	if (event->type() == QEvent::ShortcutOverride) {
    		QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
    
    		if (keyEvent->matches(QKeySequence::Paste))
    		{
    			MyPaste(); // Override Ctrl+V functionality
    			return true;
    		}
    	}
    
    	return QLineEdit::eventFilter(watched, event);
    }
    

    Especially these two lines:

    QShortcut* shortcut = new QShortcut(QKeySequence::Paste, this); // It works only with these two lines.
    qApp->installEventFilter(this);                                 // And I've no idea how it works.
    

    It doesn't want to work without creating a new QShortcut.

    Am I doing something wrong?



  • @Rian-Firth said in How to override Paste or catch the moment before Paste happens in QLineEdit?:

    Am I doing something wrong?

    You install the event filter on qApp. This is not a good idea. For all widgets this one MyLineEdit will intercept Ctrl+V. Just try having two line edits and you probably will see some weird behavior.

    If you want to use eventFilter() (which is a reasonable choice), you should make a separate class (derived from QObject) just for event filtering and install it on a single QLineEdit, or rather MyLineEdit in your case. The reason for this is that the filter should only apply to this single widget and not everyone else.

    There's no other way to paste text in QLineEdit?

    I am not entirely sure. But I suspect that there is also some drag'n'drop behavior here. At least there should be dragging and dropping within the line edit itself. Furthermore, you might be able to drop text from anywhere else.



  • @SimonSchroeder said in How to override Paste or catch the moment before Paste happens in QLineEdit?:

    But I suspect that there is also some drag'n'drop behavior here

    Ah yeah, forgot about that.



  • This works and looks better than eventFilter.

    MyLineEdit::MyLineEdit(QWidget * parent)
    	: QLineEdit(parent)
    {
    	setContextMenuPolicy(Qt::CustomContextMenu);
    	connect(this, &MyLineEdit::customContextMenuRequested, this, &MyLineEdit::ShowContextMenu);
    }
    
    void MyLineEdit::MyPaste()
    {
    	clear();
    	paste();
    }
    
    void MyLineEdit::ShowContextMenu(const QPoint& pos)
    {
    	QScopedPointer<QMenu> menu(createStandardContextMenu());
    	const auto actions = menu->actions();
    	const QString translatedActionName = QCoreApplication::translate("QLineEdit", "&Paste");
    	const auto it = std::find_if(actions.cbegin(), actions.cend(), [&translatedActionName](const QAction * action) {
    		return action->text().startsWith(translatedActionName);
    	});
    
    	if (it != actions.cend())
    	{
    		const auto actionPaste = *it;
    		actionPaste->disconnect(SIGNAL(triggered()));
    		connect(actionPaste, &QAction::triggered, this, &MyLineEdit::MyPaste); // Override the context menu's Paste functionality
    	}
    
    	menu->exec(mapToGlobal(pos));
    }
    
    void MyLineEdit::keyPressEvent(QKeyEvent *event)
    {
    	if (event->matches(QKeySequence::Paste))
    	{
    		MyPaste(); // Override Ctrl+V functionality
    		event->accept();
    		return;
    	}
    
    	QLineEdit::keyPressEvent(event);
    }
    

Log in to reply