From e72163362907359c323cfa3f4944b051fc7d58b9 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Sun, 17 Dec 2023 07:10:43 -0600 Subject: [PATCH 1/6] Add home button to quickly navigate to default radar site --- .../res/icons/font-awesome-6/house-solid.svg | 1 + scwx-qt/scwx-qt.qrc | 1 + scwx-qt/source/scwx/qt/main/main_window.cpp | 13 +++ scwx-qt/source/scwx/qt/main/main_window.hpp | 1 + scwx-qt/source/scwx/qt/main/main_window.ui | 93 +++++++++++-------- 5 files changed, 71 insertions(+), 38 deletions(-) create mode 100644 scwx-qt/res/icons/font-awesome-6/house-solid.svg diff --git a/scwx-qt/res/icons/font-awesome-6/house-solid.svg b/scwx-qt/res/icons/font-awesome-6/house-solid.svg new file mode 100644 index 00000000..ee4c062e --- /dev/null +++ b/scwx-qt/res/icons/font-awesome-6/house-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/scwx-qt/scwx-qt.qrc b/scwx-qt/scwx-qt.qrc index 53d7aec2..45e79044 100644 --- a/scwx-qt/scwx-qt.qrc +++ b/scwx-qt/scwx-qt.qrc @@ -34,6 +34,7 @@ res/icons/font-awesome-6/forward-step-solid.svg res/icons/font-awesome-6/gears-solid.svg res/icons/font-awesome-6/github.svg + res/icons/font-awesome-6/house-solid.svg res/icons/font-awesome-6/layer-group-solid.svg res/icons/font-awesome-6/palette-solid.svg res/icons/font-awesome-6/pause-solid.svg diff --git a/scwx-qt/source/scwx/qt/main/main_window.cpp b/scwx-qt/source/scwx/qt/main/main_window.cpp index d94612be..8e63d88c 100644 --- a/scwx-qt/source/scwx/qt/main/main_window.cpp +++ b/scwx-qt/source/scwx/qt/main/main_window.cpp @@ -521,6 +521,19 @@ void MainWindow::on_actionAboutSupercellWx_triggered() p->aboutDialog_->show(); } +void MainWindow::on_radarSiteHomeButton_clicked() +{ + std::string homeRadarSite = + settings::GeneralSettings::Instance().default_radar_site().GetValue(); + + for (map::MapWidget* map : p->maps_) + { + map->SelectRadarSite(homeRadarSite); + } + + p->UpdateRadarSite(); +} + void MainWindow::on_radarSiteSelectButton_clicked() { p->radarSiteDialog_->show(); diff --git a/scwx-qt/source/scwx/qt/main/main_window.hpp b/scwx-qt/source/scwx/qt/main/main_window.hpp index 82bda744..0ba8f41a 100644 --- a/scwx-qt/source/scwx/qt/main/main_window.hpp +++ b/scwx-qt/source/scwx/qt/main/main_window.hpp @@ -46,6 +46,7 @@ private slots: void on_actionGitHubRepository_triggered(); void on_actionCheckForUpdates_triggered(); void on_actionAboutSupercellWx_triggered(); + void on_radarSiteHomeButton_clicked(); void on_radarSiteSelectButton_clicked(); private: diff --git a/scwx-qt/source/scwx/qt/main/main_window.ui b/scwx-qt/source/scwx/qt/main/main_window.ui index 56c659f3..0ecb6523 100644 --- a/scwx-qt/source/scwx/qt/main/main_window.ui +++ b/scwx-qt/source/scwx/qt/main/main_window.ui @@ -39,7 +39,7 @@ 0 0 1024 - 22 + 21 @@ -141,8 +141,8 @@ 0 0 - 157 - 697 + 187 + 702 @@ -166,37 +166,7 @@ QFrame::Raised - - - - - - 16777215 - 13 - - - - ... - - - - - - - St. Louis, MO - - - - - - - Volume Coverage Pattern - - - VCP - - - + @@ -211,20 +181,67 @@ - - + + + + Volume Coverage Pattern + - Clear Air Mode + VCP - + + + + + 16777215 + 13 + + + + ... + + + + + + + + 16777215 + 13 + + + + ... + + + + :/res/icons/font-awesome-6/house-solid.svg:/res/icons/font-awesome-6/house-solid.svg + + + + + + + St. Louis, MO + + + + 35 + + + + Clear Air Mode + + + From 9d673af29178509726beff74bf6823905b10eff7 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Sun, 17 Dec 2023 22:04:28 -0600 Subject: [PATCH 2/6] Mark favorites in radar site dialog - No effect currently - Does not persist through application restart --- .../res/icons/font-awesome-6/star-solid.svg | 1 + scwx-qt/scwx-qt.qrc | 1 + .../source/scwx/qt/model/radar_site_model.cpp | 112 +++++++++++++----- .../source/scwx/qt/model/radar_site_model.hpp | 14 +++ .../source/scwx/qt/ui/radar_site_dialog.cpp | 15 ++- .../source/scwx/qt/ui/radar_site_dialog.ui | 5 +- 6 files changed, 114 insertions(+), 34 deletions(-) create mode 100644 scwx-qt/res/icons/font-awesome-6/star-solid.svg diff --git a/scwx-qt/res/icons/font-awesome-6/star-solid.svg b/scwx-qt/res/icons/font-awesome-6/star-solid.svg new file mode 100644 index 00000000..fd180e80 --- /dev/null +++ b/scwx-qt/res/icons/font-awesome-6/star-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/scwx-qt/scwx-qt.qrc b/scwx-qt/scwx-qt.qrc index 45e79044..b9155f08 100644 --- a/scwx-qt/scwx-qt.qrc +++ b/scwx-qt/scwx-qt.qrc @@ -45,6 +45,7 @@ res/icons/font-awesome-6/square-caret-right-regular.svg res/icons/font-awesome-6/square-minus-regular.svg res/icons/font-awesome-6/square-plus-regular.svg + res/icons/font-awesome-6/star-solid.svg res/icons/font-awesome-6/stop-solid.svg res/icons/font-awesome-6/volume-high-solid.svg res/palettes/wct/CC.pal diff --git a/scwx-qt/source/scwx/qt/model/radar_site_model.cpp b/scwx-qt/source/scwx/qt/model/radar_site_model.cpp index ffca3638..0aed2a31 100644 --- a/scwx-qt/source/scwx/qt/model/radar_site_model.cpp +++ b/scwx-qt/source/scwx/qt/model/radar_site_model.cpp @@ -5,6 +5,8 @@ #include #include +#include + namespace scwx { namespace qt @@ -15,15 +17,11 @@ namespace model static const std::string logPrefix_ = "scwx::qt::model::radar_site_model"; static const auto logger_ = scwx::util::Logger::Create(logPrefix_); -static constexpr size_t kColumnSiteId = 0u; -static constexpr size_t kColumnPlace = 1u; -static constexpr size_t kColumnState = 2u; -static constexpr size_t kColumnCountry = 3u; -static constexpr size_t kColumnLatitude = 4u; -static constexpr size_t kColumnLongitude = 5u; -static constexpr size_t kColumnType = 6u; -static constexpr size_t kColumnDistance = 7u; -static constexpr size_t kNumColumns = 8u; +static constexpr int kFirstColumn = + static_cast(RadarSiteModel::Column::SiteId); +static constexpr int kLastColumn = + static_cast(RadarSiteModel::Column::Favorite); +static constexpr int kNumColumns = kLastColumn - kFirstColumn + 1; class RadarSiteModelImpl { @@ -32,12 +30,15 @@ public: ~RadarSiteModelImpl() = default; QList> radarSites_; + std::vector favorites_; const GeographicLib::Geodesic& geodesic_; std::unordered_map distanceMap_; scwx::common::DistanceType distanceDisplay_; scwx::common::Coordinate previousPosition_; + + QIcon starIcon_ {":/res/icons/font-awesome-6/star-solid.svg"}; }; RadarSiteModel::RadarSiteModel(QObject* parent) : @@ -53,28 +54,32 @@ int RadarSiteModel::rowCount(const QModelIndex& parent) const int RadarSiteModel::columnCount(const QModelIndex& parent) const { - return parent.isValid() ? 0 : static_cast(kNumColumns); + return parent.isValid() ? 0 : kNumColumns; } QVariant RadarSiteModel::data(const QModelIndex& index, int role) const { - if (index.isValid() && index.row() >= 0 && - index.row() < p->radarSites_.size() && - (role == Qt::DisplayRole || role == types::SortRole)) + if (!index.isValid() || index.row() < 0 || + index.row() >= p->radarSites_.size()) { - const auto& site = p->radarSites_.at(index.row()); + return QVariant(); + } + const auto& site = p->radarSites_.at(index.row()); + + if (role == Qt::DisplayRole || role == types::SortRole) + { switch (index.column()) { - case kColumnSiteId: + case static_cast(Column::SiteId): return QString::fromStdString(site->id()); - case kColumnPlace: + case static_cast(Column::Place): return QString::fromStdString(site->place()); - case kColumnState: + case static_cast(Column::State): return QString::fromStdString(site->state()); - case kColumnCountry: + case static_cast(Column::Country): return QString::fromStdString(site->country()); - case kColumnLatitude: + case static_cast(Column::Latitude): if (role == Qt::DisplayRole) { return QString::fromStdString( @@ -84,7 +89,7 @@ QVariant RadarSiteModel::data(const QModelIndex& index, int role) const { return site->latitude(); } - case kColumnLongitude: + case static_cast(Column::Longitude): if (role == Qt::DisplayRole) { return QString::fromStdString( @@ -94,9 +99,9 @@ QVariant RadarSiteModel::data(const QModelIndex& index, int role) const { return site->longitude(); } - case kColumnType: + case static_cast(Column::Type): return QString::fromStdString(site->type_name()); - case kColumnDistance: + case static_cast(Column::Distance): if (role == Qt::DisplayRole) { if (p->distanceDisplay_ == scwx::common::DistanceType::Miles) @@ -116,6 +121,26 @@ QVariant RadarSiteModel::data(const QModelIndex& index, int role) const { return p->distanceMap_.at(site->id()); } + case static_cast(Column::Favorite): + if (role == types::SortRole) + { + return QVariant(p->favorites_.at(index.row())); + } + break; + default: + break; + } + } + else if (role == Qt::DecorationRole) + { + switch (index.column()) + { + case static_cast(Column::Favorite): + if (p->favorites_.at(index.row())) + { + return p->starIcon_; + } + break; default: break; } @@ -134,27 +159,40 @@ QVariant RadarSiteModel::headerData(int section, { switch (section) { - case kColumnSiteId: + case static_cast(Column::SiteId): return tr("Site ID"); - case kColumnPlace: + case static_cast(Column::Place): return tr("Place"); - case kColumnState: + case static_cast(Column::State): return tr("State"); - case kColumnCountry: + case static_cast(Column::Country): return tr("Country"); - case kColumnLatitude: + case static_cast(Column::Latitude): return tr("Latitude"); - case kColumnLongitude: + case static_cast(Column::Longitude): return tr("Longitude"); - case kColumnType: + case static_cast(Column::Type): return tr("Type"); - case kColumnDistance: + case static_cast(Column::Distance): return tr("Distance"); default: break; } } } + else if (role == Qt::DecorationRole) + { + if (orientation == Qt::Horizontal) + { + switch (section) + { + case static_cast(Column::Favorite): + return p->starIcon_; + default: + break; + } + } + } return QVariant(); } @@ -175,12 +213,21 @@ void RadarSiteModel::HandleMapUpdate(double latitude, double longitude) p->distanceMap_[site->id()] = distanceInMeters; } - QModelIndex topLeft = createIndex(0, kColumnDistance); - QModelIndex bottomRight = createIndex(rowCount() - 1, kColumnDistance); + QModelIndex topLeft = createIndex(0, static_cast(Column::Distance)); + QModelIndex bottomRight = + createIndex(rowCount() - 1, static_cast(Column::Distance)); Q_EMIT dataChanged(topLeft, bottomRight); } +void RadarSiteModel::ToggleFavorite(int row) +{ + if (row >= 0 && row < p->favorites_.size()) + { + p->favorites_.at(row) = !p->favorites_.at(row); + } +} + RadarSiteModelImpl::RadarSiteModelImpl() : radarSites_ {}, geodesic_(util::GeographicLib::DefaultGeodesic()), @@ -197,6 +244,7 @@ RadarSiteModelImpl::RadarSiteModelImpl() : { distanceMap_[site->id()] = 0.0; radarSites_.emplace_back(std::move(site)); + favorites_.emplace_back(false); } } diff --git a/scwx-qt/source/scwx/qt/model/radar_site_model.hpp b/scwx-qt/source/scwx/qt/model/radar_site_model.hpp index 520be1b7..7a50f016 100644 --- a/scwx-qt/source/scwx/qt/model/radar_site_model.hpp +++ b/scwx-qt/source/scwx/qt/model/radar_site_model.hpp @@ -16,6 +16,19 @@ class RadarSiteModelImpl; class RadarSiteModel : public QAbstractTableModel { public: + enum class Column : int + { + SiteId = 0, + Place = 1, + State = 2, + Country = 3, + Latitude = 4, + Longitude = 5, + Type = 6, + Distance = 7, + Favorite = 8 + }; + explicit RadarSiteModel(QObject* parent = nullptr); ~RadarSiteModel(); @@ -29,6 +42,7 @@ public: int role = Qt::DisplayRole) const override; void HandleMapUpdate(double latitude, double longitude); + void ToggleFavorite(int row); private: std::unique_ptr p; diff --git a/scwx-qt/source/scwx/qt/ui/radar_site_dialog.cpp b/scwx-qt/source/scwx/qt/ui/radar_site_dialog.cpp index c7275b06..b35743cc 100644 --- a/scwx-qt/source/scwx/qt/ui/radar_site_dialog.cpp +++ b/scwx-qt/source/scwx/qt/ui/radar_site_dialog.cpp @@ -70,9 +70,22 @@ RadarSiteDialog::RadarSiteDialog(QWidget* parent) : p->proxyModel_, &QSortFilterProxyModel::setFilterWildcard); connect(ui->radarSiteView, - &QTreeView::doubleClicked, + &QAbstractItemView::doubleClicked, this, [this]() { Q_EMIT accept(); }); + connect(ui->radarSiteView, + &QAbstractItemView::pressed, + this, + [this](const QModelIndex& index) + { + QModelIndex selectedIndex = p->proxyModel_->mapToSource(index); + + if (selectedIndex.column() == + static_cast(model::RadarSiteModel::Column::Favorite)) + { + p->radarSiteModel_->ToggleFavorite(selectedIndex.row()); + } + }); connect( ui->radarSiteView->selectionModel(), &QItemSelectionModel::selectionChanged, diff --git a/scwx-qt/source/scwx/qt/ui/radar_site_dialog.ui b/scwx-qt/source/scwx/qt/ui/radar_site_dialog.ui index c923054a..74eae33b 100644 --- a/scwx-qt/source/scwx/qt/ui/radar_site_dialog.ui +++ b/scwx-qt/source/scwx/qt/ui/radar_site_dialog.ui @@ -6,7 +6,7 @@ 0 0 - 576 + 627 550 @@ -16,6 +16,9 @@ + + QAbstractItemView::CurrentChanged|QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed|QAbstractItemView::SelectedClicked + true From 436a3e0a9f83ff196bf96945af672b2f809a8dfe Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Wed, 20 Dec 2023 09:10:48 -0600 Subject: [PATCH 3/6] Add favorite selection to radar toolbox --- scwx-qt/source/scwx/qt/main/main_window.cpp | 63 +++++++ scwx-qt/source/scwx/qt/main/main_window.ui | 154 ++++++++++++------ .../source/scwx/qt/model/radar_site_model.cpp | 27 ++- .../source/scwx/qt/model/radar_site_model.hpp | 7 + .../source/scwx/qt/ui/radar_site_dialog.cpp | 7 +- 5 files changed, 205 insertions(+), 53 deletions(-) diff --git a/scwx-qt/source/scwx/qt/main/main_window.cpp b/scwx-qt/source/scwx/qt/main/main_window.cpp index 8e63d88c..61cc1cd6 100644 --- a/scwx-qt/source/scwx/qt/main/main_window.cpp +++ b/scwx-qt/source/scwx/qt/main/main_window.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -187,6 +188,11 @@ public: std::shared_ptr timelineManager_; std::shared_ptr updateManager_; + std::shared_ptr radarSiteModel_ { + model::RadarSiteModel::Instance()}; + std::map> radarSiteFavoriteActions_ {}; + QMenu* radarSiteFavoriteMenu_ {nullptr}; + std::vector maps_; std::vector elevationCuts_; @@ -213,9 +219,14 @@ MainWindow::MainWindow(QWidget* parent) : // Assign the bottom left corner to the left dock widget setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea); + // Configure Radar Site Box ui->vcpLabel->setVisible(false); ui->vcpValueLabel->setVisible(false); ui->vcpDescriptionLabel->setVisible(false); + ui->radarSiteFavoriteButton->setVisible(false); + + p->radarSiteFavoriteMenu_ = new QMenu(this); + ui->radarSiteFavoriteButton->setMenu(p->radarSiteFavoriteMenu_); // Configure Alert Dock p->alertDockWidget_ = new ui::AlertDockWidget(this); @@ -944,6 +955,58 @@ void MainWindowImpl::ConnectOtherSignals() UpdateRadarSite(); }); + connect( + radarSiteModel_.get(), + &model::RadarSiteModel::FavoriteToggled, + [this](const std::string& siteId, bool isFavorite) + { + if (isFavorite && !radarSiteFavoriteActions_.contains(siteId)) + { + auto radarSite = config::RadarSite::Get(siteId); + std::string actionText = + fmt::format("{}: {}", siteId, radarSite->location_name()); + + auto pair = radarSiteFavoriteActions_.emplace( + siteId, + std::make_shared(QString::fromStdString(actionText))); + auto& action = pair.first->second; + + QAction* before = nullptr; + + // If the radar site is not at the end + if (pair.first != std::prev(radarSiteFavoriteActions_.cend())) + { + // Insert before the next entry in the list + before = std::next(pair.first)->second.get(); + } + + radarSiteFavoriteMenu_->insertAction(before, action.get()); + + connect(action.get(), + &QAction::triggered, + [this, siteId]() + { + for (map::MapWidget* map : maps_) + { + map->SelectRadarSite(siteId); + } + + UpdateRadarSite(); + }); + } + else if (!isFavorite) + { + auto entry = radarSiteFavoriteActions_.find(siteId); + if (entry != radarSiteFavoriteActions_.cend()) + { + radarSiteFavoriteMenu_->removeAction(entry->second.get()); + radarSiteFavoriteActions_.erase(entry); + } + } + + mainWindow_->ui->radarSiteFavoriteButton->setVisible( + !radarSiteFavoriteActions_.empty()); + }); connect(updateManager_.get(), &manager::UpdateManager::UpdateAvailable, this, diff --git a/scwx-qt/source/scwx/qt/main/main_window.ui b/scwx-qt/source/scwx/qt/main/main_window.ui index 0ecb6523..2cc0cbed 100644 --- a/scwx-qt/source/scwx/qt/main/main_window.ui +++ b/scwx-qt/source/scwx/qt/main/main_window.ui @@ -141,8 +141,8 @@ 0 0 - 187 - 702 + 193 + 688 @@ -166,28 +166,122 @@ QFrame::Raised - - + + + + + 0 + 0 + + KLSX - + + + + + 0 + 0 + + + + Volume Coverage Pattern + + + VCP + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 16777215 + 13 + + + + ... + + + + :/res/icons/font-awesome-6/house-solid.svg:/res/icons/font-awesome-6/house-solid.svg + + + + + + + + 16777215 + 13 + + + + ... + + + + :/res/icons/font-awesome-6/star-solid.svg:/res/icons/font-awesome-6/star-solid.svg + + + QToolButton::InstantPopup + + + + + + + Radar Site - - - - Volume Coverage Pattern - + + - VCP + St. Louis, MO + + + + + + + 35 + + + + + + + Clear Air Mode @@ -204,44 +298,6 @@ - - - - - 16777215 - 13 - - - - ... - - - - :/res/icons/font-awesome-6/house-solid.svg:/res/icons/font-awesome-6/house-solid.svg - - - - - - - St. Louis, MO - - - - - - - 35 - - - - - - - Clear Air Mode - - - diff --git a/scwx-qt/source/scwx/qt/model/radar_site_model.cpp b/scwx-qt/source/scwx/qt/model/radar_site_model.cpp index 0aed2a31..c5638f2d 100644 --- a/scwx-qt/source/scwx/qt/model/radar_site_model.cpp +++ b/scwx-qt/source/scwx/qt/model/radar_site_model.cpp @@ -224,7 +224,13 @@ void RadarSiteModel::ToggleFavorite(int row) { if (row >= 0 && row < p->favorites_.size()) { - p->favorites_.at(row) = !p->favorites_.at(row); + bool isFavorite = !p->favorites_.at(row); + p->favorites_.at(row) = isFavorite; + + QModelIndex index = createIndex(row, static_cast(Column::Favorite)); + Q_EMIT dataChanged(index, index); + + Q_EMIT FavoriteToggled(p->radarSites_.at(row)->id(), isFavorite); } } @@ -248,6 +254,25 @@ RadarSiteModelImpl::RadarSiteModelImpl() : } } +std::shared_ptr RadarSiteModel::Instance() +{ + static std::weak_ptr radarSiteModelReference_ {}; + static std::mutex instanceMutex_ {}; + + std::unique_lock lock(instanceMutex_); + + std::shared_ptr radarSiteModel = + radarSiteModelReference_.lock(); + + if (radarSiteModel == nullptr) + { + radarSiteModel = std::make_shared(); + radarSiteModelReference_ = radarSiteModel; + } + + return radarSiteModel; +} + } // namespace model } // namespace qt } // namespace scwx diff --git a/scwx-qt/source/scwx/qt/model/radar_site_model.hpp b/scwx-qt/source/scwx/qt/model/radar_site_model.hpp index 7a50f016..22e1deb5 100644 --- a/scwx-qt/source/scwx/qt/model/radar_site_model.hpp +++ b/scwx-qt/source/scwx/qt/model/radar_site_model.hpp @@ -15,6 +15,8 @@ class RadarSiteModelImpl; class RadarSiteModel : public QAbstractTableModel { + Q_OBJECT + public: enum class Column : int { @@ -44,6 +46,11 @@ public: void HandleMapUpdate(double latitude, double longitude); void ToggleFavorite(int row); + static std::shared_ptr Instance(); + +signals: + void FavoriteToggled(const std::string& siteId, bool isFavorite); + private: std::unique_ptr p; diff --git a/scwx-qt/source/scwx/qt/ui/radar_site_dialog.cpp b/scwx-qt/source/scwx/qt/ui/radar_site_dialog.cpp index b35743cc..fff1b0ce 100644 --- a/scwx-qt/source/scwx/qt/ui/radar_site_dialog.cpp +++ b/scwx-qt/source/scwx/qt/ui/radar_site_dialog.cpp @@ -24,13 +24,13 @@ class RadarSiteDialogImpl public: explicit RadarSiteDialogImpl(RadarSiteDialog* self) : self_ {self}, - radarSiteModel_ {new model::RadarSiteModel(self_)}, + radarSiteModel_ {model::RadarSiteModel::Instance()}, proxyModel_ {new QSortFilterProxyModel(self_)}, mapPosition_ {}, mapUpdateDeferred_ {false}, selectedRadarSite_ {"?"} { - proxyModel_->setSourceModel(radarSiteModel_); + proxyModel_->setSourceModel(radarSiteModel_.get()); proxyModel_->setSortRole(types::SortRole); proxyModel_->setFilterCaseSensitivity(Qt::CaseInsensitive); proxyModel_->setFilterKeyColumn(-1); @@ -38,9 +38,10 @@ public: ~RadarSiteDialogImpl() = default; RadarSiteDialog* self_; - model::RadarSiteModel* radarSiteModel_; QSortFilterProxyModel* proxyModel_; + std::shared_ptr radarSiteModel_; + scwx::common::Coordinate mapPosition_; bool mapUpdateDeferred_; From a76816b14c1b70bf36300dab2a81315e622d0389 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Wed, 20 Dec 2023 12:49:47 -0600 Subject: [PATCH 4/6] Save radar presets to file - Renamed from favorites --- scwx-qt/source/scwx/qt/main/main_window.cpp | 123 +++++++------ scwx-qt/source/scwx/qt/main/main_window.ui | 2 +- .../source/scwx/qt/model/radar_site_model.cpp | 167 ++++++++++++++---- .../source/scwx/qt/model/radar_site_model.hpp | 9 +- .../source/scwx/qt/ui/radar_site_dialog.cpp | 4 +- 5 files changed, 208 insertions(+), 97 deletions(-) diff --git a/scwx-qt/source/scwx/qt/main/main_window.cpp b/scwx-qt/source/scwx/qt/main/main_window.cpp index 61cc1cd6..03a43fff 100644 --- a/scwx-qt/source/scwx/qt/main/main_window.cpp +++ b/scwx-qt/source/scwx/qt/main/main_window.cpp @@ -130,6 +130,7 @@ public: } ~MainWindowImpl() { threadPool_.join(); } + void AddRadarSitePreset(const std::string& id); void AsyncSetup(); void ConfigureMapLayout(); void ConfigureMapStyles(); @@ -190,8 +191,8 @@ public: std::shared_ptr radarSiteModel_ { model::RadarSiteModel::Instance()}; - std::map> radarSiteFavoriteActions_ {}; - QMenu* radarSiteFavoriteMenu_ {nullptr}; + std::map> radarSitePresetsActions_ {}; + QMenu* radarSitePresetsMenu_ {nullptr}; std::vector maps_; std::vector elevationCuts_; @@ -223,10 +224,17 @@ MainWindow::MainWindow(QWidget* parent) : ui->vcpLabel->setVisible(false); ui->vcpValueLabel->setVisible(false); ui->vcpDescriptionLabel->setVisible(false); - ui->radarSiteFavoriteButton->setVisible(false); - p->radarSiteFavoriteMenu_ = new QMenu(this); - ui->radarSiteFavoriteButton->setMenu(p->radarSiteFavoriteMenu_); + p->radarSitePresetsMenu_ = new QMenu(this); + ui->radarSitePresetsButton->setMenu(p->radarSitePresetsMenu_); + + auto radarSitePresets = p->radarSiteModel_->presets(); + for (auto preset : radarSitePresets) + { + p->AddRadarSitePreset(preset); + } + + ui->radarSitePresetsButton->setVisible(!radarSitePresets.empty()); // Configure Alert Dock p->alertDockWidget_ = new ui::AlertDockWidget(this); @@ -955,58 +963,27 @@ void MainWindowImpl::ConnectOtherSignals() UpdateRadarSite(); }); - connect( - radarSiteModel_.get(), - &model::RadarSiteModel::FavoriteToggled, - [this](const std::string& siteId, bool isFavorite) - { - if (isFavorite && !radarSiteFavoriteActions_.contains(siteId)) - { - auto radarSite = config::RadarSite::Get(siteId); - std::string actionText = - fmt::format("{}: {}", siteId, radarSite->location_name()); + connect(radarSiteModel_.get(), + &model::RadarSiteModel::PresetToggled, + [this](const std::string& siteId, bool isPreset) + { + if (isPreset && !radarSitePresetsActions_.contains(siteId)) + { + AddRadarSitePreset(siteId); + } + else if (!isPreset) + { + auto entry = radarSitePresetsActions_.find(siteId); + if (entry != radarSitePresetsActions_.cend()) + { + radarSitePresetsMenu_->removeAction(entry->second.get()); + radarSitePresetsActions_.erase(entry); + } + } - auto pair = radarSiteFavoriteActions_.emplace( - siteId, - std::make_shared(QString::fromStdString(actionText))); - auto& action = pair.first->second; - - QAction* before = nullptr; - - // If the radar site is not at the end - if (pair.first != std::prev(radarSiteFavoriteActions_.cend())) - { - // Insert before the next entry in the list - before = std::next(pair.first)->second.get(); - } - - radarSiteFavoriteMenu_->insertAction(before, action.get()); - - connect(action.get(), - &QAction::triggered, - [this, siteId]() - { - for (map::MapWidget* map : maps_) - { - map->SelectRadarSite(siteId); - } - - UpdateRadarSite(); - }); - } - else if (!isFavorite) - { - auto entry = radarSiteFavoriteActions_.find(siteId); - if (entry != radarSiteFavoriteActions_.cend()) - { - radarSiteFavoriteMenu_->removeAction(entry->second.get()); - radarSiteFavoriteActions_.erase(entry); - } - } - - mainWindow_->ui->radarSiteFavoriteButton->setVisible( - !radarSiteFavoriteActions_.empty()); - }); + mainWindow_->ui->radarSitePresetsButton->setVisible( + !radarSitePresetsActions_.empty()); + }); connect(updateManager_.get(), &manager::UpdateManager::UpdateAvailable, this, @@ -1018,6 +995,40 @@ void MainWindowImpl::ConnectOtherSignals() }); } +void MainWindowImpl::AddRadarSitePreset(const std::string& siteId) +{ + auto radarSite = config::RadarSite::Get(siteId); + std::string actionText = + fmt::format("{}: {}", siteId, radarSite->location_name()); + + auto pair = radarSitePresetsActions_.emplace( + siteId, std::make_shared(QString::fromStdString(actionText))); + auto& action = pair.first->second; + + QAction* before = nullptr; + + // If the radar site is not at the end + if (pair.first != std::prev(radarSitePresetsActions_.cend())) + { + // Insert before the next entry in the list + before = std::next(pair.first)->second.get(); + } + + radarSitePresetsMenu_->insertAction(before, action.get()); + + connect(action.get(), + &QAction::triggered, + [this, siteId]() + { + for (map::MapWidget* map : maps_) + { + map->SelectRadarSite(siteId); + } + + UpdateRadarSite(); + }); +} + void MainWindowImpl::HandleFocusChange(QWidget* focused) { map::MapWidget* mapWidget = dynamic_cast(focused); diff --git a/scwx-qt/source/scwx/qt/main/main_window.ui b/scwx-qt/source/scwx/qt/main/main_window.ui index 2cc0cbed..4bd9b09c 100644 --- a/scwx-qt/source/scwx/qt/main/main_window.ui +++ b/scwx-qt/source/scwx/qt/main/main_window.ui @@ -235,7 +235,7 @@ - + 16777215 diff --git a/scwx-qt/source/scwx/qt/model/radar_site_model.cpp b/scwx-qt/source/scwx/qt/model/radar_site_model.cpp index c5638f2d..66683ae3 100644 --- a/scwx-qt/source/scwx/qt/model/radar_site_model.cpp +++ b/scwx-qt/source/scwx/qt/model/radar_site_model.cpp @@ -2,10 +2,16 @@ #include #include #include +#include #include #include +#include + +#include +#include #include +#include namespace scwx { @@ -20,17 +26,40 @@ static const auto logger_ = scwx::util::Logger::Create(logPrefix_); static constexpr int kFirstColumn = static_cast(RadarSiteModel::Column::SiteId); static constexpr int kLastColumn = - static_cast(RadarSiteModel::Column::Favorite); + static_cast(RadarSiteModel::Column::Preset); static constexpr int kNumColumns = kLastColumn - kFirstColumn + 1; class RadarSiteModelImpl { public: - explicit RadarSiteModelImpl(); + explicit RadarSiteModelImpl() : + radarSites_ {}, + geodesic_(util::GeographicLib::DefaultGeodesic()), + distanceMap_ {}, + distanceDisplay_ {scwx::common::DistanceType::Miles}, + previousPosition_ {} + { + // Get all loaded radar sites + std::vector> radarSites = + config::RadarSite::GetAll(); + + // Setup radar site list + for (auto& site : radarSites) + { + distanceMap_[site->id()] = 0.0; + radarSites_.emplace_back(std::move(site)); + } + } ~RadarSiteModelImpl() = default; + void InitializePresets(); + void ReadPresets(); + void WritePresets(); + QList> radarSites_; - std::vector favorites_; + std::unordered_set presets_ {}; + + std::string presetsPath_ {}; const GeographicLib::Geodesic& geodesic_; @@ -44,8 +73,88 @@ public: RadarSiteModel::RadarSiteModel(QObject* parent) : QAbstractTableModel(parent), p(std::make_unique()) { + p->InitializePresets(); + p->ReadPresets(); +} + +RadarSiteModel::~RadarSiteModel() +{ + // Write presets on shutdown + p->WritePresets(); +}; + +std::unordered_set RadarSiteModel::presets() const +{ + return p->presets_; +} + +void RadarSiteModelImpl::InitializePresets() +{ + std::string appDataPath { + QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + .toStdString()}; + + if (!std::filesystem::exists(appDataPath)) + { + if (!std::filesystem::create_directories(appDataPath)) + { + logger_->error("Unable to create application data directory: \"{}\"", + appDataPath); + } + } + + presetsPath_ = appDataPath + "/radar-presets.json"; +} + +void RadarSiteModelImpl::ReadPresets() +{ + logger_->info("Reading presets"); + + boost::json::value presetsJson = nullptr; + + // Determine if presets exists + if (std::filesystem::exists(presetsPath_)) + { + presetsJson = util::json::ReadJsonFile(presetsPath_); + } + + // If presets was successfully read + if (presetsJson != nullptr && presetsJson.is_array()) + { + auto& presetsArray = presetsJson.as_array(); + for (auto& presetsEntry : presetsArray) + { + if (presetsEntry.is_string()) + { + // Get radar site ID from JSON value + std::string preset = + boost::json::value_to(presetsEntry); + boost::to_upper(preset); + + // Find the preset in the list of radar sites + auto it = std::find_if( + radarSites_.cbegin(), + radarSites_.cend(), + [&preset](const std::shared_ptr& radarSite) + { return (radarSite->id() == preset); }); + + // If a match, add to the presets + if (it != radarSites_.cend()) + { + presets_.insert(preset); + } + } + } + } +} + +void RadarSiteModelImpl::WritePresets() +{ + logger_->info("Saving presets"); + + auto presetsJson = boost::json::value_from(presets_); + util::json::WriteJsonFile(presetsPath_, presetsJson); } -RadarSiteModel::~RadarSiteModel() = default; int RadarSiteModel::rowCount(const QModelIndex& parent) const { @@ -121,10 +230,10 @@ QVariant RadarSiteModel::data(const QModelIndex& index, int role) const { return p->distanceMap_.at(site->id()); } - case static_cast(Column::Favorite): + case static_cast(Column::Preset): if (role == types::SortRole) { - return QVariant(p->favorites_.at(index.row())); + return QVariant(p->presets_.contains(site->id())); } break; default: @@ -135,8 +244,8 @@ QVariant RadarSiteModel::data(const QModelIndex& index, int role) const { switch (index.column()) { - case static_cast(Column::Favorite): - if (p->favorites_.at(index.row())) + case static_cast(Column::Preset): + if (p->presets_.contains(site->id())) { return p->starIcon_; } @@ -186,7 +295,7 @@ QVariant RadarSiteModel::headerData(int section, { switch (section) { - case static_cast(Column::Favorite): + case static_cast(Column::Preset): return p->starIcon_; default: break; @@ -220,37 +329,25 @@ void RadarSiteModel::HandleMapUpdate(double latitude, double longitude) Q_EMIT dataChanged(topLeft, bottomRight); } -void RadarSiteModel::ToggleFavorite(int row) +void RadarSiteModel::TogglePreset(int row) { - if (row >= 0 && row < p->favorites_.size()) + if (row >= 0 && row < p->radarSites_.size()) { - bool isFavorite = !p->favorites_.at(row); - p->favorites_.at(row) = isFavorite; + std::string siteId = p->radarSites_.at(row)->id(); + bool isPreset = false; - QModelIndex index = createIndex(row, static_cast(Column::Favorite)); + // Attempt to erase the radar site from presets + if (p->presets_.erase(siteId) == 0) + { + // If the radar site did not exist, add it + p->presets_.insert(siteId); + isPreset = true; + } + + QModelIndex index = createIndex(row, static_cast(Column::Preset)); Q_EMIT dataChanged(index, index); - Q_EMIT FavoriteToggled(p->radarSites_.at(row)->id(), isFavorite); - } -} - -RadarSiteModelImpl::RadarSiteModelImpl() : - radarSites_ {}, - geodesic_(util::GeographicLib::DefaultGeodesic()), - distanceMap_ {}, - distanceDisplay_ {scwx::common::DistanceType::Miles}, - previousPosition_ {} -{ - // Get all loaded radar sites - std::vector> radarSites = - config::RadarSite::GetAll(); - - // Setup radar site list - for (auto& site : radarSites) - { - distanceMap_[site->id()] = 0.0; - radarSites_.emplace_back(std::move(site)); - favorites_.emplace_back(false); + Q_EMIT PresetToggled(siteId, isPreset); } } diff --git a/scwx-qt/source/scwx/qt/model/radar_site_model.hpp b/scwx-qt/source/scwx/qt/model/radar_site_model.hpp index 22e1deb5..73e3d425 100644 --- a/scwx-qt/source/scwx/qt/model/radar_site_model.hpp +++ b/scwx-qt/source/scwx/qt/model/radar_site_model.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include @@ -28,12 +29,14 @@ public: Longitude = 5, Type = 6, Distance = 7, - Favorite = 8 + Preset = 8 }; explicit RadarSiteModel(QObject* parent = nullptr); ~RadarSiteModel(); + std::unordered_set presets() const; + int rowCount(const QModelIndex& parent = QModelIndex()) const override; int columnCount(const QModelIndex& parent = QModelIndex()) const override; @@ -44,12 +47,12 @@ public: int role = Qt::DisplayRole) const override; void HandleMapUpdate(double latitude, double longitude); - void ToggleFavorite(int row); + void TogglePreset(int row); static std::shared_ptr Instance(); signals: - void FavoriteToggled(const std::string& siteId, bool isFavorite); + void PresetToggled(const std::string& siteId, bool isPreset); private: std::unique_ptr p; diff --git a/scwx-qt/source/scwx/qt/ui/radar_site_dialog.cpp b/scwx-qt/source/scwx/qt/ui/radar_site_dialog.cpp index fff1b0ce..d80a9990 100644 --- a/scwx-qt/source/scwx/qt/ui/radar_site_dialog.cpp +++ b/scwx-qt/source/scwx/qt/ui/radar_site_dialog.cpp @@ -82,9 +82,9 @@ RadarSiteDialog::RadarSiteDialog(QWidget* parent) : QModelIndex selectedIndex = p->proxyModel_->mapToSource(index); if (selectedIndex.column() == - static_cast(model::RadarSiteModel::Column::Favorite)) + static_cast(model::RadarSiteModel::Column::Preset)) { - p->radarSiteModel_->ToggleFavorite(selectedIndex.row()); + p->radarSiteModel_->TogglePreset(selectedIndex.row()); } }); connect( From 3b15b11badf5624c28c0c7dfbcadee8458984f18 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Wed, 20 Dec 2023 12:50:11 -0600 Subject: [PATCH 5/6] Remove unused elevation variables from main window --- scwx-qt/source/scwx/qt/main/main_window.cpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/scwx-qt/source/scwx/qt/main/main_window.cpp b/scwx-qt/source/scwx/qt/main/main_window.cpp index 03a43fff..d546b1fb 100644 --- a/scwx-qt/source/scwx/qt/main/main_window.cpp +++ b/scwx-qt/source/scwx/qt/main/main_window.cpp @@ -90,10 +90,7 @@ public: textEventManager_ {manager::TextEventManager::Instance()}, timelineManager_ {manager::TimelineManager::Instance()}, updateManager_ {manager::UpdateManager::Instance()}, - maps_ {}, - elevationCuts_ {}, - elevationButtonsChanged_ {false}, - resizeElevationButtons_ {false} + maps_ {} { mapProvider_ = map::GetMapProvider( settings::GeneralSettings::Instance().map_provider().GetValue()); @@ -195,13 +192,9 @@ public: QMenu* radarSitePresetsMenu_ {nullptr}; std::vector maps_; - std::vector elevationCuts_; std::chrono::system_clock::time_point volumeTime_ {}; - bool elevationButtonsChanged_; - bool resizeElevationButtons_; - public slots: void UpdateMapParameters(double latitude, double longitude, From c04da0208c130e377ec6258401b33494071c22aa Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Wed, 20 Dec 2023 15:41:32 -0600 Subject: [PATCH 6/6] Fix radar site model initialization order --- scwx-qt/source/scwx/qt/ui/radar_site_dialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scwx-qt/source/scwx/qt/ui/radar_site_dialog.cpp b/scwx-qt/source/scwx/qt/ui/radar_site_dialog.cpp index d80a9990..66b7c3cd 100644 --- a/scwx-qt/source/scwx/qt/ui/radar_site_dialog.cpp +++ b/scwx-qt/source/scwx/qt/ui/radar_site_dialog.cpp @@ -24,8 +24,8 @@ class RadarSiteDialogImpl public: explicit RadarSiteDialogImpl(RadarSiteDialog* self) : self_ {self}, - radarSiteModel_ {model::RadarSiteModel::Instance()}, proxyModel_ {new QSortFilterProxyModel(self_)}, + radarSiteModel_ {model::RadarSiteModel::Instance()}, mapPosition_ {}, mapUpdateDeferred_ {false}, selectedRadarSite_ {"?"}