diff --git a/resource/UKControllerPlugin.rc b/resource/UKControllerPlugin.rc index dc9898b2c..d5ce82a3f 100644 --- a/resource/UKControllerPlugin.rc +++ b/resource/UKControllerPlugin.rc @@ -122,6 +122,8 @@ BEGIN "Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,25,118,10 CONTROL "Notify Departure Release Activity In Chat Area",IDC_RELEASE_CHAT_MESSAGE, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,34,152,10 + COMBOBOX IDC_COLOUR_PALETTE,178,16,48,56,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + LTEXT "Colour Palette",IDC_STATIC,230,18,43,8 END IDD_TIMER_CONFIGURATION DIALOGEX 0, 0, 179, 158 @@ -391,6 +393,8 @@ BEGIN RIGHTMARGIN, 302 TOPMARGIN, 7 BOTTOMMARGIN, 169 + HORZGUIDE, 16 + HORZGUIDE, 22 END IDD_TIMER_CONFIGURATION, DIALOG diff --git a/resource/resource.h b/resource/resource.h index a151d55ec..8645f6311 100644 --- a/resource/resource.h +++ b/resource/resource.h @@ -207,6 +207,7 @@ #define IDC_API_KEY_STATIC 1144 #define IDC_ 1145 #define IDC_RELEASE_CHAT_MESSAGE 1145 +#define IDC_COLOUR_PALETTE 1146 // Next default values for new objects // @@ -214,7 +215,7 @@ #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 147 #define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1146 +#define _APS_NEXT_CONTROL_VALUE 1147 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/src/plugin/CMakeLists.txt b/src/plugin/CMakeLists.txt index 8a5c76ddb..f15fba875 100644 --- a/src/plugin/CMakeLists.txt +++ b/src/plugin/CMakeLists.txt @@ -294,6 +294,10 @@ set(src__geometry source_group("src\\geometry" FILES ${src__geometry}) set(src__graphics + "graphics/ColourPaletteDefinitions.cpp" + "graphics/ColourPaletteDefinitions.h" + "graphics/ThemingModule.cpp" + "graphics/ThemingModule.h" "graphics/GdiGraphicsInterface.h" "graphics/GdiGraphicsWrapper.cpp" "graphics/GdiGraphicsWrapper.h" diff --git a/src/plugin/approach/ApproachBootstrapProvider.cpp b/src/plugin/approach/ApproachBootstrapProvider.cpp index ac4bdadbc..8899ed76c 100644 --- a/src/plugin/approach/ApproachBootstrapProvider.cpp +++ b/src/plugin/approach/ApproachBootstrapProvider.cpp @@ -26,6 +26,9 @@ #include "radarscreen/RadarRenderableCollection.h" #include "tag/TagItemCollection.h" #include "timedevent/TimedEventCollection.h" +#include "graphics/GdiplusBrushes.h" + +using UKControllerPlugin::Windows::GdiplusBrushes; namespace UKControllerPlugin::Approach { @@ -92,6 +95,7 @@ namespace UKControllerPlugin::Approach { container.moduleFactories->Approach().SequencerOptions(), displayOptions), "Toggle sequencer airfield separation selector"), *container.plugin, + *container.brushes, sequencerScreenObjectId), RadarScreen::RadarRenderableCollection::beforeTags); diff --git a/src/plugin/approach/ApproachBootstrapProvider.h b/src/plugin/approach/ApproachBootstrapProvider.h index bbb8c1a68..f5e0d60b6 100644 --- a/src/plugin/approach/ApproachBootstrapProvider.h +++ b/src/plugin/approach/ApproachBootstrapProvider.h @@ -1,6 +1,12 @@ #pragma once #include "bootstrap/BootstrapProviderInterface.h" +namespace UKControllerPlugin { + namespace Windows { + struct GdiplusBrushes; + } // namespace Windows +} + namespace UKControllerPlugin::Approach { class ApproachBootstrapProvider : public Bootstrap::BootstrapProviderInterface { diff --git a/src/plugin/approach/ApproachSequencerDisplay.cpp b/src/plugin/approach/ApproachSequencerDisplay.cpp index 4fd25a124..c3c3f0a13 100644 --- a/src/plugin/approach/ApproachSequencerDisplay.cpp +++ b/src/plugin/approach/ApproachSequencerDisplay.cpp @@ -17,10 +17,12 @@ #include "helper/HelperFunctions.h" #include "list/PopupListInterface.h" #include "number/NumberFormat.h" +#include "graphics/GdiplusBrushes.h" using UKControllerPlugin::Components::CollapsibleWindowTitleBar; using UKControllerPlugin::Number::To1Dp; using UKControllerPlugin::Number::To1DpWide; +using UKControllerPlugin::Windows::GdiplusBrushes; namespace UKControllerPlugin::Approach { @@ -35,17 +37,19 @@ namespace UKControllerPlugin::Approach { std::shared_ptr airfieldTargetSelector, std::shared_ptr airfieldSeparationSelector, Euroscope::EuroscopePluginLoopbackInterface& plugin, + const GdiplusBrushes& brushes, int screenObjectId) : sequencer(sequencer), spacingCalculator(spacingCalculator), options(options), displayOptions(std::move(displayOptions)), airfieldSelector(std::move(airfieldSelector)), callsignSelector(std::move(callsignSelector)), targetSelector(std::move(targetSelector)), airfieldTargetSelector(std::move(airfieldTargetSelector)), airfieldSeparationSelector(std::move(airfieldSeparationSelector)), plugin(plugin), - screenObjectId(screenObjectId), titleBar(CollapsibleWindowTitleBar::Create( + brushes(brushes), screenObjectId(screenObjectId), titleBar(CollapsibleWindowTitleBar::Create( L"Approach Sequencer", titleBarArea, [this]() -> bool { return this->displayOptions->ContentCollapsed(); }, - screenObjectId)), + screenObjectId, + brushes)), airfieldClickspot(Components::ClickableArea::Create( this->airfieldTextArea, screenObjectId, AIRFIELD_SELECTOR_CLICKSPOT, false)), addClickspot( @@ -53,10 +57,7 @@ namespace UKControllerPlugin::Approach { airfieldTargetClickspot(Components::ClickableArea::Create( this->airfieldTargetTextArea, screenObjectId, AIRFIELD_TARGET_CLICKSPOT, false)), airfieldSeparationClickspot(Components::ClickableArea::Create( - this->airfieldSeparationTextArea, screenObjectId, AIRFIELD_SEPARATION_CLICKSPOT, false)), - backgroundBrush(std::make_shared(BACKGROUND_COLOUR)), - textBrush(std::make_shared(TEXT_COLOUR)), - dividingPen(std::make_shared(TEXT_COLOUR)) + this->airfieldSeparationTextArea, screenObjectId, AIRFIELD_SEPARATION_CLICKSPOT, false)) { } @@ -76,12 +77,12 @@ namespace UKControllerPlugin::Approach { graphics.Translated( displayOptions->Position().x, displayOptions->Position().y, [this, &graphics, &radarScreen]() { if (this->displayOptions->ContentCollapsed()) { - this->titleBar->Draw(graphics, radarScreen); + this->titleBar->DrawTheme(graphics, radarScreen, brushes); return; } this->RenderBackground(graphics); - this->titleBar->Draw(graphics, radarScreen); + this->titleBar->DrawTheme(graphics, radarScreen, brushes); this->RenderAirfield(graphics, radarScreen); this->RenderAddButton(graphics, radarScreen); this->RenderAirfieldTarget(graphics, radarScreen); @@ -173,7 +174,7 @@ namespace UKControllerPlugin::Approach { graphics.DrawString( L"Airfield:", airfieldStaticArea, - *textBrush, + Gdiplus::SolidBrush(this->brushes.text), Graphics::StringFormatManager::Instance().GetLeftAlign(), Graphics::FontManager::Instance().GetDefault()); @@ -181,7 +182,7 @@ namespace UKControllerPlugin::Approach { HelperFunctions::ConvertToWideString( displayOptions->Airfield().empty() ? "--" : displayOptions->Airfield()), airfieldTextArea, - *textBrush, + Gdiplus::SolidBrush(this->brushes.text), Graphics::StringFormatManager::Instance().GetLeftAlign(), Graphics::FontManager::Instance().GetDefault()); this->airfieldClickspot->Apply(graphics, radarScreen); @@ -189,23 +190,23 @@ namespace UKControllerPlugin::Approach { void ApproachSequencerDisplay::RenderDivider(Windows::GdiGraphicsInterface& graphics) { - graphics.DrawLine(*dividingPen, dividerLeft, dividerRight); + graphics.DrawLine(Gdiplus::Pen(this->brushes.text), dividerLeft, dividerRight); } void ApproachSequencerDisplay::RenderHeaders(Windows::GdiGraphicsInterface& graphics) { - graphics.DrawString(L"#", numberHeader, *textBrush); - graphics.DrawString(L"Callsign", callsignHeader, *textBrush); - graphics.DrawString(L"Target", targetHeader, *textBrush); - graphics.DrawString(L"Actual", actualHeader, *textBrush); - graphics.DrawString(L"Actions", actionsHeader, *textBrush); + graphics.DrawString(L"#", numberHeader, Gdiplus::SolidBrush(this->brushes.text)); + graphics.DrawString(L"Callsign", callsignHeader, Gdiplus::SolidBrush(this->brushes.text)); + graphics.DrawString(L"Target", targetHeader, Gdiplus::SolidBrush(this->brushes.text)); + graphics.DrawString(L"Actual", actualHeader, Gdiplus::SolidBrush(this->brushes.text)); + graphics.DrawString(L"Actions", actionsHeader, Gdiplus::SolidBrush(this->brushes.text)); } void ApproachSequencerDisplay::RenderAddButton( Windows::GdiGraphicsInterface& graphics, Euroscope::EuroscopeRadarLoopbackInterface& radarScreen) { - graphics.DrawRect(addButton, *dividingPen); - graphics.DrawString(L"Add Aircraft", addButton, *textBrush); + graphics.DrawRect(addButton, Gdiplus::Pen(this->brushes.text)); + graphics.DrawString(L"Add Aircraft", addButton, Gdiplus::SolidBrush(this->brushes.text)); addClickspot->Apply(graphics, radarScreen); } @@ -248,15 +249,15 @@ namespace UKControllerPlugin::Approach { int sequenceNumber = 1; while (aircraftToProcess != nullptr) { - graphics.DrawString(std::to_wstring(sequenceNumber), numberRect, *textBrush); + graphics.DrawString(std::to_wstring(sequenceNumber), numberRect, Gdiplus::SolidBrush(this->brushes.text)); graphics.DrawString( - HelperFunctions::ConvertToWideString(aircraftToProcess->Callsign()), callsignRect, *textBrush); + HelperFunctions::ConvertToWideString(aircraftToProcess->Callsign()), callsignRect, Gdiplus::SolidBrush(this->brushes.text)); // The target distance / wake if (aircraftToProcess->Mode() == ApproachSequencingMode::WakeTurbulence) { - graphics.DrawString(L"Wake", targetRect, *textBrush); + graphics.DrawString(L"Wake", targetRect, Gdiplus::SolidBrush(this->brushes.text)); } else { - graphics.DrawString(To1DpWide(aircraftToProcess->ExpectedDistance()), targetRect, *textBrush); + graphics.DrawString(To1DpWide(aircraftToProcess->ExpectedDistance()), targetRect, Gdiplus::SolidBrush(this->brushes.text)); } Components::ClickableArea::Create( targetRect, screenObjectId, "approachTarget" + aircraftToProcess->Callsign(), false) @@ -264,30 +265,30 @@ namespace UKControllerPlugin::Approach { double requiredSpacing = spacingCalculator.Calculate(displayOptions->Airfield(), *aircraftToProcess); if (requiredSpacing == spacingCalculator.NoSpacing()) { - graphics.DrawString(L"--", actualRect, *textBrush); + graphics.DrawString(L"--", actualRect, Gdiplus::SolidBrush(this->brushes.text)); } else { - graphics.DrawString(To1DpWide(requiredSpacing), actualRect, *textBrush); + graphics.DrawString(To1DpWide(requiredSpacing), actualRect, Gdiplus::SolidBrush(this->brushes.text)); } auto upButton = Components::Button::Create( upButtonRect, screenObjectId, "moveUp" + aircraftToProcess->Callsign(), - Components::UpArrow(TEXT_COLOUR)); + Components::UpArrow(this->brushes.text)); upButton->Draw(graphics, radarScreen); auto downButton = Components::Button::Create( downButtonRect, screenObjectId, "moveDown" + aircraftToProcess->Callsign(), - Components::DownArrow(TEXT_COLOUR)); + Components::DownArrow(this->brushes.text)); downButton->Draw(graphics, radarScreen); auto deleteButton = Components::Button::Create( deleteButtonRect, screenObjectId, "deleteButton" + aircraftToProcess->Callsign(), - Components::DeleteButton(TEXT_COLOUR)); + Components::DeleteButton(this->brushes.text)); deleteButton->Draw(graphics, radarScreen); auto toggleButton = Components::Button::Create( @@ -297,13 +298,13 @@ namespace UKControllerPlugin::Approach { [&aircraftToProcess, &radarScreen, this]( Windows::GdiGraphicsInterface& graphics, const Gdiplus::Rect& area) { Gdiplus::Rect drawArea = {0, 0, area.Width, area.Height}; - graphics.FillCircle(drawArea, *textBrush); + graphics.FillCircle(drawArea, Gdiplus::SolidBrush(this->brushes.text)); if (!aircraftToProcess->ShouldDraw()) { Components::Button::Create( drawArea, screenObjectId, "toggleDraw" + aircraftToProcess->Callsign(), - Components::DeleteButton(BACKGROUND_COLOUR)) + Components::DeleteButton(this->brushes.background)) ->Draw(graphics, radarScreen); } }); @@ -332,7 +333,7 @@ namespace UKControllerPlugin::Approach { TITLE_BAR_HEIGHT, WINDOW_WIDTH, static_cast(callsignHeader.GetBottom() + INSETS + (numberOfCallsigns * callsignHeader.Height))}; - graphics.FillRect(contentArea, *backgroundBrush); + graphics.FillRect(contentArea, Gdiplus::SolidBrush(this->brushes.background)); } void ApproachSequencerDisplay::RenderAirfieldTarget( @@ -341,7 +342,7 @@ namespace UKControllerPlugin::Approach { graphics.DrawString( L"Target:", airfieldTargetStatic, - *textBrush, + Gdiplus::SolidBrush(this->brushes.text), Graphics::StringFormatManager::Instance().GetLeftAlign(), Graphics::FontManager::Instance().GetDefault()); @@ -356,7 +357,7 @@ namespace UKControllerPlugin::Approach { graphics.DrawString( targetString, airfieldTargetTextArea, - *textBrush, + Gdiplus::SolidBrush(this->brushes.text), Graphics::StringFormatManager::Instance().GetLeftAlign(), Graphics::FontManager::Instance().GetDefault()); this->airfieldTargetClickspot->Apply(graphics, radarScreen); @@ -368,7 +369,7 @@ namespace UKControllerPlugin::Approach { graphics.DrawString( L"Separation:", airfieldSeparationStatic, - *textBrush, + Gdiplus::SolidBrush(this->brushes.text), Graphics::StringFormatManager::Instance().GetLeftAlign(), Graphics::FontManager::Instance().GetDefault()); @@ -377,7 +378,7 @@ namespace UKControllerPlugin::Approach { ? L"--" : To1DpWide(options.Get(displayOptions->Airfield()).minimumSeparationRequirement), airfieldSeparationTextArea, - *textBrush, + Gdiplus::SolidBrush(this->brushes.text), Graphics::StringFormatManager::Instance().GetLeftAlign(), Graphics::FontManager::Instance().GetDefault()); this->airfieldSeparationClickspot->Apply(graphics, radarScreen); diff --git a/src/plugin/approach/ApproachSequencerDisplay.h b/src/plugin/approach/ApproachSequencerDisplay.h index 483906d49..541b2bc3a 100644 --- a/src/plugin/approach/ApproachSequencerDisplay.h +++ b/src/plugin/approach/ApproachSequencerDisplay.h @@ -12,6 +12,9 @@ namespace UKControllerPlugin { namespace List { class PopupListInterface; } // namespace List + namespace Windows { + struct GdiplusBrushes; + } // namespace Windows } // namespace UKControllerPlugin namespace UKControllerPlugin::Approach { @@ -37,6 +40,7 @@ namespace UKControllerPlugin::Approach { std::shared_ptr airfieldTargetSelector, std::shared_ptr airfieldSeparationSelector, Euroscope::EuroscopePluginLoopbackInterface& plugin, + const UKControllerPlugin::Windows::GdiplusBrushes& brushes, int screenObjectId); [[nodiscard]] auto IsVisible() const -> bool override; void LeftClick( @@ -67,7 +71,7 @@ namespace UKControllerPlugin::Approach { // Dimensions inline static const int WINDOW_WIDTH = 435; - inline static const int TITLE_BAR_HEIGHT = 20; + inline static const int TITLE_BAR_HEIGHT = 15; inline static const int INSETS = 5; // Clickspot @@ -105,6 +109,9 @@ namespace UKControllerPlugin::Approach { // The plugin Euroscope::EuroscopePluginLoopbackInterface& plugin; + + // Pens and brushes + const UKControllerPlugin::Windows::GdiplusBrushes& brushes; // The screen object id int screenObjectId; @@ -138,10 +145,5 @@ namespace UKControllerPlugin::Approach { std::shared_ptr airfieldTargetClickspot; std::shared_ptr airfieldSeparationClickspot; - const Gdiplus::Color BACKGROUND_COLOUR = Gdiplus::Color(64, 64, 64); - const Gdiplus::Color TEXT_COLOUR = Gdiplus::Color(225, 225, 225); - std::shared_ptr backgroundBrush; - std::shared_ptr textBrush; - std::shared_ptr dividingPen; }; } // namespace UKControllerPlugin::Approach diff --git a/src/plugin/bootstrap/InitialisePlugin.cpp b/src/plugin/bootstrap/InitialisePlugin.cpp index b2161cf23..3b992fb5e 100644 --- a/src/plugin/bootstrap/InitialisePlugin.cpp +++ b/src/plugin/bootstrap/InitialisePlugin.cpp @@ -64,6 +64,7 @@ #include "task/TaskRunnerInterface.h" #include "update/PluginVersion.h" #include "wake/WakeModule.h" +#include "graphics/ThemingModule.h" using UKControllerPlugin::Bootstrap::CollectionBootstrap; using UKControllerPlugin::Bootstrap::EventHandlerCollectionBootstrap; @@ -92,6 +93,7 @@ using UKControllerPlugin::Plugin::PluginVersion; using UKControllerPlugin::Prenote::PrenoteModule; using UKControllerPlugin::Regional::RegionalPressureModule; using UKControllerPlugin::Squawk::SquawkModule; +using UKControllerPlugin::Graphics::ThemingModule; namespace UKControllerPlugin { /* @@ -236,13 +238,16 @@ namespace UKControllerPlugin { LoginModule::BootstrapPlugin(*this->container); SectorFile::BootstrapPlugin(*this->container); + ThemingModule::BootstrapPlugin(*this->container, *this->container->pluginUserSettingHandler); + // General settings config bootstrap GeneralSettingsConfigurationBootstrap::BootstrapPlugin( *this->container->dialogManager, *this->container->pluginUserSettingHandler, *this->container->userSettingHandlers, *this->container->settingsRepository, - *this->container->windows); + *this->container->windows, + *this->container->brushes); // Bootstrap the modules Metar::BootstrapPlugin(*this->container); diff --git a/src/plugin/components/CollapsibleWindowTitleBar.cpp b/src/plugin/components/CollapsibleWindowTitleBar.cpp index 80fa1cf65..72d2e5b25 100644 --- a/src/plugin/components/CollapsibleWindowTitleBar.cpp +++ b/src/plugin/components/CollapsibleWindowTitleBar.cpp @@ -5,27 +5,27 @@ namespace UKControllerPlugin::Components { CollapsibleWindowTitleBar::CollapsibleWindowTitleBar( - std::wstring title, Gdiplus::Rect area, std::function collapseState, int screenObjectId) - : TitleBar(title, area) + std::wstring title, Gdiplus::Rect area, std::function collapseState, int screenObjectId, const Windows::GdiplusBrushes& brushes) + : TitleBar(title, area), brushes(brushes) { this->closeButton = Button::Create( {area.GetRight() - 20, area.GetTop() + 5, 10, 10}, screenObjectId, "closeButton", - Components::CloseButton()); + Components::CloseButton(this->brushes)); this->collapseButton = Button::Create( {area.GetRight() - 35, area.GetTop() + 5, 10, 10}, screenObjectId, "collapseButton", - Components::CollapseButton(collapseState)); + Components::CollapseButton(this->brushes, collapseState)); } std::shared_ptr CollapsibleWindowTitleBar::Create( - std::wstring title, Gdiplus::Rect area, std::function collapseState, int screenObjectId) + std::wstring title, Gdiplus::Rect area, std::function collapseState, int screenObjectId, const Windows::GdiplusBrushes& brushes) { auto titlebar = std::shared_ptr( - new CollapsibleWindowTitleBar(title, area, collapseState, screenObjectId)); + new CollapsibleWindowTitleBar(title, area, collapseState, screenObjectId, brushes)); titlebar->WithDefaultBorder()->WithDefaultTextBrush()->WithDefaultBackgroundBrush()->WithDrag(screenObjectId); return titlebar; @@ -38,4 +38,12 @@ namespace UKControllerPlugin::Components { this->closeButton->Draw(graphics, radarScreen); this->collapseButton->Draw(graphics, radarScreen); } + + void CollapsibleWindowTitleBar::DrawTheme( + Windows::GdiGraphicsInterface& graphics, Euroscope::EuroscopeRadarLoopbackInterface& radarScreen, const Windows::GdiplusBrushes& brushes) const + { + TitleBar::DrawTheme(graphics, radarScreen, brushes); + this->closeButton->Draw(graphics, radarScreen); + this->collapseButton->Draw(graphics, radarScreen); + } } // namespace UKControllerPlugin::Components diff --git a/src/plugin/components/CollapsibleWindowTitleBar.h b/src/plugin/components/CollapsibleWindowTitleBar.h index 603e28789..2ee65ba63 100644 --- a/src/plugin/components/CollapsibleWindowTitleBar.h +++ b/src/plugin/components/CollapsibleWindowTitleBar.h @@ -12,15 +12,19 @@ namespace UKControllerPlugin::Components { { public: static std::shared_ptr - Create(std::wstring title, Gdiplus::Rect area, std::function collapseState, int screenObjectId); + Create(std::wstring title, Gdiplus::Rect area, std::function collapseState, int screenObjectId, const Windows::GdiplusBrushes& brushes); void Draw(Windows::GdiGraphicsInterface& graphics, Euroscope::EuroscopeRadarLoopbackInterface& radarScreen) const override; + void DrawTheme(Windows::GdiGraphicsInterface& graphics, Euroscope::EuroscopeRadarLoopbackInterface& radarScreen, const Windows::GdiplusBrushes& brushes) const override; protected: CollapsibleWindowTitleBar( - std::wstring title, Gdiplus::Rect area, std::function collapseState, int screenObjectId); + std::wstring title, Gdiplus::Rect area, std::function collapseState, int screenObjectId, const Windows::GdiplusBrushes& brushes); private: + // The brushes for theming + const Windows::GdiplusBrushes& brushes; + // The close button std::shared_ptr