manage conflicting hotkeys in QComboBox, QLineEdit, QSpinBox

This commit is contained in:
Garux 2022-12-20 23:41:16 +06:00
parent 88a03b07be
commit 2ed868d254
8 changed files with 145 additions and 25 deletions

41
libs/gtkutil/combobox.h Normal file
View File

@ -0,0 +1,41 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
#include <QComboBox>
#include <QKeyEvent>
/// @brief Subclassed QComboBox not comsuming Enter key (why does it do it? works as expected for editable ComboBox)
/// purpose is to have working confirmation by Enter in dialogs
/// fixme unsolved crude problem here is triggering arrows, page, home, end global shortcuts when pressed in popup; even if modal dialog 😱
class ComboBox : public QComboBox
{
protected:
void keyPressEvent( QKeyEvent *event ) override {
if( event->key() == Qt::Key_Enter
|| event->key() == Qt::Key_Return ){
event->ignore();
return;
}
QComboBox::keyPressEvent( event );
}
};

54
libs/gtkutil/lineedit.h Normal file
View File

@ -0,0 +1,54 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
#include <QLineEdit>
#include <QKeyEvent>
#include <QCompleter>
#include <QAbstractItemView>
/// @brief Subclassed QLineEdit not comsuming undo/redo shortcuts
/// it's more useful to have working undo of editor
/// Qt should better not eat these shortcuts when QLineEdit's undo is disabled, but it does
class LineEdit : public QLineEdit
{
protected:
bool event( QEvent *event ) override {
if( event->type() == QEvent::ShortcutOverride ){
QKeyEvent *keyEvent = static_cast<QKeyEvent *>( event );
if( keyEvent == QKeySequence::StandardKey::Undo
|| keyEvent == QKeySequence::StandardKey::Redo )
return false;
// fix QCompleter leaking shortcuts
if( completer() != nullptr && completer()->popup() != nullptr && completer()->popup()->isVisible() )
if( keyEvent->key() == Qt::Key_Return
|| keyEvent->key() == Qt::Key_Enter
|| keyEvent->key() == Qt::Key_Escape
|| keyEvent->key() == Qt::Key_Up
|| keyEvent->key() == Qt::Key_Down
|| keyEvent->key() == Qt::Key_PageUp
|| keyEvent->key() == Qt::Key_PageDown )
event->accept();
}
return QLineEdit::event( event );
}
};

View File

@ -21,22 +21,20 @@
#pragma once
#include <QLineEdit>
#include <QKeyEvent>
#include "lineedit.h"
#include "spinbox.h"
#include <QTimer>
#include "generic/callback.h"
#include "spinbox.h"
class NonModalEntry : public QLineEdit
class NonModalEntry : public LineEdit
{
bool m_editing{};
Callback m_apply;
Callback m_cancel;
public:
NonModalEntry( const Callback& apply, const Callback& cancel ) : QLineEdit(), m_apply( apply ), m_cancel( cancel ){
NonModalEntry( const Callback& apply, const Callback& cancel ) : LineEdit(), m_apply( apply ), m_cancel( cancel ){
QObject::connect( this, &QLineEdit::textEdited, [this](){ m_editing = true; } );
QObject::connect( this, &QLineEdit::editingFinished, [this](){ // on enter or focus out
if( m_editing ){
@ -53,11 +51,12 @@ protected:
if( keyEvent->key() == Qt::Key_Escape ){
m_editing = false;
m_cancel();
clearFocus();
event->accept();
// defer clearFocus(); as immediately done after certain actions = cursor visible + not handling key input
QTimer::singleShot( 0, [this](){ clearFocus(); } );
}
}
return QLineEdit::event( event );
return LineEdit::event( event );
}
void focusInEvent( QFocusEvent *event ) override {
if( event->reason() == Qt::FocusReason::MouseFocusReason )

View File

@ -45,14 +45,23 @@ public:
}
protected:
bool event( QEvent *event ) override {
/* QAbstractSpinBox has no well defined ShortcutOverride routine, unlike underlying QLineEdit
thus box' portion of key events ends up firing application's shortcuts (e.g. up, down, pgUp, pgDown)
let's lock entire input on the box except a few keys, handled by parent window */
if( event->type() == QEvent::ShortcutOverride ){
QKeyEvent *keyEvent = static_cast<QKeyEvent *>( event );
if( keyEvent->key() != Qt::Key_Return
&& keyEvent->key() != Qt::Key_Enter
&& keyEvent->key() != Qt::Key_Escape )
// don't pass undo/redo to be eaten by underlying lineedit
// it's more useful to have working undo of editor
if( keyEvent == QKeySequence::StandardKey::Undo
|| keyEvent == QKeySequence::StandardKey::Redo )
return false;
/* QAbstractSpinBox has no well defined ShortcutOverride routine, unlike underlying QLineEdit
thus box' portion of key events ends up firing application's shortcuts (e.g. up, down, pgUp, pgDown)
let's implement it kinda */
if( keyEvent->key() == Qt::Key_PageUp
|| keyEvent->key() == Qt::Key_PageDown
|| keyEvent->key() == Qt::Key_Up
|| keyEvent->key() == Qt::Key_Down
|| keyEvent->key() == Qt::Key_End
|| keyEvent->key() == Qt::Key_Home
|| keyEvent == QKeySequence::StandardKey::SelectAll )
event->accept();
}
return SpinT::event( event );

View File

@ -45,7 +45,7 @@
#include "gtkmisc.h"
#include <QCheckBox>
#include <QComboBox>
#include "gtkutil/combobox.h"
#include <QSlider>
#include <QRadioButton>
#include <QButtonGroup>
@ -411,7 +411,7 @@ QCheckBox* Dialog::addCheckBox( QGridLayout* grid, const char* name, const char*
}
QComboBox* Dialog::addCombo( QGridLayout* grid, const char* name, StringArrayRange values, const IntImportCallback& importViewer, const IntExportCallback& exportViewer ){
auto combo = new QComboBox;
auto combo = new ComboBox;
for ( const char *value : values )
combo->addItem( value );

View File

@ -44,7 +44,7 @@
#include <QGridLayout>
#include <QScrollArea>
#include <QCheckBox>
#include <QLineEdit>
#include "gtkutil/lineedit.h"
#include <QLabel>
#include <QPushButton>
#include <QToolButton>
@ -1229,13 +1229,13 @@ QWidget* EntityInspector_constructWindow( QWidget* toplevel ){
grid->addWidget( new QLabel( "Value" ), 1, 0 );
}
{
auto line = g_entityKeyEntry = new QLineEdit;
auto line = g_entityKeyEntry = new LineEdit;
grid->addWidget( line, 0, 1 );
QObject::connect( line, &QLineEdit::returnPressed, [](){ g_entityValueEntry->setFocus(); g_entityValueEntry->selectAll(); } );
}
{
auto line = g_entityValueEntry = new QLineEdit;
auto line = g_entityValueEntry = new LineEdit;
grid->addWidget( line, 1, 1 );
QObject::connect( line, &QLineEdit::returnPressed, [](){ EntityInspector_applyKeyValue(); } );
}

View File

@ -33,7 +33,7 @@
#include <QHBoxLayout>
#include <QFormLayout>
#include <QLineEdit>
#include "gtkutil/lineedit.h"
#include <QLabel>
#include <QCheckBox>
#include <QEvent>
@ -105,6 +105,21 @@ protected:
FindActiveTracker s_find_focus_in( true );
FindActiveTracker s_replace_focus_in( false );
class : public QObject
{
protected:
bool eventFilter( QObject *obj, QEvent *event ) override {
if( event->type() == QEvent::ShortcutOverride ) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>( event );
if( keyEvent->key() == Qt::Key_Tab ){
event->accept();
}
}
return QObject::eventFilter( obj, event ); // standard event processing
}
}
s_pressedKeysFilter;
}
@ -120,6 +135,8 @@ FindTextureDialog::~FindTextureDialog(){
void FindTextureDialog::BuildDialog(){
GetWidget()->setWindowTitle( "Find / Replace Texture(s)" );
GetWidget()->installEventFilter( &s_pressedKeysFilter );
g_guiSettings.addWindow( GetWidget(), "TextureBrowser/FindReplace" );
auto hbox = new QHBoxLayout( GetWidget() );
@ -127,14 +144,14 @@ void FindTextureDialog::BuildDialog(){
hbox->addLayout( form );
{
auto entry = new QLineEdit;
auto entry = new LineEdit;
form->addRow( "Find:", entry );
AddDialogData( *entry, m_strFind );
entry->installEventFilter( &s_find_focus_in );
GlobalTextureEntryCompletion::instance().connect( entry );
}
{
auto entry = new QLineEdit;
auto entry = new LineEdit;
auto label = new QLabel( "Replace:" );
form->addRow( label, entry );
entry->setPlaceholderText( "Empty = search mode" );

View File

@ -820,7 +820,7 @@ void Patch_constructMenu( QMenu* menu ){
#include "gtkutil/spinbox.h"
#include <QDialog>
#include <QComboBox>
#include "gtkutil/combobox.h"
#include <QCheckBox>
#include <QFormLayout>
#include <QDialogButtonBox>
@ -831,8 +831,8 @@ void DoNewPatchDlg( EPatchPrefab prefab, int minrows, int mincols, int defrows,
QDialog dialog( MainFrame_getWindow(), Qt::Dialog | Qt::WindowCloseButtonHint );
dialog.setWindowTitle( "Patch density" );
auto width = new QComboBox;
auto height = new QComboBox;
auto width = new ComboBox;
auto height = new ComboBox;
auto redisperseCheckBox = new QCheckBox( "Square" );
{