Most of the remaining work to support theme loading. Solarized theme

included!
This commit is contained in:
casey langen 2017-03-12 18:47:41 -07:00
parent 4f8cc32557
commit a90bd2eec7
15 changed files with 427 additions and 25 deletions

View File

@ -10,6 +10,7 @@ set (BOX_SRCS
./app/layout/SearchLayout.cpp
./app/layout/TrackSearchLayout.cpp
./app/model/DirectoryAdapter.cpp
./app/overlay/ColorThemeOverlay.cpp
./app/overlay/PlaybackOverlays.cpp
./app/overlay/PlayQueueOverlays.cpp
./app/overlay/PluginOverlay.cpp

View File

@ -140,6 +140,12 @@ int main(int argc, char* argv[])
app.SetCustomColorsDisabled(prefs->GetBool(
musik::box::prefs::keys::DisableCustomColors.c_str(), false));
std::string theme = prefs->GetString(musik::box::prefs::keys::ColorTheme);
if (theme.size()) {
theme = GetApplicationDirectory() + "/themes/" + theme + ".json";
app.SetColorTheme(theme);
}
app.SetMinimumSize(MIN_WIDTH, MIN_HEIGHT);
using Layout = std::shared_ptr<LayoutBase>;

View File

@ -48,6 +48,7 @@
#include <app/util/Hotkeys.h>
#include <app/util/PreferenceKeys.h>
#include <app/overlay/ColorThemeOverlay.h>
#include <app/overlay/PlaybackOverlays.h>
#include <app/overlay/PluginOverlay.h>
@ -115,10 +116,6 @@ void SettingsLayout::OnCheckboxChanged(cursespp::Checkbox* cb, bool checked) {
this->browseAdapter->SetDotfilesVisible(showDotfiles);
this->browseList->OnAdapterChanged();
}
else if (cb == customColorsCheckbox.get()) {
this->libraryPrefs->SetBool(box::prefs::keys::DisableCustomColors, checked);
this->libraryPrefs->Save();
}
}
void SettingsLayout::OnOutputDropdownActivated(cursespp::TextLabel* label) {
@ -163,6 +160,10 @@ void SettingsLayout::OnHotkeyDropdownActivate(cursespp::TextLabel* label) {
App::Overlays().Push(overlay);
}
void SettingsLayout::OnThemeDropdownActivate(cursespp::TextLabel* label) {
ColorThemeOverlay::Show([this]() { this->LoadPreferences(); });
}
void SettingsLayout::OnLayout() {
int x = this->GetX(), y = this->GetY();
int cx = this->GetWidth(), cy = this->GetHeight();
@ -186,11 +187,11 @@ void SettingsLayout::OnLayout() {
this->outputDropdown->MoveAndResize(1, y++, cx - 1, LABEL_HEIGHT);
this->transportDropdown->MoveAndResize(1, y++, cx - 1, LABEL_HEIGHT);
this->pluginsDropdown->MoveAndResize(1, y++, cx - 1, LABEL_HEIGHT);
this->themeDropdown->MoveAndResize(1, y++, cx - 1, LABEL_HEIGHT);
this->hotkeyDropdown->MoveAndResize(1, y++, cx - 1, LABEL_HEIGHT);
this->dotfileCheckbox->MoveAndResize(1, y++, cx - 1, LABEL_HEIGHT);
this->syncOnStartupCheckbox->MoveAndResize(1, y++, cx - 1, LABEL_HEIGHT);
this->removeCheckbox->MoveAndResize(1, y++, cx - 1, LABEL_HEIGHT);
this->customColorsCheckbox->MoveAndResize(1, y++, cx - 1, LABEL_HEIGHT);
}
void SettingsLayout::RefreshAddedPaths() {
@ -259,6 +260,10 @@ void SettingsLayout::InitializeWindows() {
this->pluginsDropdown->SetText(arrow + " enable/disable plugins");
this->pluginsDropdown->Activated.connect(this, &SettingsLayout::OnPluginsDropdownActivate);
this->themeDropdown.reset(new TextLabel());
this->themeDropdown->SetText(arrow + " color theme: default");
this->themeDropdown->Activated.connect(this, &SettingsLayout::OnThemeDropdownActivate);
this->hotkeyDropdown.reset(new TextLabel());
this->hotkeyDropdown->SetText(arrow + " hotkey tester");
this->hotkeyDropdown->Activated.connect(this, &SettingsLayout::OnHotkeyDropdownActivate);
@ -266,18 +271,17 @@ void SettingsLayout::InitializeWindows() {
CREATE_CHECKBOX(this->dotfileCheckbox, "show dotfiles in directory browser");
CREATE_CHECKBOX(this->syncOnStartupCheckbox, "sync metadata on startup");
CREATE_CHECKBOX(this->removeCheckbox, "remove missing files from library");
CREATE_CHECKBOX(this->customColorsCheckbox, "disable custom colors (requires restart)");
this->browseList->SetFocusOrder(0);
this->addedPathsList->SetFocusOrder(1);
this->outputDropdown->SetFocusOrder(2);
this->transportDropdown->SetFocusOrder(3);
this->pluginsDropdown->SetFocusOrder(4);
this->hotkeyDropdown->SetFocusOrder(5);
this->dotfileCheckbox->SetFocusOrder(6);
this->syncOnStartupCheckbox->SetFocusOrder(7);
this->removeCheckbox->SetFocusOrder(8);
this->customColorsCheckbox->SetFocusOrder(9);
this->themeDropdown->SetFocusOrder(5);
this->hotkeyDropdown->SetFocusOrder(6);
this->dotfileCheckbox->SetFocusOrder(7);
this->syncOnStartupCheckbox->SetFocusOrder(8);
this->removeCheckbox->SetFocusOrder(9);
this->AddWindow(this->browseLabel);
this->AddWindow(this->addedPathsLabel);
@ -286,11 +290,11 @@ void SettingsLayout::InitializeWindows() {
this->AddWindow(this->outputDropdown);
this->AddWindow(this->transportDropdown);
this->AddWindow(this->pluginsDropdown);
this->AddWindow(this->themeDropdown);
this->AddWindow(this->hotkeyDropdown);
this->AddWindow(this->dotfileCheckbox);
this->AddWindow(this->syncOnStartupCheckbox);
this->AddWindow(this->removeCheckbox);
this->AddWindow(this->customColorsCheckbox);
}
void SettingsLayout::SetShortcutsWindow(ShortcutsWindow* shortcuts) {
@ -347,13 +351,27 @@ void SettingsLayout::CheckShowFirstRunDialog() {
void SettingsLayout::LoadPreferences() {
this->syncOnStartupCheckbox->SetChecked(this->libraryPrefs->GetBool(core::prefs::keys::SyncOnStartup, true));
this->removeCheckbox->SetChecked(this->libraryPrefs->GetBool(core::prefs::keys::RemoveMissingFiles, true));
this->customColorsCheckbox->SetChecked(this->libraryPrefs->GetBool(box::prefs::keys::DisableCustomColors));
/* color theme */
bool disableCustomColors = this->libraryPrefs->GetBool(box::prefs::keys::DisableCustomColors);
std::string colorTheme = this->libraryPrefs->GetString(box::prefs::keys::ColorTheme);
if (colorTheme == "" && !disableCustomColors) {
colorTheme = "default";
}
else if (disableCustomColors) {
colorTheme = "8 colors";
}
this->themeDropdown->SetText(arrow + " color theme: " + colorTheme);
/* output plugin */
std::shared_ptr<IOutput> output = outputs::SelectedOutput();
if (output) {
this->outputDropdown->SetText(arrow + " output device: " + output->Name());
}
/* transport type */
std::string transportName =
this->transport.GetType() == MasterTransport::Gapless
? "gapless"

View File

@ -98,6 +98,7 @@ namespace musik {
void OnTransportDropdownActivate(cursespp::TextLabel* label);
void OnPluginsDropdownActivate(cursespp::TextLabel* label);
void OnHotkeyDropdownActivate(cursespp::TextLabel* label);
void OnThemeDropdownActivate(cursespp::TextLabel* label);
int64 ListItemDecorator(
cursespp::ScrollableWindow* w,
@ -116,11 +117,11 @@ namespace musik {
std::shared_ptr<cursespp::TextLabel> transportDropdown;
std::shared_ptr<cursespp::TextLabel> pluginsDropdown;
std::shared_ptr<cursespp::TextLabel> hotkeyDropdown;
std::shared_ptr<cursespp::TextLabel> themeDropdown;
std::shared_ptr<cursespp::Checkbox> dotfileCheckbox;
std::shared_ptr<cursespp::Checkbox> syncOnStartupCheckbox;
std::shared_ptr<cursespp::Checkbox> removeCheckbox;
std::shared_ptr<cursespp::Checkbox> customColorsCheckbox;
std::shared_ptr<cursespp::TextLabel> browseLabel;
std::shared_ptr<cursespp::TextLabel> addedPathsLabel;

View File

@ -0,0 +1,176 @@
//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2007-2016 musikcube team
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// * Neither the name of the author nor the names of other contributors may
// be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "ColorThemeOverlay.h"
#include <core/support/Common.h>
#include <core/support/PreferenceKeys.h>
#include <core/support/Preferences.h>
#include <musikbox/app/util/PreferenceKeys.h>
#include <cursespp/App.h>
#include <cursespp/Colors.h>
#include <cursespp/SimpleScrollAdapter.h>
#include <cursespp/ListOverlay.h>
#include <cursespp/DialogOverlay.h>
#include <boost/filesystem.hpp>
using namespace musik;
using namespace musik::core;
using namespace musik::box;
using namespace cursespp;
using namespace boost::filesystem;
static void showMayNeedRestart() {
std::shared_ptr<DialogOverlay> dialog(new DialogOverlay());
(*dialog)
.SetTitle("musikbox")
.SetMessage("depending on your terminal mode, you may need to restart musikbox for this change to take effect.")
.AddButton(
"KEY_ENTER",
"ENTER",
"ok");
App::Overlays().Push(dialog);
}
static void showNeedsRestart() {
std::shared_ptr<DialogOverlay> dialog(new DialogOverlay());
(*dialog)
.SetTitle("musikbox")
.SetMessage("you will need to restart musikbox for this change to take effect.")
.AddButton(
"KEY_ENTER",
"ENTER",
"ok");
App::Overlays().Push(dialog);
}
static std::string ThemesDirectory() {
return musik::core::GetApplicationDirectory() + "/themes/";
}
ColorThemeOverlay::ColorThemeOverlay() {
}
void ColorThemeOverlay::Show(std::function<void()> callback) {
using Adapter = cursespp::SimpleScrollAdapter;
using ListOverlay = cursespp::ListOverlay;
auto prefs = core::Preferences::
ForComponent(core::prefs::components::Settings);
std::string currentTheme = prefs->GetString(box::prefs::keys::ColorTheme);
bool disableCustomColors = prefs->GetBool(box::prefs::keys::DisableCustomColors);
int selectedIndex = disableCustomColors ? 1 : 0;
std::shared_ptr<Adapter> adapter(new Adapter());
adapter->AddEntry("default");
adapter->AddEntry("8 colors");
std::shared_ptr<std::vector<std::string>> themes(new std::vector<std::string>());
path colorPath(ThemesDirectory());
if (exists(colorPath)) {
int i = 2;
directory_iterator end;
for (directory_iterator file(colorPath); file != end; file++) {
const path& p = file->path();
if (p.has_extension() && p.extension().string() == ".json") {
std::string fn = p.filename().string();
fn = fn.substr(0, fn.rfind("."));
themes->push_back(fn);
adapter->AddEntry(fn);
if (currentTheme == fn) {
selectedIndex = i;
}
++i;
}
}
}
adapter->SetSelectable(true);
std::shared_ptr<ListOverlay> dialog(new ListOverlay());
dialog->SetAdapter(adapter)
.SetTitle("color themes")
.SetSelectedIndex(selectedIndex)
.SetItemSelectedCallback(
[callback, prefs, themes, currentTheme, disableCustomColors]
(ListOverlay* overlay, IScrollAdapterPtr adapter, size_t index) {
if (index == 0) {
prefs->SetString(box::prefs::keys::ColorTheme, "");
prefs->SetBool(box::prefs::keys::DisableCustomColors, false);
Colors::SetTheme("");
disableCustomColors ? showNeedsRestart() : showMayNeedRestart();
}
else if (index == 1) {
prefs->SetString(box::prefs::keys::ColorTheme, "");
prefs->SetBool(box::prefs::keys::DisableCustomColors, true);
if (!disableCustomColors) {
showNeedsRestart();
}
}
else {
std::string selected = themes->at(index - 2).c_str();
if (selected != currentTheme) {
prefs->SetString(box::prefs::keys::ColorTheme, selected.c_str());
prefs->SetBool(box::prefs::keys::DisableCustomColors, false);
Colors::SetTheme(ThemesDirectory() + selected + ".json");
disableCustomColors ? showNeedsRestart() : showMayNeedRestart();
}
}
prefs->Save();
if (callback) {
callback();
}
});
cursespp::App::Overlays().Push(dialog);
}

View File

@ -0,0 +1,49 @@
//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2007-2016 musikcube team
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// * Neither the name of the author nor the names of other contributors may
// be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////////
#pragma once
#include <functional>
namespace musik {
namespace box {
class ColorThemeOverlay {
public:
static void Show(std::function<void()> callback);
private:
ColorThemeOverlay();
};
}
}

View File

@ -39,6 +39,7 @@ namespace musik { namespace box { namespace prefs {
const std::string keys::DisableCustomColors = "DisableCustomColors";
const std::string keys::FirstRunSettingsDisplayed = "FirstRunSettingsDisplayed";
const std::string keys::ColorTheme = "ColorTheme";
} } }

View File

@ -41,6 +41,7 @@ namespace musik { namespace box { namespace prefs {
namespace keys {
extern const std::string DisableCustomColors;
extern const std::string FirstRunSettingsDisplayed;
extern const std::string ColorTheme;
}
} } }

View File

@ -119,6 +119,10 @@ void App::SetCustomColorsDisabled(bool disabled) {
this->disableCustomColors = disabled;
}
void App::SetColorTheme(const std::string& colorTheme) {
this->colorTheme = colorTheme;
}
void App::SetMinimumSize(int minWidth, int minHeight) {
this->minWidth = std::max(0, minWidth);
this->minHeight = std::max(0, minHeight);
@ -161,6 +165,10 @@ void App::OnResized() {
void App::Run(ILayoutPtr layout) {
Colors::Init(this->disableCustomColors);
if (this->colorTheme.size()) {
Colors::SetTheme(this->colorTheme);
}
int64 ch;
bool quit = false;

View File

@ -52,6 +52,7 @@ namespace cursespp {
void SetKeyHandler(MainKeyHandler handler);
void SetResizeHandler(ResizeHandler handler);
void SetCustomColorsDisabled(bool disable);
void SetColorTheme(const std::string& fn);
void SetMinimumSize(int width, int height);
bool IsOverlayVisible() { return this->state.overlay != nullptr; }
@ -100,6 +101,7 @@ namespace cursespp {
MainKeyHandler keyHandler;
ResizeHandler resizeHandler;
bool disableCustomColors;
std::string colorTheme;
int minWidth, minHeight;
#ifdef WIN32

View File

@ -174,8 +174,11 @@ struct Theme {
int palette;
};
/* initialized with default values. the user can override them, then call Apply() */
Theme() {
this->Reset();
}
void Reset() {
/* main */
background.Set(THEME_COLOR_BACKGROUND, 0, 0, 0, COLOR_BLACK);
foreground.Set(THEME_COLOR_FOREGROUND, 230, 230, 230, COLOR_256_OFFWHITE);
@ -447,18 +450,23 @@ static bool canChangeColors() {
Colors::Colors() {
}
static Theme defaultTheme;
static Theme theme;
static Theme::Color::Mode colorMode = Theme::Color::Standard;
void Colors::Init(bool disableCustomColors) {
start_color();
use_default_colors();
using Color = Theme::Color;
Color::Mode mode = Color::Standard;
if (!disableCustomColors && COLORS > 8) {
mode = canChangeColors() ? Color::Custom : Color::Palette;
colorMode = canChangeColors()
? Theme::Color::Custom : Theme::Color::Palette;
}
//defaultTheme.LoadFromFile("d:\\solarized_dark.json");
defaultTheme.Apply(mode);
theme.Apply(colorMode);
}
void Colors::SetTheme(const std::string& fn) {
theme.Reset();
theme.LoadFromFile(fn);
theme.Apply(colorMode);
}

View File

@ -74,5 +74,6 @@ namespace cursespp {
public:
static void Init(bool disableCustomColors = false);
static void SetTheme(const std::string& fn);
};
}

View File

@ -0,0 +1,122 @@
{
"name": "solarized dark",
"schemaVersion": 1,
"colors": {
"background": {
"hex": "#002b36",
"palette": 234
},
"foreground": {
"hex": "#eee8d5",
"palette": 254
},
"focused_border": {
"hex": "#dc322f",
"palette": 160
},
"text_focused": {
"hex": "#dc322f",
"palette": 160
},
"text_active": {
"hex": "#859900",
"palette": 64
},
"text_disabled": {
"hex": "#93a1a1",
"palette": 245
},
"text_hidden": {
"hex": "#073642",
"palette": 235
},
"text_warning": {
"hex": "#b58900",
"palette": 136
},
"text_error": {
"hex": "#dc322f",
"palette": 160
},
"overlay_background": {
"hex": "#586e75",
"palette": 240
},
"overlay_foreground": {
"hex": "#fdf6e3",
"palette": 230
},
"overlay_border": {
"hex": "#073642",
"palette": 235
},
"overlay_focused_border": {
"hex": "#dc322f",
"palette": 160
},
"shortcuts_background": {
"hex": "#586e75",
"palette": 240
},
"shortcuts_foreground": {
"hex": "#fdf6e3",
"palette": 230
},
"shortcuts_background_focused": {
"hex": "#dc322f",
"palette": 160
},
"shortcuts_foreground_focused": {
"hex": "#fdf6e3",
"palette": 230
},
"button_background_normal": {
"hex": "#eee8d5",
"palette": 254
},
"button_foreground_normal": {
"hex": "#002b36",
"palette": 234
},
"button_background_active": {
"hex": "#859900",
"palette": 64
},
"button_foreground_active": {
"hex": "#fdf6e3",
"palette": 230
},
"banner_background": {
"hex": "#cb4b16",
"palette": 166
},
"banner_foreground": {
"hex": "#eee8d5",
"palette": 254
},
"list_header_background": {
"hex": "#002b36",
"palette": 234
},
"list_header_foreground": {
"hex": "#859900",
"palette": 64
},
"list_item_highlighted_background": {
"hex": "#859900",
"palette": 64
},
"list_item_highlighted_foreground": {
"hex": "#002b36",
"palette": 234
},
"list_item_active_background": {
"hex": "#073642",
"palette": 235
},
"list_item_active_foreground": {
"hex": "#268bd2",
"palette": 33
}
}
}

View File

@ -84,8 +84,8 @@
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
</Link>
<PostBuildEvent>
<Command>
</Command>
<Command>if not exist "$(TargetDir)themes" mkdir "$(TargetDir)themes"
xcopy "$(ProjectDir)data\themes\*" "$(TargetDir)themes\" /Y /e</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
@ -120,8 +120,8 @@
<ImageHasSafeExceptionHandlers>true</ImageHasSafeExceptionHandlers>
</Link>
<PostBuildEvent>
<Command>
</Command>
<Command>if not exist "$(TargetDir)themes" mkdir "$(TargetDir)themes"
xcopy "$(ProjectDir)data\themes\*" "$(TargetDir)themes\" /Y /e</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>
@ -134,6 +134,7 @@
<ClCompile Include="app\layout\SettingsLayout.cpp" />
<ClCompile Include="app\layout\TrackSearchLayout.cpp" />
<ClCompile Include="app\model\DirectoryAdapter.cpp" />
<ClCompile Include="app\overlay\ColorThemeOverlay.cpp" />
<ClCompile Include="app\overlay\PlaybackOverlays.cpp" />
<ClCompile Include="app\overlay\PlayQueueOverlays.cpp" />
<ClCompile Include="app\overlay\PluginOverlay.cpp" />
@ -185,6 +186,7 @@
<ClInclude Include="app\layout\SettingsLayout.h" />
<ClInclude Include="app\layout\TrackSearchLayout.h" />
<ClInclude Include="app\model\DirectoryAdapter.h" />
<ClInclude Include="app\overlay\ColorThemeOverlay.h" />
<ClInclude Include="app\overlay\PlaybackOverlays.h" />
<ClInclude Include="app\overlay\PlayQueueOverlays.h" />
<ClInclude Include="app\overlay\PluginOverlay.h" />

View File

@ -132,6 +132,9 @@
<ClCompile Include="cursespp\Win32Util.cpp">
<Filter>cursespp</Filter>
</ClCompile>
<ClCompile Include="app\overlay\ColorThemeOverlay.cpp">
<Filter>app\overlay</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="stdafx.h" />
@ -310,6 +313,9 @@
<ClInclude Include="cursespp\Win32Util.h">
<Filter>cursespp</Filter>
</ClInclude>
<ClInclude Include="app\overlay\ColorThemeOverlay.h">
<Filter>app\overlay</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="cursespp">