Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a widget for editing file names #5375

Merged
merged 2 commits into from
May 15, 2017
Merged

Add a widget for editing file names #5375

merged 2 commits into from
May 15, 2017

Conversation

zeule
Copy link
Contributor

@zeule zeule commented Jun 15, 2016

The combinations of QLineEdit or QComboBox with a browse button for file system paths editing most likely results in selecting files or dirs via the button and a file dialog. However, typing with auto completion might provide a better alternative. This PR implements it in the standard for Qt way, using QCompleter and QFileSystemModel.

The PR adds FileSystemPathLineEdit and FileSystemPathComboEdit classes (share most of the implementation) that provides widgets for editing file names with QLineEdit or QComboBox (in editable mode). The widgets also contain the "Browse" button, which launches a file dialog. Properties of the dialog (title, file filter, etc.) are specified as FileSystemPathEdit class properties.

The class also uses Utils::Fs::fromNativePath() and Utils::Fs::toNativePath() internally thus eliminating the need to use them with each widget explicitly.

Screenshots show example for Add torrent dialog:

qbt-adddialog-completion
qbt-adddialog-completion-menu

#endif
}

#include "moc_fspathedit.cpp"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you include this file?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Due to Q_PRIVATE_SLOT(d_func(), void browseActionTriggered()) in the FileSystemPathEdit class.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q_PRIVATE_SLOT replaced with lambda.

@Chocobo1
Copy link
Member

Is it a good idea to move the new code to its own folder gui/fspathedit? There are already 99 files + dirs in gui...

@glassez
Copy link
Member

glassez commented Jun 16, 2016

Is it a good idea to move the new code to its own folder gui/fspathedit?

IMO, it's good.

@zeule
Copy link
Contributor Author

zeule commented Jun 16, 2016

Is it a good idea to move the new code to its own folder gui/fspathedit?

Maybe gui/widgets is a better name?

@glassez
Copy link
Member

glassez commented Jun 17, 2016

Maybe gui/widgets is a better name?

Then we should move all other widgets there.

@zeule
Copy link
Contributor Author

zeule commented Jun 17, 2016

... and do the same for gui/views, gui/models, and, gui/dialogs.

size_string += ")";
ui->size_lbl->setText(size_string);
}

void AddNewTorrentDialog::onSavePathChanged(int index)
void AddNewTorrentDialog::onSavePathChanged(const QString &/*text*/)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can connect slots with fewer parameters than signal has.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excuse me, I don't understand what did you mean here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean you can turn it to void AddNewTorrentDialog::onSavePathChanged() unless you need QString param.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, thanks.

@@ -658,6 +623,15 @@ void AddNewTorrentDialog::accept()
BitTorrent::Session::instance()->addTorrent(m_torrentInfo, params);

m_torrentGuard->markAsAddedToSession();

// add selected save path to the save path history
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused about it outside of class. Why not do this in the destructor?

Copy link
Member

@glassez glassez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have not delved much into the logic (leave that for the author), but the code structure looks good. I left some comments. This is a very useful widget. I hope you will expand it in the future to support relative paths too.

#include <QToolButton>

#include "fspathedit_p.h"
#include "base/utils/fs.h"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move one line up.

break;
case FileSystemPathEdit::Mode::DirectoryOpen:
case FileSystemPathEdit::Mode::DirectorySave:
selectedPath = QFileDialog::getExistingDirectory(q, dialogCaptionOrDefault(), directory, QFileDialog::DontResolveSymlinks
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would split this line on some param boundary.

connect(d->m_browseAction, SIGNAL(triggered(bool)), this, SLOT(browseActionTriggered()));
}

FileSystemPathEdit::~FileSystemPathEdit()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it would be better to omit this body and use = default in header?

Q_D(FileSystemPathEdit);
d->m_fileNameFilter = val;

#if 0 // QFileSystemModel applies name filters to directories too.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move comment to the next line.

Q_D(FileSystemPathEdit);
QString newPath = selectedPath();
if (newPath != d->m_lastSignaledPath) {
Q_EMIT selectedPathChanged(newPath);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We support emit keyword, isn't it?


enum class Mode
{
FileOpen, //!< Working mode for opening files, shows open dialog
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you like to put these comments closer to the enum items?

protected slots:
void onPathEdited();

signals:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Public members should appear before protected/private. Signals are public.
Also protected/private slots is less protected/private than other protected/private members.

Q_DISABLE_COPY(FileSystemPathEdit)
class FileSystemPathEditPrivate;
Q_DECLARE_PRIVATE(FileSystemPathEdit)
Q_PRIVATE_SLOT(d_func(), void browseActionTriggered())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Qt has a naming convention for private slots (don't remember exactly how it looks).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replaced with lambda.

class FileSystemPathEditPrivate;
Q_DECLARE_PRIVATE(FileSystemPathEdit)
Q_PRIVATE_SLOT(d_func(), void browseActionTriggered())
QScopedPointer<FileSystemPathEditPrivate> const d_ptr;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you use QScopedPointers in these places? Why not usual Qt ownership?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The private class does not inherit QObject.

, m_completer {new QCompleter(this)}
, m_browseAction {nullptr}
{
#if !defined QBT_USES_QT5 && defined Q_OS_WIN
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

QBT_USES_QT5 is outdated.

{
public:
virtual ~FileEditorWithCompletion() = default;
virtual void completeDirectoriesOnly(bool completeDirsOnly) = 0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder why C++ still does not have abstract keyword for this purpose?

@zeule
Copy link
Contributor Author

zeule commented Apr 24, 2017

When I update this PR now, do you think I should move its files into gui/widgets or not? Should I submit another PR instead to rearrange all files (gui/models, gui/views, gui/widgets) and break basically all other PRs? @qbittorrent/frequent-contributors?

@glassez
Copy link
Member

glassez commented Apr 24, 2017

When I update this PR now, do you think I should move its files into gui/widgets or not?

Not here, exactly.

@Chocobo1
Copy link
Member

When I update this PR now, do you think I should move its files into gui/widgets or not? Should I submit another PR instead to rearrange all files (gui/models, gui/views, gui/widgets) and break basically all other PRs?

Or you can wait till most existing PRs are merged, we are merging fast recently...

@zeule
Copy link
Contributor Author

zeule commented Apr 24, 2017

OK, thank you for your input! Then for this one the files will be kept at the same place.

@zeule
Copy link
Contributor Author

zeule commented May 5, 2017

PR updated:

  • Fixed filter string for IP filters in options dialog (@Chocobo1)
  • Widget classes migrated to Qt5: removed #ifdefs and new signal/slot syntax is used. Private slot is replaced with lambda.
  • Various formatting issues were corrected.
  • Button icon is fetched from QStyle now.

@zeule zeule requested review from glassez and Chocobo1 May 5, 2017 16:03

virtual void clear() = 0;

signals:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Broken indentation. And next line too.

m_completer->setCompletionMode(QCompleter::PopupCompletion);
setCompleter(m_completer);

connect(m_completerModel, SIGNAL(directoryLoaded(QString)), this, SLOT(showCompletionPopup()));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, use Qt5 style here too.

m_browseAction->setIconText(browseButtonBriefText.tr());
m_browseAction->setText(browseButtonFullText.tr());
m_browseAction->setToolTip(browseButtonFullText.tr().remove(QLatin1Char('&')));
m_browseAction->setShortcut({Qt::CTRL + Qt::Key_B});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

curly braces required?


void FileSystemPathEdit::FileSystemPathEditPrivate::browseActionTriggered()
{
QString selectedPath;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would move this to just above switch expression.

pixmap = QStyle::SP_DirOpenIcon;
showDirsOnly = true;
break;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if a default case would make sense... but it doesn't hurt to add one?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, yes! default with throw was mistakenly forgotten here.

Q_D(FileSystemPathEdit);
d->m_fileNameFilter = val;

#if 0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#if 0 for comments? /**/ does not suffice?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, #if 0 is perfect for commenting out a block of code which itself contains comments inside.

#include <QFileSystemModel>
#include <QKeyEvent>
#include <QLineEdit>
#include <QMenu>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

forward declare as much as possible?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it is important here, because this is a private header, included from only single other place.


m_ui->textFilterPath->setDialogCaption(tr("Choose an IP filter file"));
m_ui->textFilterPath->setFileNameFilter(tr("All supported filters")
+ QString(" (*.dat *.p2p *.p2b);;.dat (*.dat);;.p2p (*.p2p);;.p2b (*.p2b)"));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

QString -> QLatin1String ?

@zeule
Copy link
Contributor Author

zeule commented May 6, 2017

Comments from reviewers are addressed.

glassez
glassez previously approved these changes May 8, 2017
Copy link
Member

@glassez glassez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have commented something, but it's optional, you don't have to fix anything anymore.

@@ -34,6 +34,7 @@
#include <QUrl>
#include <QMenu>
#include <QFileDialog>
#include <QPushButton>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this include needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it is needed. Otherwise:

../../src/gui/addnewtorrentdialog.cpp:148:45: error: invalid use of incomplete type ‘class QPushButton’
  ui->buttonBox->button(QDialogButtonBox::Ok)->setFocus();
                                             ^~

QString m_dialogCaption;
};

FileSystemPathEdit::FileSystemPathEditPrivate::FileSystemPathEditPrivate(FileSystemPathEdit *q,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO, it would be better if you insert line break before first param (instead of second one).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Taken.

@@ -357,6 +353,25 @@ OptionsDialog::OptionsDialog(QWidget *parent)
m_ui->advPageLayout->addWidget(advancedSettings);
connect(advancedSettings, SIGNAL(settingsChanged()), this, SLOT(enableApplyButton()));

m_ui->textFileLogPath->setDialogCaption(tr("Choose a save directory"));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't you set these properties in designer?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, I do not export the widget to Designer completely. Probably, with only a single custom widget in the app it does not make sense.

@zeule
Copy link
Contributor Author

zeule commented May 8, 2017

PR updated: taken two last suggestions from the reviewers. @sledgehammer999, do you want to say anything?

Copy link
Member

@glassez glassez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you.

@zeule
Copy link
Contributor Author

zeule commented May 8, 2017

@sledgehammer999, should I count a week? Are you interested in this?

@zeule
Copy link
Contributor Author

zeule commented May 15, 2017

OK, a week has passed, merging.

@zeule zeule merged commit 954f05b into qbittorrent:master May 15, 2017
@zeule zeule deleted the filename-edit-widget branch May 15, 2017 07:33
@Chocobo1
Copy link
Member

Might be related to this PR, I got the following when opening options dialog:

QObject::connect: No such signal FileSystemPathEdit::textChanged(QString)
QObject::connect:  (sender name:   'textFilterPath')
QObject::connect:  (receiver name: 'OptionsDialog')

@glassez
Copy link
Member

glassez commented May 17, 2017

Damn, we should prohibit using old connect syntax in new code to prevent this bugs in future.

@zeule
Copy link
Contributor Author

zeule commented May 17, 2017

I'm sorry for that. See #6795, please.

<width>779</width>
<height>591</height>
<width>1116</width>
<height>838</height>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is causing the default dialog size to be large than ever, is it intentional?
Do you mind we switch back?

@hrastnik
Copy link

Did you consider the cases where the users manually writes invalid file paths?

@zeule
Copy link
Contributor Author

zeule commented May 18, 2017

@Chocobo1: sure
@hrastnik: no, I did not.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants