From e80ec7b7a440afac086b57543a5c9cb0a75e9683 Mon Sep 17 00:00:00 2001 From: Nick Date: Mon, 25 Dec 2023 23:09:20 +0100 Subject: [PATCH] feat: add labels to DetailsDialog of Qt client --- qt/DetailsDialog.cc | 81 +++++++++++++++++++++++++++++++++++++++++++-- qt/DetailsDialog.h | 5 +++ qt/DetailsDialog.ui | 19 ++++++++++- qt/Session.cc | 6 ++-- qt/Torrent.cc | 1 + qt/Torrent.h | 8 +++++ 6 files changed, 115 insertions(+), 5 deletions(-) diff --git a/qt/DetailsDialog.cc b/qt/DetailsDialog.cc index 522679101..f8a7de331 100644 --- a/qt/DetailsDialog.cc +++ b/qt/DetailsDialog.cc @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -282,6 +283,9 @@ DetailsDialog::DetailsDialog(Session& session, Prefs& prefs, TorrentModel const& // set up the debounce timer connect(&ui_debounce_timer_, &QTimer::timeout, this, &DetailsDialog::refreshUI); ui_debounce_timer_.setSingleShot(true); + + // set labels + connect(ui_.dialogButtons, &QDialogButtonBox::clicked, this, &DetailsDialog::onButtonBoxClicked); } DetailsDialog::~DetailsDialog() @@ -300,6 +304,8 @@ void DetailsDialog::setIds(torrent_ids_t const& ids) session_.refreshDetailInfo(ids_); tracker_model_->refresh(model_, ids_); + labels_need_refresh_ = true; + refreshModel(); refreshUI(); } @@ -386,6 +392,40 @@ void DetailsDialog::onSessionCalled(Session::Tag tag) } } +void DetailsDialog::onButtonBoxClicked(QAbstractButton* button) +{ + if (ui_.dialogButtons->standardButton(button) == QDialogButtonBox::Close) + { + if (ui_.labelsTextEdit->isReadOnly()) // no edits could have been made + { + return; + } + + QString const labels_text = ui_.labelsTextEdit->toPlainText().trimmed(); + + if (labels_text == labels_baseline_) // no edits have been made + { + return; + } + + QString const re = QStringLiteral("((,|;)\\s*)"); + +//see https://doc.qt.io/qt-5/qt.html#SplitBehaviorFlags-enum +#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) + QStringList const labels_list = labels_text.split(QRegularExpression(re), QString::SkipEmptyParts); +#else + QStringList const labels_list = labels_text.split(QRegularExpression(re), Qt::SkipEmptyParts); +#endif + + torrentSet(TR_KEY_labels, labels_list); + + if (!ids_.empty()) + { + session_.refreshDetailInfo(ids_); + } + } +} + namespace { @@ -849,6 +889,39 @@ void DetailsDialog::refreshUI() ui_.privacyValueLabel->setText(string); + // myLabelsTextEdit + if (labels_need_refresh_) + { + labels_need_refresh_ = false; + + if (torrents.empty()) + { + labels_baseline_.clear(); + ui_.labelsTextEdit->setText({}); + ui_.labelsTextEdit->setPlaceholderText(none); + ui_.labelsTextEdit->setReadOnly(true); + ui_.labelsTextEdit->setEnabled(true); + } + else if (auto const& baseline = torrents[0]->labels(); std::all_of( + std::begin(torrents), + std::end(torrents), + [&baseline](auto const* tor) { return tor->labels() == baseline; })) + { + labels_baseline_ = baseline.join(QStringLiteral(", ")); + ui_.labelsTextEdit->setText(labels_baseline_); + ui_.labelsTextEdit->setPlaceholderText(none); + ui_.labelsTextEdit->setReadOnly(false); + ui_.labelsTextEdit->setEnabled(true); + } + else // mixed + { + labels_baseline_.clear(); + ui_.labelsTextEdit->setText({}); + ui_.labelsTextEdit->setPlaceholderText(mixed); + ui_.labelsTextEdit->setEnabled(false); + } + } + // myCommentBrowser string = none; bool is_comment_mixed = false; @@ -1250,8 +1323,12 @@ void DetailsDialog::setEnabled(bool enabled) void DetailsDialog::initInfoTab() { - int const h = QFontMetrics{ ui_.commentBrowser->font() }.lineSpacing() * 4; - ui_.commentBrowser->setFixedHeight(h); + int const cbh = QFontMetrics{ ui_.commentBrowser->font() }.lineSpacing() * 4; + ui_.commentBrowser->setFixedHeight(cbh); + + int const lteh = QFontMetrics{ ui_.labelsTextEdit->font() }.lineSpacing() * 2; + ui_.labelsTextEdit->setFixedHeight(lteh); + ui_.labelsTextEdit->setText(QStringLiteral("Initializing...")); auto* cr = new ColumnResizer{ this }; cr->addLayout(ui_.activitySectionLayout); diff --git a/qt/DetailsDialog.h b/qt/DetailsDialog.h index 462bdad63..1fc2fe024 100644 --- a/qt/DetailsDialog.h +++ b/qt/DetailsDialog.h @@ -66,6 +66,9 @@ private slots: void onTorrentsChanged(torrent_ids_t const& ids, Torrent::fields_t const& fields); void onSessionCalled(Session::Tag tag); + // Details tab + void onButtonBoxClicked(QAbstractButton* button); + // Tracker tab void onTrackerSelectionChanged(); void onAddTrackerClicked(); @@ -131,6 +134,8 @@ private: torrent_ids_t ids_; QTimer model_timer_; QTimer ui_debounce_timer_; + bool labels_need_refresh_ = true; + QString labels_baseline_; std::shared_ptr tracker_model_; std::shared_ptr tracker_filter_; diff --git a/qt/DetailsDialog.ui b/qt/DetailsDialog.ui index 37bd5f1ed..29cf65916 100644 --- a/qt/DetailsDialog.ui +++ b/qt/DetailsDialog.ui @@ -457,6 +457,23 @@ + + + Labels: + + + labelsTextEdit + + + + + + + false + + + + Comment: @@ -466,7 +483,7 @@ - + Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse diff --git a/qt/Session.cc b/qt/Session.cc index 9386c2a60..292d388e0 100644 --- a/qt/Session.cc +++ b/qt/Session.cc @@ -524,11 +524,12 @@ std::vector const& Session::getKeyNames(TorrentProperties prop if (names.empty()) { // unchanging fields needed by the main window - static auto constexpr MainInfoKeys = std::array{ + static auto constexpr MainInfoKeys = std::array{ TR_KEY_addedDate, // TR_KEY_downloadDir, // TR_KEY_file_count, // TR_KEY_hashString, // + TR_KEY_labels, // TR_KEY_name, // TR_KEY_primary_mime_type, // TR_KEY_totalSize, // @@ -565,12 +566,13 @@ std::vector const& Session::getKeyNames(TorrentProperties prop }; // unchanging fields needed by the details dialog - static auto constexpr DetailInfoKeys = std::array{ + static auto constexpr DetailInfoKeys = std::array{ TR_KEY_comment, // TR_KEY_creator, // TR_KEY_dateCreated, // TR_KEY_files, // TR_KEY_isPrivate, // + TR_KEY_labels, // TR_KEY_pieceCount, // TR_KEY_pieceSize, // TR_KEY_trackerList, // diff --git a/qt/Torrent.cc b/qt/Torrent.cc index fea99680c..2ee887542 100644 --- a/qt/Torrent.cc +++ b/qt/Torrent.cc @@ -202,6 +202,7 @@ Torrent::fields_t Torrent::update(tr_quark const* keys, tr_variant const* const* HANDLE_KEY(isFinished, is_finished, IS_FINISHED) HANDLE_KEY(isPrivate, is_private, IS_PRIVATE) HANDLE_KEY(isStalled, is_stalled, IS_STALLED) + HANDLE_KEY(labels, labels, LABELS) HANDLE_KEY(leftUntilDone, left_until_done, LEFT_UNTIL_DONE) HANDLE_KEY(manualAnnounceTime, manual_announce_time, MANUAL_ANNOUNCE_TIME) HANDLE_KEY(metadataPercentComplete, metadata_percent_complete, METADATA_PERCENT_COMPLETE) diff --git a/qt/Torrent.h b/qt/Torrent.h index 324ac2a5a..05692e81f 100644 --- a/qt/Torrent.h +++ b/qt/Torrent.h @@ -16,6 +16,7 @@ #include #include #include +#include #include @@ -415,6 +416,11 @@ public: bool includesTracker(QString const& sitename) const; + [[nodiscard]] constexpr auto const& labels() const noexcept + { + return labels_; + } + [[nodiscard]] constexpr auto const& sitenames() const noexcept { return sitenames_; @@ -588,6 +594,7 @@ public: IS_FINISHED, IS_PRIVATE, IS_STALLED, + LABELS, LEFT_UNTIL_DONE, MANUAL_ANNOUNCE_TIME, METADATA_PERCENT_COMPLETE, @@ -690,6 +697,7 @@ private: PeerList peers_; FileList files_; + QStringList labels_; std::vector sitenames_; TrackerStatsList tracker_stats_;