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

Add support for setting a tab's color at runtime w/ context menu #3789

Merged
32 commits merged into from
May 4, 2020
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
eeeed7f
Add tab colors #2994
gbaychev Oct 26, 2019
a223c8d
Merge from upstream/master
gbaychev Nov 25, 2019
524ce1c
Use events when picked color changes, adjust the preset colors (#2994)
gbaychev Nov 29, 2019
2109095
Merge from master (#2994)
gbaychev Nov 30, 2019
894d8f9
Do code formatting (#2994)
gbaychev Dec 2, 2019
a4b01b3
Merge branch 'master' into dev/tabs-color
gbaychev Dec 2, 2019
306a8da
Remove unwanted merge artifact (#2994)
gbaychev Dec 4, 2019
3fcfe7f
Merge from master
gbaychev Dec 14, 2019
a92a0e7
Merge branch 'master' into dev/tabs-color
gbaychev Dec 16, 2019
a5aa717
Merge branch 'master' into dev/tabs-color
gbaychev Jan 6, 2020
1cf9407
Incorporate PR feedback #2994
gbaychev Jan 6, 2020
1487ffe
Fix code formatting errors #2994
gbaychev Jan 6, 2020
851b4d4
Merge from master #2994
gbaychev Jan 12, 2020
701b7b3
Improve the ColorPickupFlyout UI #2994
gbaychev Jan 14, 2020
8a21d91
Merge branch 'master' into gbaychev/dev/tabs-color
miniksa Feb 5, 2020
63981ef
Incorporate PR feedback and paint the non-client area #2994
gbaychev Jan 19, 2020
4e20147
Remove the non-client area painting code #2994
gbaychev Feb 7, 2020
e8edec9
Use the brush instead of the color when adjusting the tab color #2994
gbaychev Feb 7, 2020
7cf6b46
Merge branch 'master' into dev/tabs-color
gbaychev Feb 11, 2020
5e735f8
Tweak the color helper a bit for better contrast, now that not the wh…
gbaychev Feb 11, 2020
cfeda22
Merge branch 'master' into dev/tabs-color
gbaychev Feb 15, 2020
5e2490c
Remove merge artefact from a code comment in TerminalPage.cpp #2994
gbaychev Feb 20, 2020
d45b70a
Fix the code formatting and change the quotes in TerminalApp.UnitTest…
gbaychev Feb 20, 2020
226bcd6
Merge remote-tracking branch 'origin/master' into HEAD
DHowett Mar 9, 2020
6686fc2
Tidy up the resources and Xaml code a bit; change to Color...
DHowett Mar 9, 2020
81e32ee
Fix WUI; temp. disable new tab btn color; clean xaml
DHowett Mar 9, 2020
1900cfd
reduce diffs for TA UT vcxproj
DHowett Mar 9, 2020
44df3bf
Code format
DHowett Mar 9, 2020
63bc8a5
Merge remote-tracking branch 'origin/master' into HEAD
zadjii-msft May 1, 2020
a430e60
Apply spellcheck suggestions from code review
zadjii-msft May 1, 2020
611bbf6
Fix typos (hopefully)
zadjii-msft May 1, 2020
ec5be66
Merge branch 'dev/tabs-color' of https://github.com/gbaychev/terminal…
zadjii-msft May 1, 2020
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
1 change: 1 addition & 0 deletions src/cascadia/LocalTests_TerminalApp/TabTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ using namespace Microsoft::Console;
using namespace TerminalApp;
using namespace WEX::Logging;
using namespace WEX::TestExecution;
using namespace winrt::Windows::ApplicationModel::DataTransfer;

namespace TerminalAppLocalTests
{
Expand Down
2 changes: 2 additions & 0 deletions src/cascadia/LocalTests_TerminalApp/pch.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ Author(s):
#include "../../types/inc/utils.hpp"
#include "../../inc/DefaultSettings.h"

#include <winrt/Windows.ApplicationModel.Resources.Core.h>
#include "winrt/Windows.UI.Xaml.Markup.h"
#include <winrt/Windows.system.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
Expand Down
270 changes: 270 additions & 0 deletions src/cascadia/TerminalApp/ColorHelper.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
#include "pch.h"
#include "ColorHelper.h"
#include <limits>

using namespace winrt::TerminalApp;
namespace WUI = ::winrt::Windows::UI;

// Method Description:
// Determines whether or not a given color is light
// Arguments:
// - color: this color is going to be examined whether it
// is light or not
// Return Value:
// - true of light, false if dark
bool ColorHelper::IsBrightColor(const winrt::Windows::UI::Color& color)
{
//http://www.w3.org/TR/AERT#color-contrast
auto brightness = (color.R * 299 + color.G * 587 + color.B * 114) / 1000.f;
return brightness > 128.f;
}

// Method Description:
// Converts a rgb color to hsl one
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved
// Arguments:
// - color: the rgb color, which is going to be converted
// Return Value:
// - a hsl color with the following ranges
// - H: [0.f -360.f]
// - L: [0.f - 1.f] (rounded to the third decimal place)
// - S: [0.f - 1.f] (rounded to the third decimal place)
HSL ColorHelper::RgbToHsl(const winrt::Windows::UI::Color& color)
{
// https://www.rapidtables.com/convert/color/rgb-to-hsl.html
auto epsilon = std::numeric_limits<float>::epsilon();
auto r = color.R / 255.f;
auto g = color.G / 255.f;
auto b = color.B / 255.f;

auto max = std::max(r, std::max(g, b));
auto min = std::min(r, std::min(g, b));

auto delta = max - min;

auto h = 0.f;
auto s = 0.f;
auto l = (max + min) / 2;

if (delta < epsilon || max < epsilon) /* delta == 0 || max == 0*/
{
l = std::roundf(l * 1000) / 1000;
return HSL{ h, s, l };
}

s = l > 0.5 ? delta / (2 - max - min) : delta / (max + min);

if (max - r < epsilon) // max == r
{
h = (g - b) / delta + (g < b ? 6 : 0);
}
else if (max - g < epsilon) // max == g
{
h = (b - r) / delta + 2;
}
else if (max - b < epsilon) // max == b
{
h = (r - g) / delta + 4;
}

// three decimal places after the comma ought
// to be enough for everybody - Bill Gates, 1981
float finalH = std::roundf(h * 60);
float finalS = std::roundf(s * 1000) / 1000;
float finalL = std::roundf(l * 1000) / 1000;

return HSL{ finalH, finalS, finalL };
}

// Method Description:
// Converts a hsl color to rgb one
// Arguments:
// - color: the hsl color, which is going to be converted
// Return Value:
// - the rgb color (r,g,b - [0, 255] range)
winrt::Windows::UI::Color ColorHelper::HslToRgb(const HSL& color)
{
auto epsilon = std::numeric_limits<float>::epsilon();

auto h = (color.H - 1.f > epsilon) ? color.H / 360.f : color.H;
auto s = (color.S - 1.f > epsilon) ? color.S / 100.f : color.S;
auto l = (color.L - 1.f > epsilon) ? color.L / 100.f : color.L;

auto r = l;
auto g = l;
auto b = l;

if (s > epsilon)
{
auto q = l < 0.5 ? l * (1 + s) : l + s - l * s;
auto p = 2 * l - q;
r = HueToRgb(p, q, h + 1.f / 3.f);
g = HueToRgb(p, q, h);
b = HueToRgb(p, q, h - 1.f / 3.f);
}

auto finalR = static_cast<uint8_t>(std::roundf(r * 255));
auto finalG = static_cast<uint8_t>(std::roundf(g * 255));
auto finalB = static_cast<uint8_t>(std::roundf(b * 255));
uint8_t finalA = 255; //opaque

return winrt::Windows::UI::ColorHelper::FromArgb(finalA, finalR, finalG, finalB);
}

float ColorHelper::HueToRgb(float p, float q, float t)
{
auto epsilon = std::numeric_limits<float>::epsilon();

if (t < 0)
t += 1;
if (t > 1)
t -= 1;
if (t - (1.f / 6.f) < epsilon)
return p + (q - p) * 6 * t;
if (t - .5f < epsilon)
return q;
if (t - 2.f / 3.f < epsilon)
return p + (q - p) * (2.f / 3.f - t) * 6;
return p;
}

// Method Description:
// Lightens a color by a given amount
// Arguments:
// - color: the color which is going to be lightened
// - amount: the lighten amount (0-100)
// Return Value:
// - the lightened color in RGB format
WUI::Color ColorHelper::Lighten(const WUI::Color& color, float amount /* = 10.f*/)
{
auto hsl = RgbToHsl(color);
hsl.L += amount / 100;
hsl.L = std::clamp(hsl.L, 0.f, 1.f);
return HslToRgb(hsl);
}

// Method Description:
// Darkens a color by a given amount
// Arguments:
// - color: the color which is going to be darkened
// - amount: the darken amount (0-100)
// Return Value:
// - the darkened color in RGB format
WUI::Color ColorHelper::Darken(const WUI::Color& color, float amount /* = 10.f*/)
{
auto hsl = RgbToHsl(color);
hsl.L -= amount / 100;
hsl.L = std::clamp(hsl.L, 0.f, 1.f);
return HslToRgb(hsl);
}

// Method Description:
// Gets an accent color to a given color. Basically, generates
// 16 shades of the color and finds the first which has a good
// contrast according to http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef (WCAG Version 2)
// Readability ratio of 3.5 seems to look quite nicely
// Arguments:
// - color: the color for which we need an accent
// Return Value:
// - the accemt color in RGB format
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved
WUI::Color ColorHelper::GetAccentColor(const WUI::Color& color)
{
auto accentColor = RgbToHsl(color);

if (accentColor.S < 0.15)
{
accentColor.S = 0.15f;
}

constexpr auto shadeCount = 16;
constexpr auto shadeStep = 1.f / shadeCount;
auto shades = std::map<float, HSL>();
for (auto i = 0; i < 15; i++)
{
auto shade = HSL{ accentColor.H, accentColor.S, i * shadeStep };
auto contrast = GetReadability(shade, accentColor);
shades.insert(std::make_pair(contrast, shade));
}

// 3f is quite nice of the whole non-client area is painted
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved
constexpr auto readability = 1.75f;
for (auto shade : shades)
{
if (shade.first >= readability)
{
return HslToRgb(shade.second);
}
}
return HslToRgb(shades.end()->second);
}

// Method Description:
// Gets the readability of two colors according to
// http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef (WCAG Version 2)
// Arguments:
// - firstColor: the first color for the readability check (hsl)
// - secondColor: the second color for the readability check (hsl)
// Return Value:
// - the readability of the colors according to (WCAG Version 2)
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved
float ColorHelper::GetReadability(const HSL& first, const HSL& second)
{
return GetReadability(HslToRgb(first), HslToRgb(second));
}

// Method Description:
// Gets the readability of two colors according to
// http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef (WCAG Version 2)
// Arguments:
// - firstColor: the first color for the readability check (rgb)
// - secondColor: the second color for the readability check (rgb)
// Return Value:
// - the readability of the colors according to (WCAG Version 2)
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved
float ColorHelper::GetReadability(const WUI::Color& first, const WUI::Color& second)
{
auto l1 = GetLuminance(first);
auto l2 = GetLuminance(second);

return (std::max(l1, l2) + 0.05f) / std::min(l1, l2) + 0.05f;
}

// Method Description:
// Calculates the luminance of a given color according to
// http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
// Arguments:
// - color: its luminance is going to be calculated
// Return Value:
// - the luminance of the color
float ColorHelper::GetLuminance(const WUI::Color& color)
{
auto epsilon = std::numeric_limits<float>::epsilon();
float R, G, B;
auto RsRGB = color.R / 255.f;
auto GsRGB = color.G / 255.f;
auto BsRGB = color.B / 255.f;

if (RsRGB - 0.03928f <= epsilon)
{
R = RsRGB / 12.92f;
}
else
{
R = std::pow(((RsRGB + 0.055f) / 1.055f), 2.4f);
}
if (GsRGB - 0.03928f <= epsilon)
{
G = GsRGB / 12.92f;
}
else
{
G = std::pow(((GsRGB + 0.055f) / 1.055f), 2.4f);
}
if (BsRGB - 0.03928f <= epsilon)
{
B = BsRGB / 12.92f;
}
else
{
B = std::pow(((BsRGB + 0.055f) / 1.055f), 2.4f);
}
float luminance = (0.2126f * R) + (0.7152f * G) + (0.0722f * B);
return std::roundf(luminance * 10000) / 10000.f;
}
32 changes: 32 additions & 0 deletions src/cascadia/TerminalApp/ColorHelper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#pragma once
#include "pch.h"

#include <winrt/windows.ui.core.h>

namespace winrt::TerminalApp
{
class HSL
{
public:
float H;
float S;
float L;
};

class ColorHelper
{
public:
static bool IsBrightColor(const Windows::UI::Color& color);
static HSL RgbToHsl(const Windows::UI::Color& color);
static Windows::UI::Color HslToRgb(const HSL& color);
static Windows::UI::Color Lighten(const Windows::UI::Color& color, float amount = 10.f);
static Windows::UI::Color Darken(const Windows::UI::Color& color, float amount = 10.f);
static Windows::UI::Color GetAccentColor(const Windows::UI::Color& color);
static float GetLuminance(const Windows::UI::Color& color);
static float GetReadability(const Windows::UI::Color& first, const Windows::UI::Color& second);
static float GetReadability(const HSL& first, const HSL& second);

private:
static float HueToRgb(float p, float q, float t);
};
}
Loading