Qt: Handle UAT editor dialogs explicitly.

QAbstractItemView assumes that editors are widgets, not windows.
QAbstractItemView::edit calls QAbstractItemViewPrivate::openEditor,
which simply calls show() and setFocus() on the editor widget. If that
widget happens to be a native dialog, its event loop might not be
processed. This is the case on macOS at least.

Create widgets derived from QLineEdit that edit the value directly along
with a button that can open the associated modal dialog. Install event
filters so that we keep the correct tab behavior between fields of the
UAT.

Bug: 13958
Ping-Bug: 14031
Bug: 7761
Change-Id: Ie5f0a5cbde33bb9add8217029c2063a0bbfd804a
Reviewed-on: https://code.wireshark.org/review/23015
Reviewed-by: Gerald Combs <gerald@wireshark.org>
Petri-Dish: Gerald Combs <gerald@wireshark.org>
Tested-by: Petri Dish Buildbot
Reviewed-by: Roland Knall <rknall@gmail.com>
This commit is contained in:
Michael Mann 2017-09-16 17:45:05 -04:00 committed by Roland Knall
parent 9a5217bdd4
commit f789736521
6 changed files with 184 additions and 85 deletions

View File

@ -23,6 +23,9 @@
#include <ui/qt/widgets/editor_file_dialog.h>
#include <ui/qt/widgets/editor_color_dialog.h>
// The Qt docs suggest overriding updateEditorGeometry, but the
// defaults seem sane.
UatDelegate::UatDelegate(QObject *parent) : QStyledItemDelegate(parent)
{
}
@ -42,12 +45,10 @@ QWidget *UatDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &
case PT_TXTMOD_DIRECTORYNAME:
if (index.isValid()) {
QString filename_old = index.model()->data(index, Qt::EditRole).toString();
EditorFileDialog* fileDialog = new EditorFileDialog(index, parent, QString(field->title), filename_old);
fileDialog->setFileMode(QFileDialog::DirectoryOnly);
EditorFileDialog* fileDialog = new EditorFileDialog(index, EditorFileDialog::Directory, parent, QString(field->title), filename_old);
//Use signals to accept data from cell
connect(fileDialog, SIGNAL(acceptEdit(const QModelIndex &)), this, SLOT(applyDirectory(const QModelIndex&)));
connect(fileDialog, SIGNAL(acceptEdit(const QModelIndex &)), this, SLOT(applyFilename(const QModelIndex&)));
return fileDialog;
}
@ -57,9 +58,8 @@ QWidget *UatDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &
case PT_TXTMOD_FILENAME:
if (index.isValid()) {
QString filename_old = index.model()->data(index, Qt::EditRole).toString();
EditorFileDialog* fileDialog = new EditorFileDialog(index, parent, QString(field->title), filename_old);
EditorFileDialog* fileDialog = new EditorFileDialog(index, EditorFileDialog::ExistingFile, parent, QString(field->title), filename_old);
fileDialog->setFileMode(QFileDialog::ExistingFile);
fileDialog->setOption(QFileDialog::DontConfirmOverwrite);
//Use signals to accept data from cell
@ -73,9 +73,7 @@ QWidget *UatDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &
case PT_TXTMOD_COLOR:
if (index.isValid()) {
QColor color(index.model()->data(index, Qt::DecorationRole).toString());
EditorColorDialog *colorDialog = new EditorColorDialog(index, color, new QWidget(parent));
colorDialog->setWindowFlags(Qt::Window);
EditorColorDialog *colorDialog = new EditorColorDialog(index, color, parent);
//Use signals to accept data from cell
connect(colorDialog, SIGNAL(acceptEdit(const QModelIndex &)), this, SLOT(applyColor(const QModelIndex &)));
@ -179,8 +177,6 @@ void UatDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
model->setData(index, data, Qt::EditRole);
break;
}
case PT_TXTMOD_DIRECTORYNAME:
case PT_TXTMOD_FILENAME:
case PT_TXTMOD_COLOR:
//do nothing, dialog signals will update table
break;
@ -190,58 +186,18 @@ void UatDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
}
}
void UatDelegate::updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option, const QModelIndex &index) const
{
uat_field_t *field = indexToField(index);
switch (field->mode) {
case PT_TXTMOD_DIRECTORYNAME:
{
QRect rect = option.rect;
rect.setBottom(rect.width());
editor->setGeometry(rect);
break;
}
case PT_TXTMOD_FILENAME:
{
QRect rect = option.rect;
rect.setWidth(600);
rect.setHeight(600);
editor->setGeometry(rect);
break;
}
default:
//the defaults for other editors seem sane.
QStyledItemDelegate::updateEditorGeometry(editor, option, index);
}
}
void UatDelegate::applyFilename(const QModelIndex& index)
{
if (index.isValid()) {
EditorFileDialog* fileDialog = static_cast<EditorFileDialog*>(sender());
QStringList files = fileDialog->selectedFiles();
if (files.size() > 0) {
((QAbstractItemModel *)index.model())->setData(index, files[0], Qt::EditRole);
}
}
}
void UatDelegate::applyDirectory(const QModelIndex& index)
{
if (index.isValid()) {
EditorFileDialog* fileDialog = static_cast<EditorFileDialog*>(sender());
const QString &data = fileDialog->directory().absolutePath();
((QAbstractItemModel *)index.model())->setData(index, data, Qt::EditRole);
((QAbstractItemModel *)index.model())->setData(index, fileDialog->text(), Qt::EditRole);
}
}
void UatDelegate::applyColor(const QModelIndex& index)
{
if (index.isValid()) {
QColorDialog *colorDialog = static_cast<QColorDialog*>(sender());
EditorColorDialog *colorDialog = static_cast<EditorColorDialog*>(sender());
QColor newColor = colorDialog->currentColor();
((QAbstractItemModel *)index.model())->setData(index, newColor.name(), Qt::EditRole);
}

View File

@ -32,11 +32,7 @@ public:
void setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const;
void updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option, const QModelIndex &index) const;
private slots:
void applyDirectory(const QModelIndex& index);
void applyFilename(const QModelIndex& index);
void applyColor(const QModelIndex& index);

View File

@ -10,24 +10,67 @@
#include <ui/qt/widgets/editor_color_dialog.h>
EditorColorDialog::EditorColorDialog(const QModelIndex& index, QWidget* parent)
: QColorDialog(parent)
, index_(index)
{
}
#include <QColorDialog>
#include <QKeyEvent>
#include <QStyle>
EditorColorDialog::EditorColorDialog(const QModelIndex& index, const QColor& initial, QWidget* parent)
: QColorDialog(initial, parent)
: QLineEdit(parent)
, color_button_(new QPushButton(this))
, index_(index)
, current_(initial)
{
connect(color_button_, SIGNAL(clicked()), this, SLOT(applyColor()));
}
void EditorColorDialog::accept()
// QAbstractItemView installs QAbstractItemDelegate's event filter after
// we've been created. We need to install our own event filter after that
// happens so that we can steal tab keypresses.
void EditorColorDialog::focusInEvent(QFocusEvent *event)
{
emit acceptEdit(index_);
QColorDialog::accept();
installEventFilter(this);
QLineEdit::focusInEvent(event);
}
void EditorColorDialog::focusOutEvent(QFocusEvent *event)
{
removeEventFilter(this);
QLineEdit::focusOutEvent(event);
}
bool EditorColorDialog::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::KeyPress) {
QKeyEvent* key = static_cast<QKeyEvent*>(event);
if ( (key->key() == Qt::Key_Tab) && !color_button_->hasFocus()) {
color_button_->setFocus();
return true;
}
}
return QLineEdit::eventFilter(obj, event);
}
void EditorColorDialog::resizeEvent(QResizeEvent *)
{
// Move the button to the end of the line edit and set its height.
QSize sz = color_button_->sizeHint();
int frame_width = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
color_button_->move(rect().right() - frame_width - sz.width(),
contentsRect().top());
color_button_->setMinimumHeight(contentsRect().height());
color_button_->setMaximumHeight(contentsRect().height());
}
void EditorColorDialog::applyColor()
{
QColorDialog color_dlg;
color_dlg.setCurrentColor(current_);
if (color_dlg.exec() == QDialog::Accepted) {
current_ = color_dlg.currentColor();
emit acceptEdit(index_);
}
}
/*

View File

@ -11,23 +11,33 @@
#ifndef EDITOR_COLOR_DIALOG_H_
#define EDITOR_COLOR_DIALOG_H_
#include <QColorDialog>
#include <QLineEdit>
#include <QPushButton>
#include <QModelIndex>
class EditorColorDialog : public QColorDialog
class EditorColorDialog : public QLineEdit
{
Q_OBJECT
public:
EditorColorDialog(const QModelIndex& index, QWidget* parent = 0);
EditorColorDialog(const QModelIndex& index, const QColor& initial, QWidget* parent = 0);
void accept();
QColor currentColor() { return current_; }
virtual void focusInEvent(QFocusEvent *event);
virtual void focusOutEvent(QFocusEvent *event);
virtual bool eventFilter(QObject *obj, QEvent *event);
signals:
void acceptEdit(const QModelIndex& index);
private slots:
void applyColor();
protected:
void resizeEvent(QResizeEvent *);
QPushButton* color_button_;
const QModelIndex index_; //saved index of table cell
QColor current_; //initial color in edit
};
#endif /* EDITOR_COLOR_DIALOG_H_ */

View File

@ -10,22 +10,100 @@
#include <ui/qt/widgets/editor_file_dialog.h>
EditorFileDialog::EditorFileDialog(const QModelIndex& index, QWidget* parent, Qt::WindowFlags flags)
: QFileDialog(parent, flags)
#include <QKeyEvent>
#include <QStyle>
#include "wsutil/utf8_entities.h"
EditorFileDialog::EditorFileDialog(const QModelIndex& index, enum FileMode mode, QWidget* parent, const QString& caption, const QString& directory, const QString& filter)
: QLineEdit(parent)
, file_dialog_button_(new QPushButton(this))
, index_(index)
, mode_(mode)
, caption_(caption)
, directory_(directory)
, filter_(filter)
, options_(0)
{
if (mode_ == Directory)
options_ = QFileDialog::ShowDirsOnly;
if (!directory.isEmpty())
setText(directory);
file_dialog_button_->setText(UTF8_HORIZONTAL_ELLIPSIS);
connect(file_dialog_button_, SIGNAL(clicked()), this, SLOT(applyFilename()));
}
EditorFileDialog::EditorFileDialog(const QModelIndex& index, QWidget* parent, const QString& caption, const QString& directory, const QString& filter)
: QFileDialog(parent, caption, directory, filter)
, index_(index)
void EditorFileDialog::setOption(QFileDialog::Option option, bool on)
{
if (on)
{
options_ |= option;
}
else
{
options_ &= (~option);
}
}
void EditorFileDialog::accept()
// QAbstractItemView installs QAbstractItemDelegate's event filter after
// we've been created. We need to install our own event filter after that
// happens so that we can steal tab keypresses.
void EditorFileDialog::focusInEvent(QFocusEvent *event)
{
emit acceptEdit(index_);
QFileDialog::accept();
installEventFilter(this);
QLineEdit::focusInEvent(event);
}
void EditorFileDialog::focusOutEvent(QFocusEvent *event)
{
removeEventFilter(this);
QLineEdit::focusOutEvent(event);
}
bool EditorFileDialog::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::KeyPress) {
QKeyEvent* key = static_cast<QKeyEvent*>(event);
if ( (key->key() == Qt::Key_Tab) && !file_dialog_button_->hasFocus()) {
file_dialog_button_->setFocus();
return true;
}
}
return QLineEdit::eventFilter(obj, event);
}
void EditorFileDialog::resizeEvent(QResizeEvent *)
{
// Move the button to the end of the line edit and set its height.
QSize sz = file_dialog_button_->sizeHint();
int frame_width = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
file_dialog_button_->move(rect().right() - frame_width - sz.width(),
contentsRect().top());
file_dialog_button_->setMinimumHeight(contentsRect().height());
file_dialog_button_->setMaximumHeight(contentsRect().height());
}
void EditorFileDialog::applyFilename()
{
QString file;
if (mode_ == Directory)
{
file = QFileDialog::getExistingDirectory(this, caption_, directory_, options_);
}
else
{
file = QFileDialog::getOpenFileName(this, caption_, directory_, filter_, NULL, options_);
}
if (!file.isEmpty())
{
setText(file);
emit acceptEdit(index_);
}
}
/*

View File

@ -11,23 +11,39 @@
#ifndef EDITOR_FILE_DIALOG_H_
#define EDITOR_FILE_DIALOG_H_
#include <QFileDialog>
#include <QModelIndex>
#include <QLineEdit>
#include <QFileDialog>
#include <QPushButton>
class EditorFileDialog : public QFileDialog
class EditorFileDialog : public QLineEdit
{
Q_OBJECT
public:
explicit EditorFileDialog(const QModelIndex& index, QWidget* parent, Qt::WindowFlags flags);
explicit EditorFileDialog(const QModelIndex& index, QWidget* parent = 0, const QString & caption = QString(), const QString & directory = QString(), const QString & filter = QString());
enum FileMode { ExistingFile, Directory };
void accept();
explicit EditorFileDialog(const QModelIndex& index, enum FileMode mode, QWidget* parent = 0, const QString & caption = QString(), const QString & directory = QString(), const QString & filter = QString());
void setOption(QFileDialog::Option option, bool on = true);
virtual void focusInEvent(QFocusEvent *event);
virtual void focusOutEvent(QFocusEvent *event);
virtual bool eventFilter(QObject *obj, QEvent *event);
signals:
void acceptEdit(const QModelIndex& index);
private slots:
void applyFilename();
protected:
void resizeEvent(QResizeEvent *);
QPushButton* file_dialog_button_;
const QModelIndex index_; //saved index of table cell
enum FileMode mode_;
QString caption_;
QString directory_;
QString filter_;
QFileDialog::Options options_;
};
#endif /* EDITOR_FILE_DIALOG_H_ */