Utils: Change browse button to OptionPushButton

Allows the user to choose whether he wants to browse local files,
or remote files.

Change-Id: I0d262034a5c2df56bea2efca876c9019099e3da9
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: David Schulz <david.schulz@qt.io>
Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
Marcus Tillmanns
2022-11-22 11:33:24 +01:00
parent 36be7b8375
commit b3f82887a3
5 changed files with 77 additions and 20 deletions

View File

@@ -481,15 +481,29 @@ FilePath firstOrEmpty(const FilePaths &filePaths)
return filePaths.isEmpty() ? FilePath() : filePaths.first(); return filePaths.isEmpty() ? FilePath() : filePaths.first();
} }
bool FileUtils::hasNativeFileDialog()
{
static std::optional<bool> hasNative;
if (!hasNative.has_value()) {
// Checking QFileDialog::itemDelegate() seems to be the only way to determine
// whether the dialog is native or not.
QFileDialog dialog;
hasNative = dialog.itemDelegate() == nullptr;
}
return *hasNative;
}
FilePath FileUtils::getOpenFilePath(QWidget *parent, FilePath FileUtils::getOpenFilePath(QWidget *parent,
const QString &caption, const QString &caption,
const FilePath &dir, const FilePath &dir,
const QString &filter, const QString &filter,
QString *selectedFilter, QString *selectedFilter,
QFileDialog::Options options, QFileDialog::Options options,
bool fromDeviceIfShiftIsPressed) bool fromDeviceIfShiftIsPressed,
bool forceNonNativeDialog)
{ {
bool forceNonNativeDialog = dir.needsDevice(); forceNonNativeDialog = forceNonNativeDialog || dir.needsDevice();
#ifdef QT_GUI_LIB #ifdef QT_GUI_LIB
if (fromDeviceIfShiftIsPressed && qApp->queryKeyboardModifiers() & Qt::ShiftModifier) { if (fromDeviceIfShiftIsPressed && qApp->queryKeyboardModifiers() & Qt::ShiftModifier) {
forceNonNativeDialog = true; forceNonNativeDialog = true;
@@ -514,9 +528,10 @@ FilePath FileUtils::getSaveFilePath(QWidget *parent,
const FilePath &dir, const FilePath &dir,
const QString &filter, const QString &filter,
QString *selectedFilter, QString *selectedFilter,
QFileDialog::Options options) QFileDialog::Options options,
bool forceNonNativeDialog)
{ {
bool forceNonNativeDialog = dir.needsDevice(); forceNonNativeDialog = forceNonNativeDialog || dir.needsDevice();
const QStringList schemes = QStringList(QStringLiteral("file")); const QStringList schemes = QStringList(QStringLiteral("file"));
return firstOrEmpty(getFilePaths(dialogParent(parent), return firstOrEmpty(getFilePaths(dialogParent(parent),
@@ -535,9 +550,10 @@ FilePath FileUtils::getExistingDirectory(QWidget *parent,
const QString &caption, const QString &caption,
const FilePath &dir, const FilePath &dir,
QFileDialog::Options options, QFileDialog::Options options,
bool fromDeviceIfShiftIsPressed) bool fromDeviceIfShiftIsPressed,
bool forceNonNativeDialog)
{ {
bool forceNonNativeDialog = dir.needsDevice(); forceNonNativeDialog = forceNonNativeDialog || dir.needsDevice();
#ifdef QT_GUI_LIB #ifdef QT_GUI_LIB
if (fromDeviceIfShiftIsPressed && qApp->queryKeyboardModifiers() & Qt::ShiftModifier) { if (fromDeviceIfShiftIsPressed && qApp->queryKeyboardModifiers() & Qt::ShiftModifier) {

View File

@@ -90,26 +90,31 @@ public:
#ifdef QT_WIDGETS_LIB #ifdef QT_WIDGETS_LIB
static void setDialogParentGetter(const std::function<QWidget *()> &getter); static void setDialogParentGetter(const std::function<QWidget *()> &getter);
static bool hasNativeFileDialog();
static FilePath getOpenFilePath(QWidget *parent, static FilePath getOpenFilePath(QWidget *parent,
const QString &caption, const QString &caption,
const FilePath &dir = {}, const FilePath &dir = {},
const QString &filter = {}, const QString &filter = {},
QString *selectedFilter = nullptr, QString *selectedFilter = nullptr,
QFileDialog::Options options = {}, QFileDialog::Options options = {},
bool fromDeviceIfShiftIsPressed = false); bool fromDeviceIfShiftIsPressed = false,
bool forceNonNativeDialog = false);
static FilePath getSaveFilePath(QWidget *parent, static FilePath getSaveFilePath(QWidget *parent,
const QString &caption, const QString &caption,
const FilePath &dir = {}, const FilePath &dir = {},
const QString &filter = {}, const QString &filter = {},
QString *selectedFilter = nullptr, QString *selectedFilter = nullptr,
QFileDialog::Options options = {}); QFileDialog::Options options = {},
bool forceNonNativeDialog = false);
static FilePath getExistingDirectory(QWidget *parent, static FilePath getExistingDirectory(QWidget *parent,
const QString &caption, const QString &caption,
const FilePath &dir = {}, const FilePath &dir = {},
QFileDialog::Options options = QFileDialog::ShowDirsOnly, QFileDialog::Options options = QFileDialog::ShowDirsOnly,
bool fromDeviceIfShiftIsPressed = false); bool fromDeviceIfShiftIsPressed = false,
bool forceNonNativeDialog = false);
static FilePaths getOpenFilePaths(QWidget *parent, static FilePaths getOpenFilePaths(QWidget *parent,
const QString &caption, const QString &caption,

View File

@@ -8,11 +8,13 @@
#include "fileutils.h" #include "fileutils.h"
#include "hostosinfo.h" #include "hostosinfo.h"
#include "macroexpander.h" #include "macroexpander.h"
#include "optionpushbutton.h"
#include "qtcassert.h" #include "qtcassert.h"
#include "qtcprocess.h" #include "qtcprocess.h"
#include "utilstr.h"
#include <QGuiApplication>
#include <QFileDialog> #include <QFileDialog>
#include <QGuiApplication>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QMenu> #include <QMenu>
#include <QPushButton> #include <QPushButton>
@@ -174,6 +176,9 @@ public:
const MacroExpander *m_macroExpander = globalMacroExpander(); const MacroExpander *m_macroExpander = globalMacroExpander();
std::function<void()> m_openTerminal; std::function<void()> m_openTerminal;
bool m_allowPathFromDevice = false; bool m_allowPathFromDevice = false;
QMenu *m_contextMenu = nullptr;
OptionPushButton *m_browseButton = nullptr;
}; };
PathChooserPrivate::PathChooserPrivate() PathChooserPrivate::PathChooserPrivate()
@@ -254,7 +259,15 @@ PathChooser::PathChooser(QWidget *parent) :
d->m_hLayout->addWidget(d->m_lineEdit); d->m_hLayout->addWidget(d->m_lineEdit);
d->m_hLayout->setSizeConstraint(QLayout::SetMinimumSize); d->m_hLayout->setSizeConstraint(QLayout::SetMinimumSize);
addButton(browseButtonLabel(), this, [this] { slotBrowse(); }); d->m_contextMenu = new QMenu(d->m_browseButton);
d->m_contextMenu->addAction(Tr::tr("Local"), this, [this] { slotBrowse(false); });
d->m_contextMenu->addAction(Tr::tr("Remote"), this, [this] { slotBrowse(true); });
d->m_browseButton = new OptionPushButton();
d->m_browseButton->setText(browseButtonLabel());
connect(d->m_browseButton, &OptionPushButton::clicked, this, [this] { slotBrowse(false); });
insertButton(d->m_buttons.count(), d->m_browseButton);
setLayout(d->m_hLayout); setLayout(d->m_hLayout);
setFocusProxy(d->m_lineEdit); setFocusProxy(d->m_lineEdit);
@@ -277,13 +290,18 @@ void PathChooser::addButton(const QString &text, QObject *context, const std::fu
insertButton(d->m_buttons.count(), text, context, callback); insertButton(d->m_buttons.count(), text, context, callback);
} }
void PathChooser::insertButton(int index, QAbstractButton *button)
{
d->m_hLayout->insertWidget(index + 1 /*line edit*/, button);
d->m_buttons.insert(index, button);
}
void PathChooser::insertButton(int index, const QString &text, QObject *context, const std::function<void ()> &callback) void PathChooser::insertButton(int index, const QString &text, QObject *context, const std::function<void ()> &callback)
{ {
auto button = new QPushButton; auto button = new QPushButton;
button->setText(text); button->setText(text);
connect(button, &QAbstractButton::clicked, context, callback); connect(button, &QAbstractButton::clicked, context, callback);
d->m_hLayout->insertWidget(index + 1/*line edit*/, button); insertButton(index, button);
d->m_buttons.insert(index, button);
} }
QString PathChooser::browseButtonLabel() QString PathChooser::browseButtonLabel()
@@ -371,7 +389,7 @@ void PathChooser::setReadOnly(bool b)
button->setEnabled(!b); button->setEnabled(!b);
} }
void PathChooser::slotBrowse() void PathChooser::slotBrowse(bool remote)
{ {
emit beforeBrowsing(); emit beforeBrowsing();
@@ -387,6 +405,8 @@ void PathChooser::slotBrowse()
predefined.clear(); predefined.clear();
} }
remote = remote || filePath().needsDevice();
// Prompt for a file/dir // Prompt for a file/dir
FilePath newPath; FilePath newPath;
switch (d->m_acceptingKind) { switch (d->m_acceptingKind) {
@@ -394,7 +414,10 @@ void PathChooser::slotBrowse()
case PathChooser::ExistingDirectory: case PathChooser::ExistingDirectory:
newPath = FileUtils::getExistingDirectory(this, newPath = FileUtils::getExistingDirectory(this,
makeDialogTitle(tr("Choose Directory")), makeDialogTitle(tr("Choose Directory")),
predefined, {}, d->m_allowPathFromDevice); predefined,
{},
d->m_allowPathFromDevice,
remote);
break; break;
case PathChooser::ExistingCommand: case PathChooser::ExistingCommand:
case PathChooser::Command: case PathChooser::Command:
@@ -404,7 +427,8 @@ void PathChooser::slotBrowse()
d->m_dialogFilter, d->m_dialogFilter,
nullptr, nullptr,
{}, {},
d->m_allowPathFromDevice); d->m_allowPathFromDevice,
remote);
newPath = appBundleExpandedPath(newPath); newPath = appBundleExpandedPath(newPath);
break; break;
case PathChooser::File: // fall through case PathChooser::File: // fall through
@@ -414,14 +438,18 @@ void PathChooser::slotBrowse()
d->m_dialogFilter, d->m_dialogFilter,
nullptr, nullptr,
{}, {},
d->m_allowPathFromDevice); d->m_allowPathFromDevice,
remote);
newPath = appBundleExpandedPath(newPath); newPath = appBundleExpandedPath(newPath);
break; break;
case PathChooser::SaveFile: case PathChooser::SaveFile:
newPath = FileUtils::getSaveFilePath(this, newPath = FileUtils::getSaveFilePath(this,
makeDialogTitle(tr("Choose File")), makeDialogTitle(tr("Choose File")),
predefined, predefined,
d->m_dialogFilter); d->m_dialogFilter,
nullptr,
{},
remote);
break; break;
case PathChooser::Any: { case PathChooser::Any: {
newPath = FileUtils::getOpenFilePath(this, newPath = FileUtils::getOpenFilePath(this,
@@ -430,7 +458,8 @@ void PathChooser::slotBrowse()
d->m_dialogFilter, d->m_dialogFilter,
nullptr, nullptr,
{}, {},
d->m_allowPathFromDevice); d->m_allowPathFromDevice,
remote);
break; break;
} }
default: default:
@@ -740,6 +769,11 @@ void PathChooser::setCommandVersionArguments(const QStringList &arguments)
void PathChooser::setAllowPathFromDevice(bool allow) void PathChooser::setAllowPathFromDevice(bool allow)
{ {
d->m_allowPathFromDevice = allow; d->m_allowPathFromDevice = allow;
if (allow && FileUtils::hasNativeFileDialog())
d->m_browseButton->setOptionalMenu(d->m_contextMenu);
else
d->m_browseButton->setOptionalMenu(nullptr);
} }
bool PathChooser::allowPathFromDevice() const bool PathChooser::allowPathFromDevice() const

View File

@@ -91,6 +91,7 @@ public:
static FilePath homePath(); static FilePath homePath();
void addButton(const QString &text, QObject *context, const std::function<void()> &callback); void addButton(const QString &text, QObject *context, const std::function<void()> &callback);
void insertButton(int index, QAbstractButton *button);
void insertButton(int index, const QString &text, QObject *context, const std::function<void()> &callback); void insertButton(int index, const QString &text, QObject *context, const std::function<void()> &callback);
QAbstractButton *buttonAtIndex(int index) const; QAbstractButton *buttonAtIndex(int index) const;
@@ -157,7 +158,7 @@ private:
bool validatePath(FancyLineEdit *edit, QString *errorMessage) const; bool validatePath(FancyLineEdit *edit, QString *errorMessage) const;
// Returns overridden title or the one from <title> // Returns overridden title or the one from <title>
QString makeDialogTitle(const QString &title); QString makeDialogTitle(const QString &title);
void slotBrowse(); void slotBrowse(bool remote);
void contextMenuRequested(const QPoint &pos); void contextMenuRequested(const QPoint &pos);
PathChooserPrivate *d = nullptr; PathChooserPrivate *d = nullptr;

View File

@@ -417,6 +417,7 @@ CMakeToolItemConfigWidget::CMakeToolItemConfigWidget(CMakeToolItemModel *model)
m_binaryChooser->setMinimumWidth(400); m_binaryChooser->setMinimumWidth(400);
m_binaryChooser->setHistoryCompleter(QLatin1String("Cmake.Command.History")); m_binaryChooser->setHistoryCompleter(QLatin1String("Cmake.Command.History"));
m_binaryChooser->setCommandVersionArguments({"--version"}); m_binaryChooser->setCommandVersionArguments({"--version"});
m_binaryChooser->setAllowPathFromDevice(true);
m_qchFileChooser = new PathChooser(this); m_qchFileChooser = new PathChooser(this);
m_qchFileChooser->setExpectedKind(PathChooser::File); m_qchFileChooser->setExpectedKind(PathChooser::File);