diff --git a/scwx-qt/res/icons/font-awesome-6/square-caret-down-regular.svg b/scwx-qt/res/icons/font-awesome-6/square-caret-down-regular.svg
new file mode 100644
index 00000000..98e322ef
--- /dev/null
+++ b/scwx-qt/res/icons/font-awesome-6/square-caret-down-regular.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/scwx-qt/res/icons/font-awesome-6/square-caret-right-regular.svg b/scwx-qt/res/icons/font-awesome-6/square-caret-right-regular.svg
new file mode 100644
index 00000000..06e4c955
--- /dev/null
+++ b/scwx-qt/res/icons/font-awesome-6/square-caret-right-regular.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/scwx-qt/scwx-qt.cmake b/scwx-qt/scwx-qt.cmake
index 4b32eeb3..225d2b19 100644
--- a/scwx-qt/scwx-qt.cmake
+++ b/scwx-qt/scwx-qt.cmake
@@ -121,7 +121,8 @@ set(HDR_SETTINGS source/scwx/qt/settings/general_settings.hpp
source/scwx/qt/settings/settings_interface.hpp
source/scwx/qt/settings/settings_interface_base.hpp
source/scwx/qt/settings/settings_variable.hpp
- source/scwx/qt/settings/settings_variable_base.hpp)
+ source/scwx/qt/settings/settings_variable_base.hpp
+ source/scwx/qt/settings/ui_settings.hpp)
set(SRC_SETTINGS source/scwx/qt/settings/general_settings.cpp
source/scwx/qt/settings/map_settings.cpp
source/scwx/qt/settings/palette_settings.cpp
@@ -130,7 +131,8 @@ set(SRC_SETTINGS source/scwx/qt/settings/general_settings.cpp
source/scwx/qt/settings/settings_interface.cpp
source/scwx/qt/settings/settings_interface_base.cpp
source/scwx/qt/settings/settings_variable.cpp
- source/scwx/qt/settings/settings_variable_base.cpp)
+ source/scwx/qt/settings/settings_variable_base.cpp
+ source/scwx/qt/settings/ui_settings.cpp)
set(HDR_TYPES source/scwx/qt/types/alert_types.hpp
source/scwx/qt/types/font_types.hpp
source/scwx/qt/types/github_types.hpp
@@ -147,6 +149,7 @@ set(HDR_UI source/scwx/qt/ui/about_dialog.hpp
source/scwx/qt/ui/alert_dialog.hpp
source/scwx/qt/ui/alert_dock_widget.hpp
source/scwx/qt/ui/animation_dock_widget.hpp
+ source/scwx/qt/ui/collapsible_group.hpp
source/scwx/qt/ui/flow_layout.hpp
source/scwx/qt/ui/imgui_debug_dialog.hpp
source/scwx/qt/ui/imgui_debug_widget.hpp
@@ -160,6 +163,7 @@ 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/flow_layout.cpp
source/scwx/qt/ui/imgui_debug_dialog.cpp
source/scwx/qt/ui/imgui_debug_widget.cpp
@@ -173,6 +177,7 @@ 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/imgui_debug_dialog.ui
source/scwx/qt/ui/radar_site_dialog.ui
source/scwx/qt/ui/settings_dialog.ui
diff --git a/scwx-qt/scwx-qt.qrc b/scwx-qt/scwx-qt.qrc
index 36618bee..3939ddf4 100644
--- a/scwx-qt/scwx-qt.qrc
+++ b/scwx-qt/scwx-qt.qrc
@@ -28,6 +28,8 @@
res/icons/font-awesome-6/play-solid.svg
res/icons/font-awesome-6/rotate-left-solid.svg
res/icons/font-awesome-6/sliders-solid.svg
+ res/icons/font-awesome-6/square-caret-down-regular.svg
+ res/icons/font-awesome-6/square-caret-right-regular.svg
res/icons/font-awesome-6/square-minus-regular.svg
res/icons/font-awesome-6/square-plus-regular.svg
res/palettes/wct/CC.pal
diff --git a/scwx-qt/source/scwx/qt/main/main_window.cpp b/scwx-qt/source/scwx/qt/main/main_window.cpp
index 23fd0785..225f439b 100644
--- a/scwx-qt/source/scwx/qt/main/main_window.cpp
+++ b/scwx-qt/source/scwx/qt/main/main_window.cpp
@@ -13,10 +13,12 @@
#include
#include
#include
-#include
-#include
+#include
#include
+#include
#include
+#include
+#include
#include
#include
#include
@@ -61,6 +63,11 @@ public:
mainWindow_ {mainWindow},
settings_ {},
activeMap_ {nullptr},
+ mapSettingsGroup_ {nullptr},
+ level2ProductsGroup_ {nullptr},
+ level2SettingsGroup_ {nullptr},
+ level3ProductsGroup_ {nullptr},
+ timelineGroup_ {nullptr},
level2ProductsWidget_ {nullptr},
level2SettingsWidget_ {nullptr},
level3ProductsWidget_ {nullptr},
@@ -115,6 +122,7 @@ public:
void AsyncSetup();
void ConfigureMapLayout();
void ConfigureMapStyles();
+ void ConfigureUiSettings();
void ConnectAnimationSignals();
void ConnectMapSignals();
void ConnectOtherSignals();
@@ -142,6 +150,11 @@ public:
map::MapProvider mapProvider_;
map::MapWidget* activeMap_;
+ ui::CollapsibleGroup* mapSettingsGroup_;
+ ui::CollapsibleGroup* level2ProductsGroup_;
+ ui::CollapsibleGroup* level2SettingsGroup_;
+ ui::CollapsibleGroup* level3ProductsGroup_;
+ ui::CollapsibleGroup* timelineGroup_;
ui::Level2ProductsWidget* level2ProductsWidget_;
ui::Level2SettingsWidget* level2SettingsWidget_;
@@ -193,23 +206,12 @@ MainWindow::MainWindow(QWidget* parent) :
p->alertDockWidget_->setVisible(false);
addDockWidget(Qt::BottomDockWidgetArea, p->alertDockWidget_);
- // Animation Dock Widget
- p->animationDockWidget_ = new ui::AnimationDockWidget(this);
- p->animationDockWidget_->setVisible(true);
- addDockWidget(Qt::LeftDockWidgetArea, p->animationDockWidget_);
-
// Configure Menu
ui->menuView->insertAction(ui->actionRadarToolbox,
ui->radarToolboxDock->toggleViewAction());
ui->radarToolboxDock->toggleViewAction()->setText(tr("Radar &Toolbox"));
ui->actionRadarToolbox->setVisible(false);
- ui->menuView->insertAction(ui->actionAnimationToolbox,
- p->animationDockWidget_->toggleViewAction());
- p->animationDockWidget_->toggleViewAction()->setText(
- tr("A&nimation Toolbox"));
- ui->actionAnimationToolbox->setVisible(false);
-
ui->menuView->insertAction(ui->actionResourceExplorer,
ui->resourceExplorerDock->toggleViewAction());
ui->resourceExplorerDock->toggleViewAction()->setText(
@@ -240,24 +242,55 @@ MainWindow::MainWindow(QWidget* parent) :
// Settings Dialog
p->settingsDialog_ = new ui::SettingsDialog(this);
+ // Map Settings
+ p->mapSettingsGroup_ = new ui::CollapsibleGroup(tr("Map Settings"), this);
+ p->mapSettingsGroup_->GetContentsLayout()->addWidget(ui->mapStyleLabel);
+ p->mapSettingsGroup_->GetContentsLayout()->addWidget(ui->mapStyleComboBox);
+ ui->radarToolboxScrollAreaContents->layout()->replaceWidget(
+ ui->mapSettingsGroupBox, p->mapSettingsGroup_);
+ ui->mapSettingsGroupBox->setVisible(false);
+
// Add Level 2 Products
+ p->level2ProductsGroup_ =
+ new ui::CollapsibleGroup(tr("Level 2 Products"), this);
p->level2ProductsWidget_ = new ui::Level2ProductsWidget(this);
- ui->radarProductGroupBox->layout()->replaceWidget(ui->level2ProductFrame,
- p->level2ProductsWidget_);
- delete ui->level2ProductFrame;
- ui->level2ProductFrame = p->level2ProductsWidget_;
+ p->level2ProductsGroup_->GetContentsLayout()->addWidget(
+ p->level2ProductsWidget_);
+ ui->radarToolboxScrollAreaContents->layout()->addWidget(
+ p->level2ProductsGroup_);
// Add Level 3 Products
+ p->level3ProductsGroup_ =
+ new ui::CollapsibleGroup(tr("Level 3 Products"), this);
p->level3ProductsWidget_ = new ui::Level3ProductsWidget(this);
- ui->radarProductGroupBox->layout()->replaceWidget(ui->level3ProductFrame,
- p->level3ProductsWidget_);
- delete ui->level3ProductFrame;
- ui->level3ProductFrame = p->level3ProductsWidget_;
+ p->level3ProductsGroup_->GetContentsLayout()->addWidget(
+ p->level3ProductsWidget_);
+ ui->radarToolboxScrollAreaContents->layout()->addWidget(
+ p->level3ProductsGroup_);
// Add Level 2 Settings
- p->level2SettingsWidget_ = new ui::Level2SettingsWidget(ui->settingsFrame);
- ui->settingsFrame->layout()->addWidget(p->level2SettingsWidget_);
- p->level2SettingsWidget_->setVisible(false);
+ p->level2SettingsGroup_ =
+ new ui::CollapsibleGroup(tr("Level 2 Settings"), this);
+ p->level2SettingsWidget_ = new ui::Level2SettingsWidget(this);
+ p->level2SettingsGroup_->GetContentsLayout()->addWidget(
+ p->level2SettingsWidget_);
+ ui->radarToolboxScrollAreaContents->layout()->addWidget(
+ p->level2SettingsGroup_);
+ p->level2SettingsGroup_->setVisible(false);
+ ui->radarToolboxScrollAreaContents->layout()->addWidget(
+ p->level2SettingsGroup_);
+
+ // Timeline
+ p->timelineGroup_ = new ui::CollapsibleGroup(tr("Timeline"), this);
+ p->animationDockWidget_ = new ui::AnimationDockWidget(this);
+ p->timelineGroup_->GetContentsLayout()->addWidget(p->animationDockWidget_);
+ ui->radarToolboxScrollAreaContents->layout()->addWidget(p->timelineGroup_);
+
+ // Reset toolbox spacer at the bottom
+ ui->radarToolboxScrollAreaContents->layout()->removeItem(
+ ui->radarToolboxSpacer);
+ ui->radarToolboxScrollAreaContents->layout()->addItem(
+ ui->radarToolboxSpacer);
// ImGui Debug Dialog
p->imGuiDebugDialog_ = new ui::ImGuiDebugDialog(this);
@@ -280,6 +313,7 @@ MainWindow::MainWindow(QWidget* parent) :
p->PopulateMapStyles();
p->ConfigureMapStyles();
+ p->ConfigureUiSettings();
p->ConnectMapSignals();
p->ConnectAnimationSignals();
p->ConnectOtherSignals();
@@ -637,6 +671,42 @@ void MainWindowImpl::ConfigureMapStyles()
}
}
+void MainWindowImpl::ConfigureUiSettings()
+{
+ auto& uiSettings = settings::UiSettings::Instance();
+
+ level2ProductsGroup_->SetExpanded(
+ uiSettings.level2_products_expanded().GetValue());
+ level2SettingsGroup_->SetExpanded(
+ uiSettings.level2_settings_expanded().GetValue());
+ level3ProductsGroup_->SetExpanded(
+ uiSettings.level3_products_expanded().GetValue());
+ mapSettingsGroup_->SetExpanded(
+ uiSettings.map_settings_expanded().GetValue());
+ timelineGroup_->SetExpanded(uiSettings.timeline_expanded().GetValue());
+
+ connect(level2ProductsGroup_,
+ &ui::CollapsibleGroup::StateChanged,
+ [&](bool expanded)
+ { uiSettings.level2_products_expanded().StageValue(expanded); });
+ connect(level2SettingsGroup_,
+ &ui::CollapsibleGroup::StateChanged,
+ [&](bool expanded)
+ { uiSettings.level2_settings_expanded().StageValue(expanded); });
+ connect(level3ProductsGroup_,
+ &ui::CollapsibleGroup::StateChanged,
+ [&](bool expanded)
+ { uiSettings.level3_products_expanded().StageValue(expanded); });
+ connect(mapSettingsGroup_,
+ &ui::CollapsibleGroup::StateChanged,
+ [&](bool expanded)
+ { uiSettings.map_settings_expanded().StageValue(expanded); });
+ connect(timelineGroup_,
+ &ui::CollapsibleGroup::StateChanged,
+ [&](bool expanded)
+ { uiSettings.timeline_expanded().StageValue(expanded); });
+}
+
void MainWindowImpl::ConnectMapSignals()
{
for (const auto& mapWidget : maps_)
@@ -1011,11 +1081,11 @@ void MainWindowImpl::UpdateRadarProductSettings()
if (activeMap_->GetRadarProductGroup() == common::RadarProductGroup::Level2)
{
level2SettingsWidget_->UpdateSettings(activeMap_);
- level2SettingsWidget_->setVisible(true);
+ level2SettingsGroup_->setVisible(true);
}
else
{
- level2SettingsWidget_->setVisible(false);
+ level2SettingsGroup_->setVisible(false);
}
}
diff --git a/scwx-qt/source/scwx/qt/main/main_window.ui b/scwx-qt/source/scwx/qt/main/main_window.ui
index 64cab927..369c8d28 100644
--- a/scwx-qt/source/scwx/qt/main/main_window.ui
+++ b/scwx-qt/source/scwx/qt/main/main_window.ui
@@ -76,7 +76,6 @@
&View
-
@@ -121,7 +120,7 @@
QFrame::NoFrame
- Qt::ScrollBarAlwaysOff
+ Qt::ScrollBarAsNeeded
QAbstractScrollArea::AdjustToContents
@@ -134,8 +133,8 @@
0
0
- 175
- 696
+ 157
+ 702
@@ -240,60 +239,6 @@
- -
-
-
- Radar Products
-
-
-
-
-
-
- Level 2
-
-
-
- -
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- -
-
-
- Level 3
-
-
-
- -
-
-
-
-
-
- -
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
-
-
-
@@ -470,11 +415,6 @@
&Check for Updates
-
-
- A&nimation Toolbox
-
-
diff --git a/scwx-qt/source/scwx/qt/manager/settings_manager.cpp b/scwx-qt/source/scwx/qt/manager/settings_manager.cpp
index 2572ed2a..c504e15e 100644
--- a/scwx-qt/source/scwx/qt/manager/settings_manager.cpp
+++ b/scwx-qt/source/scwx/qt/manager/settings_manager.cpp
@@ -1,5 +1,6 @@
#include
#include
+#include
#include
#include
@@ -96,6 +97,7 @@ void Shutdown()
dataChanged |= general_settings().Shutdown();
dataChanged |= map_settings().Shutdown();
+ dataChanged |= settings::UiSettings::Instance().Shutdown();
if (dataChanged)
{
@@ -128,6 +130,7 @@ static boost::json::value ConvertSettingsToJson()
general_settings().WriteJson(settingsJson);
map_settings().WriteJson(settingsJson);
palette_settings().WriteJson(settingsJson);
+ settings::UiSettings::Instance().WriteJson(settingsJson);
return settingsJson;
}
@@ -139,6 +142,7 @@ static void GenerateDefaultSettings()
general_settings().SetDefaults();
map_settings().SetDefaults();
palette_settings().SetDefaults();
+ settings::UiSettings::Instance().SetDefaults();
}
static bool LoadSettings(const boost::json::object& settingsJson)
@@ -150,6 +154,7 @@ static bool LoadSettings(const boost::json::object& settingsJson)
jsonDirty |= !general_settings().ReadJson(settingsJson);
jsonDirty |= !map_settings().ReadJson(settingsJson);
jsonDirty |= !palette_settings().ReadJson(settingsJson);
+ jsonDirty |= !settings::UiSettings::Instance().ReadJson(settingsJson);
return jsonDirty;
}
diff --git a/scwx-qt/source/scwx/qt/settings/ui_settings.cpp b/scwx-qt/source/scwx/qt/settings/ui_settings.cpp
new file mode 100644
index 00000000..dc131d96
--- /dev/null
+++ b/scwx-qt/source/scwx/qt/settings/ui_settings.cpp
@@ -0,0 +1,104 @@
+#include
+
+namespace scwx
+{
+namespace qt
+{
+namespace settings
+{
+
+static const std::string logPrefix_ = "scwx::qt::settings::ui_settings";
+
+class UiSettingsImpl
+{
+public:
+ explicit UiSettingsImpl()
+ {
+ level2ProductsExpanded_.SetDefault(false);
+ level2SettingsExpanded_.SetDefault(true);
+ level3ProductsExpanded_.SetDefault(true);
+ mapSettingsExpanded_.SetDefault(true);
+ timelineExpanded_.SetDefault(true);
+ }
+
+ ~UiSettingsImpl() {}
+
+ SettingsVariable level2ProductsExpanded_ {"level2_products_expanded"};
+ SettingsVariable level2SettingsExpanded_ {"level2_settings_expanded"};
+ SettingsVariable level3ProductsExpanded_ {"level3_products_expanded"};
+ SettingsVariable mapSettingsExpanded_ {"map_settings_expanded"};
+ SettingsVariable timelineExpanded_ {"timeline_expanded"};
+};
+
+UiSettings::UiSettings() :
+ SettingsCategory("ui"), p(std::make_unique())
+{
+ RegisterVariables({&p->level2ProductsExpanded_,
+ &p->level2SettingsExpanded_,
+ &p->level3ProductsExpanded_,
+ &p->mapSettingsExpanded_,
+ &p->timelineExpanded_});
+ SetDefaults();
+}
+UiSettings::~UiSettings() = default;
+
+UiSettings::UiSettings(UiSettings&&) noexcept = default;
+UiSettings& UiSettings::operator=(UiSettings&&) noexcept = default;
+
+SettingsVariable& UiSettings::level2_products_expanded() const
+{
+ return p->level2ProductsExpanded_;
+}
+
+SettingsVariable& UiSettings::level2_settings_expanded() const
+{
+ return p->level2SettingsExpanded_;
+}
+
+SettingsVariable& UiSettings::level3_products_expanded() const
+{
+ return p->level3ProductsExpanded_;
+}
+
+SettingsVariable& UiSettings::map_settings_expanded() const
+{
+ return p->mapSettingsExpanded_;
+}
+
+SettingsVariable& UiSettings::timeline_expanded() const
+{
+ return p->timelineExpanded_;
+}
+
+bool UiSettings::Shutdown()
+{
+ bool dataChanged = false;
+
+ // Commit settings that are managed separate from the settings dialog
+ dataChanged |= p->level2ProductsExpanded_.Commit();
+ dataChanged |= p->level2SettingsExpanded_.Commit();
+ dataChanged |= p->level3ProductsExpanded_.Commit();
+ dataChanged |= p->mapSettingsExpanded_.Commit();
+ dataChanged |= p->timelineExpanded_.Commit();
+
+ return dataChanged;
+}
+
+UiSettings& UiSettings::Instance()
+{
+ static UiSettings uiSettings_;
+ return uiSettings_;
+}
+
+bool operator==(const UiSettings& lhs, const UiSettings& rhs)
+{
+ return (lhs.p->level2ProductsExpanded_ == rhs.p->level2ProductsExpanded_ &&
+ lhs.p->level2SettingsExpanded_ == rhs.p->level2SettingsExpanded_ &&
+ lhs.p->level3ProductsExpanded_ == rhs.p->level3ProductsExpanded_ &&
+ lhs.p->mapSettingsExpanded_ == rhs.p->mapSettingsExpanded_ &&
+ lhs.p->timelineExpanded_ == rhs.p->timelineExpanded_);
+}
+
+} // namespace settings
+} // namespace qt
+} // namespace scwx
diff --git a/scwx-qt/source/scwx/qt/settings/ui_settings.hpp b/scwx-qt/source/scwx/qt/settings/ui_settings.hpp
new file mode 100644
index 00000000..e3045bcb
--- /dev/null
+++ b/scwx-qt/source/scwx/qt/settings/ui_settings.hpp
@@ -0,0 +1,48 @@
+#pragma once
+
+#include
+#include
+
+#include
+#include
+
+namespace scwx
+{
+namespace qt
+{
+namespace settings
+{
+
+class UiSettingsImpl;
+
+class UiSettings : public SettingsCategory
+{
+public:
+ explicit UiSettings();
+ ~UiSettings();
+
+ UiSettings(const UiSettings&) = delete;
+ UiSettings& operator=(const UiSettings&) = delete;
+
+ UiSettings(UiSettings&&) noexcept;
+ UiSettings& operator=(UiSettings&&) noexcept;
+
+ SettingsVariable& level2_products_expanded() const;
+ SettingsVariable& level2_settings_expanded() const;
+ SettingsVariable& level3_products_expanded() const;
+ SettingsVariable& map_settings_expanded() const;
+ SettingsVariable& timeline_expanded() const;
+
+ bool Shutdown();
+
+ static UiSettings& Instance();
+
+ friend bool operator==(const UiSettings& lhs, const UiSettings& rhs);
+
+private:
+ std::unique_ptr p;
+};
+
+} // namespace settings
+} // namespace qt
+} // namespace scwx
diff --git a/scwx-qt/source/scwx/qt/ui/animation_dock_widget.cpp b/scwx-qt/source/scwx/qt/ui/animation_dock_widget.cpp
index 3d391407..34b1001a 100644
--- a/scwx-qt/source/scwx/qt/ui/animation_dock_widget.cpp
+++ b/scwx-qt/source/scwx/qt/ui/animation_dock_widget.cpp
@@ -51,7 +51,7 @@ public:
};
AnimationDockWidget::AnimationDockWidget(QWidget* parent) :
- QDockWidget(parent),
+ QFrame(parent),
p {std::make_unique(this)},
ui(new Ui::AnimationDockWidget)
{
diff --git a/scwx-qt/source/scwx/qt/ui/animation_dock_widget.hpp b/scwx-qt/source/scwx/qt/ui/animation_dock_widget.hpp
index 8b64d0c6..abc79c88 100644
--- a/scwx-qt/source/scwx/qt/ui/animation_dock_widget.hpp
+++ b/scwx-qt/source/scwx/qt/ui/animation_dock_widget.hpp
@@ -4,7 +4,7 @@
#include
-#include
+#include
namespace Ui
{
@@ -20,7 +20,7 @@ namespace ui
class AnimationDockWidgetImpl;
-class AnimationDockWidget : public QDockWidget
+class AnimationDockWidget : public QFrame
{
Q_OBJECT
diff --git a/scwx-qt/source/scwx/qt/ui/animation_dock_widget.ui b/scwx-qt/source/scwx/qt/ui/animation_dock_widget.ui
index 33485032..1c79eb48 100644
--- a/scwx-qt/source/scwx/qt/ui/animation_dock_widget.ui
+++ b/scwx-qt/source/scwx/qt/ui/animation_dock_widget.ui
@@ -1,353 +1,304 @@
AnimationDockWidget
-
+
0
0
- 200
- 543
+ 189
+ 264
-
- Animation Toolbox
+
+ QFrame::StyledPanel
-
-
-
-
-
-
- QFrame::NoFrame
-
-
+
+ QFrame::Raised
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+ Auto Update: Enabled
+
+
+
+ -
+
+
+ Live View
+
+
+ true
+
+
+
+ -
+
+
+ Archive View
+
+
+
+ -
+
+
+ QAbstractSpinBox::CorrectToNearestValue
+
+
+
+ 0
+ 0
+ 0
+ 1991
+ 6
+ 1
+
+
+
+ yyyy-MM-dd
+
+
+ true
+
+
+
+ -
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+
0
-
- Qt::ScrollBarAlwaysOff
+
+ 0
-
- true
+
+ 0
-
-
-
- 0
- 0
- 182
- 506
-
-
-
-
- 0
+
+ 0
+
+
-
+
+
+ QAbstractSpinBox::CorrectToNearestValue
-
- 0
+
+ HH:mm
-
- 0
+
+
+ -
+
+
+ UTC
-
- 0
+
+
+
+
+
+ -
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+ QAbstractSpinBox::CorrectToNearestValue
-
-
-
-
- Timeline
-
-
-
-
-
-
- Auto Update: Enabled
-
-
-
- -
-
-
- Live View
-
-
- true
-
-
-
- -
-
-
- Archive View
-
-
-
- -
-
-
- QAbstractSpinBox::CorrectToNearestValue
-
-
-
- 0
- 0
- 0
- 1991
- 6
- 1
-
-
-
- yyyy-MM-dd
-
-
- true
-
-
-
- -
-
-
- QFrame::StyledPanel
-
-
- QFrame::Raised
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
-
-
-
- QAbstractSpinBox::CorrectToNearestValue
-
-
- HH:mm
-
-
-
- -
-
-
- UTC
-
-
-
-
-
-
- -
-
-
- QFrame::StyledPanel
-
-
- QFrame::Raised
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
-
-
-
- QAbstractSpinBox::CorrectToNearestValue
-
-
- min
-
-
- 1
-
-
- 1440
-
-
- 30
-
-
-
- -
-
-
- Loop Time
-
-
-
- -
-
-
- Loop Speed
-
-
-
- -
-
-
- QAbstractSpinBox::CorrectToNearestValue
-
-
- x
-
-
- 1.000000000000000
-
-
- 1.000000000000000
-
-
-
- -
-
-
- Loop Delay
-
-
-
- -
-
-
- sec
-
-
- 1
-
-
- 15.000000000000000
-
-
- 0.100000000000000
-
-
- 2.500000000000000
-
-
-
-
-
-
- -
-
-
- QFrame::StyledPanel
-
-
- QFrame::Raised
-
-
-
- 1
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
-
-
-
- ...
-
-
-
- :/res/icons/font-awesome-6/backward-step-solid.svg:/res/icons/font-awesome-6/backward-step-solid.svg
-
-
-
- -
-
-
- ...
-
-
-
- :/res/icons/font-awesome-6/angle-left-solid.svg:/res/icons/font-awesome-6/angle-left-solid.svg
-
-
-
- -
-
-
- ...
-
-
-
- :/res/icons/font-awesome-6/play-solid.svg:/res/icons/font-awesome-6/play-solid.svg
-
-
-
- -
-
-
- ...
-
-
-
- :/res/icons/font-awesome-6/angle-right-solid.svg:/res/icons/font-awesome-6/angle-right-solid.svg
-
-
-
- -
-
-
- ...
-
-
-
- :/res/icons/font-awesome-6/forward-step-solid.svg:/res/icons/font-awesome-6/forward-step-solid.svg
-
-
-
-
-
-
-
-
-
- -
-
-
- Qt::Vertical
-
-
-
- 20
- 40
-
-
-
-
-
-
-
-
-
-
+
+ min
+
+
+ 1
+
+
+ 1440
+
+
+ 30
+
+
+
+ -
+
+
+ Loop Speed
+
+
+
+ -
+
+
+ Loop Delay
+
+
+
+ -
+
+
+ sec
+
+
+ 1
+
+
+ 15.000000000000000
+
+
+ 0.100000000000000
+
+
+ 2.500000000000000
+
+
+
+ -
+
+
+ QAbstractSpinBox::CorrectToNearestValue
+
+
+ x
+
+
+ 1.000000000000000
+
+
+ 1.000000000000000
+
+
+
+ -
+
+
+ Loop Time
+
+
+
+
+
+
+ -
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+
+ 1
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+ ...
+
+
+
+ :/res/icons/font-awesome-6/backward-step-solid.svg:/res/icons/font-awesome-6/backward-step-solid.svg
+
+
+
+ -
+
+
+ ...
+
+
+
+ :/res/icons/font-awesome-6/angle-left-solid.svg:/res/icons/font-awesome-6/angle-left-solid.svg
+
+
+
+ -
+
+
+ ...
+
+
+
+ :/res/icons/font-awesome-6/play-solid.svg:/res/icons/font-awesome-6/play-solid.svg
+
+
+
+ -
+
+
+ ...
+
+
+
+ :/res/icons/font-awesome-6/angle-right-solid.svg:/res/icons/font-awesome-6/angle-right-solid.svg
+
+
+
+ -
+
+
+ ...
+
+
+
+ :/res/icons/font-awesome-6/forward-step-solid.svg:/res/icons/font-awesome-6/forward-step-solid.svg
+
+
+
+
+
+
+
diff --git a/scwx-qt/source/scwx/qt/ui/collapsible_group.cpp b/scwx-qt/source/scwx/qt/ui/collapsible_group.cpp
new file mode 100644
index 00000000..a66d9703
--- /dev/null
+++ b/scwx-qt/source/scwx/qt/ui/collapsible_group.cpp
@@ -0,0 +1,102 @@
+#include "collapsible_group.hpp"
+#include "ui_collapsible_group.h"
+
+namespace scwx
+{
+namespace qt
+{
+namespace ui
+{
+
+class CollapsibleGroupImpl
+{
+public:
+ explicit CollapsibleGroupImpl(CollapsibleGroup* self) : self_ {self} {}
+ ~CollapsibleGroupImpl() = default;
+
+ void Initialize();
+
+ const QIcon kCollapsedIcon_ {
+ ":/res/icons/font-awesome-6/square-caret-right-regular.svg"};
+ const QIcon kExpandedIcon_ {
+ ":/res/icons/font-awesome-6/square-caret-down-regular.svg"};
+
+ const std::map kIcon_ {{false, kCollapsedIcon_},
+ {true, kExpandedIcon_}};
+
+ CollapsibleGroup* self_;
+
+ bool expanded_ {true};
+};
+
+CollapsibleGroup::CollapsibleGroup(QWidget* parent) :
+ QFrame(parent),
+ p {std::make_unique(this)},
+ ui(new Ui::CollapsibleGroup)
+{
+ ui->setupUi(this);
+ p->Initialize();
+}
+
+CollapsibleGroup::CollapsibleGroup(const QString& title, QWidget* parent) :
+ QFrame(parent),
+ p {std::make_unique(this)},
+ ui(new Ui::CollapsibleGroup)
+{
+ ui->setupUi(this);
+ ui->titleButton->setText(title);
+ p->Initialize();
+}
+
+CollapsibleGroup::~CollapsibleGroup()
+{
+ delete ui;
+}
+
+void CollapsibleGroupImpl::Initialize()
+{
+ QObject::connect(
+ self_->ui->titleButton,
+ &QAbstractButton::clicked,
+ self_,
+ [this]() { self_->SetExpanded(!expanded_); },
+ Qt::DirectConnection);
+
+ self_->SetExpanded(true);
+}
+
+QLayout* CollapsibleGroup::GetContentsLayout()
+{
+ return ui->contentsFrame->layout();
+}
+
+void CollapsibleGroup::SetContentsLayout(QLayout* layout)
+{
+ ui->contentsFrame->setLayout(layout);
+}
+
+void CollapsibleGroup::SetTitle(const QString& title)
+{
+ ui->titleButton->setText(title);
+}
+
+void CollapsibleGroup::SetExpanded(bool expanded)
+{
+ // Update icon
+ ui->titleButton->setIcon(p->kIcon_.at(expanded));
+
+ // Update contents visibility
+ ui->contentsFrame->setVisible(expanded);
+
+ // Update internal state
+ if (p->expanded_ != expanded)
+ {
+ p->expanded_ = expanded;
+
+ Q_EMIT StateChanged(expanded);
+ }
+}
+
+} // namespace ui
+} // namespace qt
+} // namespace scwx
diff --git a/scwx-qt/source/scwx/qt/ui/collapsible_group.hpp b/scwx-qt/source/scwx/qt/ui/collapsible_group.hpp
new file mode 100644
index 00000000..65966b48
--- /dev/null
+++ b/scwx-qt/source/scwx/qt/ui/collapsible_group.hpp
@@ -0,0 +1,46 @@
+#pragma once
+
+#include
+
+namespace Ui
+{
+class CollapsibleGroup;
+}
+
+namespace scwx
+{
+namespace qt
+{
+namespace ui
+{
+
+class CollapsibleGroupImpl;
+
+class CollapsibleGroup : public QFrame
+{
+ Q_OBJECT
+
+public:
+ explicit CollapsibleGroup(QWidget* parent = nullptr);
+ explicit CollapsibleGroup(const QString& title, QWidget* parent = nullptr);
+ ~CollapsibleGroup();
+
+ QLayout* GetContentsLayout();
+ void SetContentsLayout(QLayout* contents);
+ void SetTitle(const QString& title);
+
+public slots:
+ void SetExpanded(bool expanded);
+
+signals:
+ void StateChanged(bool expanded);
+
+private:
+ friend class CollapsibleGroupImpl;
+ std::unique_ptr p;
+ Ui::CollapsibleGroup* ui;
+};
+
+} // namespace ui
+} // namespace qt
+} // namespace scwx
diff --git a/scwx-qt/source/scwx/qt/ui/collapsible_group.ui b/scwx-qt/source/scwx/qt/ui/collapsible_group.ui
new file mode 100644
index 00000000..1c7f670e
--- /dev/null
+++ b/scwx-qt/source/scwx/qt/ui/collapsible_group.ui
@@ -0,0 +1,64 @@
+
+
+ CollapsibleGroup
+
+
+
+ 0
+ 0
+ 400
+ 300
+
+
+
+ Frame
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ text-align: left;
+
+
+ Title
+
+
+
+ :/res/icons/font-awesome-6/square-caret-right-regular.svg
+ :/res/icons/font-awesome-6/square-caret-down-regular.svg:/res/icons/font-awesome-6/square-caret-right-regular.svg
+
+
+
+ -
+
+
+ QFrame::Box
+
+
+ QFrame::Sunken
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/data b/test/data
index 875afa5e..b2fa7d86 160000
--- a/test/data
+++ b/test/data
@@ -1 +1 @@
-Subproject commit 875afa5ead329536f802096ca1d2ef1010a7cc6b
+Subproject commit b2fa7d866800902f4c092c567017c832bcdbe702