Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial Theme support #12992

Merged
merged 70 commits into from
Jul 7, 2022
Merged
Show file tree
Hide file tree
Changes from 61 commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
15e4fd9
Settings project builds at least
zadjii-msft Mar 24, 2022
7e0ae35
this fixes the App bits as well
zadjii-msft Mar 24, 2022
00c135b
on reload, this changes the BG of the titlebar, but not the tab view
zadjii-msft Mar 24, 2022
b4cd7b3
this is better. Apphost is gonna need to be involved anyways so fuck it
zadjii-msft Mar 24, 2022
9627483
okay so i bet that resource changed. Whatever, we'll work with it.
zadjii-msft Mar 24, 2022
84805f5
dumbly accept alpha channel in colors
zadjii-msft Mar 24, 2022
9670d13
mica works, but the titlebar looks DUMB
zadjii-msft Mar 24, 2022
c555e6e
this did seem to work, but will it work for acrylic?
zadjii-msft Mar 25, 2022
01f07be
okay so you can't change a solid brush to an acylic one at runtime
zadjii-msft Mar 25, 2022
ee9333a
A pile of dead ends.
zadjii-msft Mar 25, 2022
9eff0a2
i feel like I'm getting closer
zadjii-msft Mar 25, 2022
44112fc
really, no good progress here. It's rough
zadjii-msft Mar 28, 2022
f92724d
We can't be clever here. Just make an observable property and slam th…
zadjii-msft Apr 25, 2022
cd0012a
guess what, it works
zadjii-msft Apr 25, 2022
ac49459
change color based on if the window is activated or now
zadjii-msft Apr 26, 2022
03211c8
Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/themin…
zadjii-msft Apr 26, 2022
1d0a5d7
Merge remote-tracking branch 'origin/dev/migrie/titebar-colors' into …
zadjii-msft Apr 26, 2022
e22046f
accept accent color as a ThemeColor
zadjii-msft Apr 26, 2022
99dc75d
this actually works now. Remember kids, break statements in switch/ca…
zadjii-msft Apr 26, 2022
a7ce860
cleanup, works on start and when unset
zadjii-msft Apr 26, 2022
4f42661
Lookup themes from a list of themes, use that one to style the window
zadjii-msft Apr 26, 2022
5081118
This is horrifying and yet I'm totally shipping it. Move the objects …
zadjii-msft Apr 26, 2022
fe26114
better, better,
zadjii-msft Apr 26, 2022
7fcfda3
wow
zadjii-msft Apr 26, 2022
df0efff
stragglers
zadjii-msft Apr 26, 2022
7243603
hey this is easier
zadjii-msft Apr 26, 2022
606db76
this is like 99% of the work here
zadjii-msft Apr 26, 2022
de22df0
switches when a tab does, not the pane
zadjii-msft Apr 26, 2022
01d6907
I think that's everything
zadjii-msft Apr 26, 2022
39b6fdd
Some minor todos
zadjii-msft Apr 26, 2022
9bebb74
Some settings saving roundtripping bugs
zadjii-msft Apr 27, 2022
cfb99d9
Change theme with the SUI and actually have it persist
zadjii-msft Apr 27, 2022
a57187b
include the themes
zadjii-msft Apr 27, 2022
b6b9aea
Use better display names
zadjii-msft Apr 27, 2022
e2318b4
This is horrifying
zadjii-msft Apr 27, 2022
bc8bb8e
more comments
zadjii-msft Apr 27, 2022
446e17b
Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/themin…
zadjii-msft Apr 27, 2022
fd2dd3a
cleanup for the review
zadjii-msft Apr 27, 2022
a1ea0fd
forgot you
zadjii-msft Apr 28, 2022
9b9a644
spell
zadjii-msft Apr 28, 2022
03da3b7
runformat
zadjii-msft Apr 28, 2022
24f945a
Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/themin…
zadjii-msft Apr 29, 2022
3281a3a
Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/themin…
zadjii-msft May 3, 2022
68d70a0
this is REALLY janky but seems to work
zadjii-msft May 3, 2022
99bc963
this is simpler
zadjii-msft May 3, 2022
6c28ba6
make the code a lot cleaner
zadjii-msft May 3, 2022
342eb2b
Removes the ability to add Mica in the titlebar, but good enough for v0
zadjii-msft May 4, 2022
cd2fd43
Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/themin…
zadjii-msft May 5, 2022
44319a1
Fixes the Settings UI terminalBackground settings
zadjii-msft May 5, 2022
d73e450
this fixes the terminalBG + runtime change
zadjii-msft May 5, 2022
e52b7a9
well, this was part of it...
zadjii-msft May 5, 2022
3af471a
derp
zadjii-msft May 5, 2022
b2abca6
Add comments
zadjii-msft May 6, 2022
cae5218
Start by just adding a test file
zadjii-msft May 6, 2022
45fb4b1
Fill in more tests
zadjii-msft May 6, 2022
28119c7
This is what I was looking for
zadjii-msft May 6, 2022
2712e46
Fix having a null window, cause that was actually very possible
zadjii-msft May 6, 2022
331b98f
sensible defaults
zadjii-msft May 6, 2022
f38dc5c
typos
zadjii-msft May 6, 2022
cb788b0
Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/themin…
zadjii-msft May 6, 2022
f5007fa
Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/themin…
zadjii-msft May 25, 2022
861cd2a
the more important color thing
zadjii-msft May 26, 2022
c07b01e
more nits and such
zadjii-msft May 26, 2022
72904b9
typo
zadjii-msft May 26, 2022
8d86538
Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/themin…
zadjii-msft Jun 16, 2022
ee4ae62
Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/themin…
zadjii-msft Jul 6, 2022
9806d64
just disable mica for now
zadjii-msft Jul 6, 2022
4245964
This is what happens when a PR has been open for ages
zadjii-msft Jul 6, 2022
9ab99b3
nits across the board
zadjii-msft Jul 6, 2022
ab81648
runformat
zadjii-msft Jul 7, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/actions/spelling/allow/apis.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ DERR
dlldata
DONTADDTORECENT
DWORDLONG
<<<<<<< HEAD
DWMSBT
DWMWA
=======
>>>>>>> origin/main
endfor
enumset
environstrings
Expand Down Expand Up @@ -95,6 +100,7 @@ lround
Lsa
lsass
LSHIFT
MAINWINDOW
memchr
memicmp
MENUCOMMAND
Expand Down Expand Up @@ -175,6 +181,8 @@ Stubless
Subheader
Subpage
syscall
SYSTEMBACKDROP
TABROW
TASKBARCREATED
TBPF
THEMECHANGED
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
<ClCompile Include="DeserializationTests.cpp" />
<ClCompile Include="SerializationTests.cpp" />
<ClCompile Include="TerminalSettingsTests.cpp" />
<ClCompile Include="ThemeTests.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
Expand Down
280 changes: 280 additions & 0 deletions src/cascadia/LocalTests_SettingsModel/ThemeTests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,280 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

#include "pch.h"

#include "../TerminalSettingsModel/Theme.h"
#include "../TerminalSettingsModel/CascadiaSettings.h"
#include "../types/inc/colorTable.hpp"
#include "JsonTestClass.h"

#include <defaults.h>

using namespace Microsoft::Console;
using namespace winrt::Microsoft::Terminal;
using namespace winrt::Microsoft::Terminal::Settings::Model::implementation;
using namespace WEX::Logging;
using namespace WEX::TestExecution;
using namespace WEX::Common;

namespace SettingsModelLocalTests
{
// TODO:microsoft/terminal#3838:
// Unfortunately, these tests _WILL NOT_ work in our CI. We're waiting for
// an updated TAEF that will let us install framework packages when the test
// package is deployed. Until then, these tests won't deploy in CI.
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved

class ThemeTests : public JsonTestClass
{
// Use a custom AppxManifest to ensure that we can activate winrt types
// from our test. This property will tell taef to manually use this as
// the AppxManifest for this test class.
// This does not yet work for anything XAML-y. See TabTests.cpp for more
// details on that.
BEGIN_TEST_CLASS(ThemeTests)
TEST_CLASS_PROPERTY(L"RunAs", L"UAP")
TEST_CLASS_PROPERTY(L"UAP:AppXManifest", L"TestHostAppXManifest.xml")
END_TEST_CLASS()

TEST_METHOD(ParseSimpleTheme);
TEST_METHOD(ParseEmptyTheme);
TEST_METHOD(ParseNoWindowTheme);
TEST_METHOD(ParseNullWindowTheme);
TEST_METHOD(ParseThemeWithNullThemeColor);
TEST_METHOD(InvalidCurrentTheme);

static Core::Color rgb(uint8_t r, uint8_t g, uint8_t b) noexcept
{
return Core::Color{ r, g, b, 255 };
}
};

void ThemeTests::ParseSimpleTheme()
{
static constexpr std::string_view orangeTheme{ R"({
"name": "orange",
"tabRow":
{
"background": "#FFFF8800",
"unfocusedBackground": "#FF884400"
Comment on lines +58 to +59
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wait but utils parses RGBA

how is this test passing?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wait, i am so confused

},
"window":
{
"applicationTheme": "light",
"useMica": true
}
})" };

const auto schemeObject = VerifyParseSucceeded(orangeTheme);
auto theme = Theme::FromJson(schemeObject);
VERIFY_ARE_EQUAL(L"orange", theme->Name());

VERIFY_IS_NOT_NULL(theme->TabRow());
VERIFY_IS_NOT_NULL(theme->TabRow().Background());
VERIFY_ARE_EQUAL(Settings::Model::ThemeColorType::Color, theme->TabRow().Background().ColorType());
VERIFY_ARE_EQUAL(rgb(0xff, 0x88, 0x00), theme->TabRow().Background().Color());

VERIFY_IS_NOT_NULL(theme->Window());
VERIFY_ARE_EQUAL(winrt::Windows::UI::Xaml::ElementTheme::Light, theme->Window().RequestedTheme());
VERIFY_ARE_EQUAL(true, theme->Window().UseMica());
}

void ThemeTests::ParseEmptyTheme()
{
Log::Comment(L"This theme doesn't have any elements defined.");
static constexpr std::string_view emptyTheme{ R"({
"name": "empty"
})" };

const auto schemeObject = VerifyParseSucceeded(emptyTheme);
auto theme = Theme::FromJson(schemeObject);
VERIFY_ARE_EQUAL(L"empty", theme->Name());
VERIFY_IS_NULL(theme->TabRow());
VERIFY_IS_NULL(theme->Window());
VERIFY_ARE_EQUAL(winrt::Windows::UI::Xaml::ElementTheme::Default, theme->RequestedTheme());
}

void ThemeTests::ParseNoWindowTheme()
{
Log::Comment(L"This theme doesn't have a window defined.");
static constexpr std::string_view emptyTheme{ R"({
"name": "noWindow",
"tabRow":
{
"background": "#FF112233",
"unfocusedBackground": "#FF884400"
},
})" };

const auto schemeObject = VerifyParseSucceeded(emptyTheme);
auto theme = Theme::FromJson(schemeObject);
VERIFY_ARE_EQUAL(L"noWindow", theme->Name());

VERIFY_IS_NOT_NULL(theme->TabRow());
VERIFY_IS_NOT_NULL(theme->TabRow().Background());
VERIFY_ARE_EQUAL(Settings::Model::ThemeColorType::Color, theme->TabRow().Background().ColorType());
VERIFY_ARE_EQUAL(rgb(0x11, 0x22, 0x33), theme->TabRow().Background().Color());

VERIFY_IS_NULL(theme->Window());
VERIFY_ARE_EQUAL(winrt::Windows::UI::Xaml::ElementTheme::Default, theme->RequestedTheme());
}

void ThemeTests::ParseNullWindowTheme()
{
Log::Comment(L"This theme doesn't have a window defined.");
static constexpr std::string_view emptyTheme{ R"({
"name": "nullWindow",
"tabRow":
{
"background": "#FF112233",
"unfocusedBackground": "#FF884400"
},
"window": null
})" };

const auto schemeObject = VerifyParseSucceeded(emptyTheme);
auto theme = Theme::FromJson(schemeObject);
VERIFY_ARE_EQUAL(L"nullWindow", theme->Name());

VERIFY_IS_NOT_NULL(theme->TabRow());
VERIFY_IS_NOT_NULL(theme->TabRow().Background());
VERIFY_ARE_EQUAL(Settings::Model::ThemeColorType::Color, theme->TabRow().Background().ColorType());
VERIFY_ARE_EQUAL(rgb(0x11, 0x22, 0x33), theme->TabRow().Background().Color());

VERIFY_IS_NULL(theme->Window());
VERIFY_ARE_EQUAL(winrt::Windows::UI::Xaml::ElementTheme::Default, theme->RequestedTheme());
}

void ThemeTests::ParseThemeWithNullThemeColor()
{
Log::Comment(L"These themes are all missing a tabRow background. Make sure we don't somehow default-construct one for them");

static constexpr std::string_view settingsString{ R"json({
"themes": [
{
"name": "backgroundEmpty",
"tabRow":
{
},
"window":
{
"applicationTheme": "light",
"useMica": true
}
},
{
"name": "backgroundNull",
"tabRow":
{
"background": null
},
"window":
{
"applicationTheme": "light",
"useMica": true
}
},
{
"name": "backgroundOmittedEntirely",
"window":
{
"applicationTheme": "light",
"useMica": true
}
}
]
})json" };

try
{
const auto settings{ winrt::make_self<CascadiaSettings>(settingsString, DefaultJson) };

const auto& themes{ settings->GlobalSettings().Themes() };
{
const auto& backgroundEmpty{ themes.Lookup(L"backgroundEmpty") };
VERIFY_ARE_EQUAL(L"backgroundEmpty", backgroundEmpty.Name());
VERIFY_IS_NOT_NULL(backgroundEmpty.TabRow());
VERIFY_IS_NULL(backgroundEmpty.TabRow().Background());
}
{
const auto& backgroundNull{ themes.Lookup(L"backgroundNull") };
VERIFY_ARE_EQUAL(L"backgroundNull", backgroundNull.Name());
VERIFY_IS_NOT_NULL(backgroundNull.TabRow());
VERIFY_IS_NULL(backgroundNull.TabRow().Background());
}
{
const auto& backgroundOmittedEntirely{ themes.Lookup(L"backgroundOmittedEntirely") };
VERIFY_ARE_EQUAL(L"backgroundOmittedEntirely", backgroundOmittedEntirely.Name());
VERIFY_IS_NULL(backgroundOmittedEntirely.TabRow());
}
}
catch (const SettingsException& ex)
{
auto loadError = ex.Error();
loadError;
DebugBreak();
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved
throw ex;
}
catch (const SettingsTypedDeserializationException& e)
{
auto deserializationErrorMessage = til::u8u16(e.what());
Log::Comment(NoThrowString().Format(deserializationErrorMessage.c_str()));
DebugBreak();
throw e;
}
}

void ThemeTests::InvalidCurrentTheme()
{
Log::Comment(L"Make sure specifying an invalid theme falls back to a sensible default.");

static constexpr std::string_view settingsString{ R"json({
"theme": "foo",
"themes": [
{
"name": "bar",
"tabRow": {},
"window":
{
"applicationTheme": "light",
"useMica": true
}
}
]
})json" };

try
{
const auto settings{ winrt::make_self<CascadiaSettings>(settingsString, DefaultJson) };

VERIFY_ARE_EQUAL(1u, settings->Warnings().Size());
VERIFY_ARE_EQUAL(Settings::Model::SettingsLoadWarnings::UnknownTheme, settings->Warnings().GetAt(0));

const auto& themes{ settings->GlobalSettings().Themes() };
{
const auto& bar{ themes.Lookup(L"bar") };
VERIFY_ARE_EQUAL(L"bar", bar.Name());
VERIFY_IS_NOT_NULL(bar.TabRow());
VERIFY_IS_NULL(bar.TabRow().Background());
}

const auto currentTheme{ settings->GlobalSettings().CurrentTheme() };
VERIFY_IS_NOT_NULL(currentTheme);
VERIFY_ARE_EQUAL(L"system", currentTheme.Name());
}
catch (const SettingsException& ex)
{
auto loadError = ex.Error();
loadError;
DebugBreak();
throw ex;
}
catch (const SettingsTypedDeserializationException& e)
{
auto deserializationErrorMessage = til::u8u16(e.what());
Log::Comment(NoThrowString().Format(deserializationErrorMessage.c_str()));
DebugBreak();
throw e;
}
}
}
34 changes: 30 additions & 4 deletions src/cascadia/TerminalApp/AppLogic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ static const std::array settingsLoadWarningsLabels {
USES_RESOURCE(L"InvalidSplitSize"),
USES_RESOURCE(L"FailedToParseStartupActions"),
USES_RESOURCE(L"FailedToParseSubCommands"),
USES_RESOURCE(L"UnknownTheme"),
};
static const std::array settingsLoadErrorsLabels {
USES_RESOURCE(L"NoProfilesText"),
Expand Down Expand Up @@ -367,11 +368,12 @@ namespace winrt::TerminalApp::implementation
// details here, but it does have the desired effect.
// It's not enough to set the theme on the dialog alone.
auto themingLambda{ [this](const Windows::Foundation::IInspectable& sender, const RoutedEventArgs&) {
auto theme{ _settings.GlobalSettings().Theme() };
auto theme{ _settings.GlobalSettings().CurrentTheme() };
auto requestedTheme{ theme.RequestedTheme() };
auto element{ sender.try_as<winrt::Windows::UI::Xaml::FrameworkElement>() };
while (element)
{
element.RequestedTheme(theme);
element.RequestedTheme(requestedTheme);
element = element.Parent().try_as<winrt::Windows::UI::Xaml::FrameworkElement>();
}
} };
Expand Down Expand Up @@ -743,7 +745,7 @@ namespace winrt::TerminalApp::implementation
LoadSettings();
}

return _settings.GlobalSettings().Theme();
return _settings.GlobalSettings().CurrentTheme().RequestedTheme();
}

bool AppLogic::GetShowTabsInTitlebar()
Expand Down Expand Up @@ -964,7 +966,7 @@ namespace winrt::TerminalApp::implementation

void AppLogic::_RefreshThemeRoutine()
{
_ApplyTheme(_settings.GlobalSettings().Theme());
_ApplyTheme(_settings.GlobalSettings().CurrentTheme().RequestedTheme());
}

// Function Description:
Expand Down Expand Up @@ -1219,6 +1221,19 @@ namespace winrt::TerminalApp::implementation
return {};
}

winrt::Windows::UI::Xaml::Media::Brush AppLogic::TitlebarBrush()
{
if (_root)
{
return _root->TitlebarBrush();
}
return { nullptr };
}
void AppLogic::WindowActivated(const bool activated)
{
_root->WindowActivated(activated);
}

bool AppLogic::HasCommandlineArguments() const noexcept
{
return _hasCommandLineArguments;
Expand Down Expand Up @@ -1645,4 +1660,15 @@ namespace winrt::TerminalApp::implementation
{
return _settings.GlobalSettings().ShowTitleInTitlebar();
}

Microsoft::Terminal::Settings::Model::Theme AppLogic::Theme()
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved
{
if (!_loadedInitialSettings)
{
// Load settings if we haven't already
LoadSettings();
}
return _settings.GlobalSettings().CurrentTheme();
}

}
Loading