mirror of
				https://github.com/ciphervance/supercell-wx.git
				synced 2025-10-30 21:30:05 +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) | ||||
| set(SRC_REQUEST source/scwx/qt/request/download_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/hotkey_settings.hpp | ||||
|                  source/scwx/qt/settings/line_settings.hpp | ||||
|                  source/scwx/qt/settings/map_settings.hpp | ||||
|                  source/scwx/qt/settings/palette_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/ui_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/hotkey_settings.cpp | ||||
|                  source/scwx/qt/settings/line_settings.cpp | ||||
|                  source/scwx/qt/settings/map_settings.cpp | ||||
|                  source/scwx/qt/settings/palette_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/collapsible_group.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/edit_line_dialog.hpp | ||||
|            source/scwx/qt/ui/flow_layout.hpp | ||||
|            source/scwx/qt/ui/gps_info_dialog.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_settings_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/placefile_dialog.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/serial_port_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 | ||||
|            source/scwx/qt/ui/alert_dialog.cpp | ||||
|            source/scwx/qt/ui/alert_dock_widget.cpp | ||||
|            source/scwx/qt/ui/animation_dock_widget.cpp | ||||
|            source/scwx/qt/ui/collapsible_group.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/edit_line_dialog.cpp | ||||
|            source/scwx/qt/ui/flow_layout.cpp | ||||
|            source/scwx/qt/ui/gps_info_dialog.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_settings_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/placefile_dialog.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/settings_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 | ||||
|            source/scwx/qt/ui/alert_dialog.ui | ||||
|            source/scwx/qt/ui/alert_dock_widget.ui | ||||
|            source/scwx/qt/ui/animation_dock_widget.ui | ||||
|            source/scwx/qt/ui/collapsible_group.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/imgui_debug_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/settings_dialog.ui | ||||
|            source/scwx/qt/ui/serial_port_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/update_dialog.ui | ||||
|            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/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/unit_settings_widget.cpp) | ||||
| set(HDR_UI_SETUP source/scwx/qt/ui/setup/audio_codec_page.hpp | ||||
|  |  | |||
|  | @ -9,6 +9,7 @@ | |||
| 
 | ||||
| #include <chrono> | ||||
| #include <mutex> | ||||
| #include <ranges> | ||||
| #include <unordered_map> | ||||
| #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; | ||||
| }; | ||||
| 
 | ||||
| static bool IsAlertActive(const std::shared_ptr<const awips::Segment>& segment); | ||||
| 
 | ||||
| class AlertLayerHandler : public QObject | ||||
| { | ||||
|    Q_OBJECT | ||||
|  | @ -111,25 +114,27 @@ signals: | |||
| class AlertLayer::Impl | ||||
| { | ||||
| 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, | ||||
|                  std::shared_ptr<MapContext> context, | ||||
|                  awips::Phenomenon           phenomenon) : | ||||
|        self_ {self}, | ||||
|        phenomenon_ {phenomenon}, | ||||
|        ibw_ {awips::ibw::GetImpactBasedWarningInfo(phenomenon)}, | ||||
|        geoLines_ {{false, std::make_shared<gl::draw::GeoLines>(context)}, | ||||
|                   {true, std::make_shared<gl::draw::GeoLines>(context)}} | ||||
|    { | ||||
|       auto& paletteSettings = settings::PaletteSettings::Instance(); | ||||
| 
 | ||||
|       for (auto alertActive : {false, true}) | ||||
|       { | ||||
|          lineColor_.emplace( | ||||
|             alertActive, | ||||
|             util::color::ToRgba32fPixelT( | ||||
|                paletteSettings.alert_color(phenomenon_, alertActive) | ||||
|                   .GetValue())); | ||||
|       } | ||||
| 
 | ||||
|       UpdateLineData(); | ||||
|       ConnectSignals(); | ||||
|       ScheduleRefresh(); | ||||
|    } | ||||
|  | @ -158,6 +163,10 @@ public: | |||
|                             const QPointF& mouseGlobalPos); | ||||
|    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, | ||||
|                 std::shared_ptr<gl::draw::GeoLineDrawItem>& di, | ||||
|                 const common::Coordinate&                   p1, | ||||
|  | @ -176,6 +185,9 @@ public: | |||
|                  bool                                   enableHover, | ||||
|                  boost::container::stable_vector< | ||||
|                     std::shared_ptr<gl::draw::GeoLineDrawItem>>& drawItems); | ||||
|    void UpdateLines(); | ||||
| 
 | ||||
|    static LineData CreateLineData(const settings::LineSettings& lineSettings); | ||||
| 
 | ||||
|    boost::asio::thread_pool threadPool_ {1u}; | ||||
| 
 | ||||
|  | @ -184,7 +196,8 @@ public: | |||
|    boost::asio::system_timer refreshTimer_ {threadPool_}; | ||||
|    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>()}; | ||||
| 
 | ||||
|  | @ -199,12 +212,18 @@ public: | |||
|               segmentsByLine_; | ||||
|    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::shared_ptr<const gl::draw::GeoLineDrawItem> lastHoverDi_ {nullptr}; | ||||
|    std::string                                      tooltip_ {}; | ||||
| 
 | ||||
|    std::vector<boost::signals2::scoped_connection> connections_ {}; | ||||
| }; | ||||
| 
 | ||||
| AlertLayer::AlertLayer(std::shared_ptr<MapContext> context, | ||||
|  | @ -289,6 +308,15 @@ void AlertLayer::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, | ||||
|                                     size_t                     messageIndex) | ||||
| { | ||||
|  | @ -331,10 +359,9 @@ void AlertLayerHandler::HandleAlert(const types::TextEventKey& key, | |||
|          continue; | ||||
|       } | ||||
| 
 | ||||
|       auto&             vtec       = segment->header_->vtecString_.front(); | ||||
|       auto              action     = vtec.pVtec_.action(); | ||||
|       awips::Phenomenon phenomenon = vtec.pVtec_.phenomenon(); | ||||
|       bool alertActive             = (action != awips::PVtec::Action::Canceled); | ||||
|       auto&             vtec        = segment->header_->vtecString_.front(); | ||||
|       awips::Phenomenon phenomenon  = vtec.pVtec_.phenomenon(); | ||||
|       bool              alertActive = IsAlertActive(segment); | ||||
| 
 | ||||
|       auto& segmentsForType = segmentsByType_[{key.phenomenon_, alertActive}]; | ||||
| 
 | ||||
|  | @ -393,6 +420,8 @@ void AlertLayer::Impl::ConnectAlertHandlerSignals() | |||
| 
 | ||||
| void AlertLayer::Impl::ConnectSignals() | ||||
| { | ||||
|    auto& alertPaletteSettings = | ||||
|       settings::PaletteSettings::Instance().alert_palette(phenomenon_); | ||||
|    auto timelineManager = manager::TimelineManager::Instance(); | ||||
| 
 | ||||
|    QObject::connect(timelineManager.get(), | ||||
|  | @ -400,6 +429,13 @@ void AlertLayer::Impl::ConnectSignals() | |||
|                     receiver_.get(), | ||||
|                     [this](std::chrono::system_clock::time_point dateTime) | ||||
|                     { selectedTime_ = dateTime; }); | ||||
| 
 | ||||
|    connections_.push_back(alertPaletteSettings.changed_signal().connect( | ||||
|       [this]() | ||||
|       { | ||||
|          UpdateLineData(); | ||||
|          UpdateLines(); | ||||
|       })); | ||||
| } | ||||
| 
 | ||||
| void AlertLayer::Impl::ScheduleRefresh() | ||||
|  | @ -439,14 +475,12 @@ void AlertLayer::Impl::AddAlert( | |||
| { | ||||
|    auto& segment = segmentRecord->segment_; | ||||
| 
 | ||||
|    auto& vtec        = segment->header_->vtecString_.front(); | ||||
|    auto  action      = vtec.pVtec_.action(); | ||||
|    bool  alertActive = (action != awips::PVtec::Action::Canceled); | ||||
|    bool  alertActive = IsAlertActive(segment); | ||||
|    auto& startTime   = segmentRecord->segmentBegin_; | ||||
|    auto& endTime     = segmentRecord->segmentEnd_; | ||||
| 
 | ||||
|    auto& lineColor = lineColor_.at(alertActive); | ||||
|    auto& geoLines  = geoLines_.at(alertActive); | ||||
|    auto& lineData = GetLineData(segment, alertActive); | ||||
|    auto& geoLines = geoLines_.at(alertActive); | ||||
| 
 | ||||
|    const auto& coordinates = segment->codedLocation_->coordinates(); | ||||
| 
 | ||||
|  | @ -462,30 +496,51 @@ void AlertLayer::Impl::AddAlert( | |||
|    // If draw items were added
 | ||||
|    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
 | ||||
|       AddLines(geoLines, | ||||
|                coordinates, | ||||
|                kBlack_, | ||||
|                5.0f, | ||||
|                lineData.borderColor_, | ||||
|                totalBorderWidth, | ||||
|                startTime, | ||||
|                endTime, | ||||
|                true, | ||||
|                borderHover, | ||||
|                drawItems.first->second); | ||||
| 
 | ||||
|       // Add only border to segmentsByLine_
 | ||||
|       // Add border to segmentsByLine_
 | ||||
|       for (auto& di : drawItems.first->second) | ||||
|       { | ||||
|          segmentsByLine_.insert({di, segmentRecord}); | ||||
|       } | ||||
| 
 | ||||
|       // Add highlight
 | ||||
|       AddLines(geoLines, | ||||
|                coordinates, | ||||
|                lineData.highlightColor_, | ||||
|                totalHighlightWidth, | ||||
|                startTime, | ||||
|                endTime, | ||||
|                highlightHover, | ||||
|                drawItems.first->second); | ||||
| 
 | ||||
|       // Add line
 | ||||
|       AddLines(geoLines, | ||||
|                coordinates, | ||||
|                lineColor, | ||||
|                3.0f, | ||||
|                lineData.lineColor_, | ||||
|                lineWidth, | ||||
|                startTime, | ||||
|                endTime, | ||||
|                false, | ||||
|                lineHover, | ||||
|                drawItems.first->second); | ||||
|    } | ||||
| } | ||||
|  | @ -499,11 +554,8 @@ void AlertLayer::Impl::UpdateAlert( | |||
|    auto it = linesBySegment_.find(segmentRecord); | ||||
|    if (it != linesBySegment_.cend()) | ||||
|    { | ||||
|       auto& segment = segmentRecord->segment_; | ||||
| 
 | ||||
|       auto& vtec        = segment->header_->vtecString_.front(); | ||||
|       auto  action      = vtec.pVtec_.action(); | ||||
|       bool  alertActive = (action != awips::PVtec::Action::Canceled); | ||||
|       auto& segment     = segmentRecord->segment_; | ||||
|       bool  alertActive = IsAlertActive(segment); | ||||
| 
 | ||||
|       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( | ||||
|    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() | ||||
| { | ||||
|    static AlertLayerHandler alertLayerHandler_ {}; | ||||
|  |  | |||
|  | @ -10,7 +10,6 @@ | |||
| #include <scwx/util/strings.hpp> | ||||
| #include <scwx/util/time.hpp> | ||||
| 
 | ||||
| 
 | ||||
| #include <format> | ||||
| 
 | ||||
| #include <QApplication> | ||||
|  | @ -37,9 +36,9 @@ public: | |||
|    explicit AlertModelImpl(); | ||||
|    ~AlertModelImpl() = default; | ||||
| 
 | ||||
|    bool                  GetObserved(const types::TextEventKey& key); | ||||
|    awips::ThreatCategory GetThreatCategory(const types::TextEventKey& key); | ||||
|    bool                  GetTornadoPossible(const types::TextEventKey& key); | ||||
|    bool                       GetObserved(const types::TextEventKey& key); | ||||
|    awips::ibw::ThreatCategory GetThreatCategory(const types::TextEventKey& key); | ||||
|    bool GetTornadoPossible(const types::TextEventKey& key); | ||||
| 
 | ||||
|    static std::string GetCounties(const types::TextEventKey& key); | ||||
|    static std::string GetState(const types::TextEventKey& key); | ||||
|  | @ -61,7 +60,7 @@ public: | |||
|                       types::TextEventHash<types::TextEventKey>> | ||||
|       observedMap_; | ||||
|    std::unordered_map<types::TextEventKey, | ||||
|                       awips::ThreatCategory, | ||||
|                       awips::ibw::ThreatCategory, | ||||
|                       types::TextEventHash<types::TextEventKey>> | ||||
|       threatCategoryMap_; | ||||
|    std::unordered_map<types::TextEventKey, | ||||
|  | @ -75,8 +74,8 @@ public: | |||
|    std::unordered_map<types::TextEventKey, | ||||
|                       double, | ||||
|                       types::TextEventHash<types::TextEventKey>> | ||||
|                               distanceMap_; | ||||
|    scwx::common::Coordinate   previousPosition_; | ||||
|                             distanceMap_; | ||||
|    scwx::common::Coordinate previousPosition_; | ||||
| }; | ||||
| 
 | ||||
| AlertModel::AlertModel(QObject* parent) : | ||||
|  | @ -158,7 +157,7 @@ QVariant AlertModel::data(const QModelIndex& index, int role) const | |||
|       case static_cast<int>(Column::ThreatCategory): | ||||
|          if (role == Qt::DisplayRole) | ||||
|          { | ||||
|             return QString::fromStdString(awips::GetThreatCategoryName( | ||||
|             return QString::fromStdString(awips::ibw::GetThreatCategoryName( | ||||
|                p->GetThreatCategory(textEventKey))); | ||||
|          } | ||||
|          else | ||||
|  | @ -439,10 +438,10 @@ bool AlertModelImpl::GetObserved(const types::TextEventKey& key) | |||
|    return observed; | ||||
| } | ||||
| 
 | ||||
| awips::ThreatCategory | ||||
| awips::ibw::ThreatCategory | ||||
| 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); | ||||
|    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/util/color.hpp> | ||||
| 
 | ||||
| #include <boost/algorithm/string/case_conv.hpp> | ||||
| #include <boost/gil.hpp> | ||||
| #include <fmt/format.h> | ||||
| #include <re2/re2.h> | ||||
| 
 | ||||
| namespace scwx | ||||
| { | ||||
|  | @ -76,59 +76,20 @@ static const awips::Phenomenon kDefaultPhenomenon_ {awips::Phenomenon::Marine}; | |||
| class PaletteSettings::Impl | ||||
| { | ||||
| public: | ||||
|    explicit Impl() | ||||
|    explicit Impl(PaletteSettings* self) : self_ {self} | ||||
|    { | ||||
|       palette_.reserve(kPaletteKeys_.size()); | ||||
| 
 | ||||
|       for (const auto& name : kPaletteKeys_) | ||||
|       { | ||||
|          const std::string& defaultValue = kDefaultPalettes_.at(name); | ||||
| 
 | ||||
|          auto result = | ||||
|             palette_.emplace(name, SettingsVariable<std::string> {name}); | ||||
| 
 | ||||
|          SettingsVariable<std::string>& settingsVariable = result.first->second; | ||||
| 
 | ||||
|          settingsVariable.SetDefault(defaultValue); | ||||
| 
 | ||||
|          variables_.push_back(&settingsVariable); | ||||
|       }; | ||||
| 
 | ||||
|       activeAlertColor_.reserve(kAlertColors_.size()); | ||||
|       inactiveAlertColor_.reserve(kAlertColors_.size()); | ||||
| 
 | ||||
|       for (auto& alert : kAlertColors_) | ||||
|       { | ||||
|          std::string phenomenonCode = awips::GetPhenomenonCode(alert.first); | ||||
|          std::string activeName     = fmt::format("{}-active", phenomenonCode); | ||||
|          std::string inactiveName = fmt::format("{}-inactive", phenomenonCode); | ||||
| 
 | ||||
|          auto activeResult = activeAlertColor_.emplace( | ||||
|             alert.first, SettingsVariable<std::string> {activeName}); | ||||
|          auto inactiveResult = inactiveAlertColor_.emplace( | ||||
|             alert.first, SettingsVariable<std::string> {inactiveName}); | ||||
| 
 | ||||
|          SettingsVariable<std::string>& activeVariable = | ||||
|             activeResult.first->second; | ||||
|          SettingsVariable<std::string>& inactiveVariable = | ||||
|             inactiveResult.first->second; | ||||
| 
 | ||||
|          activeVariable.SetDefault( | ||||
|             util::color::ToArgbString(alert.second.first)); | ||||
|          inactiveVariable.SetDefault( | ||||
|             util::color::ToArgbString(alert.second.second)); | ||||
| 
 | ||||
|          activeVariable.SetValidator(&ValidateColor); | ||||
|          inactiveVariable.SetValidator(&ValidateColor); | ||||
| 
 | ||||
|          variables_.push_back(&activeVariable); | ||||
|          variables_.push_back(&inactiveVariable); | ||||
|       } | ||||
|       InitializeColorTables(); | ||||
|       InitializeLegacyAlerts(); | ||||
|       InitializeAlerts(); | ||||
|    } | ||||
| 
 | ||||
|    ~Impl() {} | ||||
| 
 | ||||
|    static bool ValidateColor(const std::string& value); | ||||
|    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>> | ||||
|  | @ -136,16 +97,13 @@ public: | |||
|    std::unordered_map<awips::Phenomenon, SettingsVariable<std::string>> | ||||
|                                       inactiveAlertColor_ {}; | ||||
|    std::vector<SettingsVariableBase*> variables_ {}; | ||||
| 
 | ||||
|    std::unordered_map<awips::Phenomenon, AlertPaletteSettings> | ||||
|       alertPaletteMap_ {}; | ||||
| }; | ||||
| 
 | ||||
| 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>()) | ||||
|     SettingsCategory("palette"), p(std::make_unique<Impl>(this)) | ||||
| { | ||||
|    RegisterVariables(p->variables_); | ||||
|    SetDefaults(); | ||||
|  | @ -158,6 +116,75 @@ PaletteSettings::PaletteSettings(PaletteSettings&&) noexcept = default; | |||
| PaletteSettings& | ||||
| PaletteSettings::operator=(PaletteSettings&&) noexcept = default; | ||||
| 
 | ||||
| void PaletteSettings::Impl::InitializeColorTables() | ||||
| { | ||||
|    palette_.reserve(kPaletteKeys_.size()); | ||||
| 
 | ||||
|    for (const auto& name : kPaletteKeys_) | ||||
|    { | ||||
|       const std::string& defaultValue = kDefaultPalettes_.at(name); | ||||
| 
 | ||||
|       auto result = | ||||
|          palette_.emplace(name, SettingsVariable<std::string> {name}); | ||||
| 
 | ||||
|       SettingsVariable<std::string>& settingsVariable = result.first->second; | ||||
| 
 | ||||
|       settingsVariable.SetDefault(defaultValue); | ||||
| 
 | ||||
|       variables_.push_back(&settingsVariable); | ||||
|    }; | ||||
| } | ||||
| 
 | ||||
| void PaletteSettings::Impl::InitializeLegacyAlerts() | ||||
| { | ||||
|    activeAlertColor_.reserve(kAlertColors_.size()); | ||||
|    inactiveAlertColor_.reserve(kAlertColors_.size()); | ||||
| 
 | ||||
|    for (auto& alert : kAlertColors_) | ||||
|    { | ||||
|       std::string phenomenonCode = awips::GetPhenomenonCode(alert.first); | ||||
|       std::string activeName     = fmt::format("{}-active", phenomenonCode); | ||||
|       std::string inactiveName   = fmt::format("{}-inactive", phenomenonCode); | ||||
| 
 | ||||
|       auto activeResult = activeAlertColor_.emplace( | ||||
|          alert.first, SettingsVariable<std::string> {activeName}); | ||||
|       auto inactiveResult = inactiveAlertColor_.emplace( | ||||
|          alert.first, SettingsVariable<std::string> {inactiveName}); | ||||
| 
 | ||||
|       SettingsVariable<std::string>& activeVariable = | ||||
|          activeResult.first->second; | ||||
|       SettingsVariable<std::string>& inactiveVariable = | ||||
|          inactiveResult.first->second; | ||||
| 
 | ||||
|       activeVariable.SetDefault(util::color::ToArgbString(alert.second.first)); | ||||
|       inactiveVariable.SetDefault( | ||||
|          util::color::ToArgbString(alert.second.second)); | ||||
| 
 | ||||
|       activeVariable.SetValidator(&util::color::ValidateArgbString); | ||||
|       inactiveVariable.SetValidator(&util::color::ValidateArgbString); | ||||
| 
 | ||||
|       variables_.push_back(&activeVariable); | ||||
|       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); | ||||
|    } | ||||
| 
 | ||||
|    self_->RegisterSubcategoryArray("alerts", alertSettings); | ||||
| } | ||||
| 
 | ||||
| SettingsVariable<std::string>& | ||||
| 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() | ||||
| { | ||||
|    static const std::vector<awips::Phenomenon> kAlertPhenomena_ { | ||||
|  |  | |||
|  | @ -1,7 +1,9 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <scwx/qt/settings/alert_palette_settings.hpp> | ||||
| #include <scwx/qt/settings/settings_category.hpp> | ||||
| #include <scwx/qt/settings/settings_variable.hpp> | ||||
| #include <scwx/awips/impact_based_warnings.hpp> | ||||
| #include <scwx/awips/phenomenon.hpp> | ||||
| 
 | ||||
| #include <memory> | ||||
|  | @ -29,6 +31,7 @@ public: | |||
|    SettingsVariable<std::string>& palette(const std::string& name) const; | ||||
|    SettingsVariable<std::string>& alert_color(awips::Phenomenon phenomenon, | ||||
|                                               bool              active) const; | ||||
|    AlertPaletteSettings&          alert_palette(awips::Phenomenon); | ||||
| 
 | ||||
|    static const std::vector<awips::Phenomenon>& alert_phenomena(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -21,11 +21,21 @@ public: | |||
| 
 | ||||
|    ~Impl() {} | ||||
| 
 | ||||
|    void ConnectSubcategory(SettingsCategory& category); | ||||
|    void ConnectVariable(SettingsVariableBase* variable); | ||||
| 
 | ||||
|    const std::string name_; | ||||
| 
 | ||||
|    std::vector<std::pair<std::string, std::vector<SettingsCategory*>>> | ||||
|                                       subcategoryArrays_; | ||||
|    std::vector<SettingsCategory*>     subcategories_; | ||||
|    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) : | ||||
|  | @ -38,13 +48,88 @@ SettingsCategory::SettingsCategory(SettingsCategory&&) noexcept = default; | |||
| SettingsCategory& | ||||
| 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 | ||||
| { | ||||
|    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() | ||||
| { | ||||
|    // Don't allow individual variables to invoke the signal when operating over
 | ||||
|    // the entire category
 | ||||
|    p->blockSignals_ = true; | ||||
| 
 | ||||
|    // Set subcategory array defaults
 | ||||
|    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
 | ||||
|    for (auto& variable : p->variables_) | ||||
|    { | ||||
|       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) | ||||
|  | @ -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
 | ||||
|       for (auto& variable : p->variables_) | ||||
|       { | ||||
|  | @ -154,6 +363,12 @@ void SettingsCategory::WriteJson(boost::json::object& json) const | |||
|       object.insert_or_assign(subcategoryArray.first, arrayObject); | ||||
|    } | ||||
| 
 | ||||
|    // Write subcategories
 | ||||
|    for (auto& subcategory : p->subcategories_) | ||||
|    { | ||||
|       subcategory->WriteJson(object); | ||||
|    } | ||||
| 
 | ||||
|    // Write 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); | ||||
| } | ||||
| 
 | ||||
| void SettingsCategory::RegisterSubcategory(SettingsCategory& subcategory) | ||||
| { | ||||
|    p->ConnectSubcategory(subcategory); | ||||
|    p->subcategories_.push_back(&subcategory); | ||||
| } | ||||
| 
 | ||||
| void SettingsCategory::RegisterSubcategoryArray( | ||||
|    const std::string& name, std::vector<SettingsCategory>& subcategories) | ||||
| { | ||||
|  | @ -172,22 +393,92 @@ void SettingsCategory::RegisterSubcategoryArray( | |||
|    std::transform(subcategories.begin(), | ||||
|                   subcategories.end(), | ||||
|                   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( | ||||
|    std::initializer_list<SettingsVariableBase*> variables) | ||||
| { | ||||
|    for (auto& variable : variables) | ||||
|    { | ||||
|       p->ConnectVariable(variable); | ||||
|    } | ||||
|    p->variables_.insert(p->variables_.end(), variables); | ||||
| } | ||||
| 
 | ||||
| void SettingsCategory::RegisterVariables( | ||||
|    std::vector<SettingsVariableBase*> variables) | ||||
| { | ||||
|    for (auto& variable : variables) | ||||
|    { | ||||
|       p->ConnectVariable(variable); | ||||
|    } | ||||
|    p->variables_.insert( | ||||
|       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 qt
 | ||||
| } // namespace scwx
 | ||||
|  |  | |||
|  | @ -6,6 +6,7 @@ | |||
| #include <string> | ||||
| 
 | ||||
| #include <boost/json/object.hpp> | ||||
| #include <boost/signals2/signal.hpp> | ||||
| 
 | ||||
| namespace scwx | ||||
| { | ||||
|  | @ -28,11 +29,61 @@ public: | |||
| 
 | ||||
|    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. | ||||
|     */ | ||||
|    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. | ||||
|     * | ||||
|  | @ -50,12 +101,18 @@ public: | |||
|     */ | ||||
|    virtual void WriteJson(boost::json::object& json) const; | ||||
| 
 | ||||
|    void RegisterSubcategory(SettingsCategory& subcategory); | ||||
|    void RegisterSubcategoryArray(const std::string&             name, | ||||
|                                  std::vector<SettingsCategory>& subcategories); | ||||
|    void RegisterSubcategoryArray(const std::string&              name, | ||||
|                                  std::vector<SettingsCategory*>& subcategories); | ||||
|    void | ||||
|    RegisterVariables(std::initializer_list<SettingsVariableBase*> variables); | ||||
|    void RegisterVariables(std::vector<SettingsVariableBase*> variables); | ||||
| 
 | ||||
| protected: | ||||
|    void set_block_signals(bool blockSignals); | ||||
| 
 | ||||
| private: | ||||
|    class Impl; | ||||
|    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> | ||||
| 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; | ||||
|       validated = true; | ||||
| 
 | ||||
|       changed_signal()(); | ||||
|       for (auto& callback : p->valueChangedCallbackFunctions_) | ||||
|       { | ||||
|          callback.second(p->value_); | ||||
|       } | ||||
| 
 | ||||
|       staged_signal()(); | ||||
|       for (auto& callback : p->valueStagedCallbackFunctions_) | ||||
|       { | ||||
|          callback.second(p->value_); | ||||
|  | @ -129,10 +144,13 @@ bool SettingsVariable<T>::SetValueOrDefault(const T& value) | |||
|       p->value_ = p->default_; | ||||
|    } | ||||
| 
 | ||||
|    changed_signal()(); | ||||
|    for (auto& callback : p->valueChangedCallbackFunctions_) | ||||
|    { | ||||
|       callback.second(p->value_); | ||||
|    } | ||||
| 
 | ||||
|    staged_signal()(); | ||||
|    for (auto& callback : p->valueStagedCallbackFunctions_) | ||||
|    { | ||||
|       callback.second(p->value_); | ||||
|  | @ -146,10 +164,13 @@ void SettingsVariable<T>::SetValueToDefault() | |||
| { | ||||
|    p->value_ = p->default_; | ||||
| 
 | ||||
|    changed_signal()(); | ||||
|    for (auto& callback : p->valueChangedCallbackFunctions_) | ||||
|    { | ||||
|       callback.second(p->value_); | ||||
|    } | ||||
| 
 | ||||
|    staged_signal()(); | ||||
|    for (auto& callback : p->valueStagedCallbackFunctions_) | ||||
|    { | ||||
|       callback.second(p->value_); | ||||
|  | @ -168,6 +189,7 @@ void SettingsVariable<T>::StageDefault() | |||
|       p->staged_.reset(); | ||||
|    } | ||||
| 
 | ||||
|    staged_signal()(); | ||||
|    for (auto& callback : p->valueStagedCallbackFunctions_) | ||||
|    { | ||||
|       callback.second(p->default_); | ||||
|  | @ -194,6 +216,7 @@ bool SettingsVariable<T>::StageValue(const T& value) | |||
| 
 | ||||
|       validated = true; | ||||
| 
 | ||||
|       staged_signal()(); | ||||
|       for (auto& callback : p->valueStagedCallbackFunctions_) | ||||
|       { | ||||
|          callback.second(transformed); | ||||
|  | @ -214,10 +237,13 @@ bool SettingsVariable<T>::Commit() | |||
|       p->staged_.reset(); | ||||
|       committed = true; | ||||
| 
 | ||||
|       changed_signal()(); | ||||
|       for (auto& callback : p->valueChangedCallbackFunctions_) | ||||
|       { | ||||
|          callback.second(p->value_); | ||||
|       } | ||||
| 
 | ||||
|       staged_signal()(); | ||||
|       for (auto& callback : p->valueStagedCallbackFunctions_) | ||||
|       { | ||||
|          callback.second(p->value_); | ||||
|  | @ -232,6 +258,7 @@ void SettingsVariable<T>::Reset() | |||
| { | ||||
|    p->staged_.reset(); | ||||
| 
 | ||||
|    staged_signal()(); | ||||
|    for (auto& callback : p->valueStagedCallbackFunctions_) | ||||
|    { | ||||
|       callback.second(p->value_); | ||||
|  | @ -336,10 +363,13 @@ bool SettingsVariable<T>::ReadValue(const boost::json::object& json) | |||
|       p->value_ = p->default_; | ||||
|    } | ||||
| 
 | ||||
|    changed_signal()(); | ||||
|    for (auto& callback : p->valueChangedCallbackFunctions_) | ||||
|    { | ||||
|       callback.second(p->value_); | ||||
|    } | ||||
| 
 | ||||
|    staged_signal()(); | ||||
|    for (auto& callback : p->valueStagedCallbackFunctions_) | ||||
|    { | ||||
|       callback.second(p->value_); | ||||
|  |  | |||
|  | @ -29,7 +29,7 @@ public: | |||
|    typedef std::function<void(const T& value)> ValueCallbackFunction; | ||||
| 
 | ||||
|    explicit SettingsVariable(const std::string& name); | ||||
|    ~SettingsVariable(); | ||||
|    virtual ~SettingsVariable(); | ||||
| 
 | ||||
|    SettingsVariable(const SettingsVariable&)            = delete; | ||||
|    SettingsVariable& operator=(const SettingsVariable&) = delete; | ||||
|  | @ -37,6 +37,24 @@ public: | |||
|    SettingsVariable(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. | ||||
|     * | ||||
|  | @ -96,7 +114,7 @@ public: | |||
|    /**
 | ||||
|     * Clears the staged value of the settings variable. | ||||
|     */ | ||||
|    void Reset(); | ||||
|    void Reset() override; | ||||
| 
 | ||||
|    /**
 | ||||
|     * Gets the staged value of the settings variable, if defined. | ||||
|  |  | |||
|  | @ -18,6 +18,9 @@ public: | |||
|    ~Impl() {} | ||||
| 
 | ||||
|    const std::string name_; | ||||
| 
 | ||||
|    boost::signals2::signal<void()> changedSignal_ {}; | ||||
|    boost::signals2::signal<void()> stagedSignal_ {}; | ||||
| }; | ||||
| 
 | ||||
| SettingsVariableBase::SettingsVariableBase(const std::string& name) : | ||||
|  | @ -38,6 +41,16 @@ std::string SettingsVariableBase::name() const | |||
|    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 | ||||
| { | ||||
|    return p->name_ == o.p->name_; | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ | |||
| #include <string> | ||||
| 
 | ||||
| #include <boost/json/object.hpp> | ||||
| #include <boost/signals2/signal.hpp> | ||||
| 
 | ||||
| namespace scwx | ||||
| { | ||||
|  | @ -19,7 +20,7 @@ class SettingsVariableBase | |||
| { | ||||
| protected: | ||||
|    explicit SettingsVariableBase(const std::string& name); | ||||
|    ~SettingsVariableBase(); | ||||
|    virtual ~SettingsVariableBase(); | ||||
| 
 | ||||
| public: | ||||
|    SettingsVariableBase(const SettingsVariableBase&)            = delete; | ||||
|  | @ -30,6 +31,38 @@ public: | |||
| 
 | ||||
|    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. | ||||
|     */ | ||||
|  | @ -48,6 +81,11 @@ public: | |||
|     */ | ||||
|    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, | ||||
|     * 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() {} | ||||
|    ~Impl() = default; | ||||
| 
 | ||||
|    std::vector<settings::SettingsCategory*>      categories_; | ||||
|    std::vector<settings::SettingsInterfaceBase*> settings_; | ||||
| }; | ||||
| 
 | ||||
|  | @ -29,6 +30,12 @@ SettingsPageWidget::SettingsPageWidget(QWidget* parent) : | |||
| 
 | ||||
| SettingsPageWidget::~SettingsPageWidget() = default; | ||||
| 
 | ||||
| void SettingsPageWidget::AddSettingsCategory( | ||||
|    settings::SettingsCategory* category) | ||||
| { | ||||
|    p->categories_.push_back(category); | ||||
| } | ||||
| 
 | ||||
| void SettingsPageWidget::AddSettingsInterface( | ||||
|    settings::SettingsInterfaceBase* setting) | ||||
| { | ||||
|  | @ -39,6 +46,11 @@ bool SettingsPageWidget::CommitChanges() | |||
| { | ||||
|    bool committed = false; | ||||
| 
 | ||||
|    for (auto& category : p->categories_) | ||||
|    { | ||||
|       committed |= category->Commit(); | ||||
|    } | ||||
| 
 | ||||
|    for (auto& setting : p->settings_) | ||||
|    { | ||||
|       committed |= setting->Commit(); | ||||
|  | @ -49,6 +61,11 @@ bool SettingsPageWidget::CommitChanges() | |||
| 
 | ||||
| void SettingsPageWidget::DiscardChanges() | ||||
| { | ||||
|    for (auto& category : p->categories_) | ||||
|    { | ||||
|       category->Reset(); | ||||
|    } | ||||
| 
 | ||||
|    for (auto& setting : p->settings_) | ||||
|    { | ||||
|       setting->Reset(); | ||||
|  |  | |||
|  | @ -1,5 +1,6 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <scwx/qt/settings/settings_category.hpp> | ||||
| #include <scwx/qt/settings/settings_interface_base.hpp> | ||||
| 
 | ||||
| #include <QWidget> | ||||
|  | @ -61,6 +62,15 @@ public: | |||
| protected: | ||||
|    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: | ||||
|    class Impl; | ||||
|    std::shared_ptr<Impl> p; | ||||
|  |  | |||
|  | @ -23,11 +23,12 @@ | |||
| #include <scwx/qt/types/time_types.hpp> | ||||
| #include <scwx/qt/types/unit_types.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/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/unit_settings_widget.hpp> | ||||
| #include <scwx/qt/ui/wfo_dialog.hpp> | ||||
| #include <scwx/qt/util/color.hpp> | ||||
| #include <scwx/qt/util/file.hpp> | ||||
| #include <scwx/util/logger.hpp> | ||||
|  | @ -181,7 +182,6 @@ public: | |||
|    void SetupTextTab(); | ||||
|    void SetupHotkeysTab(); | ||||
| 
 | ||||
|    void ShowColorDialog(QLineEdit* lineEdit); | ||||
|    void UpdateRadarDialogLocation(const std::string& id); | ||||
|    void UpdateAlertRadarDialogLocation(const std::string& id); | ||||
| 
 | ||||
|  | @ -203,8 +203,7 @@ public: | |||
|                                      const std::string& value, | ||||
|                                      QLabel*            imageLabel); | ||||
|    static std::string | ||||
|                RadarSiteLabel(std::shared_ptr<config::RadarSite>& radarSite); | ||||
|    static void SetBackgroundColor(const std::string& value, QFrame* frame); | ||||
|    RadarSiteLabel(std::shared_ptr<config::RadarSite>& radarSite); | ||||
| 
 | ||||
|    SettingsDialog*   self_; | ||||
|    RadarSiteDialog*  radarSiteDialog_; | ||||
|  | @ -224,6 +223,7 @@ public: | |||
|       manager::PositionManager::Instance()}; | ||||
| 
 | ||||
|    std::vector<SettingsPageWidget*> settingsPages_ {}; | ||||
|    AlertPaletteSettingsWidget*      alertPaletteSettingsWidget_ {}; | ||||
|    HotkeySettingsWidget*            hotkeySettingsWidget_ {}; | ||||
|    UnitSettingsWidget*              unitSettingsWidget_ {}; | ||||
| 
 | ||||
|  | @ -252,12 +252,6 @@ public: | |||
| 
 | ||||
|    std::unordered_map<std::string, settings::SettingsInterface<std::string>> | ||||
|       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> alertAudioLocationMethod_ {}; | ||||
|  | @ -358,24 +352,23 @@ void SettingsDialogImpl::ConnectSignals() | |||
|                     self_, | ||||
|                     [this]() { alertAudioRadarSiteDialog_->show(); }); | ||||
| 
 | ||||
|    QObject::connect(alertAudioRadarSiteDialog_, | ||||
|                     &RadarSiteDialog::accepted, | ||||
|                     self_, | ||||
|                     [this]() | ||||
|                     { | ||||
|                        std::string id = | ||||
|                           alertAudioRadarSiteDialog_->radar_site(); | ||||
|    QObject::connect( | ||||
|       alertAudioRadarSiteDialog_, | ||||
|       &RadarSiteDialog::accepted, | ||||
|       self_, | ||||
|       [this]() | ||||
|       { | ||||
|          std::string id = alertAudioRadarSiteDialog_->radar_site(); | ||||
| 
 | ||||
|                        std::shared_ptr<config::RadarSite> radarSite = | ||||
|                           config::RadarSite::Get(id); | ||||
|          std::shared_ptr<config::RadarSite> radarSite = | ||||
|             config::RadarSite::Get(id); | ||||
| 
 | ||||
|                        if (radarSite != nullptr) | ||||
|                        { | ||||
|                           self_->ui->alertAudioRadarSiteComboBox | ||||
|                              ->setCurrentText(QString::fromStdString( | ||||
|                                 RadarSiteLabel(radarSite))); | ||||
|                        } | ||||
|                     }); | ||||
|          if (radarSite != nullptr) | ||||
|          { | ||||
|             self_->ui->alertAudioRadarSiteComboBox->setCurrentText( | ||||
|                QString::fromStdString(RadarSiteLabel(radarSite))); | ||||
|          } | ||||
|       }); | ||||
| 
 | ||||
|    QObject::connect(self_->ui->gpsSourceSelectButton, | ||||
|                     &QAbstractButton::clicked, | ||||
|  | @ -803,123 +796,14 @@ void SettingsDialogImpl::SetupPalettesColorTablesTab() | |||
| 
 | ||||
| void SettingsDialogImpl::SetupPalettesAlertsTab() | ||||
| { | ||||
|    settings::PaletteSettings& paletteSettings = | ||||
|       settings::PaletteSettings::Instance(); | ||||
| 
 | ||||
|    // Palettes > Alerts
 | ||||
|    QGridLayout* alertsLayout = | ||||
|       reinterpret_cast<QGridLayout*>(self_->ui->alertsFrame->layout()); | ||||
|    QVBoxLayout* layout = new QVBoxLayout(self_->ui->alertsPalette); | ||||
| 
 | ||||
|    QLabel* phenomenonLabel = new QLabel(QObject::tr("Phenomenon"), self_); | ||||
|    QLabel* activeLabel     = new QLabel(QObject::tr("Active"), self_); | ||||
|    QLabel* inactiveLabel   = new QLabel(QObject::tr("Inactive"), self_); | ||||
|    alertPaletteSettingsWidget_ = | ||||
|       new AlertPaletteSettingsWidget(self_->ui->hotkeys); | ||||
|    layout->addWidget(alertPaletteSettingsWidget_); | ||||
| 
 | ||||
|    QFont boldFont; | ||||
|    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); }); | ||||
|    } | ||||
|    settingsPages_.push_back(alertPaletteSettingsWidget_); | ||||
| } | ||||
| 
 | ||||
| void SettingsDialogImpl::SetupUnitsTab() | ||||
|  | @ -953,8 +837,7 @@ void SettingsDialogImpl::SetupAudioTab() | |||
|             locationMethod == types::LocationMethod::RadarSite; | ||||
|          bool countyEntryEnabled = | ||||
|             locationMethod == types::LocationMethod::County; | ||||
|          bool wfoEntryEnabled = | ||||
|             locationMethod == types::LocationMethod::WFO; | ||||
|          bool wfoEntryEnabled = locationMethod == types::LocationMethod::WFO; | ||||
| 
 | ||||
|          self_->ui->alertAudioLatitudeSpinBox->setEnabled( | ||||
|             coordinateEntryEnabled); | ||||
|  | @ -972,10 +855,8 @@ void SettingsDialogImpl::SetupAudioTab() | |||
|          self_->ui->resetAlertAudioRadarSiteButton->setEnabled( | ||||
|             radarSiteEntryEnable); | ||||
| 
 | ||||
|          self_->ui->alertAudioRadiusSpinBox->setEnabled( | ||||
|             radiusEntryEnable); | ||||
|          self_->ui->resetAlertAudioRadiusButton->setEnabled( | ||||
|             radiusEntryEnable); | ||||
|          self_->ui->alertAudioRadiusSpinBox->setEnabled(radiusEntryEnable); | ||||
|          self_->ui->resetAlertAudioRadiusButton->setEnabled(radiusEntryEnable); | ||||
| 
 | ||||
|          self_->ui->alertAudioCountyLineEdit->setEnabled(countyEntryEnabled); | ||||
|          self_->ui->alertAudioCountySelectButton->setEnabled( | ||||
|  | @ -1091,8 +972,7 @@ void SettingsDialogImpl::SetupAudioTab() | |||
| 
 | ||||
|    alertAudioRadius_.SetSettingsVariable(audioSettings.alert_radius()); | ||||
|    alertAudioRadius_.SetEditWidget(self_->ui->alertAudioRadiusSpinBox); | ||||
|    alertAudioRadius_.SetResetButton( | ||||
|       self_->ui->resetAlertAudioRadiusButton); | ||||
|    alertAudioRadius_.SetResetButton(self_->ui->resetAlertAudioRadiusButton); | ||||
|    alertAudioRadius_.SetUnitLabel(self_->ui->alertAudioRadiusUnitsLabel); | ||||
|    auto alertAudioRadiusUpdateUnits = [this](const std::string& newValue) | ||||
|    { | ||||
|  | @ -1206,14 +1086,10 @@ void SettingsDialogImpl::SetupAudioTab() | |||
|    alertAudioCounty_.SetEditWidget(self_->ui->alertAudioCountyLineEdit); | ||||
|    alertAudioCounty_.SetResetButton(self_->ui->resetAlertAudioCountyButton); | ||||
| 
 | ||||
|    QObject::connect( | ||||
|       self_->ui->alertAudioWFOSelectButton, | ||||
|       &QAbstractButton::clicked, | ||||
|       self_, | ||||
|       [this]() | ||||
|       { | ||||
|          wfoDialog_->show(); | ||||
|       }); | ||||
|    QObject::connect(self_->ui->alertAudioWFOSelectButton, | ||||
|                     &QAbstractButton::clicked, | ||||
|                     self_, | ||||
|                     [this]() { wfoDialog_->show(); }); | ||||
|    QObject::connect(wfoDialog_, | ||||
|                     &WFODialog::accepted, | ||||
|                     self_, | ||||
|  | @ -1232,9 +1108,8 @@ void SettingsDialogImpl::SetupAudioTab() | |||
|                     self_, | ||||
|                     [this](const QString& text) | ||||
|                     { | ||||
|                        std::string wfoName = | ||||
|                           config::CountyDatabase::GetWFOName( | ||||
|                              text.toStdString()); | ||||
|                        std::string wfoName = config::CountyDatabase::GetWFOName( | ||||
|                           text.toStdString()); | ||||
|                        self_->ui->alertAudioWFOLabel->setText( | ||||
|                           QString::fromStdString(wfoName)); | ||||
|                     }); | ||||
|  | @ -1242,7 +1117,6 @@ void SettingsDialogImpl::SetupAudioTab() | |||
|    alertAudioWFO_.SetSettingsVariable(audioSettings.alert_wfo()); | ||||
|    alertAudioWFO_.SetEditWidget(self_->ui->alertAudioWFOLineEdit); | ||||
|    alertAudioWFO_.SetResetButton(self_->ui->resetAlertAudioWFOButton); | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| 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) | ||||
| { | ||||
|    std::shared_ptr<config::RadarSite> radarSite = config::RadarSite::Get(id); | ||||
|  | @ -1444,8 +1280,6 @@ void SettingsDialogImpl::UpdateAlertRadarDialogLocation(const std::string& id) | |||
|    } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| QFont SettingsDialogImpl::GetSelectedFont() | ||||
| { | ||||
|    std::string fontFamily = fontFamilies_.at(selectedFontCategory_) | ||||
|  |  | |||
|  | @ -122,7 +122,7 @@ | |||
|           </sizepolicy> | ||||
|          </property> | ||||
|          <property name="currentIndex"> | ||||
|           <number>3</number> | ||||
|           <number>0</number> | ||||
|          </property> | ||||
|          <widget class="QWidget" name="general"> | ||||
|           <layout class="QVBoxLayout" name="verticalLayout_2"> | ||||
|  | @ -136,8 +136,8 @@ | |||
|                <rect> | ||||
|                 <x>0</x> | ||||
|                 <y>0</y> | ||||
|                 <width>274</width> | ||||
|                 <height>691</height> | ||||
|                 <width>513</width> | ||||
|                 <height>622</height> | ||||
|                </rect> | ||||
|               </property> | ||||
|               <layout class="QVBoxLayout" name="verticalLayout"> | ||||
|  | @ -610,8 +610,8 @@ | |||
|                    <rect> | ||||
|                     <x>0</x> | ||||
|                     <y>0</y> | ||||
|                     <width>98</width> | ||||
|                     <height>28</height> | ||||
|                     <width>506</width> | ||||
|                     <height>383</height> | ||||
|                    </rect> | ||||
|                   </property> | ||||
|                   <layout class="QGridLayout" name="gridLayout_3"> | ||||
|  | @ -634,49 +634,10 @@ | |||
|                </item> | ||||
|               </layout> | ||||
|              </widget> | ||||
|              <widget class="QWidget" name="tab_2"> | ||||
|              <widget class="QWidget" name="alertsPalette"> | ||||
|               <attribute name="title"> | ||||
|                <string>Alerts</string> | ||||
|               </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> | ||||
|            </item> | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| #include <scwx/qt/util/color.hpp> | ||||
| 
 | ||||
| #include <fmt/format.h> | ||||
| #include <re2/re2.h> | ||||
| #include <QColor> | ||||
| 
 | ||||
| namespace scwx | ||||
|  | @ -38,6 +39,12 @@ boost::gil::rgba32f_pixel_t ToRgba32fPixelT(const std::string& argbString) | |||
|                                        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 util
 | ||||
| } // 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); | ||||
| 
 | ||||
| /**
 | ||||
|  * Validates an ARGB string used by Qt libraries. | ||||
|  * | ||||
|  * @param argbString | ||||
|  * | ||||
|  * @return Validity of ARGB string | ||||
|  */ | ||||
| bool ValidateArgbString(const std::string& argbString); | ||||
| 
 | ||||
| } // namespace color
 | ||||
| } // namespace util
 | ||||
| } // namespace qt
 | ||||
|  |  | |||
|  | @ -1 +1 @@ | |||
| Subproject commit 20a1ca1752499222d33869e37148321936ca6354 | ||||
| Subproject commit 40a367ca89b5b197353ca58dea547a3e3407c7f3 | ||||
|  | @ -1,11 +1,16 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <scwx/awips/phenomenon.hpp> | ||||
| 
 | ||||
| #include <string> | ||||
| #include <vector> | ||||
| 
 | ||||
| namespace scwx | ||||
| { | ||||
| namespace awips | ||||
| { | ||||
| namespace ibw | ||||
| { | ||||
| 
 | ||||
| enum class ThreatCategory : int | ||||
| { | ||||
|  | @ -17,8 +22,18 @@ enum class ThreatCategory : int | |||
|    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); | ||||
| const std::string& GetThreatCategoryName(ThreatCategory threatCategory); | ||||
| 
 | ||||
| } // namespace ibw
 | ||||
| } // namespace awips
 | ||||
| } // namespace scwx
 | ||||
|  |  | |||
|  | @ -69,6 +69,7 @@ enum class Phenomenon | |||
| }; | ||||
| 
 | ||||
| Phenomenon         GetPhenomenon(const std::string& code); | ||||
| Phenomenon         GetPhenomenonFromText(const std::string& text); | ||||
| const std::string& GetPhenomenonCode(Phenomenon phenomenon); | ||||
| const std::string& GetPhenomenonText(Phenomenon phenomenon); | ||||
| 
 | ||||
|  |  | |||
|  | @ -64,9 +64,9 @@ struct Segment | |||
|    std::optional<CodedLocation>           codedLocation_ {}; | ||||
|    std::optional<CodedTimeMotionLocation> codedMotion_ {}; | ||||
| 
 | ||||
|    bool           observed_ {false}; | ||||
|    ThreatCategory threatCategory_ {ThreatCategory::Base}; | ||||
|    bool           tornadoPossible_ {false}; | ||||
|    bool                observed_ {false}; | ||||
|    ibw::ThreatCategory threatCategory_ {ibw::ThreatCategory::Base}; | ||||
|    bool                tornadoPossible_ {false}; | ||||
| 
 | ||||
|    Segment() = default; | ||||
| 
 | ||||
|  |  | |||
|  | @ -4,13 +4,40 @@ | |||
| #include <unordered_map> | ||||
| 
 | ||||
| #include <boost/algorithm/string.hpp> | ||||
| #include <boost/unordered/unordered_flat_map.hpp> | ||||
| 
 | ||||
| namespace scwx | ||||
| { | ||||
| 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> | ||||
|    threatCategoryName_ {{ThreatCategory::Base, "Base"}, | ||||
|  | @ -20,6 +47,16 @@ static const std::unordered_map<ThreatCategory, std::string> | |||
|                         {ThreatCategory::Catastrophic, "Catastrophic"}, | ||||
|                         {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_) | ||||
| 
 | ||||
| const std::string& GetThreatCategoryName(ThreatCategory threatCategory) | ||||
|  | @ -27,5 +64,6 @@ const std::string& GetThreatCategoryName(ThreatCategory threatCategory) | |||
|    return threatCategoryName_.at(threatCategory); | ||||
| } | ||||
| 
 | ||||
| } // namespace ibw
 | ||||
| } // namespace awips
 | ||||
| } // namespace scwx
 | ||||
|  |  | |||
|  | @ -77,64 +77,65 @@ static const PhenomenonCodesBimap phenomenonCodes_ = | |||
|    (Phenomenon::FreezingSpray, "ZY")                      //
 | ||||
|    (Phenomenon::Unknown, "??"); | ||||
| 
 | ||||
| static const std::unordered_map<Phenomenon, std::string> phenomenonText_ { | ||||
|    {Phenomenon::AshfallLand, "Ashfall (land)"},              //
 | ||||
|    {Phenomenon::AirStagnation, "Air Stagnation"},            //
 | ||||
|    {Phenomenon::BeachHazard, "Beach Hazard"},                //
 | ||||
|    {Phenomenon::BriskWind, "Brisk Wind"},                    //
 | ||||
|    {Phenomenon::Blizzard, "Blizzard"},                       //
 | ||||
|    {Phenomenon::CoastalFlood, "Coastal Flood"},              //
 | ||||
|    {Phenomenon::DebrisFlow, "Debris Flow"},                  //
 | ||||
|    {Phenomenon::DustStorm, "Dust Storm"},                    //
 | ||||
|    {Phenomenon::BlowingDust, "Blowing Dust"},                //
 | ||||
|    {Phenomenon::ExtremeCold, "Extreme Cold"},                //
 | ||||
|    {Phenomenon::ExcessiveHeat, "Excessive Heat"},            //
 | ||||
|    {Phenomenon::ExtremeWind, "Extreme Wind"},                //
 | ||||
|    {Phenomenon::Flood, "Flood"},                             //
 | ||||
|    {Phenomenon::FlashFlood, "Flash Flood"},                  //
 | ||||
|    {Phenomenon::DenseFogLand, "Dense Fog (land)"},           //
 | ||||
|    {Phenomenon::Flood, "Flood (Forecast Points)"},           //
 | ||||
|    {Phenomenon::Frost, "Frost"},                             //
 | ||||
|    {Phenomenon::FireWeather, "Fire Weather"},                //
 | ||||
|    {Phenomenon::Freeze, "Freeze"},                           //
 | ||||
|    {Phenomenon::Gale, "Gale"},                               //
 | ||||
|    {Phenomenon::HurricaneForceWind, "Hurricane Force Wind"}, //
 | ||||
|    {Phenomenon::Heat, "Heat"},                               //
 | ||||
|    {Phenomenon::Hurricane, "Hurricane"},                     //
 | ||||
|    {Phenomenon::HighWind, "High Wind"},                      //
 | ||||
|    {Phenomenon::Hydrologic, "Hydrologic"},                   //
 | ||||
|    {Phenomenon::HardFreeze, "Hard Freeze"},                  //
 | ||||
|    {Phenomenon::IceStorm, "Ice Storm"},                      //
 | ||||
|    {Phenomenon::LakeEffectSnow, "Lake Effect Snow"},         //
 | ||||
|    {Phenomenon::LowWater, "Low Water"},                      //
 | ||||
|    {Phenomenon::LakeshoreFlood, "Lakeshore Flood"},          //
 | ||||
|    {Phenomenon::LakeWind, "Lake Wind"},                      //
 | ||||
|    {Phenomenon::Marine, "Marine"},                           //
 | ||||
|    {Phenomenon::DenseFogMarine, "Dense Fog (marine)"},       //
 | ||||
|    {Phenomenon::AshfallMarine, "Ashfall (marine)"},          //
 | ||||
|    {Phenomenon::DenseSmokeMarine, "Dense Smoke (marine)"},   //
 | ||||
|    {Phenomenon::RipCurrentRisk, "Rip Current Risk"},         //
 | ||||
|    {Phenomenon::SmallCraft, "Small Craft"},                  //
 | ||||
|    {Phenomenon::HazardousSeas, "Hazardous Seas"},            //
 | ||||
|    {Phenomenon::DenseSmokeLand, "Dense Smoke (land)"},       //
 | ||||
|    {Phenomenon::Storm, "Storm"},                             //
 | ||||
|    {Phenomenon::StormSurge, "Storm Surge"},                  //
 | ||||
|    {Phenomenon::SnowSquall, "Snow Squall"},                  //
 | ||||
|    {Phenomenon::HighSurf, "High Surf"},                      //
 | ||||
|    {Phenomenon::SevereThunderstorm, "Severe Thunderstorm"},  //
 | ||||
|    {Phenomenon::Tornado, "Tornado"},                         //
 | ||||
|    {Phenomenon::TropicalStorm, "Tropical Storm"},            //
 | ||||
|    {Phenomenon::Tsunami, "Tsunami"},                         //
 | ||||
|    {Phenomenon::Typhoon, "Typhoon"},                         //
 | ||||
|    {Phenomenon::HeavyFreezingSpray, "Heavy Freezing Spray"}, //
 | ||||
|    {Phenomenon::WindChill, "Wind Chill"},                    //
 | ||||
|    {Phenomenon::Wind, "Wind"},                               //
 | ||||
|    {Phenomenon::WinterStorm, "Winter Storm"},                //
 | ||||
|    {Phenomenon::WinterWeather, "Winter Weather"},            //
 | ||||
|    {Phenomenon::FreezingFog, "Freezing Fog"},                //
 | ||||
|    {Phenomenon::FreezingRain, "Freezing Rain"},              //
 | ||||
|    {Phenomenon::FreezingSpray, "Freezing Spray"},            //
 | ||||
|    {Phenomenon::Unknown, "Unknown"}}; | ||||
| static const PhenomenonCodesBimap phenomenonText_ = | ||||
|    boost::assign::list_of<PhenomenonCodesBimap::relation>   //
 | ||||
|    (Phenomenon::AshfallLand, "Ashfall (land)")              //
 | ||||
|    (Phenomenon::AirStagnation, "Air Stagnation")            //
 | ||||
|    (Phenomenon::BeachHazard, "Beach Hazard")                //
 | ||||
|    (Phenomenon::BriskWind, "Brisk Wind")                    //
 | ||||
|    (Phenomenon::Blizzard, "Blizzard")                       //
 | ||||
|    (Phenomenon::CoastalFlood, "Coastal Flood")              //
 | ||||
|    (Phenomenon::DebrisFlow, "Debris Flow")                  //
 | ||||
|    (Phenomenon::DustStorm, "Dust Storm")                    //
 | ||||
|    (Phenomenon::BlowingDust, "Blowing Dust")                //
 | ||||
|    (Phenomenon::ExtremeCold, "Extreme Cold")                //
 | ||||
|    (Phenomenon::ExcessiveHeat, "Excessive Heat")            //
 | ||||
|    (Phenomenon::ExtremeWind, "Extreme Wind")                //
 | ||||
|    (Phenomenon::Flood, "Flood")                             //
 | ||||
|    (Phenomenon::FlashFlood, "Flash Flood")                  //
 | ||||
|    (Phenomenon::DenseFogLand, "Dense Fog (land)")           //
 | ||||
|    (Phenomenon::Flood, "Flood (Forecast Points)")           //
 | ||||
|    (Phenomenon::Frost, "Frost")                             //
 | ||||
|    (Phenomenon::FireWeather, "Fire Weather")                //
 | ||||
|    (Phenomenon::Freeze, "Freeze")                           //
 | ||||
|    (Phenomenon::Gale, "Gale")                               //
 | ||||
|    (Phenomenon::HurricaneForceWind, "Hurricane Force Wind") //
 | ||||
|    (Phenomenon::Heat, "Heat")                               //
 | ||||
|    (Phenomenon::Hurricane, "Hurricane")                     //
 | ||||
|    (Phenomenon::HighWind, "High Wind")                      //
 | ||||
|    (Phenomenon::Hydrologic, "Hydrologic")                   //
 | ||||
|    (Phenomenon::HardFreeze, "Hard Freeze")                  //
 | ||||
|    (Phenomenon::IceStorm, "Ice Storm")                      //
 | ||||
|    (Phenomenon::LakeEffectSnow, "Lake Effect Snow")         //
 | ||||
|    (Phenomenon::LowWater, "Low Water")                      //
 | ||||
|    (Phenomenon::LakeshoreFlood, "Lakeshore Flood")          //
 | ||||
|    (Phenomenon::LakeWind, "Lake Wind")                      //
 | ||||
|    (Phenomenon::Marine, "Marine")                           //
 | ||||
|    (Phenomenon::DenseFogMarine, "Dense Fog (marine)")       //
 | ||||
|    (Phenomenon::AshfallMarine, "Ashfall (marine)")          //
 | ||||
|    (Phenomenon::DenseSmokeMarine, "Dense Smoke (marine)")   //
 | ||||
|    (Phenomenon::RipCurrentRisk, "Rip Current Risk")         //
 | ||||
|    (Phenomenon::SmallCraft, "Small Craft")                  //
 | ||||
|    (Phenomenon::HazardousSeas, "Hazardous Seas")            //
 | ||||
|    (Phenomenon::DenseSmokeLand, "Dense Smoke (land)")       //
 | ||||
|    (Phenomenon::Storm, "Storm")                             //
 | ||||
|    (Phenomenon::StormSurge, "Storm Surge")                  //
 | ||||
|    (Phenomenon::SnowSquall, "Snow Squall")                  //
 | ||||
|    (Phenomenon::HighSurf, "High Surf")                      //
 | ||||
|    (Phenomenon::SevereThunderstorm, "Severe Thunderstorm")  //
 | ||||
|    (Phenomenon::Tornado, "Tornado")                         //
 | ||||
|    (Phenomenon::TropicalStorm, "Tropical Storm")            //
 | ||||
|    (Phenomenon::Tsunami, "Tsunami")                         //
 | ||||
|    (Phenomenon::Typhoon, "Typhoon")                         //
 | ||||
|    (Phenomenon::HeavyFreezingSpray, "Heavy Freezing Spray") //
 | ||||
|    (Phenomenon::WindChill, "Wind Chill")                    //
 | ||||
|    (Phenomenon::Wind, "Wind")                               //
 | ||||
|    (Phenomenon::WinterStorm, "Winter Storm")                //
 | ||||
|    (Phenomenon::WinterWeather, "Winter Weather")            //
 | ||||
|    (Phenomenon::FreezingFog, "Freezing Fog")                //
 | ||||
|    (Phenomenon::FreezingRain, "Freezing Rain")              //
 | ||||
|    (Phenomenon::FreezingSpray, "Freezing Spray")            //
 | ||||
|    (Phenomenon::Unknown, "Unknown"); | ||||
| 
 | ||||
| Phenomenon GetPhenomenon(const std::string& code) | ||||
| { | ||||
|  | @ -154,6 +155,24 @@ Phenomenon GetPhenomenon(const std::string& code) | |||
|    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) | ||||
| { | ||||
|    return phenomenonCodes_.left.at(phenomenon); | ||||
|  | @ -161,7 +180,7 @@ const std::string& GetPhenomenonCode(Phenomenon phenomenon) | |||
| 
 | ||||
| const std::string& GetPhenomenonText(Phenomenon phenomenon) | ||||
| { | ||||
|    return phenomenonText_.at(phenomenon); | ||||
|    return phenomenonText_.left.at(phenomenon); | ||||
| } | ||||
| 
 | ||||
| } // namespace awips
 | ||||
|  |  | |||
|  | @ -378,7 +378,7 @@ void ParseCodedInformation(std::shared_ptr<Segment> segment, | |||
|          segment->tornadoPossible_ = true; | ||||
|       } | ||||
| 
 | ||||
|       else if (segment->threatCategory_ == ThreatCategory::Base && | ||||
|       else if (segment->threatCategory_ == ibw::ThreatCategory::Base && | ||||
|                (threatTagIt = std::find_if(kThreatCategoryTags.cbegin(), | ||||
|                                            kThreatCategoryTags.cend(), | ||||
|                                            [&it](const std::string& tag) { | ||||
|  | @ -389,10 +389,23 @@ void ParseCodedInformation(std::shared_ptr<Segment> segment, | |||
|          const std::string threatCategoryName = | ||||
|             it->substr(threatTagIt->length()); | ||||
| 
 | ||||
|          ThreatCategory threatCategory = GetThreatCategory(threatCategoryName); | ||||
|          if (threatCategory == ThreatCategory::Unknown) | ||||
|          ibw::ThreatCategory threatCategory = | ||||
|             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; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Dan Paulat
						Dan Paulat