mirror of
				https://github.com/ciphervance/supercell-wx.git
				synced 2025-10-31 17:20:06 +00:00 
			
		
		
		
	Merge pull request #277 from dpaulat/feature/enhanced-alerts-3
Add Customizable Alert Lines
This commit is contained in:
		
						commit
						50cf49568d
					
				
					 35 changed files with 2813 additions and 427 deletions
				
			
		|  | @ -169,9 +169,11 @@ set(HDR_REQUEST source/scwx/qt/request/download_request.hpp | ||||||
|                 source/scwx/qt/request/nexrad_file_request.hpp) |                 source/scwx/qt/request/nexrad_file_request.hpp) | ||||||
| set(SRC_REQUEST source/scwx/qt/request/download_request.cpp | set(SRC_REQUEST source/scwx/qt/request/download_request.cpp | ||||||
|                 source/scwx/qt/request/nexrad_file_request.cpp) |                 source/scwx/qt/request/nexrad_file_request.cpp) | ||||||
| set(HDR_SETTINGS source/scwx/qt/settings/audio_settings.hpp | set(HDR_SETTINGS source/scwx/qt/settings/alert_palette_settings.hpp | ||||||
|  |                  source/scwx/qt/settings/audio_settings.hpp | ||||||
|                  source/scwx/qt/settings/general_settings.hpp |                  source/scwx/qt/settings/general_settings.hpp | ||||||
|                  source/scwx/qt/settings/hotkey_settings.hpp |                  source/scwx/qt/settings/hotkey_settings.hpp | ||||||
|  |                  source/scwx/qt/settings/line_settings.hpp | ||||||
|                  source/scwx/qt/settings/map_settings.hpp |                  source/scwx/qt/settings/map_settings.hpp | ||||||
|                  source/scwx/qt/settings/palette_settings.hpp |                  source/scwx/qt/settings/palette_settings.hpp | ||||||
|                  source/scwx/qt/settings/product_settings.hpp |                  source/scwx/qt/settings/product_settings.hpp | ||||||
|  | @ -185,9 +187,11 @@ set(HDR_SETTINGS source/scwx/qt/settings/audio_settings.hpp | ||||||
|                  source/scwx/qt/settings/text_settings.hpp |                  source/scwx/qt/settings/text_settings.hpp | ||||||
|                  source/scwx/qt/settings/ui_settings.hpp |                  source/scwx/qt/settings/ui_settings.hpp | ||||||
|                  source/scwx/qt/settings/unit_settings.hpp) |                  source/scwx/qt/settings/unit_settings.hpp) | ||||||
| set(SRC_SETTINGS source/scwx/qt/settings/audio_settings.cpp | set(SRC_SETTINGS source/scwx/qt/settings/alert_palette_settings.cpp | ||||||
|  |                  source/scwx/qt/settings/audio_settings.cpp | ||||||
|                  source/scwx/qt/settings/general_settings.cpp |                  source/scwx/qt/settings/general_settings.cpp | ||||||
|                  source/scwx/qt/settings/hotkey_settings.cpp |                  source/scwx/qt/settings/hotkey_settings.cpp | ||||||
|  |                  source/scwx/qt/settings/line_settings.cpp | ||||||
|                  source/scwx/qt/settings/map_settings.cpp |                  source/scwx/qt/settings/map_settings.cpp | ||||||
|                  source/scwx/qt/settings/palette_settings.cpp |                  source/scwx/qt/settings/palette_settings.cpp | ||||||
|                  source/scwx/qt/settings/product_settings.cpp |                  source/scwx/qt/settings/product_settings.cpp | ||||||
|  | @ -240,8 +244,8 @@ set(HDR_UI source/scwx/qt/ui/about_dialog.hpp | ||||||
|            source/scwx/qt/ui/animation_dock_widget.hpp |            source/scwx/qt/ui/animation_dock_widget.hpp | ||||||
|            source/scwx/qt/ui/collapsible_group.hpp |            source/scwx/qt/ui/collapsible_group.hpp | ||||||
|            source/scwx/qt/ui/county_dialog.hpp |            source/scwx/qt/ui/county_dialog.hpp | ||||||
|            source/scwx/qt/ui/wfo_dialog.hpp |  | ||||||
|            source/scwx/qt/ui/download_dialog.hpp |            source/scwx/qt/ui/download_dialog.hpp | ||||||
|  |            source/scwx/qt/ui/edit_line_dialog.hpp | ||||||
|            source/scwx/qt/ui/flow_layout.hpp |            source/scwx/qt/ui/flow_layout.hpp | ||||||
|            source/scwx/qt/ui/gps_info_dialog.hpp |            source/scwx/qt/ui/gps_info_dialog.hpp | ||||||
|            source/scwx/qt/ui/hotkey_edit.hpp |            source/scwx/qt/ui/hotkey_edit.hpp | ||||||
|  | @ -252,6 +256,7 @@ set(HDR_UI source/scwx/qt/ui/about_dialog.hpp | ||||||
|            source/scwx/qt/ui/level2_products_widget.hpp |            source/scwx/qt/ui/level2_products_widget.hpp | ||||||
|            source/scwx/qt/ui/level2_settings_widget.hpp |            source/scwx/qt/ui/level2_settings_widget.hpp | ||||||
|            source/scwx/qt/ui/level3_products_widget.hpp |            source/scwx/qt/ui/level3_products_widget.hpp | ||||||
|  |            source/scwx/qt/ui/line_label.hpp | ||||||
|            source/scwx/qt/ui/open_url_dialog.hpp |            source/scwx/qt/ui/open_url_dialog.hpp | ||||||
|            source/scwx/qt/ui/placefile_dialog.hpp |            source/scwx/qt/ui/placefile_dialog.hpp | ||||||
|            source/scwx/qt/ui/placefile_settings_widget.hpp |            source/scwx/qt/ui/placefile_settings_widget.hpp | ||||||
|  | @ -259,15 +264,16 @@ set(HDR_UI source/scwx/qt/ui/about_dialog.hpp | ||||||
|            source/scwx/qt/ui/radar_site_dialog.hpp |            source/scwx/qt/ui/radar_site_dialog.hpp | ||||||
|            source/scwx/qt/ui/serial_port_dialog.hpp |            source/scwx/qt/ui/serial_port_dialog.hpp | ||||||
|            source/scwx/qt/ui/settings_dialog.hpp |            source/scwx/qt/ui/settings_dialog.hpp | ||||||
|            source/scwx/qt/ui/update_dialog.hpp) |            source/scwx/qt/ui/update_dialog.hpp | ||||||
|  |            source/scwx/qt/ui/wfo_dialog.hpp) | ||||||
| set(SRC_UI source/scwx/qt/ui/about_dialog.cpp | set(SRC_UI source/scwx/qt/ui/about_dialog.cpp | ||||||
|            source/scwx/qt/ui/alert_dialog.cpp |            source/scwx/qt/ui/alert_dialog.cpp | ||||||
|            source/scwx/qt/ui/alert_dock_widget.cpp |            source/scwx/qt/ui/alert_dock_widget.cpp | ||||||
|            source/scwx/qt/ui/animation_dock_widget.cpp |            source/scwx/qt/ui/animation_dock_widget.cpp | ||||||
|            source/scwx/qt/ui/collapsible_group.cpp |            source/scwx/qt/ui/collapsible_group.cpp | ||||||
|            source/scwx/qt/ui/county_dialog.cpp |            source/scwx/qt/ui/county_dialog.cpp | ||||||
|            source/scwx/qt/ui/wfo_dialog.cpp |  | ||||||
|            source/scwx/qt/ui/download_dialog.cpp |            source/scwx/qt/ui/download_dialog.cpp | ||||||
|  |            source/scwx/qt/ui/edit_line_dialog.cpp | ||||||
|            source/scwx/qt/ui/flow_layout.cpp |            source/scwx/qt/ui/flow_layout.cpp | ||||||
|            source/scwx/qt/ui/gps_info_dialog.cpp |            source/scwx/qt/ui/gps_info_dialog.cpp | ||||||
|            source/scwx/qt/ui/hotkey_edit.cpp |            source/scwx/qt/ui/hotkey_edit.cpp | ||||||
|  | @ -278,6 +284,7 @@ set(SRC_UI source/scwx/qt/ui/about_dialog.cpp | ||||||
|            source/scwx/qt/ui/level2_products_widget.cpp |            source/scwx/qt/ui/level2_products_widget.cpp | ||||||
|            source/scwx/qt/ui/level2_settings_widget.cpp |            source/scwx/qt/ui/level2_settings_widget.cpp | ||||||
|            source/scwx/qt/ui/level3_products_widget.cpp |            source/scwx/qt/ui/level3_products_widget.cpp | ||||||
|  |            source/scwx/qt/ui/line_label.cpp | ||||||
|            source/scwx/qt/ui/open_url_dialog.cpp |            source/scwx/qt/ui/open_url_dialog.cpp | ||||||
|            source/scwx/qt/ui/placefile_dialog.cpp |            source/scwx/qt/ui/placefile_dialog.cpp | ||||||
|            source/scwx/qt/ui/placefile_settings_widget.cpp |            source/scwx/qt/ui/placefile_settings_widget.cpp | ||||||
|  | @ -285,14 +292,15 @@ set(SRC_UI source/scwx/qt/ui/about_dialog.cpp | ||||||
|            source/scwx/qt/ui/radar_site_dialog.cpp |            source/scwx/qt/ui/radar_site_dialog.cpp | ||||||
|            source/scwx/qt/ui/settings_dialog.cpp |            source/scwx/qt/ui/settings_dialog.cpp | ||||||
|            source/scwx/qt/ui/serial_port_dialog.cpp |            source/scwx/qt/ui/serial_port_dialog.cpp | ||||||
|            source/scwx/qt/ui/update_dialog.cpp) |            source/scwx/qt/ui/update_dialog.cpp | ||||||
|  |            source/scwx/qt/ui/wfo_dialog.cpp) | ||||||
| set(UI_UI  source/scwx/qt/ui/about_dialog.ui | set(UI_UI  source/scwx/qt/ui/about_dialog.ui | ||||||
|            source/scwx/qt/ui/alert_dialog.ui |            source/scwx/qt/ui/alert_dialog.ui | ||||||
|            source/scwx/qt/ui/alert_dock_widget.ui |            source/scwx/qt/ui/alert_dock_widget.ui | ||||||
|            source/scwx/qt/ui/animation_dock_widget.ui |            source/scwx/qt/ui/animation_dock_widget.ui | ||||||
|            source/scwx/qt/ui/collapsible_group.ui |            source/scwx/qt/ui/collapsible_group.ui | ||||||
|            source/scwx/qt/ui/county_dialog.ui |            source/scwx/qt/ui/county_dialog.ui | ||||||
|            source/scwx/qt/ui/wfo_dialog.ui |            source/scwx/qt/ui/edit_line_dialog.ui | ||||||
|            source/scwx/qt/ui/gps_info_dialog.ui |            source/scwx/qt/ui/gps_info_dialog.ui | ||||||
|            source/scwx/qt/ui/imgui_debug_dialog.ui |            source/scwx/qt/ui/imgui_debug_dialog.ui | ||||||
|            source/scwx/qt/ui/layer_dialog.ui |            source/scwx/qt/ui/layer_dialog.ui | ||||||
|  | @ -303,11 +311,14 @@ set(UI_UI  source/scwx/qt/ui/about_dialog.ui | ||||||
|            source/scwx/qt/ui/radar_site_dialog.ui |            source/scwx/qt/ui/radar_site_dialog.ui | ||||||
|            source/scwx/qt/ui/settings_dialog.ui |            source/scwx/qt/ui/settings_dialog.ui | ||||||
|            source/scwx/qt/ui/serial_port_dialog.ui |            source/scwx/qt/ui/serial_port_dialog.ui | ||||||
|            source/scwx/qt/ui/update_dialog.ui) |            source/scwx/qt/ui/update_dialog.ui | ||||||
| set(HDR_UI_SETTINGS source/scwx/qt/ui/settings/hotkey_settings_widget.hpp |            source/scwx/qt/ui/wfo_dialog.ui) | ||||||
|  | set(HDR_UI_SETTINGS source/scwx/qt/ui/settings/alert_palette_settings_widget.hpp | ||||||
|  |                     source/scwx/qt/ui/settings/hotkey_settings_widget.hpp | ||||||
|                     source/scwx/qt/ui/settings/settings_page_widget.hpp |                     source/scwx/qt/ui/settings/settings_page_widget.hpp | ||||||
|                     source/scwx/qt/ui/settings/unit_settings_widget.hpp) |                     source/scwx/qt/ui/settings/unit_settings_widget.hpp) | ||||||
| set(SRC_UI_SETTINGS source/scwx/qt/ui/settings/hotkey_settings_widget.cpp | set(SRC_UI_SETTINGS source/scwx/qt/ui/settings/alert_palette_settings_widget.cpp | ||||||
|  |                     source/scwx/qt/ui/settings/hotkey_settings_widget.cpp | ||||||
|                     source/scwx/qt/ui/settings/settings_page_widget.cpp |                     source/scwx/qt/ui/settings/settings_page_widget.cpp | ||||||
|                     source/scwx/qt/ui/settings/unit_settings_widget.cpp) |                     source/scwx/qt/ui/settings/unit_settings_widget.cpp) | ||||||
| set(HDR_UI_SETUP source/scwx/qt/ui/setup/audio_codec_page.hpp | set(HDR_UI_SETUP source/scwx/qt/ui/setup/audio_codec_page.hpp | ||||||
|  |  | ||||||
|  | @ -9,6 +9,7 @@ | ||||||
| 
 | 
 | ||||||
| #include <chrono> | #include <chrono> | ||||||
| #include <mutex> | #include <mutex> | ||||||
|  | #include <ranges> | ||||||
| #include <unordered_map> | #include <unordered_map> | ||||||
| #include <unordered_set> | #include <unordered_set> | ||||||
| 
 | 
 | ||||||
|  | @ -40,6 +41,8 @@ struct AlertTypeHash<std::pair<awips::Phenomenon, bool>> | ||||||
|    size_t operator()(const std::pair<awips::Phenomenon, bool>& x) const; |    size_t operator()(const std::pair<awips::Phenomenon, bool>& x) const; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | static bool IsAlertActive(const std::shared_ptr<const awips::Segment>& segment); | ||||||
|  | 
 | ||||||
| class AlertLayerHandler : public QObject | class AlertLayerHandler : public QObject | ||||||
| { | { | ||||||
|    Q_OBJECT |    Q_OBJECT | ||||||
|  | @ -111,25 +114,27 @@ signals: | ||||||
| class AlertLayer::Impl | class AlertLayer::Impl | ||||||
| { | { | ||||||
| public: | public: | ||||||
|  |    struct LineData | ||||||
|  |    { | ||||||
|  |       boost::gil::rgba32f_pixel_t borderColor_ {}; | ||||||
|  |       boost::gil::rgba32f_pixel_t highlightColor_ {}; | ||||||
|  |       boost::gil::rgba32f_pixel_t lineColor_ {}; | ||||||
|  | 
 | ||||||
|  |       std::size_t borderWidth_ {}; | ||||||
|  |       std::size_t highlightWidth_ {}; | ||||||
|  |       std::size_t lineWidth_ {}; | ||||||
|  |    }; | ||||||
|  | 
 | ||||||
|    explicit Impl(AlertLayer*                 self, |    explicit Impl(AlertLayer*                 self, | ||||||
|                  std::shared_ptr<MapContext> context, |                  std::shared_ptr<MapContext> context, | ||||||
|                  awips::Phenomenon           phenomenon) : |                  awips::Phenomenon           phenomenon) : | ||||||
|        self_ {self}, |        self_ {self}, | ||||||
|        phenomenon_ {phenomenon}, |        phenomenon_ {phenomenon}, | ||||||
|  |        ibw_ {awips::ibw::GetImpactBasedWarningInfo(phenomenon)}, | ||||||
|        geoLines_ {{false, std::make_shared<gl::draw::GeoLines>(context)}, |        geoLines_ {{false, std::make_shared<gl::draw::GeoLines>(context)}, | ||||||
|                   {true, std::make_shared<gl::draw::GeoLines>(context)}} |                   {true, std::make_shared<gl::draw::GeoLines>(context)}} | ||||||
|    { |    { | ||||||
|       auto& paletteSettings = settings::PaletteSettings::Instance(); |       UpdateLineData(); | ||||||
| 
 |  | ||||||
|       for (auto alertActive : {false, true}) |  | ||||||
|       { |  | ||||||
|          lineColor_.emplace( |  | ||||||
|             alertActive, |  | ||||||
|             util::color::ToRgba32fPixelT( |  | ||||||
|                paletteSettings.alert_color(phenomenon_, alertActive) |  | ||||||
|                   .GetValue())); |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       ConnectSignals(); |       ConnectSignals(); | ||||||
|       ScheduleRefresh(); |       ScheduleRefresh(); | ||||||
|    } |    } | ||||||
|  | @ -158,6 +163,10 @@ public: | ||||||
|                             const QPointF& mouseGlobalPos); |                             const QPointF& mouseGlobalPos); | ||||||
|    void ScheduleRefresh(); |    void ScheduleRefresh(); | ||||||
| 
 | 
 | ||||||
|  |    LineData& GetLineData(const std::shared_ptr<const awips::Segment>& segment, | ||||||
|  |                          bool alertActive); | ||||||
|  |    void      UpdateLineData(); | ||||||
|  | 
 | ||||||
|    void AddLine(std::shared_ptr<gl::draw::GeoLines>&        geoLines, |    void AddLine(std::shared_ptr<gl::draw::GeoLines>&        geoLines, | ||||||
|                 std::shared_ptr<gl::draw::GeoLineDrawItem>& di, |                 std::shared_ptr<gl::draw::GeoLineDrawItem>& di, | ||||||
|                 const common::Coordinate&                   p1, |                 const common::Coordinate&                   p1, | ||||||
|  | @ -176,6 +185,9 @@ public: | ||||||
|                  bool                                   enableHover, |                  bool                                   enableHover, | ||||||
|                  boost::container::stable_vector< |                  boost::container::stable_vector< | ||||||
|                     std::shared_ptr<gl::draw::GeoLineDrawItem>>& drawItems); |                     std::shared_ptr<gl::draw::GeoLineDrawItem>>& drawItems); | ||||||
|  |    void UpdateLines(); | ||||||
|  | 
 | ||||||
|  |    static LineData CreateLineData(const settings::LineSettings& lineSettings); | ||||||
| 
 | 
 | ||||||
|    boost::asio::thread_pool threadPool_ {1u}; |    boost::asio::thread_pool threadPool_ {1u}; | ||||||
| 
 | 
 | ||||||
|  | @ -185,6 +197,7 @@ public: | ||||||
|    std::mutex                refreshMutex_; |    std::mutex                refreshMutex_; | ||||||
| 
 | 
 | ||||||
|    const awips::Phenomenon                   phenomenon_; |    const awips::Phenomenon                   phenomenon_; | ||||||
|  |    const awips::ibw::ImpactBasedWarningInfo& ibw_; | ||||||
| 
 | 
 | ||||||
|    std::unique_ptr<QObject> receiver_ {std::make_unique<QObject>()}; |    std::unique_ptr<QObject> receiver_ {std::make_unique<QObject>()}; | ||||||
| 
 | 
 | ||||||
|  | @ -199,12 +212,18 @@ public: | ||||||
|               segmentsByLine_; |               segmentsByLine_; | ||||||
|    std::mutex linesMutex_ {}; |    std::mutex linesMutex_ {}; | ||||||
| 
 | 
 | ||||||
|    std::unordered_map<bool, boost::gil::rgba32f_pixel_t> lineColor_; |    std::unordered_map<awips::ibw::ThreatCategory, LineData> | ||||||
|  |             threatCategoryLineData_; | ||||||
|  |    LineData observedLineData_ {}; | ||||||
|  |    LineData tornadoPossibleLineData_ {}; | ||||||
|  |    LineData inactiveLineData_ {}; | ||||||
| 
 | 
 | ||||||
|    std::chrono::system_clock::time_point selectedTime_ {}; |    std::chrono::system_clock::time_point selectedTime_ {}; | ||||||
| 
 | 
 | ||||||
|    std::shared_ptr<const gl::draw::GeoLineDrawItem> lastHoverDi_ {nullptr}; |    std::shared_ptr<const gl::draw::GeoLineDrawItem> lastHoverDi_ {nullptr}; | ||||||
|    std::string                                      tooltip_ {}; |    std::string                                      tooltip_ {}; | ||||||
|  | 
 | ||||||
|  |    std::vector<boost::signals2::scoped_connection> connections_ {}; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| AlertLayer::AlertLayer(std::shared_ptr<MapContext> context, | AlertLayer::AlertLayer(std::shared_ptr<MapContext> context, | ||||||
|  | @ -289,6 +308,15 @@ void AlertLayer::Deinitialize() | ||||||
|    DrawLayer::Deinitialize(); |    DrawLayer::Deinitialize(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | bool IsAlertActive(const std::shared_ptr<const awips::Segment>& segment) | ||||||
|  | { | ||||||
|  |    auto& vtec        = segment->header_->vtecString_.front(); | ||||||
|  |    auto  action      = vtec.pVtec_.action(); | ||||||
|  |    bool  alertActive = (action != awips::PVtec::Action::Canceled); | ||||||
|  | 
 | ||||||
|  |    return alertActive; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void AlertLayerHandler::HandleAlert(const types::TextEventKey& key, | void AlertLayerHandler::HandleAlert(const types::TextEventKey& key, | ||||||
|                                     size_t                     messageIndex) |                                     size_t                     messageIndex) | ||||||
| { | { | ||||||
|  | @ -332,9 +360,8 @@ void AlertLayerHandler::HandleAlert(const types::TextEventKey& key, | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       auto&             vtec        = segment->header_->vtecString_.front(); |       auto&             vtec        = segment->header_->vtecString_.front(); | ||||||
|       auto              action     = vtec.pVtec_.action(); |  | ||||||
|       awips::Phenomenon phenomenon  = vtec.pVtec_.phenomenon(); |       awips::Phenomenon phenomenon  = vtec.pVtec_.phenomenon(); | ||||||
|       bool alertActive             = (action != awips::PVtec::Action::Canceled); |       bool              alertActive = IsAlertActive(segment); | ||||||
| 
 | 
 | ||||||
|       auto& segmentsForType = segmentsByType_[{key.phenomenon_, alertActive}]; |       auto& segmentsForType = segmentsByType_[{key.phenomenon_, alertActive}]; | ||||||
| 
 | 
 | ||||||
|  | @ -393,6 +420,8 @@ void AlertLayer::Impl::ConnectAlertHandlerSignals() | ||||||
| 
 | 
 | ||||||
| void AlertLayer::Impl::ConnectSignals() | void AlertLayer::Impl::ConnectSignals() | ||||||
| { | { | ||||||
|  |    auto& alertPaletteSettings = | ||||||
|  |       settings::PaletteSettings::Instance().alert_palette(phenomenon_); | ||||||
|    auto timelineManager = manager::TimelineManager::Instance(); |    auto timelineManager = manager::TimelineManager::Instance(); | ||||||
| 
 | 
 | ||||||
|    QObject::connect(timelineManager.get(), |    QObject::connect(timelineManager.get(), | ||||||
|  | @ -400,6 +429,13 @@ void AlertLayer::Impl::ConnectSignals() | ||||||
|                     receiver_.get(), |                     receiver_.get(), | ||||||
|                     [this](std::chrono::system_clock::time_point dateTime) |                     [this](std::chrono::system_clock::time_point dateTime) | ||||||
|                     { selectedTime_ = dateTime; }); |                     { selectedTime_ = dateTime; }); | ||||||
|  | 
 | ||||||
|  |    connections_.push_back(alertPaletteSettings.changed_signal().connect( | ||||||
|  |       [this]() | ||||||
|  |       { | ||||||
|  |          UpdateLineData(); | ||||||
|  |          UpdateLines(); | ||||||
|  |       })); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void AlertLayer::Impl::ScheduleRefresh() | void AlertLayer::Impl::ScheduleRefresh() | ||||||
|  | @ -439,13 +475,11 @@ void AlertLayer::Impl::AddAlert( | ||||||
| { | { | ||||||
|    auto& segment = segmentRecord->segment_; |    auto& segment = segmentRecord->segment_; | ||||||
| 
 | 
 | ||||||
|    auto& vtec        = segment->header_->vtecString_.front(); |    bool  alertActive = IsAlertActive(segment); | ||||||
|    auto  action      = vtec.pVtec_.action(); |  | ||||||
|    bool  alertActive = (action != awips::PVtec::Action::Canceled); |  | ||||||
|    auto& startTime   = segmentRecord->segmentBegin_; |    auto& startTime   = segmentRecord->segmentBegin_; | ||||||
|    auto& endTime     = segmentRecord->segmentEnd_; |    auto& endTime     = segmentRecord->segmentEnd_; | ||||||
| 
 | 
 | ||||||
|    auto& lineColor = lineColor_.at(alertActive); |    auto& lineData = GetLineData(segment, alertActive); | ||||||
|    auto& geoLines = geoLines_.at(alertActive); |    auto& geoLines = geoLines_.at(alertActive); | ||||||
| 
 | 
 | ||||||
|    const auto& coordinates = segment->codedLocation_->coordinates(); |    const auto& coordinates = segment->codedLocation_->coordinates(); | ||||||
|  | @ -462,30 +496,51 @@ void AlertLayer::Impl::AddAlert( | ||||||
|    // If draw items were added
 |    // If draw items were added
 | ||||||
|    if (drawItems.second) |    if (drawItems.second) | ||||||
|    { |    { | ||||||
|  |       const float borderWidth    = lineData.borderWidth_; | ||||||
|  |       const float highlightWidth = lineData.highlightWidth_; | ||||||
|  |       const float lineWidth      = lineData.lineWidth_; | ||||||
|  | 
 | ||||||
|  |       const float totalHighlightWidth = lineWidth + (highlightWidth * 2.0f); | ||||||
|  |       const float totalBorderWidth = totalHighlightWidth + (borderWidth * 2.0f); | ||||||
|  | 
 | ||||||
|  |       constexpr bool borderHover    = true; | ||||||
|  |       constexpr bool highlightHover = false; | ||||||
|  |       constexpr bool lineHover      = false; | ||||||
|  | 
 | ||||||
|       // Add border
 |       // Add border
 | ||||||
|       AddLines(geoLines, |       AddLines(geoLines, | ||||||
|                coordinates, |                coordinates, | ||||||
|                kBlack_, |                lineData.borderColor_, | ||||||
|                5.0f, |                totalBorderWidth, | ||||||
|                startTime, |                startTime, | ||||||
|                endTime, |                endTime, | ||||||
|                true, |                borderHover, | ||||||
|                drawItems.first->second); |                drawItems.first->second); | ||||||
| 
 | 
 | ||||||
|       // Add only border to segmentsByLine_
 |       // Add border to segmentsByLine_
 | ||||||
|       for (auto& di : drawItems.first->second) |       for (auto& di : drawItems.first->second) | ||||||
|       { |       { | ||||||
|          segmentsByLine_.insert({di, segmentRecord}); |          segmentsByLine_.insert({di, segmentRecord}); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|  |       // Add highlight
 | ||||||
|  |       AddLines(geoLines, | ||||||
|  |                coordinates, | ||||||
|  |                lineData.highlightColor_, | ||||||
|  |                totalHighlightWidth, | ||||||
|  |                startTime, | ||||||
|  |                endTime, | ||||||
|  |                highlightHover, | ||||||
|  |                drawItems.first->second); | ||||||
|  | 
 | ||||||
|       // Add line
 |       // Add line
 | ||||||
|       AddLines(geoLines, |       AddLines(geoLines, | ||||||
|                coordinates, |                coordinates, | ||||||
|                lineColor, |                lineData.lineColor_, | ||||||
|                3.0f, |                lineWidth, | ||||||
|                startTime, |                startTime, | ||||||
|                endTime, |                endTime, | ||||||
|                false, |                lineHover, | ||||||
|                drawItems.first->second); |                drawItems.first->second); | ||||||
|    } |    } | ||||||
| } | } | ||||||
|  | @ -500,10 +555,7 @@ void AlertLayer::Impl::UpdateAlert( | ||||||
|    if (it != linesBySegment_.cend()) |    if (it != linesBySegment_.cend()) | ||||||
|    { |    { | ||||||
|       auto& segment     = segmentRecord->segment_; |       auto& segment     = segmentRecord->segment_; | ||||||
| 
 |       bool  alertActive = IsAlertActive(segment); | ||||||
|       auto& vtec        = segment->header_->vtecString_.front(); |  | ||||||
|       auto  action      = vtec.pVtec_.action(); |  | ||||||
|       bool  alertActive = (action != awips::PVtec::Action::Canceled); |  | ||||||
| 
 | 
 | ||||||
|       auto& geoLines = geoLines_.at(alertActive); |       auto& geoLines = geoLines_.at(alertActive); | ||||||
| 
 | 
 | ||||||
|  | @ -590,6 +642,54 @@ void AlertLayer::Impl::AddLine(std::shared_ptr<gl::draw::GeoLines>& geoLines, | ||||||
|    } |    } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void AlertLayer::Impl::UpdateLines() | ||||||
|  | { | ||||||
|  |    std::unique_lock lock {linesMutex_}; | ||||||
|  | 
 | ||||||
|  |    for (auto& segmentLine : linesBySegment_) | ||||||
|  |    { | ||||||
|  |       auto& segmentRecord    = segmentLine.first; | ||||||
|  |       auto& geoLineDrawItems = segmentLine.second; | ||||||
|  |       auto& segment          = segmentRecord->segment_; | ||||||
|  |       bool  alertActive      = IsAlertActive(segment); | ||||||
|  |       auto& lineData         = GetLineData(segment, alertActive); | ||||||
|  |       auto& geoLines         = geoLines_.at(alertActive); | ||||||
|  | 
 | ||||||
|  |       const float borderWidth    = lineData.borderWidth_; | ||||||
|  |       const float highlightWidth = lineData.highlightWidth_; | ||||||
|  |       const float lineWidth      = lineData.lineWidth_; | ||||||
|  | 
 | ||||||
|  |       const float totalHighlightWidth = lineWidth + (highlightWidth * 2.0f); | ||||||
|  |       const float totalBorderWidth = totalHighlightWidth + (borderWidth * 2.0f); | ||||||
|  | 
 | ||||||
|  |       // Border, highlight and line
 | ||||||
|  |       std::size_t linesPerType = geoLineDrawItems.size() / 3; | ||||||
|  | 
 | ||||||
|  |       // Border
 | ||||||
|  |       for (auto& borderLine : geoLineDrawItems | std::views::take(linesPerType)) | ||||||
|  |       { | ||||||
|  |          geoLines->SetLineModulate(borderLine, lineData.borderColor_); | ||||||
|  |          geoLines->SetLineWidth(borderLine, totalBorderWidth); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       // Highlight
 | ||||||
|  |       for (auto& highlightLine : geoLineDrawItems | | ||||||
|  |                                     std::views::drop(linesPerType) | | ||||||
|  |                                     std::views::take(linesPerType)) | ||||||
|  |       { | ||||||
|  |          geoLines->SetLineModulate(highlightLine, lineData.highlightColor_); | ||||||
|  |          geoLines->SetLineWidth(highlightLine, totalHighlightWidth); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       // Line
 | ||||||
|  |       for (auto& line : geoLineDrawItems | std::views::drop(linesPerType * 2)) | ||||||
|  |       { | ||||||
|  |          geoLines->SetLineModulate(line, lineData.lineColor_); | ||||||
|  |          geoLines->SetLineWidth(line, lineWidth); | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void AlertLayer::Impl::HandleGeoLinesEvent( | void AlertLayer::Impl::HandleGeoLinesEvent( | ||||||
|    std::shared_ptr<gl::draw::GeoLineDrawItem>& di, QEvent* ev) |    std::shared_ptr<gl::draw::GeoLineDrawItem>& di, QEvent* ev) | ||||||
| { | { | ||||||
|  | @ -638,6 +738,79 @@ void AlertLayer::Impl::HandleGeoLinesHover( | ||||||
|    } |    } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | AlertLayer::Impl::LineData | ||||||
|  | AlertLayer::Impl::CreateLineData(const settings::LineSettings& lineSettings) | ||||||
|  | { | ||||||
|  |    return LineData { | ||||||
|  |       .borderColor_ {lineSettings.GetBorderColorRgba32f()}, | ||||||
|  |       .highlightColor_ {lineSettings.GetHighlightColorRgba32f()}, | ||||||
|  |       .lineColor_ {lineSettings.GetLineColorRgba32f()}, | ||||||
|  |       .borderWidth_ = | ||||||
|  |          static_cast<std::size_t>(lineSettings.border_width().GetValue()), | ||||||
|  |       .highlightWidth_ = | ||||||
|  |          static_cast<std::size_t>(lineSettings.highlight_width().GetValue()), | ||||||
|  |       .lineWidth_ = | ||||||
|  |          static_cast<std::size_t>(lineSettings.line_width().GetValue())}; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AlertLayer::Impl::UpdateLineData() | ||||||
|  | { | ||||||
|  |    auto& alertPalette = | ||||||
|  |       settings::PaletteSettings().Instance().alert_palette(phenomenon_); | ||||||
|  | 
 | ||||||
|  |    for (auto threatCategory : ibw_.threatCategories_) | ||||||
|  |    { | ||||||
|  |       auto& palette = alertPalette.threat_category(threatCategory); | ||||||
|  |       threatCategoryLineData_.insert_or_assign(threatCategory, | ||||||
|  |                                                CreateLineData(palette)); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    if (ibw_.hasObservedTag_) | ||||||
|  |    { | ||||||
|  |       observedLineData_ = CreateLineData(alertPalette.observed()); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    if (ibw_.hasTornadoPossibleTag_) | ||||||
|  |    { | ||||||
|  |       tornadoPossibleLineData_ = | ||||||
|  |          CreateLineData(alertPalette.tornado_possible()); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    inactiveLineData_ = CreateLineData(alertPalette.inactive()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | AlertLayer::Impl::LineData& AlertLayer::Impl::GetLineData( | ||||||
|  |    const std::shared_ptr<const awips::Segment>& segment, bool alertActive) | ||||||
|  | { | ||||||
|  |    if (!alertActive) | ||||||
|  |    { | ||||||
|  |       return inactiveLineData_; | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    for (auto& threatCategory : ibw_.threatCategories_) | ||||||
|  |    { | ||||||
|  |       if (segment->threatCategory_ == threatCategory) | ||||||
|  |       { | ||||||
|  |          if (threatCategory == awips::ibw::ThreatCategory::Base) | ||||||
|  |          { | ||||||
|  |             if (ibw_.hasObservedTag_ && segment->observed_) | ||||||
|  |             { | ||||||
|  |                return observedLineData_; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (ibw_.hasTornadoPossibleTag_ && segment->tornadoPossible_) | ||||||
|  |             { | ||||||
|  |                return tornadoPossibleLineData_; | ||||||
|  |             } | ||||||
|  |          } | ||||||
|  | 
 | ||||||
|  |          return threatCategoryLineData_.at(threatCategory); | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    return threatCategoryLineData_.at(awips::ibw::ThreatCategory::Base); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| AlertLayerHandler& AlertLayerHandler::Instance() | AlertLayerHandler& AlertLayerHandler::Instance() | ||||||
| { | { | ||||||
|    static AlertLayerHandler alertLayerHandler_ {}; |    static AlertLayerHandler alertLayerHandler_ {}; | ||||||
|  |  | ||||||
|  | @ -10,7 +10,6 @@ | ||||||
| #include <scwx/util/strings.hpp> | #include <scwx/util/strings.hpp> | ||||||
| #include <scwx/util/time.hpp> | #include <scwx/util/time.hpp> | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| #include <format> | #include <format> | ||||||
| 
 | 
 | ||||||
| #include <QApplication> | #include <QApplication> | ||||||
|  | @ -38,7 +37,7 @@ public: | ||||||
|    ~AlertModelImpl() = default; |    ~AlertModelImpl() = default; | ||||||
| 
 | 
 | ||||||
|    bool                       GetObserved(const types::TextEventKey& key); |    bool                       GetObserved(const types::TextEventKey& key); | ||||||
|    awips::ThreatCategory GetThreatCategory(const types::TextEventKey& key); |    awips::ibw::ThreatCategory GetThreatCategory(const types::TextEventKey& key); | ||||||
|    bool GetTornadoPossible(const types::TextEventKey& key); |    bool GetTornadoPossible(const types::TextEventKey& key); | ||||||
| 
 | 
 | ||||||
|    static std::string GetCounties(const types::TextEventKey& key); |    static std::string GetCounties(const types::TextEventKey& key); | ||||||
|  | @ -61,7 +60,7 @@ public: | ||||||
|                       types::TextEventHash<types::TextEventKey>> |                       types::TextEventHash<types::TextEventKey>> | ||||||
|       observedMap_; |       observedMap_; | ||||||
|    std::unordered_map<types::TextEventKey, |    std::unordered_map<types::TextEventKey, | ||||||
|                       awips::ThreatCategory, |                       awips::ibw::ThreatCategory, | ||||||
|                       types::TextEventHash<types::TextEventKey>> |                       types::TextEventHash<types::TextEventKey>> | ||||||
|       threatCategoryMap_; |       threatCategoryMap_; | ||||||
|    std::unordered_map<types::TextEventKey, |    std::unordered_map<types::TextEventKey, | ||||||
|  | @ -158,7 +157,7 @@ QVariant AlertModel::data(const QModelIndex& index, int role) const | ||||||
|       case static_cast<int>(Column::ThreatCategory): |       case static_cast<int>(Column::ThreatCategory): | ||||||
|          if (role == Qt::DisplayRole) |          if (role == Qt::DisplayRole) | ||||||
|          { |          { | ||||||
|             return QString::fromStdString(awips::GetThreatCategoryName( |             return QString::fromStdString(awips::ibw::GetThreatCategoryName( | ||||||
|                p->GetThreatCategory(textEventKey))); |                p->GetThreatCategory(textEventKey))); | ||||||
|          } |          } | ||||||
|          else |          else | ||||||
|  | @ -439,10 +438,10 @@ bool AlertModelImpl::GetObserved(const types::TextEventKey& key) | ||||||
|    return observed; |    return observed; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| awips::ThreatCategory | awips::ibw::ThreatCategory | ||||||
| AlertModelImpl::GetThreatCategory(const types::TextEventKey& key) | AlertModelImpl::GetThreatCategory(const types::TextEventKey& key) | ||||||
| { | { | ||||||
|    awips::ThreatCategory threatCategory = awips::ThreatCategory::Base; |    awips::ibw::ThreatCategory threatCategory = awips::ibw::ThreatCategory::Base; | ||||||
| 
 | 
 | ||||||
|    auto it = threatCategoryMap_.find(key); |    auto it = threatCategoryMap_.find(key); | ||||||
|    if (it != threatCategoryMap_.cend()) |    if (it != threatCategoryMap_.cend()) | ||||||
|  |  | ||||||
							
								
								
									
										241
									
								
								scwx-qt/source/scwx/qt/settings/alert_palette_settings.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										241
									
								
								scwx-qt/source/scwx/qt/settings/alert_palette_settings.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,241 @@ | ||||||
|  | #include <scwx/qt/settings/alert_palette_settings.hpp> | ||||||
|  | #include <scwx/qt/util/color.hpp> | ||||||
|  | 
 | ||||||
|  | #include <map> | ||||||
|  | 
 | ||||||
|  | #include <boost/algorithm/string/case_conv.hpp> | ||||||
|  | #include <boost/gil.hpp> | ||||||
|  | #include <boost/unordered/unordered_flat_map.hpp> | ||||||
|  | 
 | ||||||
|  | namespace scwx | ||||||
|  | { | ||||||
|  | namespace qt | ||||||
|  | { | ||||||
|  | namespace settings | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | static const std::string logPrefix_ = | ||||||
|  |    "scwx::qt::settings::alert_palette_settings"; | ||||||
|  | 
 | ||||||
|  | static const boost::gil::rgba8_pixel_t kColorBlack_ {0, 0, 0, 255}; | ||||||
|  | 
 | ||||||
|  | struct LineData | ||||||
|  | { | ||||||
|  |    boost::gil::rgba8_pixel_t borderColor_ {kColorBlack_}; | ||||||
|  |    boost::gil::rgba8_pixel_t highlightColor_ {kColorBlack_}; | ||||||
|  |    boost::gil::rgba8_pixel_t lineColor_; | ||||||
|  |    std::int64_t              borderWidth_ {1}; | ||||||
|  |    std::int64_t              highlightWidth_ {0}; | ||||||
|  |    std::int64_t              lineWidth_ {3}; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef boost::unordered_flat_map<awips::ibw::ThreatCategory, LineData> | ||||||
|  |    ThreatCategoryPalette; | ||||||
|  | 
 | ||||||
|  | static const boost::unordered_flat_map<awips::Phenomenon, ThreatCategoryPalette> | ||||||
|  |    kThreatCategoryPalettes_ //
 | ||||||
|  |    {{awips::Phenomenon::Marine, | ||||||
|  |      {{awips::ibw::ThreatCategory::Base, {.lineColor_ {255, 127, 0, 255}}}}}, | ||||||
|  |     {awips::Phenomenon::FlashFlood, | ||||||
|  |      {{awips::ibw::ThreatCategory::Base, {.lineColor_ {0, 255, 0, 255}}}, | ||||||
|  |       {awips::ibw::ThreatCategory::Considerable, | ||||||
|  |        {.highlightColor_ {0, 255, 0, 255}, | ||||||
|  |         .lineColor_ {kColorBlack_}, | ||||||
|  |         .highlightWidth_ = 1, | ||||||
|  |         .lineWidth_      = 1}}, | ||||||
|  |       {awips::ibw::ThreatCategory::Catastrophic, | ||||||
|  |        {.highlightColor_ {0, 255, 0, 255}, | ||||||
|  |         .lineColor_ {255, 0, 0, 255}, | ||||||
|  |         .highlightWidth_ = 1, | ||||||
|  |         .lineWidth_      = 1}}}}, | ||||||
|  |     {awips::Phenomenon::SevereThunderstorm, | ||||||
|  |      {{awips::ibw::ThreatCategory::Base, {.lineColor_ {255, 255, 0, 255}}}, | ||||||
|  |       {awips::ibw::ThreatCategory::Considerable, | ||||||
|  |        {.highlightColor_ {255, 255, 0, 255}, | ||||||
|  |         .lineColor_ {255, 0, 0, 255}, | ||||||
|  |         .highlightWidth_ = 1, | ||||||
|  |         .lineWidth_      = 1}}, | ||||||
|  |       {awips::ibw::ThreatCategory::Destructive, | ||||||
|  |        {.highlightColor_ {255, 255, 0, 255}, | ||||||
|  |         .lineColor_ {255, 0, 0, 255}, | ||||||
|  |         .highlightWidth_ = 1, | ||||||
|  |         .lineWidth_      = 2}}}}, | ||||||
|  |     {awips::Phenomenon::SnowSquall, | ||||||
|  |      {{awips::ibw::ThreatCategory::Base, {.lineColor_ {0, 255, 255, 255}}}}}, | ||||||
|  |     {awips::Phenomenon::Tornado, | ||||||
|  |      {{awips::ibw::ThreatCategory::Base, {.lineColor_ {255, 0, 0, 255}}}, | ||||||
|  |       {awips::ibw::ThreatCategory::Considerable, | ||||||
|  |        {.lineColor_ {255, 0, 255, 255}}}, | ||||||
|  |       {awips::ibw::ThreatCategory::Catastrophic, | ||||||
|  |        {.highlightColor_ {255, 0, 255, 255}, | ||||||
|  |         .lineColor_ {kColorBlack_}, | ||||||
|  |         .highlightWidth_ = 1, | ||||||
|  |         .lineWidth_      = 1}}}}}; | ||||||
|  | 
 | ||||||
|  | static const boost::unordered_flat_map<awips::Phenomenon, LineData> | ||||||
|  |    kObservedPalettes_ //
 | ||||||
|  |    {{awips::Phenomenon::Tornado, | ||||||
|  |      {.highlightColor_ {255, 0, 0, 255}, | ||||||
|  |       .lineColor_ {kColorBlack_}, | ||||||
|  |       .highlightWidth_ = 1, | ||||||
|  |       .lineWidth_      = 1}}}; | ||||||
|  | 
 | ||||||
|  | static const boost::unordered_flat_map<awips::Phenomenon, LineData> | ||||||
|  |    kTornadoPossiblePalettes_ //
 | ||||||
|  |    {{awips::Phenomenon::Marine, | ||||||
|  |      {.highlightColor_ {255, 127, 0, 255}, | ||||||
|  |       .lineColor_ {kColorBlack_}, | ||||||
|  |       .highlightWidth_ = 1, | ||||||
|  |       .lineWidth_      = 1}}, | ||||||
|  |     {awips::Phenomenon::SevereThunderstorm, | ||||||
|  |      {.highlightColor_ {255, 255, 0, 255}, | ||||||
|  |       .lineColor_ {kColorBlack_}, | ||||||
|  |       .highlightWidth_ = 1, | ||||||
|  |       .lineWidth_      = 1}}}; | ||||||
|  | 
 | ||||||
|  | static const boost::unordered_flat_map<awips::Phenomenon, LineData> | ||||||
|  |    kInactivePalettes_ //
 | ||||||
|  |    { | ||||||
|  |       {awips::Phenomenon::Marine, {.lineColor_ {127, 63, 0, 255}}}, | ||||||
|  |       {awips::Phenomenon::FlashFlood, {.lineColor_ {0, 127, 0, 255}}}, | ||||||
|  |       {awips::Phenomenon::SevereThunderstorm, {.lineColor_ {127, 127, 0, 255}}}, | ||||||
|  |       {awips::Phenomenon::SnowSquall, {.lineColor_ {0, 127, 127, 255}}}, | ||||||
|  |       {awips::Phenomenon::Tornado, {.lineColor_ {127, 0, 0, 255}}}, | ||||||
|  |    }; | ||||||
|  | 
 | ||||||
|  | class AlertPaletteSettings::Impl | ||||||
|  | { | ||||||
|  | public: | ||||||
|  |    explicit Impl(awips::Phenomenon phenomenon) : phenomenon_ {phenomenon} | ||||||
|  |    { | ||||||
|  |       const auto& info = awips::ibw::GetImpactBasedWarningInfo(phenomenon); | ||||||
|  | 
 | ||||||
|  |       const auto& threatCategoryPalettes = | ||||||
|  |          kThreatCategoryPalettes_.at(phenomenon); | ||||||
|  | 
 | ||||||
|  |       for (auto& threatCategory : info.threatCategories_) | ||||||
|  |       { | ||||||
|  |          std::string threatCategoryName = | ||||||
|  |             awips::ibw::GetThreatCategoryName(threatCategory); | ||||||
|  |          boost::algorithm::to_lower(threatCategoryName); | ||||||
|  |          auto result = | ||||||
|  |             threatCategoryMap_.emplace(threatCategory, threatCategoryName); | ||||||
|  |          auto& lineSettings = result.first->second; | ||||||
|  | 
 | ||||||
|  |          SetDefaultLineData(lineSettings, | ||||||
|  |                             threatCategoryPalettes.at(threatCategory)); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       if (info.hasObservedTag_) | ||||||
|  |       { | ||||||
|  |          SetDefaultLineData(observed_, kObservedPalettes_.at(phenomenon)); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       if (info.hasTornadoPossibleTag_) | ||||||
|  |       { | ||||||
|  |          SetDefaultLineData(tornadoPossible_, | ||||||
|  |                             kTornadoPossiblePalettes_.at(phenomenon)); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       SetDefaultLineData(inactive_, kInactivePalettes_.at(phenomenon)); | ||||||
|  |    } | ||||||
|  |    ~Impl() {} | ||||||
|  | 
 | ||||||
|  |    static void SetDefaultLineData(LineSettings&   lineSettings, | ||||||
|  |                                   const LineData& lineData); | ||||||
|  | 
 | ||||||
|  |    awips::Phenomenon phenomenon_; | ||||||
|  | 
 | ||||||
|  |    std::map<awips::ibw::ThreatCategory, LineSettings> threatCategoryMap_ {}; | ||||||
|  | 
 | ||||||
|  |    LineSettings observed_ {"observed"}; | ||||||
|  |    LineSettings tornadoPossible_ {"tornado_possible"}; | ||||||
|  |    LineSettings inactive_ {"inactive"}; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | AlertPaletteSettings::AlertPaletteSettings(awips::Phenomenon phenomenon) : | ||||||
|  |     SettingsCategory(awips::GetPhenomenonCode(phenomenon)), | ||||||
|  |     p(std::make_unique<Impl>(phenomenon)) | ||||||
|  | { | ||||||
|  |    auto& info = awips::ibw::GetImpactBasedWarningInfo(p->phenomenon_); | ||||||
|  |    for (auto& threatCategory : p->threatCategoryMap_) | ||||||
|  |    { | ||||||
|  |       RegisterSubcategory(threatCategory.second); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    if (info.hasObservedTag_) | ||||||
|  |    { | ||||||
|  |       RegisterSubcategory(p->observed_); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    if (info.hasTornadoPossibleTag_) | ||||||
|  |    { | ||||||
|  |       RegisterSubcategory(p->tornadoPossible_); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    RegisterSubcategory(p->inactive_); | ||||||
|  | 
 | ||||||
|  |    SetDefaults(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | AlertPaletteSettings::~AlertPaletteSettings() = default; | ||||||
|  | 
 | ||||||
|  | AlertPaletteSettings::AlertPaletteSettings(AlertPaletteSettings&&) noexcept = | ||||||
|  |    default; | ||||||
|  | AlertPaletteSettings& | ||||||
|  | AlertPaletteSettings::operator=(AlertPaletteSettings&&) noexcept = default; | ||||||
|  | 
 | ||||||
|  | LineSettings& AlertPaletteSettings::threat_category( | ||||||
|  |    awips::ibw::ThreatCategory threatCategory) const | ||||||
|  | { | ||||||
|  |    auto it = p->threatCategoryMap_.find(threatCategory); | ||||||
|  |    if (it != p->threatCategoryMap_.cend()) | ||||||
|  |    { | ||||||
|  |       return it->second; | ||||||
|  |    } | ||||||
|  |    return p->threatCategoryMap_.at(awips::ibw::ThreatCategory::Base); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | LineSettings& AlertPaletteSettings::inactive() const | ||||||
|  | { | ||||||
|  |    return p->inactive_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | LineSettings& AlertPaletteSettings::observed() const | ||||||
|  | { | ||||||
|  |    return p->observed_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | LineSettings& AlertPaletteSettings::tornado_possible() const | ||||||
|  | { | ||||||
|  |    return p->tornadoPossible_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AlertPaletteSettings::Impl::SetDefaultLineData(LineSettings& lineSettings, | ||||||
|  |                                                     const LineData& lineData) | ||||||
|  | { | ||||||
|  |    lineSettings.border_color().SetDefault( | ||||||
|  |       util::color::ToArgbString(lineData.borderColor_)); | ||||||
|  |    lineSettings.highlight_color().SetDefault( | ||||||
|  |       util::color::ToArgbString(lineData.highlightColor_)); | ||||||
|  |    lineSettings.line_color().SetDefault( | ||||||
|  |       util::color::ToArgbString(lineData.lineColor_)); | ||||||
|  | 
 | ||||||
|  |    lineSettings.border_width().SetDefault(lineData.borderWidth_); | ||||||
|  |    lineSettings.highlight_width().SetDefault(lineData.highlightWidth_); | ||||||
|  |    lineSettings.line_width().SetDefault(lineData.lineWidth_); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool operator==(const AlertPaletteSettings& lhs, | ||||||
|  |                 const AlertPaletteSettings& rhs) | ||||||
|  | { | ||||||
|  |    return (lhs.p->phenomenon_ == rhs.p->phenomenon_ && | ||||||
|  |            lhs.p->threatCategoryMap_ == rhs.p->threatCategoryMap_ && | ||||||
|  |            lhs.p->inactive_ == rhs.p->inactive_ && | ||||||
|  |            lhs.p->observed_ == rhs.p->observed_ && | ||||||
|  |            lhs.p->tornadoPossible_ == rhs.p->tornadoPossible_); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace settings
 | ||||||
|  | } // namespace qt
 | ||||||
|  | } // namespace scwx
 | ||||||
							
								
								
									
										46
									
								
								scwx-qt/source/scwx/qt/settings/alert_palette_settings.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								scwx-qt/source/scwx/qt/settings/alert_palette_settings.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,46 @@ | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <scwx/qt/settings/line_settings.hpp> | ||||||
|  | #include <scwx/qt/settings/settings_category.hpp> | ||||||
|  | #include <scwx/awips/impact_based_warnings.hpp> | ||||||
|  | #include <scwx/awips/phenomenon.hpp> | ||||||
|  | 
 | ||||||
|  | #include <memory> | ||||||
|  | #include <string> | ||||||
|  | 
 | ||||||
|  | namespace scwx | ||||||
|  | { | ||||||
|  | namespace qt | ||||||
|  | { | ||||||
|  | namespace settings | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | class AlertPaletteSettings : public SettingsCategory | ||||||
|  | { | ||||||
|  | public: | ||||||
|  |    explicit AlertPaletteSettings(awips::Phenomenon phenomenon); | ||||||
|  |    ~AlertPaletteSettings(); | ||||||
|  | 
 | ||||||
|  |    AlertPaletteSettings(const AlertPaletteSettings&)            = delete; | ||||||
|  |    AlertPaletteSettings& operator=(const AlertPaletteSettings&) = delete; | ||||||
|  | 
 | ||||||
|  |    AlertPaletteSettings(AlertPaletteSettings&&) noexcept; | ||||||
|  |    AlertPaletteSettings& operator=(AlertPaletteSettings&&) noexcept; | ||||||
|  | 
 | ||||||
|  |    LineSettings& | ||||||
|  |    threat_category(awips::ibw::ThreatCategory threatCategory) const; | ||||||
|  |    LineSettings& inactive() const; | ||||||
|  |    LineSettings& observed() const; | ||||||
|  |    LineSettings& tornado_possible() const; | ||||||
|  | 
 | ||||||
|  |    friend bool operator==(const AlertPaletteSettings& lhs, | ||||||
|  |                           const AlertPaletteSettings& rhs); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |    class Impl; | ||||||
|  |    std::unique_ptr<Impl> p; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace settings
 | ||||||
|  | } // namespace qt
 | ||||||
|  | } // namespace scwx
 | ||||||
							
								
								
									
										155
									
								
								scwx-qt/source/scwx/qt/settings/line_settings.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										155
									
								
								scwx-qt/source/scwx/qt/settings/line_settings.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,155 @@ | ||||||
|  | #include <scwx/qt/settings/line_settings.hpp> | ||||||
|  | #include <scwx/qt/util/color.hpp> | ||||||
|  | 
 | ||||||
|  | namespace scwx | ||||||
|  | { | ||||||
|  | namespace qt | ||||||
|  | { | ||||||
|  | namespace settings | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | static const std::string logPrefix_ = "scwx::qt::settings::line_settings"; | ||||||
|  | 
 | ||||||
|  | static const boost::gil::rgba8_pixel_t kTransparentColor_ {0, 0, 0, 0}; | ||||||
|  | static const std::string               kTransparentColorString_ { | ||||||
|  |    util::color::ToArgbString(kTransparentColor_)}; | ||||||
|  | 
 | ||||||
|  | static const boost::gil::rgba8_pixel_t kBlackColor_ {0, 0, 0, 255}; | ||||||
|  | static const std::string               kBlackColorString_ { | ||||||
|  |    util::color::ToArgbString(kBlackColor_)}; | ||||||
|  | 
 | ||||||
|  | static const boost::gil::rgba8_pixel_t kWhiteColor_ {255, 255, 255, 255}; | ||||||
|  | static const std::string               kWhiteColorString_ { | ||||||
|  |    util::color::ToArgbString(kWhiteColor_)}; | ||||||
|  | 
 | ||||||
|  | class LineSettings::Impl | ||||||
|  | { | ||||||
|  | public: | ||||||
|  |    explicit Impl() | ||||||
|  |    { | ||||||
|  |       lineColor_.SetDefault(kWhiteColorString_); | ||||||
|  |       highlightColor_.SetDefault(kTransparentColorString_); | ||||||
|  |       borderColor_.SetDefault(kBlackColorString_); | ||||||
|  | 
 | ||||||
|  |       lineWidth_.SetDefault(3); | ||||||
|  |       highlightWidth_.SetDefault(0); | ||||||
|  |       borderWidth_.SetDefault(1); | ||||||
|  | 
 | ||||||
|  |       lineWidth_.SetMinimum(1); | ||||||
|  |       highlightWidth_.SetMinimum(0); | ||||||
|  |       borderWidth_.SetMinimum(0); | ||||||
|  | 
 | ||||||
|  |       lineWidth_.SetMaximum(9); | ||||||
|  |       highlightWidth_.SetMaximum(9); | ||||||
|  |       borderWidth_.SetMaximum(9); | ||||||
|  | 
 | ||||||
|  |       lineColor_.SetValidator(&util::color::ValidateArgbString); | ||||||
|  |       highlightColor_.SetValidator(&util::color::ValidateArgbString); | ||||||
|  |       borderColor_.SetValidator(&util::color::ValidateArgbString); | ||||||
|  |    } | ||||||
|  |    ~Impl() {} | ||||||
|  | 
 | ||||||
|  |    SettingsVariable<std::string> lineColor_ {"line_color"}; | ||||||
|  |    SettingsVariable<std::string> highlightColor_ {"highlight_color"}; | ||||||
|  |    SettingsVariable<std::string> borderColor_ {"border_color"}; | ||||||
|  | 
 | ||||||
|  |    SettingsVariable<std::int64_t> lineWidth_ {"line_width"}; | ||||||
|  |    SettingsVariable<std::int64_t> highlightWidth_ {"highlight_width"}; | ||||||
|  |    SettingsVariable<std::int64_t> borderWidth_ {"border_width"}; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | LineSettings::LineSettings(const std::string& name) : | ||||||
|  |     SettingsCategory(name), p(std::make_unique<Impl>()) | ||||||
|  | { | ||||||
|  |    RegisterVariables({&p->lineColor_, | ||||||
|  |                       &p->highlightColor_, | ||||||
|  |                       &p->borderColor_, | ||||||
|  |                       &p->lineWidth_, | ||||||
|  |                       &p->highlightWidth_, | ||||||
|  |                       &p->borderWidth_}); | ||||||
|  |    SetDefaults(); | ||||||
|  | } | ||||||
|  | LineSettings::~LineSettings() = default; | ||||||
|  | 
 | ||||||
|  | LineSettings::LineSettings(LineSettings&&) noexcept            = default; | ||||||
|  | LineSettings& LineSettings::operator=(LineSettings&&) noexcept = default; | ||||||
|  | 
 | ||||||
|  | SettingsVariable<std::string>& LineSettings::border_color() const | ||||||
|  | { | ||||||
|  |    return p->borderColor_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | SettingsVariable<std::string>& LineSettings::highlight_color() const | ||||||
|  | { | ||||||
|  |    return p->highlightColor_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | SettingsVariable<std::string>& LineSettings::line_color() const | ||||||
|  | { | ||||||
|  |    return p->lineColor_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | SettingsVariable<std::int64_t>& LineSettings::border_width() const | ||||||
|  | { | ||||||
|  |    return p->borderWidth_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | SettingsVariable<std::int64_t>& LineSettings::highlight_width() const | ||||||
|  | { | ||||||
|  |    return p->highlightWidth_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | SettingsVariable<std::int64_t>& LineSettings::line_width() const | ||||||
|  | { | ||||||
|  |    return p->lineWidth_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | boost::gil::rgba32f_pixel_t LineSettings::GetBorderColorRgba32f() const | ||||||
|  | { | ||||||
|  |    return util::color::ToRgba32fPixelT(p->borderColor_.GetValue()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | boost::gil::rgba32f_pixel_t LineSettings::GetHighlightColorRgba32f() const | ||||||
|  | { | ||||||
|  |    return util::color::ToRgba32fPixelT(p->highlightColor_.GetValue()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | boost::gil::rgba32f_pixel_t LineSettings::GetLineColorRgba32f() const | ||||||
|  | { | ||||||
|  |    return util::color::ToRgba32fPixelT(p->lineColor_.GetValue()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void LineSettings::StageValues(boost::gil::rgba8_pixel_t borderColor, | ||||||
|  |                                boost::gil::rgba8_pixel_t highlightColor, | ||||||
|  |                                boost::gil::rgba8_pixel_t lineColor, | ||||||
|  |                                std::int64_t              borderWidth, | ||||||
|  |                                std::int64_t              highlightWidth, | ||||||
|  |                                std::int64_t              lineWidth) | ||||||
|  | { | ||||||
|  |    set_block_signals(true); | ||||||
|  | 
 | ||||||
|  |    p->borderColor_.StageValue(util::color::ToArgbString(borderColor)); | ||||||
|  |    p->highlightColor_.StageValue(util::color::ToArgbString(highlightColor)); | ||||||
|  |    p->lineColor_.StageValue(util::color::ToArgbString(lineColor)); | ||||||
|  |    p->borderWidth_.StageValue(borderWidth); | ||||||
|  |    p->highlightWidth_.StageValue(highlightWidth); | ||||||
|  |    p->lineWidth_.StageValue(lineWidth); | ||||||
|  | 
 | ||||||
|  |    set_block_signals(false); | ||||||
|  | 
 | ||||||
|  |    staged_signal()(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool operator==(const LineSettings& lhs, const LineSettings& rhs) | ||||||
|  | { | ||||||
|  |    return (lhs.p->borderColor_ == rhs.p->borderColor_ && | ||||||
|  |            lhs.p->highlightColor_ == rhs.p->highlightColor_ && | ||||||
|  |            lhs.p->lineColor_ == rhs.p->lineColor_ && | ||||||
|  |            lhs.p->borderWidth_ == rhs.p->borderWidth_ && | ||||||
|  |            lhs.p->highlightWidth_ == rhs.p->highlightWidth_ && | ||||||
|  |            lhs.p->lineWidth_ == rhs.p->lineWidth_); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace settings
 | ||||||
|  | } // namespace qt
 | ||||||
|  | } // namespace scwx
 | ||||||
							
								
								
									
										58
									
								
								scwx-qt/source/scwx/qt/settings/line_settings.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								scwx-qt/source/scwx/qt/settings/line_settings.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,58 @@ | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <scwx/qt/settings/settings_category.hpp> | ||||||
|  | #include <scwx/qt/settings/settings_variable.hpp> | ||||||
|  | 
 | ||||||
|  | #include <memory> | ||||||
|  | #include <string> | ||||||
|  | 
 | ||||||
|  | #include <boost/gil/typedefs.hpp> | ||||||
|  | 
 | ||||||
|  | namespace scwx | ||||||
|  | { | ||||||
|  | namespace qt | ||||||
|  | { | ||||||
|  | namespace settings | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | class LineSettings : public SettingsCategory | ||||||
|  | { | ||||||
|  | public: | ||||||
|  |    explicit LineSettings(const std::string& name); | ||||||
|  |    ~LineSettings(); | ||||||
|  | 
 | ||||||
|  |    LineSettings(const LineSettings&)            = delete; | ||||||
|  |    LineSettings& operator=(const LineSettings&) = delete; | ||||||
|  | 
 | ||||||
|  |    LineSettings(LineSettings&&) noexcept; | ||||||
|  |    LineSettings& operator=(LineSettings&&) noexcept; | ||||||
|  | 
 | ||||||
|  |    SettingsVariable<std::string>& border_color() const; | ||||||
|  |    SettingsVariable<std::string>& highlight_color() const; | ||||||
|  |    SettingsVariable<std::string>& line_color() const; | ||||||
|  | 
 | ||||||
|  |    SettingsVariable<std::int64_t>& border_width() const; | ||||||
|  |    SettingsVariable<std::int64_t>& highlight_width() const; | ||||||
|  |    SettingsVariable<std::int64_t>& line_width() const; | ||||||
|  | 
 | ||||||
|  |    boost::gil::rgba32f_pixel_t GetBorderColorRgba32f() const; | ||||||
|  |    boost::gil::rgba32f_pixel_t GetHighlightColorRgba32f() const; | ||||||
|  |    boost::gil::rgba32f_pixel_t GetLineColorRgba32f() const; | ||||||
|  | 
 | ||||||
|  |    void StageValues(boost::gil::rgba8_pixel_t borderColor, | ||||||
|  |                     boost::gil::rgba8_pixel_t highlightColor, | ||||||
|  |                     boost::gil::rgba8_pixel_t lineColor, | ||||||
|  |                     std::int64_t              borderWidth, | ||||||
|  |                     std::int64_t              highlightWidth, | ||||||
|  |                     std::int64_t              lineWidth); | ||||||
|  | 
 | ||||||
|  |    friend bool operator==(const LineSettings& lhs, const LineSettings& rhs); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |    class Impl; | ||||||
|  |    std::unique_ptr<Impl> p; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace settings
 | ||||||
|  | } // namespace qt
 | ||||||
|  | } // namespace scwx
 | ||||||
|  | @ -2,9 +2,9 @@ | ||||||
| #include <scwx/qt/settings/settings_variable.hpp> | #include <scwx/qt/settings/settings_variable.hpp> | ||||||
| #include <scwx/qt/util/color.hpp> | #include <scwx/qt/util/color.hpp> | ||||||
| 
 | 
 | ||||||
|  | #include <boost/algorithm/string/case_conv.hpp> | ||||||
| #include <boost/gil.hpp> | #include <boost/gil.hpp> | ||||||
| #include <fmt/format.h> | #include <fmt/format.h> | ||||||
| #include <re2/re2.h> |  | ||||||
| 
 | 
 | ||||||
| namespace scwx | namespace scwx | ||||||
| { | { | ||||||
|  | @ -76,8 +76,48 @@ static const awips::Phenomenon kDefaultPhenomenon_ {awips::Phenomenon::Marine}; | ||||||
| class PaletteSettings::Impl | class PaletteSettings::Impl | ||||||
| { | { | ||||||
| public: | public: | ||||||
|    explicit Impl() |    explicit Impl(PaletteSettings* self) : self_ {self} | ||||||
|    { |    { | ||||||
|  |       InitializeColorTables(); | ||||||
|  |       InitializeLegacyAlerts(); | ||||||
|  |       InitializeAlerts(); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    ~Impl() {} | ||||||
|  | 
 | ||||||
|  |    void InitializeColorTables(); | ||||||
|  |    void InitializeLegacyAlerts(); | ||||||
|  |    void InitializeAlerts(); | ||||||
|  | 
 | ||||||
|  |    PaletteSettings* self_; | ||||||
|  | 
 | ||||||
|  |    std::unordered_map<std::string, SettingsVariable<std::string>> palette_ {}; | ||||||
|  |    std::unordered_map<awips::Phenomenon, SettingsVariable<std::string>> | ||||||
|  |       activeAlertColor_ {}; | ||||||
|  |    std::unordered_map<awips::Phenomenon, SettingsVariable<std::string>> | ||||||
|  |                                       inactiveAlertColor_ {}; | ||||||
|  |    std::vector<SettingsVariableBase*> variables_ {}; | ||||||
|  | 
 | ||||||
|  |    std::unordered_map<awips::Phenomenon, AlertPaletteSettings> | ||||||
|  |       alertPaletteMap_ {}; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | PaletteSettings::PaletteSettings() : | ||||||
|  |     SettingsCategory("palette"), p(std::make_unique<Impl>(this)) | ||||||
|  | { | ||||||
|  |    RegisterVariables(p->variables_); | ||||||
|  |    SetDefaults(); | ||||||
|  | 
 | ||||||
|  |    p->variables_.clear(); | ||||||
|  | } | ||||||
|  | PaletteSettings::~PaletteSettings() = default; | ||||||
|  | 
 | ||||||
|  | PaletteSettings::PaletteSettings(PaletteSettings&&) noexcept = default; | ||||||
|  | PaletteSettings& | ||||||
|  | PaletteSettings::operator=(PaletteSettings&&) noexcept = default; | ||||||
|  | 
 | ||||||
|  | void PaletteSettings::Impl::InitializeColorTables() | ||||||
|  | { | ||||||
|    palette_.reserve(kPaletteKeys_.size()); |    palette_.reserve(kPaletteKeys_.size()); | ||||||
| 
 | 
 | ||||||
|    for (const auto& name : kPaletteKeys_) |    for (const auto& name : kPaletteKeys_) | ||||||
|  | @ -93,7 +133,10 @@ public: | ||||||
| 
 | 
 | ||||||
|       variables_.push_back(&settingsVariable); |       variables_.push_back(&settingsVariable); | ||||||
|    }; |    }; | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
|  | void PaletteSettings::Impl::InitializeLegacyAlerts() | ||||||
|  | { | ||||||
|    activeAlertColor_.reserve(kAlertColors_.size()); |    activeAlertColor_.reserve(kAlertColors_.size()); | ||||||
|    inactiveAlertColor_.reserve(kAlertColors_.size()); |    inactiveAlertColor_.reserve(kAlertColors_.size()); | ||||||
| 
 | 
 | ||||||
|  | @ -113,51 +156,35 @@ public: | ||||||
|       SettingsVariable<std::string>& inactiveVariable = |       SettingsVariable<std::string>& inactiveVariable = | ||||||
|          inactiveResult.first->second; |          inactiveResult.first->second; | ||||||
| 
 | 
 | ||||||
|          activeVariable.SetDefault( |       activeVariable.SetDefault(util::color::ToArgbString(alert.second.first)); | ||||||
|             util::color::ToArgbString(alert.second.first)); |  | ||||||
|       inactiveVariable.SetDefault( |       inactiveVariable.SetDefault( | ||||||
|          util::color::ToArgbString(alert.second.second)); |          util::color::ToArgbString(alert.second.second)); | ||||||
| 
 | 
 | ||||||
|          activeVariable.SetValidator(&ValidateColor); |       activeVariable.SetValidator(&util::color::ValidateArgbString); | ||||||
|          inactiveVariable.SetValidator(&ValidateColor); |       inactiveVariable.SetValidator(&util::color::ValidateArgbString); | ||||||
| 
 | 
 | ||||||
|       variables_.push_back(&activeVariable); |       variables_.push_back(&activeVariable); | ||||||
|       variables_.push_back(&inactiveVariable); |       variables_.push_back(&inactiveVariable); | ||||||
|    } |    } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void PaletteSettings::Impl::InitializeAlerts() | ||||||
|  | { | ||||||
|  |    std::vector<SettingsCategory*> alertSettings {}; | ||||||
|  | 
 | ||||||
|  |    for (auto phenomenon : PaletteSettings::alert_phenomena()) | ||||||
|  |    { | ||||||
|  |       auto  result = alertPaletteMap_.emplace(phenomenon, phenomenon); | ||||||
|  |       auto& it     = result.first; | ||||||
|  |       AlertPaletteSettings& alertPaletteSettings = it->second; | ||||||
|  | 
 | ||||||
|  |       // Variable registration
 | ||||||
|  |       alertSettings.push_back(&alertPaletteSettings); | ||||||
|    } |    } | ||||||
| 
 | 
 | ||||||
|    ~Impl() {} |    self_->RegisterSubcategoryArray("alerts", alertSettings); | ||||||
| 
 |  | ||||||
|    static bool ValidateColor(const std::string& value); |  | ||||||
| 
 |  | ||||||
|    std::unordered_map<std::string, SettingsVariable<std::string>> palette_ {}; |  | ||||||
|    std::unordered_map<awips::Phenomenon, SettingsVariable<std::string>> |  | ||||||
|       activeAlertColor_ {}; |  | ||||||
|    std::unordered_map<awips::Phenomenon, SettingsVariable<std::string>> |  | ||||||
|                                       inactiveAlertColor_ {}; |  | ||||||
|    std::vector<SettingsVariableBase*> variables_ {}; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| bool PaletteSettings::Impl::ValidateColor(const std::string& value) |  | ||||||
| { |  | ||||||
|    static constexpr LazyRE2 re = {"#[0-9A-Fa-f]{8}"}; |  | ||||||
|    return RE2::FullMatch(value, *re); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| PaletteSettings::PaletteSettings() : |  | ||||||
|     SettingsCategory("palette"), p(std::make_unique<Impl>()) |  | ||||||
| { |  | ||||||
|    RegisterVariables(p->variables_); |  | ||||||
|    SetDefaults(); |  | ||||||
| 
 |  | ||||||
|    p->variables_.clear(); |  | ||||||
| } |  | ||||||
| PaletteSettings::~PaletteSettings() = default; |  | ||||||
| 
 |  | ||||||
| PaletteSettings::PaletteSettings(PaletteSettings&&) noexcept = default; |  | ||||||
| PaletteSettings& |  | ||||||
| PaletteSettings::operator=(PaletteSettings&&) noexcept = default; |  | ||||||
| 
 |  | ||||||
| SettingsVariable<std::string>& | SettingsVariable<std::string>& | ||||||
| PaletteSettings::palette(const std::string& name) const | PaletteSettings::palette(const std::string& name) const | ||||||
| { | { | ||||||
|  | @ -194,6 +221,12 @@ PaletteSettings::alert_color(awips::Phenomenon phenomenon, bool active) const | ||||||
|    } |    } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | AlertPaletteSettings& | ||||||
|  | PaletteSettings::alert_palette(awips::Phenomenon phenomenon) | ||||||
|  | { | ||||||
|  |    return p->alertPaletteMap_.at(phenomenon); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| const std::vector<awips::Phenomenon>& PaletteSettings::alert_phenomena() | const std::vector<awips::Phenomenon>& PaletteSettings::alert_phenomena() | ||||||
| { | { | ||||||
|    static const std::vector<awips::Phenomenon> kAlertPhenomena_ { |    static const std::vector<awips::Phenomenon> kAlertPhenomena_ { | ||||||
|  |  | ||||||
|  | @ -1,7 +1,9 @@ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
|  | #include <scwx/qt/settings/alert_palette_settings.hpp> | ||||||
| #include <scwx/qt/settings/settings_category.hpp> | #include <scwx/qt/settings/settings_category.hpp> | ||||||
| #include <scwx/qt/settings/settings_variable.hpp> | #include <scwx/qt/settings/settings_variable.hpp> | ||||||
|  | #include <scwx/awips/impact_based_warnings.hpp> | ||||||
| #include <scwx/awips/phenomenon.hpp> | #include <scwx/awips/phenomenon.hpp> | ||||||
| 
 | 
 | ||||||
| #include <memory> | #include <memory> | ||||||
|  | @ -29,6 +31,7 @@ public: | ||||||
|    SettingsVariable<std::string>& palette(const std::string& name) const; |    SettingsVariable<std::string>& palette(const std::string& name) const; | ||||||
|    SettingsVariable<std::string>& alert_color(awips::Phenomenon phenomenon, |    SettingsVariable<std::string>& alert_color(awips::Phenomenon phenomenon, | ||||||
|                                               bool              active) const; |                                               bool              active) const; | ||||||
|  |    AlertPaletteSettings&          alert_palette(awips::Phenomenon); | ||||||
| 
 | 
 | ||||||
|    static const std::vector<awips::Phenomenon>& alert_phenomena(); |    static const std::vector<awips::Phenomenon>& alert_phenomena(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -21,11 +21,21 @@ public: | ||||||
| 
 | 
 | ||||||
|    ~Impl() {} |    ~Impl() {} | ||||||
| 
 | 
 | ||||||
|  |    void ConnectSubcategory(SettingsCategory& category); | ||||||
|  |    void ConnectVariable(SettingsVariableBase* variable); | ||||||
|  | 
 | ||||||
|    const std::string name_; |    const std::string name_; | ||||||
| 
 | 
 | ||||||
|    std::vector<std::pair<std::string, std::vector<SettingsCategory*>>> |    std::vector<std::pair<std::string, std::vector<SettingsCategory*>>> | ||||||
|                                       subcategoryArrays_; |                                       subcategoryArrays_; | ||||||
|  |    std::vector<SettingsCategory*>     subcategories_; | ||||||
|    std::vector<SettingsVariableBase*> variables_; |    std::vector<SettingsVariableBase*> variables_; | ||||||
|  | 
 | ||||||
|  |    boost::signals2::signal<void()> changedSignal_ {}; | ||||||
|  |    boost::signals2::signal<void()> stagedSignal_ {}; | ||||||
|  |    bool                            blockSignals_ {false}; | ||||||
|  | 
 | ||||||
|  |    std::vector<boost::signals2::scoped_connection> connections_ {}; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| SettingsCategory::SettingsCategory(const std::string& name) : | SettingsCategory::SettingsCategory(const std::string& name) : | ||||||
|  | @ -38,13 +48,88 @@ SettingsCategory::SettingsCategory(SettingsCategory&&) noexcept = default; | ||||||
| SettingsCategory& | SettingsCategory& | ||||||
| SettingsCategory::operator=(SettingsCategory&&) noexcept = default; | SettingsCategory::operator=(SettingsCategory&&) noexcept = default; | ||||||
| 
 | 
 | ||||||
|  | bool SettingsCategory::IsDefault() const | ||||||
|  | { | ||||||
|  |    bool isDefault = true; | ||||||
|  | 
 | ||||||
|  |    // Get subcategory array defaults
 | ||||||
|  |    for (auto& subcategoryArray : p->subcategoryArrays_) | ||||||
|  |    { | ||||||
|  |       for (auto& subcategory : subcategoryArray.second) | ||||||
|  |       { | ||||||
|  |          isDefault = isDefault && subcategory->IsDefault(); | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    // Get subcategory defaults
 | ||||||
|  |    for (auto& subcategory : p->subcategories_) | ||||||
|  |    { | ||||||
|  |       isDefault = isDefault && subcategory->IsDefault(); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    // Get variable defaults
 | ||||||
|  |    for (auto& variable : p->variables_) | ||||||
|  |    { | ||||||
|  |       isDefault = isDefault && variable->IsDefault(); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    return isDefault; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool SettingsCategory::IsDefaultStaged() const | ||||||
|  | { | ||||||
|  |    bool isDefaultStaged = true; | ||||||
|  | 
 | ||||||
|  |    // Get subcategory array defaults
 | ||||||
|  |    for (auto& subcategoryArray : p->subcategoryArrays_) | ||||||
|  |    { | ||||||
|  |       for (auto& subcategory : subcategoryArray.second) | ||||||
|  |       { | ||||||
|  |          isDefaultStaged = isDefaultStaged && subcategory->IsDefaultStaged(); | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    // Get subcategory defaults
 | ||||||
|  |    for (auto& subcategory : p->subcategories_) | ||||||
|  |    { | ||||||
|  |       isDefaultStaged = isDefaultStaged && subcategory->IsDefaultStaged(); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    // Get variable defaults
 | ||||||
|  |    for (auto& variable : p->variables_) | ||||||
|  |    { | ||||||
|  |       isDefaultStaged = isDefaultStaged && variable->IsDefaultStaged(); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    return isDefaultStaged; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| std::string SettingsCategory::name() const | std::string SettingsCategory::name() const | ||||||
| { | { | ||||||
|    return p->name_; |    return p->name_; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | boost::signals2::signal<void()>& SettingsCategory::changed_signal() | ||||||
|  | { | ||||||
|  |    return p->changedSignal_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | boost::signals2::signal<void()>& SettingsCategory::staged_signal() | ||||||
|  | { | ||||||
|  |    return p->stagedSignal_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void SettingsCategory::set_block_signals(bool blockSignals) | ||||||
|  | { | ||||||
|  |    p->blockSignals_ = blockSignals; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void SettingsCategory::SetDefaults() | void SettingsCategory::SetDefaults() | ||||||
| { | { | ||||||
|  |    // Don't allow individual variables to invoke the signal when operating over
 | ||||||
|  |    // the entire category
 | ||||||
|  |    p->blockSignals_ = true; | ||||||
|  | 
 | ||||||
|    // Set subcategory array defaults
 |    // Set subcategory array defaults
 | ||||||
|    for (auto& subcategoryArray : p->subcategoryArrays_) |    for (auto& subcategoryArray : p->subcategoryArrays_) | ||||||
|    { |    { | ||||||
|  | @ -54,11 +139,129 @@ void SettingsCategory::SetDefaults() | ||||||
|       } |       } | ||||||
|    } |    } | ||||||
| 
 | 
 | ||||||
|  |    // Set subcategory defaults
 | ||||||
|  |    for (auto& subcategory : p->subcategories_) | ||||||
|  |    { | ||||||
|  |       subcategory->SetDefaults(); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|    // Set variable defaults
 |    // Set variable defaults
 | ||||||
|    for (auto& variable : p->variables_) |    for (auto& variable : p->variables_) | ||||||
|    { |    { | ||||||
|       variable->SetValueToDefault(); |       variable->SetValueToDefault(); | ||||||
|    } |    } | ||||||
|  | 
 | ||||||
|  |    // Unblock signals
 | ||||||
|  |    p->blockSignals_ = false; | ||||||
|  | 
 | ||||||
|  |    p->changedSignal_(); | ||||||
|  |    p->stagedSignal_(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void SettingsCategory::StageDefaults() | ||||||
|  | { | ||||||
|  |    // Don't allow individual variables to invoke the signal when operating over
 | ||||||
|  |    // the entire category
 | ||||||
|  |    p->blockSignals_ = true; | ||||||
|  | 
 | ||||||
|  |    // Stage subcategory array defaults
 | ||||||
|  |    for (auto& subcategoryArray : p->subcategoryArrays_) | ||||||
|  |    { | ||||||
|  |       for (auto& subcategory : subcategoryArray.second) | ||||||
|  |       { | ||||||
|  |          subcategory->StageDefaults(); | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    // Stage subcategory defaults
 | ||||||
|  |    for (auto& subcategory : p->subcategories_) | ||||||
|  |    { | ||||||
|  |       subcategory->StageDefaults(); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    // Stage variable defaults
 | ||||||
|  |    for (auto& variable : p->variables_) | ||||||
|  |    { | ||||||
|  |       variable->StageDefault(); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    // Unblock signals
 | ||||||
|  |    p->blockSignals_ = false; | ||||||
|  | 
 | ||||||
|  |    p->stagedSignal_(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool SettingsCategory::Commit() | ||||||
|  | { | ||||||
|  |    bool committed = false; | ||||||
|  | 
 | ||||||
|  |    // Don't allow individual variables to invoke the signal when operating over
 | ||||||
|  |    // the entire category
 | ||||||
|  |    p->blockSignals_ = true; | ||||||
|  | 
 | ||||||
|  |    // Commit subcategory arrays
 | ||||||
|  |    for (auto& subcategoryArray : p->subcategoryArrays_) | ||||||
|  |    { | ||||||
|  |       for (auto& subcategory : subcategoryArray.second) | ||||||
|  |       { | ||||||
|  |          committed |= subcategory->Commit(); | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    // Commit subcategories
 | ||||||
|  |    for (auto& subcategory : p->subcategories_) | ||||||
|  |    { | ||||||
|  |       committed |= subcategory->Commit(); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    // Commit variables
 | ||||||
|  |    for (auto& variable : p->variables_) | ||||||
|  |    { | ||||||
|  |       committed |= variable->Commit(); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    // Unblock signals
 | ||||||
|  |    p->blockSignals_ = false; | ||||||
|  | 
 | ||||||
|  |    if (committed) | ||||||
|  |    { | ||||||
|  |       p->changedSignal_(); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    return committed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void SettingsCategory::Reset() | ||||||
|  | { | ||||||
|  |    // Don't allow individual variables to invoke the signal when operating over
 | ||||||
|  |    // the entire category
 | ||||||
|  |    p->blockSignals_ = true; | ||||||
|  | 
 | ||||||
|  |    // Reset subcategory arrays
 | ||||||
|  |    for (auto& subcategoryArray : p->subcategoryArrays_) | ||||||
|  |    { | ||||||
|  |       for (auto& subcategory : subcategoryArray.second) | ||||||
|  |       { | ||||||
|  |          subcategory->Reset(); | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    // Reset subcategories
 | ||||||
|  |    for (auto& subcategory : p->subcategories_) | ||||||
|  |    { | ||||||
|  |       subcategory->Reset(); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    // Reset variables
 | ||||||
|  |    for (auto& variable : p->variables_) | ||||||
|  |    { | ||||||
|  |       variable->Reset(); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    // Unblock signals
 | ||||||
|  |    p->blockSignals_ = false; | ||||||
|  | 
 | ||||||
|  |    p->stagedSignal_(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool SettingsCategory::ReadJson(const boost::json::object& json) | bool SettingsCategory::ReadJson(const boost::json::object& json) | ||||||
|  | @ -111,6 +314,12 @@ bool SettingsCategory::ReadJson(const boost::json::object& json) | ||||||
|          } |          } | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|  |       // Read subcategories
 | ||||||
|  |       for (auto& subcategory : p->subcategories_) | ||||||
|  |       { | ||||||
|  |          validated &= subcategory->ReadJson(object); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|       // Read variables
 |       // Read variables
 | ||||||
|       for (auto& variable : p->variables_) |       for (auto& variable : p->variables_) | ||||||
|       { |       { | ||||||
|  | @ -154,6 +363,12 @@ void SettingsCategory::WriteJson(boost::json::object& json) const | ||||||
|       object.insert_or_assign(subcategoryArray.first, arrayObject); |       object.insert_or_assign(subcategoryArray.first, arrayObject); | ||||||
|    } |    } | ||||||
| 
 | 
 | ||||||
|  |    // Write subcategories
 | ||||||
|  |    for (auto& subcategory : p->subcategories_) | ||||||
|  |    { | ||||||
|  |       subcategory->WriteJson(object); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|    // Write variables
 |    // Write variables
 | ||||||
|    for (auto& variable : p->variables_) |    for (auto& variable : p->variables_) | ||||||
|    { |    { | ||||||
|  | @ -163,6 +378,12 @@ void SettingsCategory::WriteJson(boost::json::object& json) const | ||||||
|    json.insert_or_assign(p->name_, object); |    json.insert_or_assign(p->name_, object); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void SettingsCategory::RegisterSubcategory(SettingsCategory& subcategory) | ||||||
|  | { | ||||||
|  |    p->ConnectSubcategory(subcategory); | ||||||
|  |    p->subcategories_.push_back(&subcategory); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void SettingsCategory::RegisterSubcategoryArray( | void SettingsCategory::RegisterSubcategoryArray( | ||||||
|    const std::string& name, std::vector<SettingsCategory>& subcategories) |    const std::string& name, std::vector<SettingsCategory>& subcategories) | ||||||
| { | { | ||||||
|  | @ -172,22 +393,92 @@ void SettingsCategory::RegisterSubcategoryArray( | ||||||
|    std::transform(subcategories.begin(), |    std::transform(subcategories.begin(), | ||||||
|                   subcategories.end(), |                   subcategories.end(), | ||||||
|                   std::back_inserter(newSubcategories.second), |                   std::back_inserter(newSubcategories.second), | ||||||
|                   [](SettingsCategory& subcategory) { return &subcategory; }); |                   [this](SettingsCategory& subcategory) | ||||||
|  |                   { | ||||||
|  |                      p->ConnectSubcategory(subcategory); | ||||||
|  |                      return &subcategory; | ||||||
|  |                   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void SettingsCategory::RegisterSubcategoryArray( | ||||||
|  |    const std::string& name, std::vector<SettingsCategory*>& subcategories) | ||||||
|  | { | ||||||
|  |    auto& newSubcategories = p->subcategoryArrays_.emplace_back( | ||||||
|  |       name, std::vector<SettingsCategory*> {}); | ||||||
|  | 
 | ||||||
|  |    std::transform(subcategories.begin(), | ||||||
|  |                   subcategories.end(), | ||||||
|  |                   std::back_inserter(newSubcategories.second), | ||||||
|  |                   [this](SettingsCategory* subcategory) | ||||||
|  |                   { | ||||||
|  |                      p->ConnectSubcategory(*subcategory); | ||||||
|  |                      return subcategory; | ||||||
|  |                   }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void SettingsCategory::RegisterVariables( | void SettingsCategory::RegisterVariables( | ||||||
|    std::initializer_list<SettingsVariableBase*> variables) |    std::initializer_list<SettingsVariableBase*> variables) | ||||||
| { | { | ||||||
|  |    for (auto& variable : variables) | ||||||
|  |    { | ||||||
|  |       p->ConnectVariable(variable); | ||||||
|  |    } | ||||||
|    p->variables_.insert(p->variables_.end(), variables); |    p->variables_.insert(p->variables_.end(), variables); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void SettingsCategory::RegisterVariables( | void SettingsCategory::RegisterVariables( | ||||||
|    std::vector<SettingsVariableBase*> variables) |    std::vector<SettingsVariableBase*> variables) | ||||||
| { | { | ||||||
|  |    for (auto& variable : variables) | ||||||
|  |    { | ||||||
|  |       p->ConnectVariable(variable); | ||||||
|  |    } | ||||||
|    p->variables_.insert( |    p->variables_.insert( | ||||||
|       p->variables_.end(), variables.cbegin(), variables.cend()); |       p->variables_.end(), variables.cbegin(), variables.cend()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void SettingsCategory::Impl::ConnectSubcategory(SettingsCategory& category) | ||||||
|  | { | ||||||
|  |    connections_.emplace_back(category.changed_signal().connect( | ||||||
|  |       [this]() | ||||||
|  |       { | ||||||
|  |          if (!blockSignals_) | ||||||
|  |          { | ||||||
|  |             changedSignal_(); | ||||||
|  |          } | ||||||
|  |       })); | ||||||
|  | 
 | ||||||
|  |    connections_.emplace_back(category.staged_signal().connect( | ||||||
|  |       [this]() | ||||||
|  |       { | ||||||
|  |          if (!blockSignals_) | ||||||
|  |          { | ||||||
|  |             stagedSignal_(); | ||||||
|  |          } | ||||||
|  |       })); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void SettingsCategory::Impl::ConnectVariable(SettingsVariableBase* variable) | ||||||
|  | { | ||||||
|  |    connections_.emplace_back(variable->changed_signal().connect( | ||||||
|  |       [this]() | ||||||
|  |       { | ||||||
|  |          if (!blockSignals_) | ||||||
|  |          { | ||||||
|  |             changedSignal_(); | ||||||
|  |          } | ||||||
|  |       })); | ||||||
|  | 
 | ||||||
|  |    connections_.emplace_back(variable->staged_signal().connect( | ||||||
|  |       [this]() | ||||||
|  |       { | ||||||
|  |          if (!blockSignals_) | ||||||
|  |          { | ||||||
|  |             stagedSignal_(); | ||||||
|  |          } | ||||||
|  |       })); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } // namespace settings
 | } // namespace settings
 | ||||||
| } // namespace qt
 | } // namespace qt
 | ||||||
| } // namespace scwx
 | } // namespace scwx
 | ||||||
|  |  | ||||||
|  | @ -6,6 +6,7 @@ | ||||||
| #include <string> | #include <string> | ||||||
| 
 | 
 | ||||||
| #include <boost/json/object.hpp> | #include <boost/json/object.hpp> | ||||||
|  | #include <boost/signals2/signal.hpp> | ||||||
| 
 | 
 | ||||||
| namespace scwx | namespace scwx | ||||||
| { | { | ||||||
|  | @ -28,11 +29,61 @@ public: | ||||||
| 
 | 
 | ||||||
|    std::string name() const; |    std::string name() const; | ||||||
| 
 | 
 | ||||||
|  |    /**
 | ||||||
|  |     * Gets the signal invoked when a variable within the category is changed. | ||||||
|  |     * | ||||||
|  |     * @return Changed signal | ||||||
|  |     */ | ||||||
|  |    boost::signals2::signal<void()>& changed_signal(); | ||||||
|  | 
 | ||||||
|  |    /**
 | ||||||
|  |     * Gets the signal invoked when a variable within the category is staged. | ||||||
|  |     * | ||||||
|  |     * @return Staged signal | ||||||
|  |     */ | ||||||
|  |    boost::signals2::signal<void()>& staged_signal(); | ||||||
|  | 
 | ||||||
|  |    /**
 | ||||||
|  |     * Gets whether or not all settings variables are currently set to default | ||||||
|  |     * values. | ||||||
|  |     * | ||||||
|  |     * @return true if all settings variables are currently set to default | ||||||
|  |     * values, otherwise false. | ||||||
|  |     */ | ||||||
|  |    bool IsDefault() const; | ||||||
|  | 
 | ||||||
|  |    /**
 | ||||||
|  |     * Gets whether or not all settings variables currently have staged values | ||||||
|  |     * set to default. | ||||||
|  |     * | ||||||
|  |     * @return true if all settings variables currently have staged values set | ||||||
|  |     * to default, otherwise false. | ||||||
|  |     */ | ||||||
|  |    bool IsDefaultStaged() const; | ||||||
|  | 
 | ||||||
|    /**
 |    /**
 | ||||||
|     * Set all variables to their defaults. |     * Set all variables to their defaults. | ||||||
|     */ |     */ | ||||||
|    void SetDefaults(); |    void SetDefaults(); | ||||||
| 
 | 
 | ||||||
|  |    /**
 | ||||||
|  |     * Stage all variables to their defaults. | ||||||
|  |     */ | ||||||
|  |    void StageDefaults(); | ||||||
|  | 
 | ||||||
|  |    /**
 | ||||||
|  |     * Sets the current value of all variables to the staged value. | ||||||
|  |     * | ||||||
|  |     * @return true if any staged value was committed, false if no staged values | ||||||
|  |     * are present. | ||||||
|  |     */ | ||||||
|  |    bool Commit(); | ||||||
|  | 
 | ||||||
|  |    /**
 | ||||||
|  |     * Clears the staged value of all variables. | ||||||
|  |     */ | ||||||
|  |    void Reset(); | ||||||
|  | 
 | ||||||
|    /**
 |    /**
 | ||||||
|     * Reads the variables from the JSON object. |     * Reads the variables from the JSON object. | ||||||
|     * |     * | ||||||
|  | @ -50,12 +101,18 @@ public: | ||||||
|     */ |     */ | ||||||
|    virtual void WriteJson(boost::json::object& json) const; |    virtual void WriteJson(boost::json::object& json) const; | ||||||
| 
 | 
 | ||||||
|  |    void RegisterSubcategory(SettingsCategory& subcategory); | ||||||
|    void RegisterSubcategoryArray(const std::string&             name, |    void RegisterSubcategoryArray(const std::string&             name, | ||||||
|                                  std::vector<SettingsCategory>& subcategories); |                                  std::vector<SettingsCategory>& subcategories); | ||||||
|  |    void RegisterSubcategoryArray(const std::string&              name, | ||||||
|  |                                  std::vector<SettingsCategory*>& subcategories); | ||||||
|    void |    void | ||||||
|    RegisterVariables(std::initializer_list<SettingsVariableBase*> variables); |    RegisterVariables(std::initializer_list<SettingsVariableBase*> variables); | ||||||
|    void RegisterVariables(std::vector<SettingsVariableBase*> variables); |    void RegisterVariables(std::vector<SettingsVariableBase*> variables); | ||||||
| 
 | 
 | ||||||
|  | protected: | ||||||
|  |    void set_block_signals(bool blockSignals); | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
|    class Impl; |    class Impl; | ||||||
|    std::unique_ptr<Impl> p; |    std::unique_ptr<Impl> p; | ||||||
|  |  | ||||||
|  | @ -65,6 +65,18 @@ inline auto FormatParameter(const T& value) | ||||||
|    } |    } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | template<class T> | ||||||
|  | bool SettingsVariable<T>::IsDefault() const | ||||||
|  | { | ||||||
|  |    return p->value_ == p->default_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | template<class T> | ||||||
|  | bool SettingsVariable<T>::IsDefaultStaged() const | ||||||
|  | { | ||||||
|  |    return p->staged_.value_or(p->value_) == p->default_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| template<class T> | template<class T> | ||||||
| T SettingsVariable<T>::GetValue() const | T SettingsVariable<T>::GetValue() const | ||||||
| { | { | ||||||
|  | @ -81,10 +93,13 @@ bool SettingsVariable<T>::SetValue(const T& value) | ||||||
|       p->value_ = (p->transform_ != nullptr) ? p->transform_(value) : value; |       p->value_ = (p->transform_ != nullptr) ? p->transform_(value) : value; | ||||||
|       validated = true; |       validated = true; | ||||||
| 
 | 
 | ||||||
|  |       changed_signal()(); | ||||||
|       for (auto& callback : p->valueChangedCallbackFunctions_) |       for (auto& callback : p->valueChangedCallbackFunctions_) | ||||||
|       { |       { | ||||||
|          callback.second(p->value_); |          callback.second(p->value_); | ||||||
|       } |       } | ||||||
|  | 
 | ||||||
|  |       staged_signal()(); | ||||||
|       for (auto& callback : p->valueStagedCallbackFunctions_) |       for (auto& callback : p->valueStagedCallbackFunctions_) | ||||||
|       { |       { | ||||||
|          callback.second(p->value_); |          callback.second(p->value_); | ||||||
|  | @ -129,10 +144,13 @@ bool SettingsVariable<T>::SetValueOrDefault(const T& value) | ||||||
|       p->value_ = p->default_; |       p->value_ = p->default_; | ||||||
|    } |    } | ||||||
| 
 | 
 | ||||||
|  |    changed_signal()(); | ||||||
|    for (auto& callback : p->valueChangedCallbackFunctions_) |    for (auto& callback : p->valueChangedCallbackFunctions_) | ||||||
|    { |    { | ||||||
|       callback.second(p->value_); |       callback.second(p->value_); | ||||||
|    } |    } | ||||||
|  | 
 | ||||||
|  |    staged_signal()(); | ||||||
|    for (auto& callback : p->valueStagedCallbackFunctions_) |    for (auto& callback : p->valueStagedCallbackFunctions_) | ||||||
|    { |    { | ||||||
|       callback.second(p->value_); |       callback.second(p->value_); | ||||||
|  | @ -146,10 +164,13 @@ void SettingsVariable<T>::SetValueToDefault() | ||||||
| { | { | ||||||
|    p->value_ = p->default_; |    p->value_ = p->default_; | ||||||
| 
 | 
 | ||||||
|  |    changed_signal()(); | ||||||
|    for (auto& callback : p->valueChangedCallbackFunctions_) |    for (auto& callback : p->valueChangedCallbackFunctions_) | ||||||
|    { |    { | ||||||
|       callback.second(p->value_); |       callback.second(p->value_); | ||||||
|    } |    } | ||||||
|  | 
 | ||||||
|  |    staged_signal()(); | ||||||
|    for (auto& callback : p->valueStagedCallbackFunctions_) |    for (auto& callback : p->valueStagedCallbackFunctions_) | ||||||
|    { |    { | ||||||
|       callback.second(p->value_); |       callback.second(p->value_); | ||||||
|  | @ -168,6 +189,7 @@ void SettingsVariable<T>::StageDefault() | ||||||
|       p->staged_.reset(); |       p->staged_.reset(); | ||||||
|    } |    } | ||||||
| 
 | 
 | ||||||
|  |    staged_signal()(); | ||||||
|    for (auto& callback : p->valueStagedCallbackFunctions_) |    for (auto& callback : p->valueStagedCallbackFunctions_) | ||||||
|    { |    { | ||||||
|       callback.second(p->default_); |       callback.second(p->default_); | ||||||
|  | @ -194,6 +216,7 @@ bool SettingsVariable<T>::StageValue(const T& value) | ||||||
| 
 | 
 | ||||||
|       validated = true; |       validated = true; | ||||||
| 
 | 
 | ||||||
|  |       staged_signal()(); | ||||||
|       for (auto& callback : p->valueStagedCallbackFunctions_) |       for (auto& callback : p->valueStagedCallbackFunctions_) | ||||||
|       { |       { | ||||||
|          callback.second(transformed); |          callback.second(transformed); | ||||||
|  | @ -214,10 +237,13 @@ bool SettingsVariable<T>::Commit() | ||||||
|       p->staged_.reset(); |       p->staged_.reset(); | ||||||
|       committed = true; |       committed = true; | ||||||
| 
 | 
 | ||||||
|  |       changed_signal()(); | ||||||
|       for (auto& callback : p->valueChangedCallbackFunctions_) |       for (auto& callback : p->valueChangedCallbackFunctions_) | ||||||
|       { |       { | ||||||
|          callback.second(p->value_); |          callback.second(p->value_); | ||||||
|       } |       } | ||||||
|  | 
 | ||||||
|  |       staged_signal()(); | ||||||
|       for (auto& callback : p->valueStagedCallbackFunctions_) |       for (auto& callback : p->valueStagedCallbackFunctions_) | ||||||
|       { |       { | ||||||
|          callback.second(p->value_); |          callback.second(p->value_); | ||||||
|  | @ -232,6 +258,7 @@ void SettingsVariable<T>::Reset() | ||||||
| { | { | ||||||
|    p->staged_.reset(); |    p->staged_.reset(); | ||||||
| 
 | 
 | ||||||
|  |    staged_signal()(); | ||||||
|    for (auto& callback : p->valueStagedCallbackFunctions_) |    for (auto& callback : p->valueStagedCallbackFunctions_) | ||||||
|    { |    { | ||||||
|       callback.second(p->value_); |       callback.second(p->value_); | ||||||
|  | @ -336,10 +363,13 @@ bool SettingsVariable<T>::ReadValue(const boost::json::object& json) | ||||||
|       p->value_ = p->default_; |       p->value_ = p->default_; | ||||||
|    } |    } | ||||||
| 
 | 
 | ||||||
|  |    changed_signal()(); | ||||||
|    for (auto& callback : p->valueChangedCallbackFunctions_) |    for (auto& callback : p->valueChangedCallbackFunctions_) | ||||||
|    { |    { | ||||||
|       callback.second(p->value_); |       callback.second(p->value_); | ||||||
|    } |    } | ||||||
|  | 
 | ||||||
|  |    staged_signal()(); | ||||||
|    for (auto& callback : p->valueStagedCallbackFunctions_) |    for (auto& callback : p->valueStagedCallbackFunctions_) | ||||||
|    { |    { | ||||||
|       callback.second(p->value_); |       callback.second(p->value_); | ||||||
|  |  | ||||||
|  | @ -29,7 +29,7 @@ public: | ||||||
|    typedef std::function<void(const T& value)> ValueCallbackFunction; |    typedef std::function<void(const T& value)> ValueCallbackFunction; | ||||||
| 
 | 
 | ||||||
|    explicit SettingsVariable(const std::string& name); |    explicit SettingsVariable(const std::string& name); | ||||||
|    ~SettingsVariable(); |    virtual ~SettingsVariable(); | ||||||
| 
 | 
 | ||||||
|    SettingsVariable(const SettingsVariable&)            = delete; |    SettingsVariable(const SettingsVariable&)            = delete; | ||||||
|    SettingsVariable& operator=(const SettingsVariable&) = delete; |    SettingsVariable& operator=(const SettingsVariable&) = delete; | ||||||
|  | @ -37,6 +37,24 @@ public: | ||||||
|    SettingsVariable(SettingsVariable&&) noexcept; |    SettingsVariable(SettingsVariable&&) noexcept; | ||||||
|    SettingsVariable& operator=(SettingsVariable&&) noexcept; |    SettingsVariable& operator=(SettingsVariable&&) noexcept; | ||||||
| 
 | 
 | ||||||
|  |    /**
 | ||||||
|  |     * Gets whether or not the settings variable is currently set to its default | ||||||
|  |     * value. | ||||||
|  |     * | ||||||
|  |     * @return true if the settings variable is currently set to its default | ||||||
|  |     * value, otherwise false. | ||||||
|  |     */ | ||||||
|  |    bool IsDefault() const override; | ||||||
|  | 
 | ||||||
|  |    /**
 | ||||||
|  |     * Gets whether or not the settings variable currently has its staged value | ||||||
|  |     * set to default. | ||||||
|  |     * | ||||||
|  |     * @return true if the settings variable currently has its staged value set | ||||||
|  |     * to default, otherwise false. | ||||||
|  |     */ | ||||||
|  |    bool IsDefaultStaged() const override; | ||||||
|  | 
 | ||||||
|    /**
 |    /**
 | ||||||
|     * Gets the current value of the settings variable. |     * Gets the current value of the settings variable. | ||||||
|     * |     * | ||||||
|  | @ -96,7 +114,7 @@ public: | ||||||
|    /**
 |    /**
 | ||||||
|     * Clears the staged value of the settings variable. |     * Clears the staged value of the settings variable. | ||||||
|     */ |     */ | ||||||
|    void Reset(); |    void Reset() override; | ||||||
| 
 | 
 | ||||||
|    /**
 |    /**
 | ||||||
|     * Gets the staged value of the settings variable, if defined. |     * Gets the staged value of the settings variable, if defined. | ||||||
|  |  | ||||||
|  | @ -18,6 +18,9 @@ public: | ||||||
|    ~Impl() {} |    ~Impl() {} | ||||||
| 
 | 
 | ||||||
|    const std::string name_; |    const std::string name_; | ||||||
|  | 
 | ||||||
|  |    boost::signals2::signal<void()> changedSignal_ {}; | ||||||
|  |    boost::signals2::signal<void()> stagedSignal_ {}; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| SettingsVariableBase::SettingsVariableBase(const std::string& name) : | SettingsVariableBase::SettingsVariableBase(const std::string& name) : | ||||||
|  | @ -38,6 +41,16 @@ std::string SettingsVariableBase::name() const | ||||||
|    return p->name_; |    return p->name_; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | boost::signals2::signal<void()>& SettingsVariableBase::changed_signal() | ||||||
|  | { | ||||||
|  |    return p->changedSignal_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | boost::signals2::signal<void()>& SettingsVariableBase::staged_signal() | ||||||
|  | { | ||||||
|  |    return p->stagedSignal_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bool SettingsVariableBase::Equals(const SettingsVariableBase& o) const | bool SettingsVariableBase::Equals(const SettingsVariableBase& o) const | ||||||
| { | { | ||||||
|    return p->name_ == o.p->name_; |    return p->name_ == o.p->name_; | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ | ||||||
| #include <string> | #include <string> | ||||||
| 
 | 
 | ||||||
| #include <boost/json/object.hpp> | #include <boost/json/object.hpp> | ||||||
|  | #include <boost/signals2/signal.hpp> | ||||||
| 
 | 
 | ||||||
| namespace scwx | namespace scwx | ||||||
| { | { | ||||||
|  | @ -19,7 +20,7 @@ class SettingsVariableBase | ||||||
| { | { | ||||||
| protected: | protected: | ||||||
|    explicit SettingsVariableBase(const std::string& name); |    explicit SettingsVariableBase(const std::string& name); | ||||||
|    ~SettingsVariableBase(); |    virtual ~SettingsVariableBase(); | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|    SettingsVariableBase(const SettingsVariableBase&)            = delete; |    SettingsVariableBase(const SettingsVariableBase&)            = delete; | ||||||
|  | @ -30,6 +31,38 @@ public: | ||||||
| 
 | 
 | ||||||
|    std::string name() const; |    std::string name() const; | ||||||
| 
 | 
 | ||||||
|  |    /**
 | ||||||
|  |     * Gets the signal invoked when the settings variable is changed. | ||||||
|  |     * | ||||||
|  |     * @return Changed signal | ||||||
|  |     */ | ||||||
|  |    boost::signals2::signal<void()>& changed_signal(); | ||||||
|  | 
 | ||||||
|  |    /**
 | ||||||
|  |     * Gets the signal invoked when the settings variable is staged. | ||||||
|  |     * | ||||||
|  |     * @return Staged signal | ||||||
|  |     */ | ||||||
|  |    boost::signals2::signal<void()>& staged_signal(); | ||||||
|  | 
 | ||||||
|  |    /**
 | ||||||
|  |     * Gets whether or not the settings variable is currently set to its default | ||||||
|  |     * value. | ||||||
|  |     * | ||||||
|  |     * @return true if the settings variable is currently set to its default | ||||||
|  |     * value, otherwise false. | ||||||
|  |     */ | ||||||
|  |    virtual bool IsDefault() const = 0; | ||||||
|  | 
 | ||||||
|  |    /**
 | ||||||
|  |     * Gets whether or not the settings variable currently has its staged value | ||||||
|  |     * set to default. | ||||||
|  |     * | ||||||
|  |     * @return true if the settings variable currently has its staged value set | ||||||
|  |     * to default, otherwise false. | ||||||
|  |     */ | ||||||
|  |    virtual bool IsDefaultStaged() const = 0; | ||||||
|  | 
 | ||||||
|    /**
 |    /**
 | ||||||
|     * Sets the current value of the settings variable to default. |     * Sets the current value of the settings variable to default. | ||||||
|     */ |     */ | ||||||
|  | @ -48,6 +81,11 @@ public: | ||||||
|     */ |     */ | ||||||
|    virtual bool Commit() = 0; |    virtual bool Commit() = 0; | ||||||
| 
 | 
 | ||||||
|  |    /**
 | ||||||
|  |     * Clears the staged value of the settings variable. | ||||||
|  |     */ | ||||||
|  |    virtual void Reset() = 0; | ||||||
|  | 
 | ||||||
|    /**
 |    /**
 | ||||||
|     * Reads the value from the JSON object. If the read value is out of range, |     * Reads the value from the JSON object. If the read value is out of range, | ||||||
|     * the value is set to the minimum or maximum. If the read value fails |     * the value is set to the minimum or maximum. If the read value fails | ||||||
|  |  | ||||||
							
								
								
									
										316
									
								
								scwx-qt/source/scwx/qt/ui/edit_line_dialog.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										316
									
								
								scwx-qt/source/scwx/qt/ui/edit_line_dialog.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,316 @@ | ||||||
|  | #include "edit_line_dialog.hpp" | ||||||
|  | #include "ui_edit_line_dialog.h" | ||||||
|  | 
 | ||||||
|  | #include <scwx/qt/ui/line_label.hpp> | ||||||
|  | #include <scwx/qt/util/color.hpp> | ||||||
|  | #include <scwx/util/logger.hpp> | ||||||
|  | 
 | ||||||
|  | #include <fmt/format.h> | ||||||
|  | #include <QColorDialog> | ||||||
|  | 
 | ||||||
|  | namespace scwx | ||||||
|  | { | ||||||
|  | namespace qt | ||||||
|  | { | ||||||
|  | namespace ui | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | static const std::string logPrefix_ = "scwx::qt::ui::edit_line_dialog"; | ||||||
|  | static const auto        logger_    = scwx::util::Logger::Create(logPrefix_); | ||||||
|  | 
 | ||||||
|  | class EditLineDialog::Impl | ||||||
|  | { | ||||||
|  | public: | ||||||
|  |    struct EditComponent | ||||||
|  |    { | ||||||
|  |       void ConnectSignals(EditLineDialog* self) | ||||||
|  |       { | ||||||
|  |          QObject::connect(colorLineEdit_, | ||||||
|  |                           &QLineEdit::textEdited, | ||||||
|  |                           self, | ||||||
|  |                           [=, this](const QString& text) | ||||||
|  |                           { | ||||||
|  |                              boost::gil::rgba8_pixel_t color = | ||||||
|  |                                 util::color::ToRgba8PixelT(text.toStdString()); | ||||||
|  |                              self->p->set_color(*this, color, false); | ||||||
|  |                           }); | ||||||
|  | 
 | ||||||
|  |          QObject::connect(colorButton_, | ||||||
|  |                           &QAbstractButton::clicked, | ||||||
|  |                           self, | ||||||
|  |                           [=, this]() { self->p->ShowColorDialog(*this); }); | ||||||
|  | 
 | ||||||
|  |          QObject::connect(widthSpinBox_, | ||||||
|  |                           &QSpinBox::valueChanged, | ||||||
|  |                           self, | ||||||
|  |                           [=, this](int width) | ||||||
|  |                           { self->p->set_width(*this, width); }); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       boost::gil::rgba8_pixel_t color_; | ||||||
|  |       std::size_t               width_; | ||||||
|  |       QFrame*                   colorFrame_ {nullptr}; | ||||||
|  |       QLineEdit*                colorLineEdit_ {nullptr}; | ||||||
|  |       QToolButton*              colorButton_ {nullptr}; | ||||||
|  |       QSpinBox*                 widthSpinBox_ {nullptr}; | ||||||
|  |    }; | ||||||
|  | 
 | ||||||
|  |    explicit Impl(EditLineDialog* self) : | ||||||
|  |        self_ {self}, lineLabel_ {new LineLabel(self)} | ||||||
|  |    { | ||||||
|  |    } | ||||||
|  |    ~Impl() = default; | ||||||
|  | 
 | ||||||
|  |    void SetDefaults(); | ||||||
|  |    void ShowColorDialog(EditComponent& component); | ||||||
|  |    void UpdateLineLabel(); | ||||||
|  | 
 | ||||||
|  |    void set_color(EditComponent&            component, | ||||||
|  |                   boost::gil::rgba8_pixel_t color, | ||||||
|  |                   bool                      updateLineEdit = true); | ||||||
|  |    void set_width(EditComponent& component, std::size_t width); | ||||||
|  | 
 | ||||||
|  |    static void SetBackgroundColor(const std::string& value, QFrame* frame); | ||||||
|  | 
 | ||||||
|  |    EditLineDialog* self_; | ||||||
|  | 
 | ||||||
|  |    LineLabel* lineLabel_; | ||||||
|  | 
 | ||||||
|  |    boost::gil::rgba8_pixel_t defaultBorderColor_ {0, 0, 0, 255}; | ||||||
|  |    boost::gil::rgba8_pixel_t defaultHighlightColor_ {0, 0, 0, 0}; | ||||||
|  |    boost::gil::rgba8_pixel_t defaultLineColor_ {255, 255, 255, 255}; | ||||||
|  | 
 | ||||||
|  |    std::size_t defaultBorderWidth_ {1u}; | ||||||
|  |    std::size_t defaultHighlightWidth_ {0u}; | ||||||
|  |    std::size_t defaultLineWidth_ {3u}; | ||||||
|  | 
 | ||||||
|  |    EditComponent borderComponent_ {}; | ||||||
|  |    EditComponent highlightComponent_ {}; | ||||||
|  |    EditComponent lineComponent_ {}; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | EditLineDialog::EditLineDialog(QWidget* parent) : | ||||||
|  |     QDialog(parent), | ||||||
|  |     p {std::make_unique<Impl>(this)}, | ||||||
|  |     ui(new Ui::EditLineDialog) | ||||||
|  | { | ||||||
|  |    ui->setupUi(this); | ||||||
|  | 
 | ||||||
|  |    p->borderComponent_.colorFrame_    = ui->borderColorFrame; | ||||||
|  |    p->borderComponent_.colorLineEdit_ = ui->borderColorLineEdit; | ||||||
|  |    p->borderComponent_.colorButton_   = ui->borderColorButton; | ||||||
|  |    p->borderComponent_.widthSpinBox_  = ui->borderWidthSpinBox; | ||||||
|  | 
 | ||||||
|  |    p->highlightComponent_.colorFrame_    = ui->highlightColorFrame; | ||||||
|  |    p->highlightComponent_.colorLineEdit_ = ui->highlightColorLineEdit; | ||||||
|  |    p->highlightComponent_.colorButton_   = ui->highlightColorButton; | ||||||
|  |    p->highlightComponent_.widthSpinBox_  = ui->highlightWidthSpinBox; | ||||||
|  | 
 | ||||||
|  |    p->lineComponent_.colorFrame_    = ui->lineColorFrame; | ||||||
|  |    p->lineComponent_.colorLineEdit_ = ui->lineColorLineEdit; | ||||||
|  |    p->lineComponent_.colorButton_   = ui->lineColorButton; | ||||||
|  |    p->lineComponent_.widthSpinBox_  = ui->lineWidthSpinBox; | ||||||
|  | 
 | ||||||
|  |    p->SetDefaults(); | ||||||
|  | 
 | ||||||
|  |    p->lineLabel_->setMinimumWidth(72); | ||||||
|  | 
 | ||||||
|  |    QHBoxLayout* lineLabelContainerLayout = | ||||||
|  |       static_cast<QHBoxLayout*>(ui->lineLabelContainer->layout()); | ||||||
|  |    lineLabelContainerLayout->insertWidget(1, p->lineLabel_); | ||||||
|  | 
 | ||||||
|  |    p->borderComponent_.ConnectSignals(this); | ||||||
|  |    p->highlightComponent_.ConnectSignals(this); | ||||||
|  |    p->lineComponent_.ConnectSignals(this); | ||||||
|  | 
 | ||||||
|  |    QObject::connect(ui->buttonBox, | ||||||
|  |                     &QDialogButtonBox::clicked, | ||||||
|  |                     this, | ||||||
|  |                     [this](QAbstractButton* button) | ||||||
|  |                     { | ||||||
|  |                        QDialogButtonBox::ButtonRole role = | ||||||
|  |                           ui->buttonBox->buttonRole(button); | ||||||
|  | 
 | ||||||
|  |                        switch (role) | ||||||
|  |                        { | ||||||
|  |                        case QDialogButtonBox::ButtonRole::ResetRole: // Reset
 | ||||||
|  |                           p->SetDefaults(); | ||||||
|  |                           break; | ||||||
|  | 
 | ||||||
|  |                        default: | ||||||
|  |                           break; | ||||||
|  |                        } | ||||||
|  |                     }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | EditLineDialog::~EditLineDialog() | ||||||
|  | { | ||||||
|  |    delete ui; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | boost::gil::rgba8_pixel_t EditLineDialog::border_color() const | ||||||
|  | { | ||||||
|  |    return p->borderComponent_.color_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | boost::gil::rgba8_pixel_t EditLineDialog::highlight_color() const | ||||||
|  | { | ||||||
|  |    return p->highlightComponent_.color_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | boost::gil::rgba8_pixel_t EditLineDialog::line_color() const | ||||||
|  | { | ||||||
|  |    return p->lineComponent_.color_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::size_t EditLineDialog::border_width() const | ||||||
|  | { | ||||||
|  |    return p->borderComponent_.width_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::size_t EditLineDialog::highlight_width() const | ||||||
|  | { | ||||||
|  |    return p->highlightComponent_.width_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::size_t EditLineDialog::line_width() const | ||||||
|  | { | ||||||
|  |    return p->lineComponent_.width_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EditLineDialog::set_border_color(boost::gil::rgba8_pixel_t color) | ||||||
|  | { | ||||||
|  |    p->set_color(p->borderComponent_, color); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EditLineDialog::set_highlight_color(boost::gil::rgba8_pixel_t color) | ||||||
|  | { | ||||||
|  |    p->set_color(p->highlightComponent_, color); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EditLineDialog::set_line_color(boost::gil::rgba8_pixel_t color) | ||||||
|  | { | ||||||
|  |    p->set_color(p->lineComponent_, color); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EditLineDialog::set_border_width(std::size_t width) | ||||||
|  | { | ||||||
|  |    p->set_width(p->borderComponent_, width); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EditLineDialog::set_highlight_width(std::size_t width) | ||||||
|  | { | ||||||
|  |    p->set_width(p->highlightComponent_, width); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EditLineDialog::set_line_width(std::size_t width) | ||||||
|  | { | ||||||
|  |    p->set_width(p->lineComponent_, width); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EditLineDialog::Impl::set_color(EditComponent&            component, | ||||||
|  |                                      boost::gil::rgba8_pixel_t color, | ||||||
|  |                                      bool                      updateLineEdit) | ||||||
|  | { | ||||||
|  |    const std::string argbString {util::color::ToArgbString(color)}; | ||||||
|  | 
 | ||||||
|  |    component.color_ = color; | ||||||
|  |    SetBackgroundColor(argbString, component.colorFrame_); | ||||||
|  | 
 | ||||||
|  |    if (updateLineEdit) | ||||||
|  |    { | ||||||
|  |       component.colorLineEdit_->setText(QString::fromStdString(argbString)); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    UpdateLineLabel(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EditLineDialog::Impl::set_width(EditComponent& component, | ||||||
|  |                                      std::size_t    width) | ||||||
|  | { | ||||||
|  |    component.width_ = width; | ||||||
|  |    component.widthSpinBox_->setValue(static_cast<int>(width)); | ||||||
|  | 
 | ||||||
|  |    UpdateLineLabel(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EditLineDialog::Impl::UpdateLineLabel() | ||||||
|  | { | ||||||
|  |    lineLabel_->set_border_color(borderComponent_.color_); | ||||||
|  |    lineLabel_->set_highlight_color(highlightComponent_.color_); | ||||||
|  |    lineLabel_->set_line_color(lineComponent_.color_); | ||||||
|  | 
 | ||||||
|  |    lineLabel_->set_border_width(borderComponent_.width_); | ||||||
|  |    lineLabel_->set_highlight_width(highlightComponent_.width_); | ||||||
|  |    lineLabel_->set_line_width(lineComponent_.width_); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EditLineDialog::Initialize(boost::gil::rgba8_pixel_t borderColor, | ||||||
|  |                                 boost::gil::rgba8_pixel_t highlightColor, | ||||||
|  |                                 boost::gil::rgba8_pixel_t lineColor, | ||||||
|  |                                 std::size_t               borderWidth, | ||||||
|  |                                 std::size_t               highlightWidth, | ||||||
|  |                                 std::size_t               lineWidth) | ||||||
|  | { | ||||||
|  |    p->defaultBorderColor_    = borderColor; | ||||||
|  |    p->defaultHighlightColor_ = highlightColor; | ||||||
|  |    p->defaultLineColor_      = lineColor; | ||||||
|  | 
 | ||||||
|  |    p->defaultBorderWidth_    = borderWidth; | ||||||
|  |    p->defaultHighlightWidth_ = highlightWidth; | ||||||
|  |    p->defaultLineWidth_      = lineWidth; | ||||||
|  | 
 | ||||||
|  |    p->SetDefaults(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EditLineDialog::Impl::SetDefaults() | ||||||
|  | { | ||||||
|  |    self_->set_border_color(defaultBorderColor_); | ||||||
|  |    self_->set_highlight_color(defaultHighlightColor_); | ||||||
|  |    self_->set_line_color(defaultLineColor_); | ||||||
|  | 
 | ||||||
|  |    self_->set_border_width(defaultBorderWidth_); | ||||||
|  |    self_->set_highlight_width(defaultHighlightWidth_); | ||||||
|  |    self_->set_line_width(defaultLineWidth_); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EditLineDialog::Impl::ShowColorDialog(EditComponent& component) | ||||||
|  | { | ||||||
|  |    QColorDialog* dialog = new QColorDialog(self_); | ||||||
|  | 
 | ||||||
|  |    dialog->setAttribute(Qt::WA_DeleteOnClose); | ||||||
|  |    dialog->setOption(QColorDialog::ColorDialogOption::ShowAlphaChannel); | ||||||
|  | 
 | ||||||
|  |    QColor initialColor(component.colorLineEdit_->text()); | ||||||
|  |    if (initialColor.isValid()) | ||||||
|  |    { | ||||||
|  |       dialog->setCurrentColor(initialColor); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    QObject::connect( | ||||||
|  |       dialog, | ||||||
|  |       &QColorDialog::colorSelected, | ||||||
|  |       self_, | ||||||
|  |       [this, &component](const QColor& qColor) | ||||||
|  |       { | ||||||
|  |          QString colorName = qColor.name(QColor::NameFormat::HexArgb); | ||||||
|  |          boost::gil::rgba8_pixel_t color = | ||||||
|  |             util::color::ToRgba8PixelT(colorName.toStdString()); | ||||||
|  | 
 | ||||||
|  |          logger_->info("Selected color: {}", colorName.toStdString()); | ||||||
|  |          set_color(component, color); | ||||||
|  |       }); | ||||||
|  | 
 | ||||||
|  |    dialog->open(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EditLineDialog::Impl::SetBackgroundColor(const std::string& value, | ||||||
|  |                                               QFrame*            frame) | ||||||
|  | { | ||||||
|  |    frame->setStyleSheet( | ||||||
|  |       QString::fromStdString(fmt::format("background-color: {}", value))); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace ui
 | ||||||
|  | } // namespace qt
 | ||||||
|  | } // namespace scwx
 | ||||||
							
								
								
									
										59
									
								
								scwx-qt/source/scwx/qt/ui/edit_line_dialog.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								scwx-qt/source/scwx/qt/ui/edit_line_dialog.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,59 @@ | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <QDialog> | ||||||
|  | 
 | ||||||
|  | #include <boost/gil/typedefs.hpp> | ||||||
|  | 
 | ||||||
|  | namespace Ui | ||||||
|  | { | ||||||
|  | class EditLineDialog; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | namespace scwx | ||||||
|  | { | ||||||
|  | namespace qt | ||||||
|  | { | ||||||
|  | namespace ui | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | class EditLineDialog : public QDialog | ||||||
|  | { | ||||||
|  |    Q_OBJECT | ||||||
|  |    Q_DISABLE_COPY_MOVE(EditLineDialog) | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |    explicit EditLineDialog(QWidget* parent = nullptr); | ||||||
|  |    ~EditLineDialog(); | ||||||
|  | 
 | ||||||
|  |    boost::gil::rgba8_pixel_t border_color() const; | ||||||
|  |    boost::gil::rgba8_pixel_t highlight_color() const; | ||||||
|  |    boost::gil::rgba8_pixel_t line_color() const; | ||||||
|  | 
 | ||||||
|  |    std::size_t border_width() const; | ||||||
|  |    std::size_t highlight_width() const; | ||||||
|  |    std::size_t line_width() const; | ||||||
|  | 
 | ||||||
|  |    void set_border_color(boost::gil::rgba8_pixel_t color); | ||||||
|  |    void set_highlight_color(boost::gil::rgba8_pixel_t color); | ||||||
|  |    void set_line_color(boost::gil::rgba8_pixel_t color); | ||||||
|  | 
 | ||||||
|  |    void set_border_width(std::size_t width); | ||||||
|  |    void set_highlight_width(std::size_t width); | ||||||
|  |    void set_line_width(std::size_t width); | ||||||
|  | 
 | ||||||
|  |    void Initialize(boost::gil::rgba8_pixel_t borderColor, | ||||||
|  |                    boost::gil::rgba8_pixel_t highlightColor, | ||||||
|  |                    boost::gil::rgba8_pixel_t lineColor, | ||||||
|  |                    std::size_t               borderWidth, | ||||||
|  |                    std::size_t               highlightWidth, | ||||||
|  |                    std::size_t               lineWidth); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |    class Impl; | ||||||
|  |    std::unique_ptr<Impl> p; | ||||||
|  |    Ui::EditLineDialog*   ui; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace ui
 | ||||||
|  | } // namespace qt
 | ||||||
|  | } // namespace scwx
 | ||||||
							
								
								
									
										306
									
								
								scwx-qt/source/scwx/qt/ui/edit_line_dialog.ui
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										306
									
								
								scwx-qt/source/scwx/qt/ui/edit_line_dialog.ui
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,306 @@ | ||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <ui version="4.0"> | ||||||
|  |  <class>EditLineDialog</class> | ||||||
|  |  <widget class="QDialog" name="EditLineDialog"> | ||||||
|  |   <property name="geometry"> | ||||||
|  |    <rect> | ||||||
|  |     <x>0</x> | ||||||
|  |     <y>0</y> | ||||||
|  |     <width>350</width> | ||||||
|  |     <height>225</height> | ||||||
|  |    </rect> | ||||||
|  |   </property> | ||||||
|  |   <property name="windowTitle"> | ||||||
|  |    <string>Edit Line</string> | ||||||
|  |   </property> | ||||||
|  |   <layout class="QGridLayout" name="gridLayout"> | ||||||
|  |    <item row="0" column="0"> | ||||||
|  |     <widget class="QLabel" name="componentLabel"> | ||||||
|  |      <property name="font"> | ||||||
|  |       <font> | ||||||
|  |        <bold>true</bold> | ||||||
|  |       </font> | ||||||
|  |      </property> | ||||||
|  |      <property name="text"> | ||||||
|  |       <string>Component</string> | ||||||
|  |      </property> | ||||||
|  |     </widget> | ||||||
|  |    </item> | ||||||
|  |    <item row="2" column="2"> | ||||||
|  |     <widget class="QLineEdit" name="highlightColorLineEdit"> | ||||||
|  |      <property name="text"> | ||||||
|  |       <string>#ff000000</string> | ||||||
|  |      </property> | ||||||
|  |     </widget> | ||||||
|  |    </item> | ||||||
|  |    <item row="3" column="4"> | ||||||
|  |     <widget class="QSpinBox" name="borderWidthSpinBox"> | ||||||
|  |      <property name="minimum"> | ||||||
|  |       <number>0</number> | ||||||
|  |      </property> | ||||||
|  |      <property name="maximum"> | ||||||
|  |       <number>9</number> | ||||||
|  |      </property> | ||||||
|  |     </widget> | ||||||
|  |    </item> | ||||||
|  |    <item row="1" column="3"> | ||||||
|  |     <widget class="QToolButton" name="lineColorButton"> | ||||||
|  |      <property name="text"> | ||||||
|  |       <string>...</string> | ||||||
|  |      </property> | ||||||
|  |      <property name="icon"> | ||||||
|  |       <iconset resource="../../../../scwx-qt.qrc"> | ||||||
|  |        <normaloff>:/res/icons/font-awesome-6/palette-solid.svg</normaloff>:/res/icons/font-awesome-6/palette-solid.svg</iconset> | ||||||
|  |      </property> | ||||||
|  |     </widget> | ||||||
|  |    </item> | ||||||
|  |    <item row="3" column="3"> | ||||||
|  |     <widget class="QToolButton" name="borderColorButton"> | ||||||
|  |      <property name="text"> | ||||||
|  |       <string>...</string> | ||||||
|  |      </property> | ||||||
|  |      <property name="icon"> | ||||||
|  |       <iconset resource="../../../../scwx-qt.qrc"> | ||||||
|  |        <normaloff>:/res/icons/font-awesome-6/palette-solid.svg</normaloff>:/res/icons/font-awesome-6/palette-solid.svg</iconset> | ||||||
|  |      </property> | ||||||
|  |     </widget> | ||||||
|  |    </item> | ||||||
|  |    <item row="3" column="0"> | ||||||
|  |     <widget class="QLabel" name="borderLabel"> | ||||||
|  |      <property name="text"> | ||||||
|  |       <string>Border</string> | ||||||
|  |      </property> | ||||||
|  |     </widget> | ||||||
|  |    </item> | ||||||
|  |    <item row="1" column="2"> | ||||||
|  |     <widget class="QLineEdit" name="lineColorLineEdit"> | ||||||
|  |      <property name="text"> | ||||||
|  |       <string>#ff000000</string> | ||||||
|  |      </property> | ||||||
|  |     </widget> | ||||||
|  |    </item> | ||||||
|  |    <item row="1" column="0"> | ||||||
|  |     <widget class="QLabel" name="lineLabel"> | ||||||
|  |      <property name="text"> | ||||||
|  |       <string>Line</string> | ||||||
|  |      </property> | ||||||
|  |     </widget> | ||||||
|  |    </item> | ||||||
|  |    <item row="6" column="0" colspan="5"> | ||||||
|  |     <widget class="QDialogButtonBox" name="buttonBox"> | ||||||
|  |      <property name="orientation"> | ||||||
|  |       <enum>Qt::Orientation::Horizontal</enum> | ||||||
|  |      </property> | ||||||
|  |      <property name="standardButtons"> | ||||||
|  |       <set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok|QDialogButtonBox::StandardButton::Reset</set> | ||||||
|  |      </property> | ||||||
|  |     </widget> | ||||||
|  |    </item> | ||||||
|  |    <item row="1" column="1"> | ||||||
|  |     <widget class="QFrame" name="lineColorFrame"> | ||||||
|  |      <property name="minimumSize"> | ||||||
|  |       <size> | ||||||
|  |        <width>24</width> | ||||||
|  |        <height>24</height> | ||||||
|  |       </size> | ||||||
|  |      </property> | ||||||
|  |      <property name="frameShape"> | ||||||
|  |       <enum>QFrame::Shape::Box</enum> | ||||||
|  |      </property> | ||||||
|  |      <property name="frameShadow"> | ||||||
|  |       <enum>QFrame::Shadow::Plain</enum> | ||||||
|  |      </property> | ||||||
|  |     </widget> | ||||||
|  |    </item> | ||||||
|  |    <item row="2" column="4"> | ||||||
|  |     <widget class="QSpinBox" name="highlightWidthSpinBox"> | ||||||
|  |      <property name="minimum"> | ||||||
|  |       <number>0</number> | ||||||
|  |      </property> | ||||||
|  |      <property name="maximum"> | ||||||
|  |       <number>9</number> | ||||||
|  |      </property> | ||||||
|  |     </widget> | ||||||
|  |    </item> | ||||||
|  |    <item row="5" column="0"> | ||||||
|  |     <spacer name="verticalSpacer"> | ||||||
|  |      <property name="orientation"> | ||||||
|  |       <enum>Qt::Orientation::Vertical</enum> | ||||||
|  |      </property> | ||||||
|  |      <property name="sizeHint" stdset="0"> | ||||||
|  |       <size> | ||||||
|  |        <width>20</width> | ||||||
|  |        <height>40</height> | ||||||
|  |       </size> | ||||||
|  |      </property> | ||||||
|  |     </spacer> | ||||||
|  |    </item> | ||||||
|  |    <item row="2" column="1"> | ||||||
|  |     <widget class="QFrame" name="highlightColorFrame"> | ||||||
|  |      <property name="minimumSize"> | ||||||
|  |       <size> | ||||||
|  |        <width>24</width> | ||||||
|  |        <height>24</height> | ||||||
|  |       </size> | ||||||
|  |      </property> | ||||||
|  |      <property name="frameShape"> | ||||||
|  |       <enum>QFrame::Shape::Box</enum> | ||||||
|  |      </property> | ||||||
|  |      <property name="frameShadow"> | ||||||
|  |       <enum>QFrame::Shadow::Plain</enum> | ||||||
|  |      </property> | ||||||
|  |     </widget> | ||||||
|  |    </item> | ||||||
|  |    <item row="0" column="1" colspan="3"> | ||||||
|  |     <widget class="QLabel" name="colorLabel"> | ||||||
|  |      <property name="font"> | ||||||
|  |       <font> | ||||||
|  |        <bold>true</bold> | ||||||
|  |       </font> | ||||||
|  |      </property> | ||||||
|  |      <property name="text"> | ||||||
|  |       <string>Color</string> | ||||||
|  |      </property> | ||||||
|  |     </widget> | ||||||
|  |    </item> | ||||||
|  |    <item row="3" column="2"> | ||||||
|  |     <widget class="QLineEdit" name="borderColorLineEdit"> | ||||||
|  |      <property name="text"> | ||||||
|  |       <string>#ff000000</string> | ||||||
|  |      </property> | ||||||
|  |     </widget> | ||||||
|  |    </item> | ||||||
|  |    <item row="3" column="1"> | ||||||
|  |     <widget class="QFrame" name="borderColorFrame"> | ||||||
|  |      <property name="minimumSize"> | ||||||
|  |       <size> | ||||||
|  |        <width>24</width> | ||||||
|  |        <height>24</height> | ||||||
|  |       </size> | ||||||
|  |      </property> | ||||||
|  |      <property name="frameShape"> | ||||||
|  |       <enum>QFrame::Shape::Box</enum> | ||||||
|  |      </property> | ||||||
|  |      <property name="frameShadow"> | ||||||
|  |       <enum>QFrame::Shadow::Plain</enum> | ||||||
|  |      </property> | ||||||
|  |     </widget> | ||||||
|  |    </item> | ||||||
|  |    <item row="0" column="4"> | ||||||
|  |     <widget class="QLabel" name="widthLabel"> | ||||||
|  |      <property name="font"> | ||||||
|  |       <font> | ||||||
|  |        <bold>true</bold> | ||||||
|  |       </font> | ||||||
|  |      </property> | ||||||
|  |      <property name="text"> | ||||||
|  |       <string>Width</string> | ||||||
|  |      </property> | ||||||
|  |     </widget> | ||||||
|  |    </item> | ||||||
|  |    <item row="1" column="4"> | ||||||
|  |     <widget class="QSpinBox" name="lineWidthSpinBox"> | ||||||
|  |      <property name="minimum"> | ||||||
|  |       <number>1</number> | ||||||
|  |      </property> | ||||||
|  |      <property name="maximum"> | ||||||
|  |       <number>9</number> | ||||||
|  |      </property> | ||||||
|  |     </widget> | ||||||
|  |    </item> | ||||||
|  |    <item row="2" column="0"> | ||||||
|  |     <widget class="QLabel" name="highlightLabel"> | ||||||
|  |      <property name="text"> | ||||||
|  |       <string>Highlight</string> | ||||||
|  |      </property> | ||||||
|  |     </widget> | ||||||
|  |    </item> | ||||||
|  |    <item row="2" column="3"> | ||||||
|  |     <widget class="QToolButton" name="highlightColorButton"> | ||||||
|  |      <property name="text"> | ||||||
|  |       <string>...</string> | ||||||
|  |      </property> | ||||||
|  |      <property name="icon"> | ||||||
|  |       <iconset resource="../../../../scwx-qt.qrc"> | ||||||
|  |        <normaloff>:/res/icons/font-awesome-6/palette-solid.svg</normaloff>:/res/icons/font-awesome-6/palette-solid.svg</iconset> | ||||||
|  |      </property> | ||||||
|  |     </widget> | ||||||
|  |    </item> | ||||||
|  |    <item row="4" column="0" colspan="5"> | ||||||
|  |     <widget class="QWidget" name="lineLabelContainer" native="true"> | ||||||
|  |      <property name="minimumSize"> | ||||||
|  |       <size> | ||||||
|  |        <width>0</width> | ||||||
|  |        <height>45</height> | ||||||
|  |       </size> | ||||||
|  |      </property> | ||||||
|  |      <layout class="QHBoxLayout" name="horizontalLayout"> | ||||||
|  |       <property name="leftMargin"> | ||||||
|  |        <number>0</number> | ||||||
|  |       </property> | ||||||
|  |       <property name="topMargin"> | ||||||
|  |        <number>0</number> | ||||||
|  |       </property> | ||||||
|  |       <property name="rightMargin"> | ||||||
|  |        <number>0</number> | ||||||
|  |       </property> | ||||||
|  |       <property name="bottomMargin"> | ||||||
|  |        <number>0</number> | ||||||
|  |       </property> | ||||||
|  |       <item> | ||||||
|  |        <spacer name="horizontalSpacer"> | ||||||
|  |         <property name="orientation"> | ||||||
|  |          <enum>Qt::Orientation::Horizontal</enum> | ||||||
|  |         </property> | ||||||
|  |        </spacer> | ||||||
|  |       </item> | ||||||
|  |       <item> | ||||||
|  |        <spacer name="horizontalSpacer_2"> | ||||||
|  |         <property name="orientation"> | ||||||
|  |          <enum>Qt::Orientation::Horizontal</enum> | ||||||
|  |         </property> | ||||||
|  |        </spacer> | ||||||
|  |       </item> | ||||||
|  |      </layout> | ||||||
|  |     </widget> | ||||||
|  |    </item> | ||||||
|  |   </layout> | ||||||
|  |  </widget> | ||||||
|  |  <resources> | ||||||
|  |   <include location="../../../../scwx-qt.qrc"/> | ||||||
|  |  </resources> | ||||||
|  |  <connections> | ||||||
|  |   <connection> | ||||||
|  |    <sender>buttonBox</sender> | ||||||
|  |    <signal>accepted()</signal> | ||||||
|  |    <receiver>EditLineDialog</receiver> | ||||||
|  |    <slot>accept()</slot> | ||||||
|  |    <hints> | ||||||
|  |     <hint type="sourcelabel"> | ||||||
|  |      <x>248</x> | ||||||
|  |      <y>254</y> | ||||||
|  |     </hint> | ||||||
|  |     <hint type="destinationlabel"> | ||||||
|  |      <x>157</x> | ||||||
|  |      <y>274</y> | ||||||
|  |     </hint> | ||||||
|  |    </hints> | ||||||
|  |   </connection> | ||||||
|  |   <connection> | ||||||
|  |    <sender>buttonBox</sender> | ||||||
|  |    <signal>rejected()</signal> | ||||||
|  |    <receiver>EditLineDialog</receiver> | ||||||
|  |    <slot>reject()</slot> | ||||||
|  |    <hints> | ||||||
|  |     <hint type="sourcelabel"> | ||||||
|  |      <x>316</x> | ||||||
|  |      <y>260</y> | ||||||
|  |     </hint> | ||||||
|  |     <hint type="destinationlabel"> | ||||||
|  |      <x>286</x> | ||||||
|  |      <y>274</y> | ||||||
|  |     </hint> | ||||||
|  |    </hints> | ||||||
|  |   </connection> | ||||||
|  |  </connections> | ||||||
|  | </ui> | ||||||
							
								
								
									
										239
									
								
								scwx-qt/source/scwx/qt/ui/line_label.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										239
									
								
								scwx-qt/source/scwx/qt/ui/line_label.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,239 @@ | ||||||
|  | #include <scwx/qt/ui/line_label.hpp> | ||||||
|  | #include <scwx/qt/util/color.hpp> | ||||||
|  | #include <scwx/util/logger.hpp> | ||||||
|  | 
 | ||||||
|  | #include <QEvent> | ||||||
|  | #include <QPainter> | ||||||
|  | 
 | ||||||
|  | namespace scwx | ||||||
|  | { | ||||||
|  | namespace qt | ||||||
|  | { | ||||||
|  | namespace ui | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | static const std::string logPrefix_ = "scwx::qt::ui::line_label"; | ||||||
|  | static const auto        logger_    = scwx::util::Logger::Create(logPrefix_); | ||||||
|  | 
 | ||||||
|  | class LineLabel::Impl | ||||||
|  | { | ||||||
|  | public: | ||||||
|  |    explicit Impl(LineLabel* self) : self_ {self} {}; | ||||||
|  |    ~Impl() = default; | ||||||
|  | 
 | ||||||
|  |    QImage GenerateImage() const; | ||||||
|  |    void   UpdateLineLabel(const settings::LineSettings& lineSettings); | ||||||
|  | 
 | ||||||
|  |    LineLabel* self_; | ||||||
|  | 
 | ||||||
|  |    std::size_t borderWidth_ {1}; | ||||||
|  |    std::size_t highlightWidth_ {1}; | ||||||
|  |    std::size_t lineWidth_ {3}; | ||||||
|  | 
 | ||||||
|  |    boost::gil::rgba8_pixel_t borderColor_ {0, 0, 0, 255}; | ||||||
|  |    boost::gil::rgba8_pixel_t highlightColor_ {255, 255, 0, 255}; | ||||||
|  |    boost::gil::rgba8_pixel_t lineColor_ {0, 0, 255, 255}; | ||||||
|  | 
 | ||||||
|  |    QPixmap pixmap_ {}; | ||||||
|  |    bool    pixmapDirty_ {true}; | ||||||
|  | 
 | ||||||
|  |    boost::signals2::scoped_connection settingsStaged_ {}; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | LineLabel::LineLabel(QWidget* parent) : | ||||||
|  |     QFrame(parent), p {std::make_unique<Impl>(this)} | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | LineLabel::~LineLabel() {} | ||||||
|  | 
 | ||||||
|  | boost::gil::rgba8_pixel_t LineLabel::border_color() const | ||||||
|  | { | ||||||
|  |    return p->borderColor_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | boost::gil::rgba8_pixel_t LineLabel::highlight_color() const | ||||||
|  | { | ||||||
|  |    return p->highlightColor_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | boost::gil::rgba8_pixel_t LineLabel::line_color() const | ||||||
|  | { | ||||||
|  |    return p->lineColor_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::size_t LineLabel::border_width() const | ||||||
|  | { | ||||||
|  |    return p->borderWidth_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::size_t LineLabel::highlight_width() const | ||||||
|  | { | ||||||
|  |    return p->highlightWidth_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::size_t LineLabel::line_width() const | ||||||
|  | { | ||||||
|  |    return p->lineWidth_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void LineLabel::set_border_width(std::size_t width) | ||||||
|  | { | ||||||
|  |    p->borderWidth_ = width; | ||||||
|  |    p->pixmapDirty_ = true; | ||||||
|  |    updateGeometry(); | ||||||
|  |    update(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void LineLabel::set_highlight_width(std::size_t width) | ||||||
|  | { | ||||||
|  |    p->highlightWidth_ = width; | ||||||
|  |    p->pixmapDirty_    = true; | ||||||
|  |    updateGeometry(); | ||||||
|  |    update(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void LineLabel::set_line_width(std::size_t width) | ||||||
|  | { | ||||||
|  |    p->lineWidth_   = width; | ||||||
|  |    p->pixmapDirty_ = true; | ||||||
|  |    updateGeometry(); | ||||||
|  |    update(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void LineLabel::set_border_color(boost::gil::rgba8_pixel_t color) | ||||||
|  | { | ||||||
|  |    p->borderColor_ = color; | ||||||
|  |    p->pixmapDirty_ = true; | ||||||
|  |    update(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void LineLabel::set_highlight_color(boost::gil::rgba8_pixel_t color) | ||||||
|  | { | ||||||
|  |    p->highlightColor_ = color; | ||||||
|  |    p->pixmapDirty_    = true; | ||||||
|  |    update(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void LineLabel::set_line_color(boost::gil::rgba8_pixel_t color) | ||||||
|  | { | ||||||
|  |    p->lineColor_   = color; | ||||||
|  |    p->pixmapDirty_ = true; | ||||||
|  |    update(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void LineLabel::set_line_settings(settings::LineSettings& lineSettings) | ||||||
|  | { | ||||||
|  |    p->settingsStaged_ = lineSettings.staged_signal().connect( | ||||||
|  |       [this, &lineSettings]() { p->UpdateLineLabel(lineSettings); }); | ||||||
|  | 
 | ||||||
|  |    p->UpdateLineLabel(lineSettings); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void LineLabel::Impl::UpdateLineLabel( | ||||||
|  |    const settings::LineSettings& lineSettings) | ||||||
|  | { | ||||||
|  |    self_->set_border_color(util::color::ToRgba8PixelT( | ||||||
|  |       lineSettings.border_color().GetStagedOrValue())); | ||||||
|  |    self_->set_highlight_color(util::color::ToRgba8PixelT( | ||||||
|  |       lineSettings.highlight_color().GetStagedOrValue())); | ||||||
|  |    self_->set_line_color( | ||||||
|  |       util::color::ToRgba8PixelT(lineSettings.line_color().GetStagedOrValue())); | ||||||
|  | 
 | ||||||
|  |    self_->set_border_width(lineSettings.border_width().GetStagedOrValue()); | ||||||
|  |    self_->set_highlight_width( | ||||||
|  |       lineSettings.highlight_width().GetStagedOrValue()); | ||||||
|  |    self_->set_line_width(lineSettings.line_width().GetStagedOrValue()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | QSize LineLabel::minimumSizeHint() const | ||||||
|  | { | ||||||
|  |    return sizeHint(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | QSize LineLabel::sizeHint() const | ||||||
|  | { | ||||||
|  |    QMargins margins = contentsMargins(); | ||||||
|  | 
 | ||||||
|  |    const std::size_t width = 1; | ||||||
|  |    const std::size_t height = | ||||||
|  |       (p->borderWidth_ + p->highlightWidth_) * 2 + p->lineWidth_; | ||||||
|  | 
 | ||||||
|  |    return QSize(static_cast<int>(width) + margins.left() + margins.right(), | ||||||
|  |                 static_cast<int>(height) + margins.top() + margins.bottom()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void LineLabel::paintEvent(QPaintEvent* e) | ||||||
|  | { | ||||||
|  |    logger_->trace("paintEvent"); | ||||||
|  | 
 | ||||||
|  |    QFrame::paintEvent(e); | ||||||
|  | 
 | ||||||
|  |    if (p->pixmapDirty_) | ||||||
|  |    { | ||||||
|  |       QImage image    = p->GenerateImage(); | ||||||
|  |       p->pixmap_      = QPixmap::fromImage(image); | ||||||
|  |       p->pixmapDirty_ = false; | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    // Don't stretch the line pixmap vertically
 | ||||||
|  |    QRect rect = contentsRect(); | ||||||
|  |    if (rect.height() > p->pixmap_.height()) | ||||||
|  |    { | ||||||
|  |       int dy  = rect.height() - p->pixmap_.height(); | ||||||
|  |       int dy1 = dy / 2; | ||||||
|  |       int dy2 = dy - dy1; | ||||||
|  |       rect.adjust(0, dy1, 0, -dy2); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    QPainter painter(this); | ||||||
|  |    painter.drawPixmap(rect, p->pixmap_); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | QImage LineLabel::Impl::GenerateImage() const | ||||||
|  | { | ||||||
|  |    const QRgb borderRgba    = qRgba(static_cast<int>(borderColor_[0]), | ||||||
|  |                                  static_cast<int>(borderColor_[1]), | ||||||
|  |                                  static_cast<int>(borderColor_[2]), | ||||||
|  |                                  static_cast<int>(borderColor_[3])); | ||||||
|  |    const QRgb highlightRgba = qRgba(static_cast<int>(highlightColor_[0]), | ||||||
|  |                                     static_cast<int>(highlightColor_[1]), | ||||||
|  |                                     static_cast<int>(highlightColor_[2]), | ||||||
|  |                                     static_cast<int>(highlightColor_[3])); | ||||||
|  |    const QRgb lineRgba      = qRgba(static_cast<int>(lineColor_[0]), | ||||||
|  |                                static_cast<int>(lineColor_[1]), | ||||||
|  |                                static_cast<int>(lineColor_[2]), | ||||||
|  |                                static_cast<int>(lineColor_[3])); | ||||||
|  | 
 | ||||||
|  |    const std::size_t width  = 1; | ||||||
|  |    const std::size_t height = (borderWidth_ + highlightWidth_) * 2 + lineWidth_; | ||||||
|  | 
 | ||||||
|  |    QImage image(static_cast<int>(width), | ||||||
|  |                 static_cast<int>(height), | ||||||
|  |                 QImage::Format::Format_ARGB32); | ||||||
|  | 
 | ||||||
|  |    std::size_t y = 0; | ||||||
|  |    for (std::size_t i = 0; i < borderWidth_; ++i, ++y) | ||||||
|  |    { | ||||||
|  |       image.setPixel(0, static_cast<int>(y), borderRgba); | ||||||
|  |       image.setPixel(0, static_cast<int>(height - 1 - y), borderRgba); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    for (std::size_t i = 0; i < highlightWidth_; ++i, ++y) | ||||||
|  |    { | ||||||
|  |       image.setPixel(0, static_cast<int>(y), highlightRgba); | ||||||
|  |       image.setPixel(0, static_cast<int>(height - 1 - y), highlightRgba); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    for (std::size_t i = 0; i < lineWidth_; ++i, ++y) | ||||||
|  |    { | ||||||
|  |       image.setPixel(0, static_cast<int>(y), lineRgba); | ||||||
|  |       image.setPixel(0, static_cast<int>(height - 1 - y), lineRgba); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    return image; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace ui
 | ||||||
|  | } // namespace qt
 | ||||||
|  | } // namespace scwx
 | ||||||
							
								
								
									
										55
									
								
								scwx-qt/source/scwx/qt/ui/line_label.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								scwx-qt/source/scwx/qt/ui/line_label.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,55 @@ | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <scwx/qt/settings/line_settings.hpp> | ||||||
|  | 
 | ||||||
|  | #include <QFrame> | ||||||
|  | 
 | ||||||
|  | #include <boost/gil/typedefs.hpp> | ||||||
|  | 
 | ||||||
|  | namespace scwx | ||||||
|  | { | ||||||
|  | namespace qt | ||||||
|  | { | ||||||
|  | namespace ui | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | class LineLabel : public QFrame | ||||||
|  | { | ||||||
|  |    Q_OBJECT | ||||||
|  |    Q_DISABLE_COPY_MOVE(LineLabel) | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |    explicit LineLabel(QWidget* parent = nullptr); | ||||||
|  |    ~LineLabel(); | ||||||
|  | 
 | ||||||
|  |    boost::gil::rgba8_pixel_t border_color() const; | ||||||
|  |    boost::gil::rgba8_pixel_t highlight_color() const; | ||||||
|  |    boost::gil::rgba8_pixel_t line_color() const; | ||||||
|  | 
 | ||||||
|  |    std::size_t border_width() const; | ||||||
|  |    std::size_t highlight_width() const; | ||||||
|  |    std::size_t line_width() const; | ||||||
|  | 
 | ||||||
|  |    void set_border_color(boost::gil::rgba8_pixel_t color); | ||||||
|  |    void set_highlight_color(boost::gil::rgba8_pixel_t color); | ||||||
|  |    void set_line_color(boost::gil::rgba8_pixel_t color); | ||||||
|  | 
 | ||||||
|  |    void set_border_width(std::size_t width); | ||||||
|  |    void set_highlight_width(std::size_t width); | ||||||
|  |    void set_line_width(std::size_t width); | ||||||
|  | 
 | ||||||
|  |    void set_line_settings(settings::LineSettings& lineSettings); | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  |    QSize minimumSizeHint() const override; | ||||||
|  |    QSize sizeHint() const override; | ||||||
|  |    void  paintEvent(QPaintEvent* e) override; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |    class Impl; | ||||||
|  |    std::unique_ptr<Impl> p; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace ui
 | ||||||
|  | } // namespace qt
 | ||||||
|  | } // namespace scwx
 | ||||||
|  | @ -0,0 +1,292 @@ | ||||||
|  | #include <scwx/qt/ui/settings/alert_palette_settings_widget.hpp> | ||||||
|  | #include <scwx/qt/ui/edit_line_dialog.hpp> | ||||||
|  | #include <scwx/qt/ui/line_label.hpp> | ||||||
|  | #include <scwx/qt/settings/palette_settings.hpp> | ||||||
|  | #include <scwx/awips/impact_based_warnings.hpp> | ||||||
|  | #include <scwx/awips/phenomenon.hpp> | ||||||
|  | 
 | ||||||
|  | #include <boost/unordered/unordered_flat_map.hpp> | ||||||
|  | #include <QGridLayout> | ||||||
|  | #include <QLabel> | ||||||
|  | #include <QListWidget> | ||||||
|  | #include <QStackedWidget> | ||||||
|  | #include <QToolButton> | ||||||
|  | #include <QVBoxLayout> | ||||||
|  | 
 | ||||||
|  | #include <boost/signals2.hpp> | ||||||
|  | 
 | ||||||
|  | namespace scwx | ||||||
|  | { | ||||||
|  | namespace qt | ||||||
|  | { | ||||||
|  | namespace ui | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | static const std::string logPrefix_ = | ||||||
|  |    "scwx::qt::ui::settings::alert_palette_settings_widget"; | ||||||
|  | 
 | ||||||
|  | class AlertPaletteSettingsWidget::Impl | ||||||
|  | { | ||||||
|  | public: | ||||||
|  |    explicit Impl(AlertPaletteSettingsWidget* self) : | ||||||
|  |        self_ {self}, | ||||||
|  |        phenomenonPagesWidget_ {new QStackedWidget(self)}, | ||||||
|  |        phenomenonListView_ {new QListWidget(self)}, | ||||||
|  |        editLineDialog_ {new EditLineDialog(self)} | ||||||
|  |    { | ||||||
|  |       SetupUi(); | ||||||
|  |       ConnectSignals(); | ||||||
|  |    } | ||||||
|  |    ~Impl() {}; | ||||||
|  | 
 | ||||||
|  |    void     AddPhenomenonLine(const std::string&      name, | ||||||
|  |                               settings::LineSettings& lineSettings, | ||||||
|  |                               QGridLayout*            layout, | ||||||
|  |                               int                     row); | ||||||
|  |    QWidget* CreateStackedWidgetPage(awips::Phenomenon phenomenon); | ||||||
|  |    void     ConnectSignals(); | ||||||
|  |    void     SelectPhenomenon(awips::Phenomenon phenomenon); | ||||||
|  |    void     SetupUi(); | ||||||
|  | 
 | ||||||
|  |    AlertPaletteSettingsWidget* self_; | ||||||
|  | 
 | ||||||
|  |    QStackedWidget* phenomenonPagesWidget_; | ||||||
|  |    QListWidget*    phenomenonListView_; | ||||||
|  | 
 | ||||||
|  |    EditLineDialog*         editLineDialog_; | ||||||
|  |    settings::LineSettings* activeLineSettings_ {nullptr}; | ||||||
|  | 
 | ||||||
|  |    boost::unordered_flat_map<awips::Phenomenon, QWidget*> phenomenonPages_ {}; | ||||||
|  | 
 | ||||||
|  |    std::vector<boost::signals2::scoped_connection> connections_ {}; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | AlertPaletteSettingsWidget::AlertPaletteSettingsWidget(QWidget* parent) : | ||||||
|  |     SettingsPageWidget(parent), p {std::make_shared<Impl>(this)} | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | AlertPaletteSettingsWidget::~AlertPaletteSettingsWidget() = default; | ||||||
|  | 
 | ||||||
|  | void AlertPaletteSettingsWidget::Impl::SetupUi() | ||||||
|  | { | ||||||
|  |    // Setup phenomenon index pane
 | ||||||
|  |    QLabel* phenomenonLabel = new QLabel(tr("Phenomenon:"), self_); | ||||||
|  |    phenomenonPagesWidget_->setSizePolicy(QSizePolicy::Policy::MinimumExpanding, | ||||||
|  |                                          QSizePolicy::Policy::Preferred); | ||||||
|  | 
 | ||||||
|  |    // Setup stacked widget
 | ||||||
|  |    for (auto& phenomenon : settings::PaletteSettings::alert_phenomena()) | ||||||
|  |    { | ||||||
|  |       QWidget* phenomenonWidget = CreateStackedWidgetPage(phenomenon); | ||||||
|  |       phenomenonPagesWidget_->addWidget(phenomenonWidget); | ||||||
|  | 
 | ||||||
|  |       phenomenonPages_.insert_or_assign(phenomenon, phenomenonWidget); | ||||||
|  | 
 | ||||||
|  |       phenomenonListView_->addItem( | ||||||
|  |          QString::fromStdString(awips::GetPhenomenonText(phenomenon))); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    phenomenonListView_->setCurrentRow(0); | ||||||
|  | 
 | ||||||
|  |    // Create phenomenon index pane layout
 | ||||||
|  |    QVBoxLayout* phenomenonIndexLayout = new QVBoxLayout(self_); | ||||||
|  |    phenomenonIndexLayout->addWidget(phenomenonLabel); | ||||||
|  |    phenomenonIndexLayout->addWidget(phenomenonListView_); | ||||||
|  | 
 | ||||||
|  |    QWidget* phenomenonIndexPane = new QWidget(self_); | ||||||
|  |    phenomenonIndexPane->setLayout(phenomenonIndexLayout); | ||||||
|  | 
 | ||||||
|  |    // Create primary widget layout
 | ||||||
|  |    QGridLayout* gridLayout = new QGridLayout(self_); | ||||||
|  |    gridLayout->setContentsMargins(0, 0, 0, 0); | ||||||
|  |    gridLayout->addWidget(phenomenonIndexPane, 0, 0); | ||||||
|  |    gridLayout->addWidget(phenomenonPagesWidget_, 0, 1); | ||||||
|  | 
 | ||||||
|  |    QSpacerItem* spacer = | ||||||
|  |       new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding); | ||||||
|  |    gridLayout->addItem(spacer, 1, 0); | ||||||
|  | 
 | ||||||
|  |    self_->setLayout(gridLayout); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AlertPaletteSettingsWidget::Impl::ConnectSignals() | ||||||
|  | { | ||||||
|  |    connect( | ||||||
|  |       phenomenonListView_->selectionModel(), | ||||||
|  |       &QItemSelectionModel::selectionChanged, | ||||||
|  |       self_, | ||||||
|  |       [this](const QItemSelection& selected, const QItemSelection& deselected) | ||||||
|  |       { | ||||||
|  |          if (selected.size() == 0 && deselected.size() == 0) | ||||||
|  |          { | ||||||
|  |             // Items which stay selected but change their index are not
 | ||||||
|  |             // included in selected and deselected. Thus, this signal might
 | ||||||
|  |             // be emitted with both selected and deselected empty, if only
 | ||||||
|  |             // the indices of selected items change.
 | ||||||
|  |             return; | ||||||
|  |          } | ||||||
|  | 
 | ||||||
|  |          if (selected.size() > 0) | ||||||
|  |          { | ||||||
|  |             QModelIndex selectedIndex = selected[0].indexes()[0]; | ||||||
|  |             QVariant    variantData = | ||||||
|  |                phenomenonListView_->model()->data(selectedIndex); | ||||||
|  |             if (variantData.typeId() == QMetaType::QString) | ||||||
|  |             { | ||||||
|  |                awips::Phenomenon phenomenon = awips::GetPhenomenonFromText( | ||||||
|  |                   variantData.toString().toStdString()); | ||||||
|  |                SelectPhenomenon(phenomenon); | ||||||
|  |             } | ||||||
|  |          } | ||||||
|  |       }); | ||||||
|  | 
 | ||||||
|  |    connect(editLineDialog_, | ||||||
|  |            &EditLineDialog::accepted, | ||||||
|  |            self_, | ||||||
|  |            [this]() | ||||||
|  |            { | ||||||
|  |               // If the active line label was set
 | ||||||
|  |               if (activeLineSettings_ != nullptr) | ||||||
|  |               { | ||||||
|  |                  // Update the active line settings with selected line settings
 | ||||||
|  |                  activeLineSettings_->StageValues( | ||||||
|  |                     editLineDialog_->border_color(), | ||||||
|  |                     editLineDialog_->highlight_color(), | ||||||
|  |                     editLineDialog_->line_color(), | ||||||
|  |                     editLineDialog_->border_width(), | ||||||
|  |                     editLineDialog_->highlight_width(), | ||||||
|  |                     editLineDialog_->line_width()); | ||||||
|  | 
 | ||||||
|  |                  // Reset the active line settings
 | ||||||
|  |                  activeLineSettings_ = nullptr; | ||||||
|  |               } | ||||||
|  |            }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AlertPaletteSettingsWidget::Impl::SelectPhenomenon( | ||||||
|  |    awips::Phenomenon phenomenon) | ||||||
|  | { | ||||||
|  |    auto it = phenomenonPages_.find(phenomenon); | ||||||
|  |    if (it != phenomenonPages_.cend()) | ||||||
|  |    { | ||||||
|  |       phenomenonPagesWidget_->setCurrentWidget(it->second); | ||||||
|  |    } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | QWidget* AlertPaletteSettingsWidget::Impl::CreateStackedWidgetPage( | ||||||
|  |    awips::Phenomenon phenomenon) | ||||||
|  | { | ||||||
|  |    QWidget*     page       = new QWidget(self_); | ||||||
|  |    QGridLayout* gridLayout = new QGridLayout(self_); | ||||||
|  |    page->setLayout(gridLayout); | ||||||
|  | 
 | ||||||
|  |    const auto& impactBasedWarningInfo = | ||||||
|  |       awips::ibw::GetImpactBasedWarningInfo(phenomenon); | ||||||
|  | 
 | ||||||
|  |    auto& alertPalette = | ||||||
|  |       settings::PaletteSettings::Instance().alert_palette(phenomenon); | ||||||
|  | 
 | ||||||
|  |    int row = 0; | ||||||
|  | 
 | ||||||
|  |    // Add a blank label to align left and right widgets
 | ||||||
|  |    gridLayout->addWidget(new QLabel(self_), row++, 0); | ||||||
|  | 
 | ||||||
|  |    AddPhenomenonLine( | ||||||
|  |       "Active", | ||||||
|  |       alertPalette.threat_category(awips::ibw::ThreatCategory::Base), | ||||||
|  |       gridLayout, | ||||||
|  |       row++); | ||||||
|  | 
 | ||||||
|  |    if (impactBasedWarningInfo.hasObservedTag_) | ||||||
|  |    { | ||||||
|  |       AddPhenomenonLine("Observed", alertPalette.observed(), gridLayout, row++); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    if (impactBasedWarningInfo.hasTornadoPossibleTag_) | ||||||
|  |    { | ||||||
|  |       AddPhenomenonLine("Tornado Possible", | ||||||
|  |                         alertPalette.tornado_possible(), | ||||||
|  |                         gridLayout, | ||||||
|  |                         row++); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    for (auto& category : impactBasedWarningInfo.threatCategories_) | ||||||
|  |    { | ||||||
|  |       if (category == awips::ibw::ThreatCategory::Base) | ||||||
|  |       { | ||||||
|  |          continue; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       AddPhenomenonLine(awips::ibw::GetThreatCategoryName(category), | ||||||
|  |                         alertPalette.threat_category(category), | ||||||
|  |                         gridLayout, | ||||||
|  |                         row++); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    AddPhenomenonLine("Inactive", alertPalette.inactive(), gridLayout, row++); | ||||||
|  | 
 | ||||||
|  |    QSpacerItem* spacer = | ||||||
|  |       new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding); | ||||||
|  |    gridLayout->addItem(spacer, row, 0); | ||||||
|  | 
 | ||||||
|  |    return page; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AlertPaletteSettingsWidget::Impl::AddPhenomenonLine( | ||||||
|  |    const std::string&      name, | ||||||
|  |    settings::LineSettings& lineSettings, | ||||||
|  |    QGridLayout*            layout, | ||||||
|  |    int                     row) | ||||||
|  | { | ||||||
|  |    QToolButton* toolButton = new QToolButton(self_); | ||||||
|  |    toolButton->setText(tr("...")); | ||||||
|  | 
 | ||||||
|  |    LineLabel* lineLabel = new LineLabel(self_); | ||||||
|  |    lineLabel->set_line_settings(lineSettings); | ||||||
|  | 
 | ||||||
|  |    QToolButton* resetButton = new QToolButton(self_); | ||||||
|  |    resetButton->setIcon( | ||||||
|  |       QIcon {":/res/icons/font-awesome-6/rotate-left-solid.svg"}); | ||||||
|  |    resetButton->setVisible(!lineSettings.IsDefaultStaged()); | ||||||
|  | 
 | ||||||
|  |    layout->addWidget(new QLabel(tr(name.c_str()), self_), row, 0); | ||||||
|  |    layout->addWidget(lineLabel, row, 1); | ||||||
|  |    layout->addWidget(toolButton, row, 2); | ||||||
|  |    layout->addWidget(resetButton, row, 3); | ||||||
|  | 
 | ||||||
|  |    self_->AddSettingsCategory(&lineSettings); | ||||||
|  | 
 | ||||||
|  |    connect(toolButton, | ||||||
|  |            &QAbstractButton::clicked, | ||||||
|  |            self_, | ||||||
|  |            [this, lineLabel, &lineSettings]() | ||||||
|  |            { | ||||||
|  |               // Set the active line label for when the dialog is finished
 | ||||||
|  |               activeLineSettings_ = &lineSettings; | ||||||
|  | 
 | ||||||
|  |               // Initialize dialog with current line settings
 | ||||||
|  |               editLineDialog_->Initialize(lineLabel->border_color(), | ||||||
|  |                                           lineLabel->highlight_color(), | ||||||
|  |                                           lineLabel->line_color(), | ||||||
|  |                                           lineLabel->border_width(), | ||||||
|  |                                           lineLabel->highlight_width(), | ||||||
|  |                                           lineLabel->line_width()); | ||||||
|  | 
 | ||||||
|  |               // Show the dialog
 | ||||||
|  |               editLineDialog_->show(); | ||||||
|  |            }); | ||||||
|  | 
 | ||||||
|  |    connect(resetButton, | ||||||
|  |            &QAbstractButton::clicked, | ||||||
|  |            self_, | ||||||
|  |            [&lineSettings]() { lineSettings.StageDefaults(); }); | ||||||
|  | 
 | ||||||
|  |    connections_.emplace_back(lineSettings.staged_signal().connect( | ||||||
|  |       [resetButton, &lineSettings]() | ||||||
|  |       { resetButton->setVisible(!lineSettings.IsDefaultStaged()); })); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace ui
 | ||||||
|  | } // namespace qt
 | ||||||
|  | } // namespace scwx
 | ||||||
|  | @ -0,0 +1,29 @@ | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <scwx/qt/ui/settings/settings_page_widget.hpp> | ||||||
|  | 
 | ||||||
|  | #include <QWidget> | ||||||
|  | 
 | ||||||
|  | namespace scwx | ||||||
|  | { | ||||||
|  | namespace qt | ||||||
|  | { | ||||||
|  | namespace ui | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | class AlertPaletteSettingsWidget : public SettingsPageWidget | ||||||
|  | { | ||||||
|  |    Q_OBJECT | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |    explicit AlertPaletteSettingsWidget(QWidget* parent = nullptr); | ||||||
|  |    ~AlertPaletteSettingsWidget(); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |    class Impl; | ||||||
|  |    std::shared_ptr<Impl> p; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace ui
 | ||||||
|  | } // namespace qt
 | ||||||
|  | } // namespace scwx
 | ||||||
|  | @ -19,6 +19,7 @@ public: | ||||||
|    explicit Impl() {} |    explicit Impl() {} | ||||||
|    ~Impl() = default; |    ~Impl() = default; | ||||||
| 
 | 
 | ||||||
|  |    std::vector<settings::SettingsCategory*>      categories_; | ||||||
|    std::vector<settings::SettingsInterfaceBase*> settings_; |    std::vector<settings::SettingsInterfaceBase*> settings_; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -29,6 +30,12 @@ SettingsPageWidget::SettingsPageWidget(QWidget* parent) : | ||||||
| 
 | 
 | ||||||
| SettingsPageWidget::~SettingsPageWidget() = default; | SettingsPageWidget::~SettingsPageWidget() = default; | ||||||
| 
 | 
 | ||||||
|  | void SettingsPageWidget::AddSettingsCategory( | ||||||
|  |    settings::SettingsCategory* category) | ||||||
|  | { | ||||||
|  |    p->categories_.push_back(category); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void SettingsPageWidget::AddSettingsInterface( | void SettingsPageWidget::AddSettingsInterface( | ||||||
|    settings::SettingsInterfaceBase* setting) |    settings::SettingsInterfaceBase* setting) | ||||||
| { | { | ||||||
|  | @ -39,6 +46,11 @@ bool SettingsPageWidget::CommitChanges() | ||||||
| { | { | ||||||
|    bool committed = false; |    bool committed = false; | ||||||
| 
 | 
 | ||||||
|  |    for (auto& category : p->categories_) | ||||||
|  |    { | ||||||
|  |       committed |= category->Commit(); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|    for (auto& setting : p->settings_) |    for (auto& setting : p->settings_) | ||||||
|    { |    { | ||||||
|       committed |= setting->Commit(); |       committed |= setting->Commit(); | ||||||
|  | @ -49,6 +61,11 @@ bool SettingsPageWidget::CommitChanges() | ||||||
| 
 | 
 | ||||||
| void SettingsPageWidget::DiscardChanges() | void SettingsPageWidget::DiscardChanges() | ||||||
| { | { | ||||||
|  |    for (auto& category : p->categories_) | ||||||
|  |    { | ||||||
|  |       category->Reset(); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|    for (auto& setting : p->settings_) |    for (auto& setting : p->settings_) | ||||||
|    { |    { | ||||||
|       setting->Reset(); |       setting->Reset(); | ||||||
|  |  | ||||||
|  | @ -1,5 +1,6 @@ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
|  | #include <scwx/qt/settings/settings_category.hpp> | ||||||
| #include <scwx/qt/settings/settings_interface_base.hpp> | #include <scwx/qt/settings/settings_interface_base.hpp> | ||||||
| 
 | 
 | ||||||
| #include <QWidget> | #include <QWidget> | ||||||
|  | @ -61,6 +62,15 @@ public: | ||||||
| protected: | protected: | ||||||
|    void AddSettingsInterface(settings::SettingsInterfaceBase* setting); |    void AddSettingsInterface(settings::SettingsInterfaceBase* setting); | ||||||
| 
 | 
 | ||||||
|  |    /**
 | ||||||
|  |     * Commits and resets all settings within a category upon page commit or | ||||||
|  |     * reset. The use of SettingsInterface is preferred, as it allows the binding | ||||||
|  |     * of widgets to these actions. | ||||||
|  |     * | ||||||
|  |     * @param [in] category Settings category | ||||||
|  |     */ | ||||||
|  |    void AddSettingsCategory(settings::SettingsCategory* category); | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
|    class Impl; |    class Impl; | ||||||
|    std::shared_ptr<Impl> p; |    std::shared_ptr<Impl> p; | ||||||
|  |  | ||||||
|  | @ -23,11 +23,12 @@ | ||||||
| #include <scwx/qt/types/time_types.hpp> | #include <scwx/qt/types/time_types.hpp> | ||||||
| #include <scwx/qt/types/unit_types.hpp> | #include <scwx/qt/types/unit_types.hpp> | ||||||
| #include <scwx/qt/ui/county_dialog.hpp> | #include <scwx/qt/ui/county_dialog.hpp> | ||||||
| #include <scwx/qt/ui/wfo_dialog.hpp> |  | ||||||
| #include <scwx/qt/ui/radar_site_dialog.hpp> | #include <scwx/qt/ui/radar_site_dialog.hpp> | ||||||
| #include <scwx/qt/ui/serial_port_dialog.hpp> | #include <scwx/qt/ui/serial_port_dialog.hpp> | ||||||
|  | #include <scwx/qt/ui/settings/alert_palette_settings_widget.hpp> | ||||||
| #include <scwx/qt/ui/settings/hotkey_settings_widget.hpp> | #include <scwx/qt/ui/settings/hotkey_settings_widget.hpp> | ||||||
| #include <scwx/qt/ui/settings/unit_settings_widget.hpp> | #include <scwx/qt/ui/settings/unit_settings_widget.hpp> | ||||||
|  | #include <scwx/qt/ui/wfo_dialog.hpp> | ||||||
| #include <scwx/qt/util/color.hpp> | #include <scwx/qt/util/color.hpp> | ||||||
| #include <scwx/qt/util/file.hpp> | #include <scwx/qt/util/file.hpp> | ||||||
| #include <scwx/util/logger.hpp> | #include <scwx/util/logger.hpp> | ||||||
|  | @ -181,7 +182,6 @@ public: | ||||||
|    void SetupTextTab(); |    void SetupTextTab(); | ||||||
|    void SetupHotkeysTab(); |    void SetupHotkeysTab(); | ||||||
| 
 | 
 | ||||||
|    void ShowColorDialog(QLineEdit* lineEdit); |  | ||||||
|    void UpdateRadarDialogLocation(const std::string& id); |    void UpdateRadarDialogLocation(const std::string& id); | ||||||
|    void UpdateAlertRadarDialogLocation(const std::string& id); |    void UpdateAlertRadarDialogLocation(const std::string& id); | ||||||
| 
 | 
 | ||||||
|  | @ -204,7 +204,6 @@ public: | ||||||
|                                      QLabel*            imageLabel); |                                      QLabel*            imageLabel); | ||||||
|    static std::string |    static std::string | ||||||
|    RadarSiteLabel(std::shared_ptr<config::RadarSite>& radarSite); |    RadarSiteLabel(std::shared_ptr<config::RadarSite>& radarSite); | ||||||
|    static void SetBackgroundColor(const std::string& value, QFrame* frame); |  | ||||||
| 
 | 
 | ||||||
|    SettingsDialog*   self_; |    SettingsDialog*   self_; | ||||||
|    RadarSiteDialog*  radarSiteDialog_; |    RadarSiteDialog*  radarSiteDialog_; | ||||||
|  | @ -224,6 +223,7 @@ public: | ||||||
|       manager::PositionManager::Instance()}; |       manager::PositionManager::Instance()}; | ||||||
| 
 | 
 | ||||||
|    std::vector<SettingsPageWidget*> settingsPages_ {}; |    std::vector<SettingsPageWidget*> settingsPages_ {}; | ||||||
|  |    AlertPaletteSettingsWidget*      alertPaletteSettingsWidget_ {}; | ||||||
|    HotkeySettingsWidget*            hotkeySettingsWidget_ {}; |    HotkeySettingsWidget*            hotkeySettingsWidget_ {}; | ||||||
|    UnitSettingsWidget*              unitSettingsWidget_ {}; |    UnitSettingsWidget*              unitSettingsWidget_ {}; | ||||||
| 
 | 
 | ||||||
|  | @ -252,12 +252,6 @@ public: | ||||||
| 
 | 
 | ||||||
|    std::unordered_map<std::string, settings::SettingsInterface<std::string>> |    std::unordered_map<std::string, settings::SettingsInterface<std::string>> | ||||||
|       colorTables_ {}; |       colorTables_ {}; | ||||||
|    std::unordered_map<awips::Phenomenon, |  | ||||||
|                       settings::SettingsInterface<std::string>> |  | ||||||
|       activeAlertColors_ {}; |  | ||||||
|    std::unordered_map<awips::Phenomenon, |  | ||||||
|                       settings::SettingsInterface<std::string>> |  | ||||||
|       inactiveAlertColors_ {}; |  | ||||||
| 
 | 
 | ||||||
|    settings::SettingsInterface<std::string> alertAudioSoundFile_ {}; |    settings::SettingsInterface<std::string> alertAudioSoundFile_ {}; | ||||||
|    settings::SettingsInterface<std::string> alertAudioLocationMethod_ {}; |    settings::SettingsInterface<std::string> alertAudioLocationMethod_ {}; | ||||||
|  | @ -358,22 +352,21 @@ void SettingsDialogImpl::ConnectSignals() | ||||||
|                     self_, |                     self_, | ||||||
|                     [this]() { alertAudioRadarSiteDialog_->show(); }); |                     [this]() { alertAudioRadarSiteDialog_->show(); }); | ||||||
| 
 | 
 | ||||||
|    QObject::connect(alertAudioRadarSiteDialog_, |    QObject::connect( | ||||||
|  |       alertAudioRadarSiteDialog_, | ||||||
|       &RadarSiteDialog::accepted, |       &RadarSiteDialog::accepted, | ||||||
|       self_, |       self_, | ||||||
|       [this]() |       [this]() | ||||||
|       { |       { | ||||||
|                        std::string id = |          std::string id = alertAudioRadarSiteDialog_->radar_site(); | ||||||
|                           alertAudioRadarSiteDialog_->radar_site(); |  | ||||||
| 
 | 
 | ||||||
|          std::shared_ptr<config::RadarSite> radarSite = |          std::shared_ptr<config::RadarSite> radarSite = | ||||||
|             config::RadarSite::Get(id); |             config::RadarSite::Get(id); | ||||||
| 
 | 
 | ||||||
|          if (radarSite != nullptr) |          if (radarSite != nullptr) | ||||||
|          { |          { | ||||||
|                           self_->ui->alertAudioRadarSiteComboBox |             self_->ui->alertAudioRadarSiteComboBox->setCurrentText( | ||||||
|                              ->setCurrentText(QString::fromStdString( |                QString::fromStdString(RadarSiteLabel(radarSite))); | ||||||
|                                 RadarSiteLabel(radarSite))); |  | ||||||
|          } |          } | ||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
|  | @ -803,123 +796,14 @@ void SettingsDialogImpl::SetupPalettesColorTablesTab() | ||||||
| 
 | 
 | ||||||
| void SettingsDialogImpl::SetupPalettesAlertsTab() | void SettingsDialogImpl::SetupPalettesAlertsTab() | ||||||
| { | { | ||||||
|    settings::PaletteSettings& paletteSettings = |  | ||||||
|       settings::PaletteSettings::Instance(); |  | ||||||
| 
 |  | ||||||
|    // Palettes > Alerts
 |    // Palettes > Alerts
 | ||||||
|    QGridLayout* alertsLayout = |    QVBoxLayout* layout = new QVBoxLayout(self_->ui->alertsPalette); | ||||||
|       reinterpret_cast<QGridLayout*>(self_->ui->alertsFrame->layout()); |  | ||||||
| 
 | 
 | ||||||
|    QLabel* phenomenonLabel = new QLabel(QObject::tr("Phenomenon"), self_); |    alertPaletteSettingsWidget_ = | ||||||
|    QLabel* activeLabel     = new QLabel(QObject::tr("Active"), self_); |       new AlertPaletteSettingsWidget(self_->ui->hotkeys); | ||||||
|    QLabel* inactiveLabel   = new QLabel(QObject::tr("Inactive"), self_); |    layout->addWidget(alertPaletteSettingsWidget_); | ||||||
| 
 | 
 | ||||||
|    QFont boldFont; |    settingsPages_.push_back(alertPaletteSettingsWidget_); | ||||||
|    boldFont.setBold(true); |  | ||||||
|    phenomenonLabel->setFont(boldFont); |  | ||||||
|    activeLabel->setFont(boldFont); |  | ||||||
|    inactiveLabel->setFont(boldFont); |  | ||||||
| 
 |  | ||||||
|    alertsLayout->addWidget(phenomenonLabel, 0, 0); |  | ||||||
|    alertsLayout->addWidget(activeLabel, 0, 1, 1, 4); |  | ||||||
|    alertsLayout->addWidget(inactiveLabel, 0, 5, 1, 4); |  | ||||||
| 
 |  | ||||||
|    auto& alertPhenomena = settings::PaletteSettings::alert_phenomena(); |  | ||||||
| 
 |  | ||||||
|    activeAlertColors_.reserve(alertPhenomena.size()); |  | ||||||
|    inactiveAlertColors_.reserve(alertPhenomena.size()); |  | ||||||
| 
 |  | ||||||
|    int alertsRow = 1; |  | ||||||
|    for (auto& phenomenon : alertPhenomena) |  | ||||||
|    { |  | ||||||
|       QFrame* activeFrame   = new QFrame(self_); |  | ||||||
|       QFrame* inactiveFrame = new QFrame(self_); |  | ||||||
| 
 |  | ||||||
|       QLineEdit* activeEdit   = new QLineEdit(self_); |  | ||||||
|       QLineEdit* inactiveEdit = new QLineEdit(self_); |  | ||||||
| 
 |  | ||||||
|       QToolButton* activeButton        = new QToolButton(self_); |  | ||||||
|       QToolButton* inactiveButton      = new QToolButton(self_); |  | ||||||
|       QToolButton* activeResetButton   = new QToolButton(self_); |  | ||||||
|       QToolButton* inactiveResetButton = new QToolButton(self_); |  | ||||||
| 
 |  | ||||||
|       activeFrame->setMinimumHeight(24); |  | ||||||
|       activeFrame->setMinimumWidth(24); |  | ||||||
|       activeFrame->setFrameShape(QFrame::Shape::Box); |  | ||||||
|       activeFrame->setFrameShadow(QFrame::Shadow::Plain); |  | ||||||
|       inactiveFrame->setMinimumHeight(24); |  | ||||||
|       inactiveFrame->setMinimumWidth(24); |  | ||||||
|       inactiveFrame->setFrameShape(QFrame::Shape::Box); |  | ||||||
|       inactiveFrame->setFrameShadow(QFrame::Shadow::Plain); |  | ||||||
| 
 |  | ||||||
|       activeButton->setIcon( |  | ||||||
|          QIcon {":/res/icons/font-awesome-6/palette-solid.svg"}); |  | ||||||
|       inactiveButton->setIcon( |  | ||||||
|          QIcon {":/res/icons/font-awesome-6/palette-solid.svg"}); |  | ||||||
|       activeResetButton->setIcon( |  | ||||||
|          QIcon {":/res/icons/font-awesome-6/rotate-left-solid.svg"}); |  | ||||||
|       inactiveResetButton->setIcon( |  | ||||||
|          QIcon {":/res/icons/font-awesome-6/rotate-left-solid.svg"}); |  | ||||||
| 
 |  | ||||||
|       alertsLayout->addWidget( |  | ||||||
|          new QLabel(QObject::tr(awips::GetPhenomenonText(phenomenon).c_str()), |  | ||||||
|                     self_), |  | ||||||
|          alertsRow, |  | ||||||
|          0); |  | ||||||
|       alertsLayout->addWidget(activeFrame, alertsRow, 1); |  | ||||||
|       alertsLayout->addWidget(activeEdit, alertsRow, 2); |  | ||||||
|       alertsLayout->addWidget(activeButton, alertsRow, 3); |  | ||||||
|       alertsLayout->addWidget(activeResetButton, alertsRow, 4); |  | ||||||
|       alertsLayout->addWidget(inactiveFrame, alertsRow, 5); |  | ||||||
|       alertsLayout->addWidget(inactiveEdit, alertsRow, 6); |  | ||||||
|       alertsLayout->addWidget(inactiveButton, alertsRow, 7); |  | ||||||
|       alertsLayout->addWidget(inactiveResetButton, alertsRow, 8); |  | ||||||
|       ++alertsRow; |  | ||||||
| 
 |  | ||||||
|       // Create settings interface
 |  | ||||||
|       auto activeResult = activeAlertColors_.emplace( |  | ||||||
|          phenomenon, settings::SettingsInterface<std::string> {}); |  | ||||||
|       auto inactiveResult = inactiveAlertColors_.emplace( |  | ||||||
|          phenomenon, settings::SettingsInterface<std::string> {}); |  | ||||||
|       auto& activeColor   = activeResult.first->second; |  | ||||||
|       auto& inactiveColor = inactiveResult.first->second; |  | ||||||
| 
 |  | ||||||
|       // Add to settings list
 |  | ||||||
|       settings_.push_back(&activeColor); |  | ||||||
|       settings_.push_back(&inactiveColor); |  | ||||||
| 
 |  | ||||||
|       auto& activeSetting   = paletteSettings.alert_color(phenomenon, true); |  | ||||||
|       auto& inactiveSetting = paletteSettings.alert_color(phenomenon, false); |  | ||||||
| 
 |  | ||||||
|       activeColor.SetSettingsVariable(activeSetting); |  | ||||||
|       activeColor.SetEditWidget(activeEdit); |  | ||||||
|       activeColor.SetResetButton(activeResetButton); |  | ||||||
| 
 |  | ||||||
|       inactiveColor.SetSettingsVariable(inactiveSetting); |  | ||||||
|       inactiveColor.SetEditWidget(inactiveEdit); |  | ||||||
|       inactiveColor.SetResetButton(inactiveResetButton); |  | ||||||
| 
 |  | ||||||
|       SetBackgroundColor(activeSetting.GetValue(), activeFrame); |  | ||||||
|       SetBackgroundColor(inactiveSetting.GetValue(), inactiveFrame); |  | ||||||
| 
 |  | ||||||
|       activeSetting.RegisterValueStagedCallback( |  | ||||||
|          [activeFrame](const std::string& value) |  | ||||||
|          { SetBackgroundColor(value, activeFrame); }); |  | ||||||
|       inactiveSetting.RegisterValueStagedCallback( |  | ||||||
|          [inactiveFrame](const std::string& value) |  | ||||||
|          { SetBackgroundColor(value, inactiveFrame); }); |  | ||||||
| 
 |  | ||||||
|       QObject::connect(activeButton, |  | ||||||
|                        &QAbstractButton::clicked, |  | ||||||
|                        self_, |  | ||||||
|                        [=, this]() |  | ||||||
|                        { ShowColorDialog(activeEdit); }); |  | ||||||
|       QObject::connect(inactiveButton, |  | ||||||
|                        &QAbstractButton::clicked, |  | ||||||
|                        self_, |  | ||||||
|                        [=, this]() |  | ||||||
|                        { ShowColorDialog(inactiveEdit); }); |  | ||||||
|    } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void SettingsDialogImpl::SetupUnitsTab() | void SettingsDialogImpl::SetupUnitsTab() | ||||||
|  | @ -953,8 +837,7 @@ void SettingsDialogImpl::SetupAudioTab() | ||||||
|             locationMethod == types::LocationMethod::RadarSite; |             locationMethod == types::LocationMethod::RadarSite; | ||||||
|          bool countyEntryEnabled = |          bool countyEntryEnabled = | ||||||
|             locationMethod == types::LocationMethod::County; |             locationMethod == types::LocationMethod::County; | ||||||
|          bool wfoEntryEnabled = |          bool wfoEntryEnabled = locationMethod == types::LocationMethod::WFO; | ||||||
|             locationMethod == types::LocationMethod::WFO; |  | ||||||
| 
 | 
 | ||||||
|          self_->ui->alertAudioLatitudeSpinBox->setEnabled( |          self_->ui->alertAudioLatitudeSpinBox->setEnabled( | ||||||
|             coordinateEntryEnabled); |             coordinateEntryEnabled); | ||||||
|  | @ -972,10 +855,8 @@ void SettingsDialogImpl::SetupAudioTab() | ||||||
|          self_->ui->resetAlertAudioRadarSiteButton->setEnabled( |          self_->ui->resetAlertAudioRadarSiteButton->setEnabled( | ||||||
|             radarSiteEntryEnable); |             radarSiteEntryEnable); | ||||||
| 
 | 
 | ||||||
|          self_->ui->alertAudioRadiusSpinBox->setEnabled( |          self_->ui->alertAudioRadiusSpinBox->setEnabled(radiusEntryEnable); | ||||||
|             radiusEntryEnable); |          self_->ui->resetAlertAudioRadiusButton->setEnabled(radiusEntryEnable); | ||||||
|          self_->ui->resetAlertAudioRadiusButton->setEnabled( |  | ||||||
|             radiusEntryEnable); |  | ||||||
| 
 | 
 | ||||||
|          self_->ui->alertAudioCountyLineEdit->setEnabled(countyEntryEnabled); |          self_->ui->alertAudioCountyLineEdit->setEnabled(countyEntryEnabled); | ||||||
|          self_->ui->alertAudioCountySelectButton->setEnabled( |          self_->ui->alertAudioCountySelectButton->setEnabled( | ||||||
|  | @ -1091,8 +972,7 @@ void SettingsDialogImpl::SetupAudioTab() | ||||||
| 
 | 
 | ||||||
|    alertAudioRadius_.SetSettingsVariable(audioSettings.alert_radius()); |    alertAudioRadius_.SetSettingsVariable(audioSettings.alert_radius()); | ||||||
|    alertAudioRadius_.SetEditWidget(self_->ui->alertAudioRadiusSpinBox); |    alertAudioRadius_.SetEditWidget(self_->ui->alertAudioRadiusSpinBox); | ||||||
|    alertAudioRadius_.SetResetButton( |    alertAudioRadius_.SetResetButton(self_->ui->resetAlertAudioRadiusButton); | ||||||
|       self_->ui->resetAlertAudioRadiusButton); |  | ||||||
|    alertAudioRadius_.SetUnitLabel(self_->ui->alertAudioRadiusUnitsLabel); |    alertAudioRadius_.SetUnitLabel(self_->ui->alertAudioRadiusUnitsLabel); | ||||||
|    auto alertAudioRadiusUpdateUnits = [this](const std::string& newValue) |    auto alertAudioRadiusUpdateUnits = [this](const std::string& newValue) | ||||||
|    { |    { | ||||||
|  | @ -1206,14 +1086,10 @@ void SettingsDialogImpl::SetupAudioTab() | ||||||
|    alertAudioCounty_.SetEditWidget(self_->ui->alertAudioCountyLineEdit); |    alertAudioCounty_.SetEditWidget(self_->ui->alertAudioCountyLineEdit); | ||||||
|    alertAudioCounty_.SetResetButton(self_->ui->resetAlertAudioCountyButton); |    alertAudioCounty_.SetResetButton(self_->ui->resetAlertAudioCountyButton); | ||||||
| 
 | 
 | ||||||
|    QObject::connect( |    QObject::connect(self_->ui->alertAudioWFOSelectButton, | ||||||
|       self_->ui->alertAudioWFOSelectButton, |  | ||||||
|                     &QAbstractButton::clicked, |                     &QAbstractButton::clicked, | ||||||
|                     self_, |                     self_, | ||||||
|       [this]() |                     [this]() { wfoDialog_->show(); }); | ||||||
|       { |  | ||||||
|          wfoDialog_->show(); |  | ||||||
|       }); |  | ||||||
|    QObject::connect(wfoDialog_, |    QObject::connect(wfoDialog_, | ||||||
|                     &WFODialog::accepted, |                     &WFODialog::accepted, | ||||||
|                     self_, |                     self_, | ||||||
|  | @ -1232,8 +1108,7 @@ void SettingsDialogImpl::SetupAudioTab() | ||||||
|                     self_, |                     self_, | ||||||
|                     [this](const QString& text) |                     [this](const QString& text) | ||||||
|                     { |                     { | ||||||
|                        std::string wfoName = |                        std::string wfoName = config::CountyDatabase::GetWFOName( | ||||||
|                           config::CountyDatabase::GetWFOName( |  | ||||||
|                           text.toStdString()); |                           text.toStdString()); | ||||||
|                        self_->ui->alertAudioWFOLabel->setText( |                        self_->ui->alertAudioWFOLabel->setText( | ||||||
|                           QString::fromStdString(wfoName)); |                           QString::fromStdString(wfoName)); | ||||||
|  | @ -1242,7 +1117,6 @@ void SettingsDialogImpl::SetupAudioTab() | ||||||
|    alertAudioWFO_.SetSettingsVariable(audioSettings.alert_wfo()); |    alertAudioWFO_.SetSettingsVariable(audioSettings.alert_wfo()); | ||||||
|    alertAudioWFO_.SetEditWidget(self_->ui->alertAudioWFOLineEdit); |    alertAudioWFO_.SetEditWidget(self_->ui->alertAudioWFOLineEdit); | ||||||
|    alertAudioWFO_.SetResetButton(self_->ui->resetAlertAudioWFOButton); |    alertAudioWFO_.SetResetButton(self_->ui->resetAlertAudioWFOButton); | ||||||
| 
 |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void SettingsDialogImpl::SetupTextTab() | void SettingsDialogImpl::SetupTextTab() | ||||||
|  | @ -1384,44 +1258,6 @@ void SettingsDialogImpl::LoadColorTablePreview(const std::string& key, | ||||||
|       }); |       }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void SettingsDialogImpl::ShowColorDialog(QLineEdit* lineEdit) |  | ||||||
| { |  | ||||||
|    QColorDialog* dialog = new QColorDialog(self_); |  | ||||||
| 
 |  | ||||||
|    dialog->setAttribute(Qt::WA_DeleteOnClose); |  | ||||||
|    dialog->setOption(QColorDialog::ColorDialogOption::ShowAlphaChannel); |  | ||||||
| 
 |  | ||||||
|    QColor initialColor(lineEdit->text()); |  | ||||||
|    if (initialColor.isValid()) |  | ||||||
|    { |  | ||||||
|       dialog->setCurrentColor(initialColor); |  | ||||||
|    } |  | ||||||
| 
 |  | ||||||
|    QObject::connect( |  | ||||||
|       dialog, |  | ||||||
|       &QColorDialog::colorSelected, |  | ||||||
|       self_, |  | ||||||
|       [lineEdit](const QColor& color) |  | ||||||
|       { |  | ||||||
|          QString colorName = color.name(QColor::NameFormat::HexArgb); |  | ||||||
| 
 |  | ||||||
|          logger_->info("Selected color: {}", colorName.toStdString()); |  | ||||||
|          lineEdit->setText(colorName); |  | ||||||
| 
 |  | ||||||
|          // setText does not emit the textEdited signal
 |  | ||||||
|          Q_EMIT lineEdit->textEdited(colorName); |  | ||||||
|       }); |  | ||||||
| 
 |  | ||||||
|    dialog->open(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void SettingsDialogImpl::SetBackgroundColor(const std::string& value, |  | ||||||
|                                             QFrame*            frame) |  | ||||||
| { |  | ||||||
|    frame->setStyleSheet( |  | ||||||
|       QString::fromStdString(fmt::format("background-color: {}", value))); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void SettingsDialogImpl::UpdateRadarDialogLocation(const std::string& id) | void SettingsDialogImpl::UpdateRadarDialogLocation(const std::string& id) | ||||||
| { | { | ||||||
|    std::shared_ptr<config::RadarSite> radarSite = config::RadarSite::Get(id); |    std::shared_ptr<config::RadarSite> radarSite = config::RadarSite::Get(id); | ||||||
|  | @ -1444,8 +1280,6 @@ void SettingsDialogImpl::UpdateAlertRadarDialogLocation(const std::string& id) | ||||||
|    } |    } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| QFont SettingsDialogImpl::GetSelectedFont() | QFont SettingsDialogImpl::GetSelectedFont() | ||||||
| { | { | ||||||
|    std::string fontFamily = fontFamilies_.at(selectedFontCategory_) |    std::string fontFamily = fontFamilies_.at(selectedFontCategory_) | ||||||
|  |  | ||||||
|  | @ -122,7 +122,7 @@ | ||||||
|           </sizepolicy> |           </sizepolicy> | ||||||
|          </property> |          </property> | ||||||
|          <property name="currentIndex"> |          <property name="currentIndex"> | ||||||
|           <number>3</number> |           <number>0</number> | ||||||
|          </property> |          </property> | ||||||
|          <widget class="QWidget" name="general"> |          <widget class="QWidget" name="general"> | ||||||
|           <layout class="QVBoxLayout" name="verticalLayout_2"> |           <layout class="QVBoxLayout" name="verticalLayout_2"> | ||||||
|  | @ -136,8 +136,8 @@ | ||||||
|                <rect> |                <rect> | ||||||
|                 <x>0</x> |                 <x>0</x> | ||||||
|                 <y>0</y> |                 <y>0</y> | ||||||
|                 <width>274</width> |                 <width>513</width> | ||||||
|                 <height>691</height> |                 <height>622</height> | ||||||
|                </rect> |                </rect> | ||||||
|               </property> |               </property> | ||||||
|               <layout class="QVBoxLayout" name="verticalLayout"> |               <layout class="QVBoxLayout" name="verticalLayout"> | ||||||
|  | @ -610,8 +610,8 @@ | ||||||
|                    <rect> |                    <rect> | ||||||
|                     <x>0</x> |                     <x>0</x> | ||||||
|                     <y>0</y> |                     <y>0</y> | ||||||
|                     <width>98</width> |                     <width>506</width> | ||||||
|                     <height>28</height> |                     <height>383</height> | ||||||
|                    </rect> |                    </rect> | ||||||
|                   </property> |                   </property> | ||||||
|                   <layout class="QGridLayout" name="gridLayout_3"> |                   <layout class="QGridLayout" name="gridLayout_3"> | ||||||
|  | @ -634,49 +634,10 @@ | ||||||
|                </item> |                </item> | ||||||
|               </layout> |               </layout> | ||||||
|              </widget> |              </widget> | ||||||
|              <widget class="QWidget" name="tab_2"> |              <widget class="QWidget" name="alertsPalette"> | ||||||
|               <attribute name="title"> |               <attribute name="title"> | ||||||
|                <string>Alerts</string> |                <string>Alerts</string> | ||||||
|               </attribute> |               </attribute> | ||||||
|               <layout class="QVBoxLayout" name="verticalLayout_3"> |  | ||||||
|                <item> |  | ||||||
|                 <widget class="QFrame" name="alertsFrame"> |  | ||||||
|                  <property name="frameShape"> |  | ||||||
|                   <enum>QFrame::Shape::StyledPanel</enum> |  | ||||||
|                  </property> |  | ||||||
|                  <property name="frameShadow"> |  | ||||||
|                   <enum>QFrame::Shadow::Raised</enum> |  | ||||||
|                  </property> |  | ||||||
|                  <layout class="QGridLayout" name="gridLayout_5"> |  | ||||||
|                   <property name="leftMargin"> |  | ||||||
|                    <number>0</number> |  | ||||||
|                   </property> |  | ||||||
|                   <property name="topMargin"> |  | ||||||
|                    <number>0</number> |  | ||||||
|                   </property> |  | ||||||
|                   <property name="rightMargin"> |  | ||||||
|                    <number>0</number> |  | ||||||
|                   </property> |  | ||||||
|                   <property name="bottomMargin"> |  | ||||||
|                    <number>0</number> |  | ||||||
|                   </property> |  | ||||||
|                  </layout> |  | ||||||
|                 </widget> |  | ||||||
|                </item> |  | ||||||
|                <item> |  | ||||||
|                 <spacer name="verticalSpacer_2"> |  | ||||||
|                  <property name="orientation"> |  | ||||||
|                   <enum>Qt::Orientation::Vertical</enum> |  | ||||||
|                  </property> |  | ||||||
|                  <property name="sizeHint" stdset="0"> |  | ||||||
|                   <size> |  | ||||||
|                    <width>20</width> |  | ||||||
|                    <height>239</height> |  | ||||||
|                   </size> |  | ||||||
|                  </property> |  | ||||||
|                 </spacer> |  | ||||||
|                </item> |  | ||||||
|               </layout> |  | ||||||
|              </widget> |              </widget> | ||||||
|             </widget> |             </widget> | ||||||
|            </item> |            </item> | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| #include <scwx/qt/util/color.hpp> | #include <scwx/qt/util/color.hpp> | ||||||
| 
 | 
 | ||||||
| #include <fmt/format.h> | #include <fmt/format.h> | ||||||
|  | #include <re2/re2.h> | ||||||
| #include <QColor> | #include <QColor> | ||||||
| 
 | 
 | ||||||
| namespace scwx | namespace scwx | ||||||
|  | @ -38,6 +39,12 @@ boost::gil::rgba32f_pixel_t ToRgba32fPixelT(const std::string& argbString) | ||||||
|                                        rgba8Pixel[3] / 255.0f}; |                                        rgba8Pixel[3] / 255.0f}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | bool ValidateArgbString(const std::string& argbString) | ||||||
|  | { | ||||||
|  |    static constexpr LazyRE2 re = {"#[0-9A-Fa-f]{8}"}; | ||||||
|  |    return RE2::FullMatch(argbString, *re); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } // namespace color
 | } // namespace color
 | ||||||
| } // namespace util
 | } // namespace util
 | ||||||
| } // namespace qt
 | } // namespace qt
 | ||||||
|  |  | ||||||
|  | @ -39,6 +39,15 @@ boost::gil::rgba8_pixel_t ToRgba8PixelT(const std::string& argbString); | ||||||
|  */ |  */ | ||||||
| boost::gil::rgba32f_pixel_t ToRgba32fPixelT(const std::string& argbString); | boost::gil::rgba32f_pixel_t ToRgba32fPixelT(const std::string& argbString); | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * Validates an ARGB string used by Qt libraries. | ||||||
|  |  * | ||||||
|  |  * @param argbString | ||||||
|  |  * | ||||||
|  |  * @return Validity of ARGB string | ||||||
|  |  */ | ||||||
|  | bool ValidateArgbString(const std::string& argbString); | ||||||
|  | 
 | ||||||
| } // namespace color
 | } // namespace color
 | ||||||
| } // namespace util
 | } // namespace util
 | ||||||
| } // namespace qt
 | } // namespace qt
 | ||||||
|  |  | ||||||
|  | @ -1 +1 @@ | ||||||
| Subproject commit 20a1ca1752499222d33869e37148321936ca6354 | Subproject commit 40a367ca89b5b197353ca58dea547a3e3407c7f3 | ||||||
|  | @ -1,11 +1,16 @@ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
|  | #include <scwx/awips/phenomenon.hpp> | ||||||
|  | 
 | ||||||
| #include <string> | #include <string> | ||||||
|  | #include <vector> | ||||||
| 
 | 
 | ||||||
| namespace scwx | namespace scwx | ||||||
| { | { | ||||||
| namespace awips | namespace awips | ||||||
| { | { | ||||||
|  | namespace ibw | ||||||
|  | { | ||||||
| 
 | 
 | ||||||
| enum class ThreatCategory : int | enum class ThreatCategory : int | ||||||
| { | { | ||||||
|  | @ -17,8 +22,18 @@ enum class ThreatCategory : int | ||||||
|    Unknown |    Unknown | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | struct ImpactBasedWarningInfo | ||||||
|  | { | ||||||
|  |    bool                        hasObservedTag_ {false}; | ||||||
|  |    bool                        hasTornadoPossibleTag_ {false}; | ||||||
|  |    std::vector<ThreatCategory> threatCategories_ {ThreatCategory::Base}; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const ImpactBasedWarningInfo& GetImpactBasedWarningInfo(Phenomenon phenomenon); | ||||||
|  | 
 | ||||||
| ThreatCategory     GetThreatCategory(const std::string& name); | ThreatCategory     GetThreatCategory(const std::string& name); | ||||||
| const std::string& GetThreatCategoryName(ThreatCategory threatCategory); | const std::string& GetThreatCategoryName(ThreatCategory threatCategory); | ||||||
| 
 | 
 | ||||||
|  | } // namespace ibw
 | ||||||
| } // namespace awips
 | } // namespace awips
 | ||||||
| } // namespace scwx
 | } // namespace scwx
 | ||||||
|  |  | ||||||
|  | @ -69,6 +69,7 @@ enum class Phenomenon | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| Phenomenon         GetPhenomenon(const std::string& code); | Phenomenon         GetPhenomenon(const std::string& code); | ||||||
|  | Phenomenon         GetPhenomenonFromText(const std::string& text); | ||||||
| const std::string& GetPhenomenonCode(Phenomenon phenomenon); | const std::string& GetPhenomenonCode(Phenomenon phenomenon); | ||||||
| const std::string& GetPhenomenonText(Phenomenon phenomenon); | const std::string& GetPhenomenonText(Phenomenon phenomenon); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -65,7 +65,7 @@ struct Segment | ||||||
|    std::optional<CodedTimeMotionLocation> codedMotion_ {}; |    std::optional<CodedTimeMotionLocation> codedMotion_ {}; | ||||||
| 
 | 
 | ||||||
|    bool                observed_ {false}; |    bool                observed_ {false}; | ||||||
|    ThreatCategory threatCategory_ {ThreatCategory::Base}; |    ibw::ThreatCategory threatCategory_ {ibw::ThreatCategory::Base}; | ||||||
|    bool                tornadoPossible_ {false}; |    bool                tornadoPossible_ {false}; | ||||||
| 
 | 
 | ||||||
|    Segment() = default; |    Segment() = default; | ||||||
|  |  | ||||||
|  | @ -4,13 +4,40 @@ | ||||||
| #include <unordered_map> | #include <unordered_map> | ||||||
| 
 | 
 | ||||||
| #include <boost/algorithm/string.hpp> | #include <boost/algorithm/string.hpp> | ||||||
|  | #include <boost/unordered/unordered_flat_map.hpp> | ||||||
| 
 | 
 | ||||||
| namespace scwx | namespace scwx | ||||||
| { | { | ||||||
| namespace awips | namespace awips | ||||||
| { | { | ||||||
|  | namespace ibw | ||||||
|  | { | ||||||
| 
 | 
 | ||||||
| static const std::string logPrefix_ = "scwx::awips::impact_based_warnings"; | static const std::string logPrefix_ = "scwx::awips::ibw::impact_based_warnings"; | ||||||
|  | 
 | ||||||
|  | static const boost::unordered_flat_map<Phenomenon, ImpactBasedWarningInfo> | ||||||
|  |    impactBasedWarningInfo_ { | ||||||
|  |       {Phenomenon::Marine, | ||||||
|  |        ImpactBasedWarningInfo {.hasTornadoPossibleTag_ = true}}, | ||||||
|  |       {Phenomenon::FlashFlood, | ||||||
|  |        ImpactBasedWarningInfo { | ||||||
|  |           .threatCategories_ {ThreatCategory::Base, | ||||||
|  |                               ThreatCategory::Considerable, | ||||||
|  |                               ThreatCategory::Catastrophic}}}, | ||||||
|  |       {Phenomenon::SevereThunderstorm, | ||||||
|  |        ImpactBasedWarningInfo { | ||||||
|  |           .hasTornadoPossibleTag_ = true, | ||||||
|  |           .threatCategories_ {ThreatCategory::Base, | ||||||
|  |                               ThreatCategory::Considerable, | ||||||
|  |                               ThreatCategory::Destructive}}}, | ||||||
|  |       {Phenomenon::SnowSquall, ImpactBasedWarningInfo {}}, | ||||||
|  |       {Phenomenon::Tornado, | ||||||
|  |        ImpactBasedWarningInfo { | ||||||
|  |           .hasObservedTag_ = true, | ||||||
|  |           .threatCategories_ {ThreatCategory::Base, | ||||||
|  |                               ThreatCategory::Considerable, | ||||||
|  |                               ThreatCategory::Catastrophic}}}, | ||||||
|  |       {Phenomenon::Unknown, ImpactBasedWarningInfo {}}}; | ||||||
| 
 | 
 | ||||||
| static const std::unordered_map<ThreatCategory, std::string> | static const std::unordered_map<ThreatCategory, std::string> | ||||||
|    threatCategoryName_ {{ThreatCategory::Base, "Base"}, |    threatCategoryName_ {{ThreatCategory::Base, "Base"}, | ||||||
|  | @ -20,6 +47,16 @@ static const std::unordered_map<ThreatCategory, std::string> | ||||||
|                         {ThreatCategory::Catastrophic, "Catastrophic"}, |                         {ThreatCategory::Catastrophic, "Catastrophic"}, | ||||||
|                         {ThreatCategory::Unknown, "?"}}; |                         {ThreatCategory::Unknown, "?"}}; | ||||||
| 
 | 
 | ||||||
|  | const ImpactBasedWarningInfo& GetImpactBasedWarningInfo(Phenomenon phenomenon) | ||||||
|  | { | ||||||
|  |    auto it = impactBasedWarningInfo_.find(phenomenon); | ||||||
|  |    if (it != impactBasedWarningInfo_.cend()) | ||||||
|  |    { | ||||||
|  |       return it->second; | ||||||
|  |    } | ||||||
|  |    return impactBasedWarningInfo_.at(Phenomenon::Unknown); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| SCWX_GET_ENUM(ThreatCategory, GetThreatCategory, threatCategoryName_) | SCWX_GET_ENUM(ThreatCategory, GetThreatCategory, threatCategoryName_) | ||||||
| 
 | 
 | ||||||
| const std::string& GetThreatCategoryName(ThreatCategory threatCategory) | const std::string& GetThreatCategoryName(ThreatCategory threatCategory) | ||||||
|  | @ -27,5 +64,6 @@ const std::string& GetThreatCategoryName(ThreatCategory threatCategory) | ||||||
|    return threatCategoryName_.at(threatCategory); |    return threatCategoryName_.at(threatCategory); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | } // namespace ibw
 | ||||||
| } // namespace awips
 | } // namespace awips
 | ||||||
| } // namespace scwx
 | } // namespace scwx
 | ||||||
|  |  | ||||||
|  | @ -77,64 +77,65 @@ static const PhenomenonCodesBimap phenomenonCodes_ = | ||||||
|    (Phenomenon::FreezingSpray, "ZY")                      //
 |    (Phenomenon::FreezingSpray, "ZY")                      //
 | ||||||
|    (Phenomenon::Unknown, "??"); |    (Phenomenon::Unknown, "??"); | ||||||
| 
 | 
 | ||||||
| static const std::unordered_map<Phenomenon, std::string> phenomenonText_ { | static const PhenomenonCodesBimap phenomenonText_ = | ||||||
|    {Phenomenon::AshfallLand, "Ashfall (land)"},              //
 |    boost::assign::list_of<PhenomenonCodesBimap::relation>   //
 | ||||||
|    {Phenomenon::AirStagnation, "Air Stagnation"},            //
 |    (Phenomenon::AshfallLand, "Ashfall (land)")              //
 | ||||||
|    {Phenomenon::BeachHazard, "Beach Hazard"},                //
 |    (Phenomenon::AirStagnation, "Air Stagnation")            //
 | ||||||
|    {Phenomenon::BriskWind, "Brisk Wind"},                    //
 |    (Phenomenon::BeachHazard, "Beach Hazard")                //
 | ||||||
|    {Phenomenon::Blizzard, "Blizzard"},                       //
 |    (Phenomenon::BriskWind, "Brisk Wind")                    //
 | ||||||
|    {Phenomenon::CoastalFlood, "Coastal Flood"},              //
 |    (Phenomenon::Blizzard, "Blizzard")                       //
 | ||||||
|    {Phenomenon::DebrisFlow, "Debris Flow"},                  //
 |    (Phenomenon::CoastalFlood, "Coastal Flood")              //
 | ||||||
|    {Phenomenon::DustStorm, "Dust Storm"},                    //
 |    (Phenomenon::DebrisFlow, "Debris Flow")                  //
 | ||||||
|    {Phenomenon::BlowingDust, "Blowing Dust"},                //
 |    (Phenomenon::DustStorm, "Dust Storm")                    //
 | ||||||
|    {Phenomenon::ExtremeCold, "Extreme Cold"},                //
 |    (Phenomenon::BlowingDust, "Blowing Dust")                //
 | ||||||
|    {Phenomenon::ExcessiveHeat, "Excessive Heat"},            //
 |    (Phenomenon::ExtremeCold, "Extreme Cold")                //
 | ||||||
|    {Phenomenon::ExtremeWind, "Extreme Wind"},                //
 |    (Phenomenon::ExcessiveHeat, "Excessive Heat")            //
 | ||||||
|    {Phenomenon::Flood, "Flood"},                             //
 |    (Phenomenon::ExtremeWind, "Extreme Wind")                //
 | ||||||
|    {Phenomenon::FlashFlood, "Flash Flood"},                  //
 |    (Phenomenon::Flood, "Flood")                             //
 | ||||||
|    {Phenomenon::DenseFogLand, "Dense Fog (land)"},           //
 |    (Phenomenon::FlashFlood, "Flash Flood")                  //
 | ||||||
|    {Phenomenon::Flood, "Flood (Forecast Points)"},           //
 |    (Phenomenon::DenseFogLand, "Dense Fog (land)")           //
 | ||||||
|    {Phenomenon::Frost, "Frost"},                             //
 |    (Phenomenon::Flood, "Flood (Forecast Points)")           //
 | ||||||
|    {Phenomenon::FireWeather, "Fire Weather"},                //
 |    (Phenomenon::Frost, "Frost")                             //
 | ||||||
|    {Phenomenon::Freeze, "Freeze"},                           //
 |    (Phenomenon::FireWeather, "Fire Weather")                //
 | ||||||
|    {Phenomenon::Gale, "Gale"},                               //
 |    (Phenomenon::Freeze, "Freeze")                           //
 | ||||||
|    {Phenomenon::HurricaneForceWind, "Hurricane Force Wind"}, //
 |    (Phenomenon::Gale, "Gale")                               //
 | ||||||
|    {Phenomenon::Heat, "Heat"},                               //
 |    (Phenomenon::HurricaneForceWind, "Hurricane Force Wind") //
 | ||||||
|    {Phenomenon::Hurricane, "Hurricane"},                     //
 |    (Phenomenon::Heat, "Heat")                               //
 | ||||||
|    {Phenomenon::HighWind, "High Wind"},                      //
 |    (Phenomenon::Hurricane, "Hurricane")                     //
 | ||||||
|    {Phenomenon::Hydrologic, "Hydrologic"},                   //
 |    (Phenomenon::HighWind, "High Wind")                      //
 | ||||||
|    {Phenomenon::HardFreeze, "Hard Freeze"},                  //
 |    (Phenomenon::Hydrologic, "Hydrologic")                   //
 | ||||||
|    {Phenomenon::IceStorm, "Ice Storm"},                      //
 |    (Phenomenon::HardFreeze, "Hard Freeze")                  //
 | ||||||
|    {Phenomenon::LakeEffectSnow, "Lake Effect Snow"},         //
 |    (Phenomenon::IceStorm, "Ice Storm")                      //
 | ||||||
|    {Phenomenon::LowWater, "Low Water"},                      //
 |    (Phenomenon::LakeEffectSnow, "Lake Effect Snow")         //
 | ||||||
|    {Phenomenon::LakeshoreFlood, "Lakeshore Flood"},          //
 |    (Phenomenon::LowWater, "Low Water")                      //
 | ||||||
|    {Phenomenon::LakeWind, "Lake Wind"},                      //
 |    (Phenomenon::LakeshoreFlood, "Lakeshore Flood")          //
 | ||||||
|    {Phenomenon::Marine, "Marine"},                           //
 |    (Phenomenon::LakeWind, "Lake Wind")                      //
 | ||||||
|    {Phenomenon::DenseFogMarine, "Dense Fog (marine)"},       //
 |    (Phenomenon::Marine, "Marine")                           //
 | ||||||
|    {Phenomenon::AshfallMarine, "Ashfall (marine)"},          //
 |    (Phenomenon::DenseFogMarine, "Dense Fog (marine)")       //
 | ||||||
|    {Phenomenon::DenseSmokeMarine, "Dense Smoke (marine)"},   //
 |    (Phenomenon::AshfallMarine, "Ashfall (marine)")          //
 | ||||||
|    {Phenomenon::RipCurrentRisk, "Rip Current Risk"},         //
 |    (Phenomenon::DenseSmokeMarine, "Dense Smoke (marine)")   //
 | ||||||
|    {Phenomenon::SmallCraft, "Small Craft"},                  //
 |    (Phenomenon::RipCurrentRisk, "Rip Current Risk")         //
 | ||||||
|    {Phenomenon::HazardousSeas, "Hazardous Seas"},            //
 |    (Phenomenon::SmallCraft, "Small Craft")                  //
 | ||||||
|    {Phenomenon::DenseSmokeLand, "Dense Smoke (land)"},       //
 |    (Phenomenon::HazardousSeas, "Hazardous Seas")            //
 | ||||||
|    {Phenomenon::Storm, "Storm"},                             //
 |    (Phenomenon::DenseSmokeLand, "Dense Smoke (land)")       //
 | ||||||
|    {Phenomenon::StormSurge, "Storm Surge"},                  //
 |    (Phenomenon::Storm, "Storm")                             //
 | ||||||
|    {Phenomenon::SnowSquall, "Snow Squall"},                  //
 |    (Phenomenon::StormSurge, "Storm Surge")                  //
 | ||||||
|    {Phenomenon::HighSurf, "High Surf"},                      //
 |    (Phenomenon::SnowSquall, "Snow Squall")                  //
 | ||||||
|    {Phenomenon::SevereThunderstorm, "Severe Thunderstorm"},  //
 |    (Phenomenon::HighSurf, "High Surf")                      //
 | ||||||
|    {Phenomenon::Tornado, "Tornado"},                         //
 |    (Phenomenon::SevereThunderstorm, "Severe Thunderstorm")  //
 | ||||||
|    {Phenomenon::TropicalStorm, "Tropical Storm"},            //
 |    (Phenomenon::Tornado, "Tornado")                         //
 | ||||||
|    {Phenomenon::Tsunami, "Tsunami"},                         //
 |    (Phenomenon::TropicalStorm, "Tropical Storm")            //
 | ||||||
|    {Phenomenon::Typhoon, "Typhoon"},                         //
 |    (Phenomenon::Tsunami, "Tsunami")                         //
 | ||||||
|    {Phenomenon::HeavyFreezingSpray, "Heavy Freezing Spray"}, //
 |    (Phenomenon::Typhoon, "Typhoon")                         //
 | ||||||
|    {Phenomenon::WindChill, "Wind Chill"},                    //
 |    (Phenomenon::HeavyFreezingSpray, "Heavy Freezing Spray") //
 | ||||||
|    {Phenomenon::Wind, "Wind"},                               //
 |    (Phenomenon::WindChill, "Wind Chill")                    //
 | ||||||
|    {Phenomenon::WinterStorm, "Winter Storm"},                //
 |    (Phenomenon::Wind, "Wind")                               //
 | ||||||
|    {Phenomenon::WinterWeather, "Winter Weather"},            //
 |    (Phenomenon::WinterStorm, "Winter Storm")                //
 | ||||||
|    {Phenomenon::FreezingFog, "Freezing Fog"},                //
 |    (Phenomenon::WinterWeather, "Winter Weather")            //
 | ||||||
|    {Phenomenon::FreezingRain, "Freezing Rain"},              //
 |    (Phenomenon::FreezingFog, "Freezing Fog")                //
 | ||||||
|    {Phenomenon::FreezingSpray, "Freezing Spray"},            //
 |    (Phenomenon::FreezingRain, "Freezing Rain")              //
 | ||||||
|    {Phenomenon::Unknown, "Unknown"}}; |    (Phenomenon::FreezingSpray, "Freezing Spray")            //
 | ||||||
|  |    (Phenomenon::Unknown, "Unknown"); | ||||||
| 
 | 
 | ||||||
| Phenomenon GetPhenomenon(const std::string& code) | Phenomenon GetPhenomenon(const std::string& code) | ||||||
| { | { | ||||||
|  | @ -154,6 +155,24 @@ Phenomenon GetPhenomenon(const std::string& code) | ||||||
|    return phenomenon; |    return phenomenon; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | Phenomenon GetPhenomenonFromText(const std::string& text) | ||||||
|  | { | ||||||
|  |    Phenomenon phenomenon; | ||||||
|  | 
 | ||||||
|  |    if (phenomenonText_.right.find(text) != phenomenonText_.right.end()) | ||||||
|  |    { | ||||||
|  |       phenomenon = phenomenonText_.right.at(text); | ||||||
|  |    } | ||||||
|  |    else | ||||||
|  |    { | ||||||
|  |       phenomenon = Phenomenon::Unknown; | ||||||
|  | 
 | ||||||
|  |       logger_->debug("Unrecognized code: \"{}\"", text); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    return phenomenon; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| const std::string& GetPhenomenonCode(Phenomenon phenomenon) | const std::string& GetPhenomenonCode(Phenomenon phenomenon) | ||||||
| { | { | ||||||
|    return phenomenonCodes_.left.at(phenomenon); |    return phenomenonCodes_.left.at(phenomenon); | ||||||
|  | @ -161,7 +180,7 @@ const std::string& GetPhenomenonCode(Phenomenon phenomenon) | ||||||
| 
 | 
 | ||||||
| const std::string& GetPhenomenonText(Phenomenon phenomenon) | const std::string& GetPhenomenonText(Phenomenon phenomenon) | ||||||
| { | { | ||||||
|    return phenomenonText_.at(phenomenon); |    return phenomenonText_.left.at(phenomenon); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| } // namespace awips
 | } // namespace awips
 | ||||||
|  |  | ||||||
|  | @ -378,7 +378,7 @@ void ParseCodedInformation(std::shared_ptr<Segment> segment, | ||||||
|          segment->tornadoPossible_ = true; |          segment->tornadoPossible_ = true; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       else if (segment->threatCategory_ == ThreatCategory::Base && |       else if (segment->threatCategory_ == ibw::ThreatCategory::Base && | ||||||
|                (threatTagIt = std::find_if(kThreatCategoryTags.cbegin(), |                (threatTagIt = std::find_if(kThreatCategoryTags.cbegin(), | ||||||
|                                            kThreatCategoryTags.cend(), |                                            kThreatCategoryTags.cend(), | ||||||
|                                            [&it](const std::string& tag) { |                                            [&it](const std::string& tag) { | ||||||
|  | @ -389,10 +389,23 @@ void ParseCodedInformation(std::shared_ptr<Segment> segment, | ||||||
|          const std::string threatCategoryName = |          const std::string threatCategoryName = | ||||||
|             it->substr(threatTagIt->length()); |             it->substr(threatTagIt->length()); | ||||||
| 
 | 
 | ||||||
|          ThreatCategory threatCategory = GetThreatCategory(threatCategoryName); |          ibw::ThreatCategory threatCategory = | ||||||
|          if (threatCategory == ThreatCategory::Unknown) |             ibw::GetThreatCategory(threatCategoryName); | ||||||
|  | 
 | ||||||
|  |          switch (threatCategory) | ||||||
|          { |          { | ||||||
|             threatCategory = ThreatCategory::Base; |          case ibw::ThreatCategory::Significant: | ||||||
|  |             // "Significant" is no longer an official tag, and has largely been
 | ||||||
|  |             // replaced with "Considerable".
 | ||||||
|  |             threatCategory = ibw::ThreatCategory::Considerable; | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |          case ibw::ThreatCategory::Unknown: | ||||||
|  |             threatCategory = ibw::ThreatCategory::Base; | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |          default: | ||||||
|  |             break; | ||||||
|          } |          } | ||||||
| 
 | 
 | ||||||
|          segment->threatCategory_ = threatCategory; |          segment->threatCategory_ = threatCategory; | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Dan Paulat
						Dan Paulat