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

Background image support #853

Merged
merged 12 commits into from
May 29, 2019
114 changes: 112 additions & 2 deletions src/cascadia/TerminalApp/Profile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ using namespace winrt::TerminalApp;
using namespace winrt::Windows::Data::Json;
using namespace ::Microsoft::Console;


static constexpr std::wstring_view NAME_KEY{ L"name" };
static constexpr std::wstring_view GUID_KEY{ L"guid" };
static constexpr std::wstring_view COLORSCHEME_KEY{ L"colorscheme" };
Expand All @@ -36,6 +35,9 @@ static constexpr std::wstring_view CLOSEONEXIT_KEY{ L"closeOnExit" };
static constexpr std::wstring_view PADDING_KEY{ L"padding" };
static constexpr std::wstring_view STARTINGDIRECTORY_KEY{ L"startingDirectory" };
static constexpr std::wstring_view ICON_KEY{ L"icon" };
static constexpr std::wstring_view BACKGROUNDIMAGE_KEY{ L"backgroundImage" };
static constexpr std::wstring_view BACKGROUNDIMAGEOPACITY_KEY{ L"backgroundImageOpacity" };
static constexpr std::wstring_view BACKGROUNDIMAGESTRETCHMODE_KEY{ L"backgroundImageStretchMode" };

// Possible values for Scrollbar state
static constexpr std::wstring_view ALWAYS_VISIBLE{ L"visible" };
Expand All @@ -48,6 +50,12 @@ static constexpr std::wstring_view CURSORSHAPE_UNDERSCORE{ L"underscore" };
static constexpr std::wstring_view CURSORSHAPE_FILLEDBOX{ L"filledBox" };
static constexpr std::wstring_view CURSORSHAPE_EMPTYBOX{ L"emptyBox" };

// Possible values for Image Stretch Mode
static constexpr std::wstring_view IMAGESTRETCHMODE_NONE{ L"none" };
static constexpr std::wstring_view IMAGESTRETCHMODE_FILL{ L"fill" };
static constexpr std::wstring_view IMAGESTRETCHMODE_UNIFORM{ L"uniform" };
static constexpr std::wstring_view IMAGESTRETCHMODE_UNIFORMTOFILL{ L"uniformToFill" };

Profile::Profile() :
Profile(Utils::CreateGuid())
{
Expand Down Expand Up @@ -76,7 +84,10 @@ Profile::Profile(const winrt::guid& guid):
_scrollbarState{ },
_closeOnExit{ true },
_padding{ DEFAULT_PADDING },
_icon{ }
_icon{ },
_backgroundImage{ },
_backgroundImageOpacity{ },
_backgroundImageStretchMode{ }
{
}

Expand Down Expand Up @@ -173,6 +184,21 @@ TerminalSettings Profile::CreateTerminalSettings(const std::vector<ColorScheme>&
terminalSettings.ScrollState(result);
}

if (_backgroundImage)
{
terminalSettings.BackgroundImage(_backgroundImage.value());
}

if (_backgroundImageOpacity)
{
terminalSettings.BackgroundImageOpacity(_backgroundImageOpacity.value());
}

if (_backgroundImageStretchMode)
{
terminalSettings.BackgroundImageStretchMode(_backgroundImageStretchMode.value());
}

return terminalSettings;
}

Expand Down Expand Up @@ -274,6 +300,25 @@ JsonObject Profile::ToJson() const
jsonObject.Insert(ICON_KEY, icon);
}

if (_backgroundImage)
{
const auto backgroundImage = JsonValue::CreateStringValue(_backgroundImage.value());
jsonObject.Insert(BACKGROUNDIMAGE_KEY, backgroundImage);
}

if (_backgroundImageOpacity)
{
const auto opacity = JsonValue::CreateNumberValue(_backgroundImageOpacity.value());
jsonObject.Insert(BACKGROUNDIMAGEOPACITY_KEY, opacity);
}

if (_backgroundImageStretchMode)
{
const auto imageStretchMode = JsonValue::CreateStringValue(
SerializeImageStretchMode(_backgroundImageStretchMode.value()));
jsonObject.Insert(BACKGROUNDIMAGESTRETCHMODE_KEY, imageStretchMode);
}

return jsonObject;
}

Expand Down Expand Up @@ -405,6 +450,19 @@ Profile Profile::FromJson(winrt::Windows::Data::Json::JsonObject json)
{
result._icon = json.GetNamedString(ICON_KEY);
}
if (json.HasKey(BACKGROUNDIMAGE_KEY))
{
result._backgroundImage = json.GetNamedString(BACKGROUNDIMAGE_KEY);
}
if (json.HasKey(BACKGROUNDIMAGEOPACITY_KEY))
{
result._backgroundImageOpacity = json.GetNamedNumber(BACKGROUNDIMAGEOPACITY_KEY);
}
if (json.HasKey(BACKGROUNDIMAGESTRETCHMODE_KEY))
{
const auto stretchMode = json.GetNamedString(BACKGROUNDIMAGESTRETCHMODE_KEY);
result._backgroundImageStretchMode = ParseImageStretchMode(stretchMode.c_str());
}

return result;
}
Expand Down Expand Up @@ -551,6 +609,58 @@ ScrollbarState Profile::ParseScrollbarState(const std::wstring& scrollbarState)
}
}

// Method Description:
// - Helper function for converting a user-specified image stretch mode
// to the appropriate enum value
// Arguments:
// - The value from the profiles.json file
// Return Value:
// - The corresponding enum value which maps to the string provided by the user
winrt::Windows::UI::Xaml::Media::Stretch Profile::ParseImageStretchMode(const std::wstring& imageStretchMode)
{
if (imageStretchMode == IMAGESTRETCHMODE_NONE)
{
return winrt::Windows::UI::Xaml::Media::Stretch::None;
}
else if (imageStretchMode == IMAGESTRETCHMODE_FILL)
{
return winrt::Windows::UI::Xaml::Media::Stretch::Fill;
}
else if (imageStretchMode == IMAGESTRETCHMODE_UNIFORM)
{
return winrt::Windows::UI::Xaml::Media::Stretch::Uniform;
}
else // Fall through to default behavior
{
return winrt::Windows::UI::Xaml::Media::Stretch::UniformToFill;
}
}

// Method Description:
// - Helper function for converting an ImageStretchMode to the
// correct string value.
// Arguments:
// - imageStretchMode: The enum value to convert to a string.
// Return Value:
// - The string value for the given ImageStretchMode
std::wstring_view Profile::SerializeImageStretchMode(const winrt::Windows::UI::Xaml::Media::Stretch imageStretchMode)
{
switch (imageStretchMode)
{
case winrt::Windows::UI::Xaml::Media::Stretch::None:
return IMAGESTRETCHMODE_NONE;
case winrt::Windows::UI::Xaml::Media::Stretch::Fill:
return IMAGESTRETCHMODE_FILL;
case winrt::Windows::UI::Xaml::Media::Stretch::Uniform:
return IMAGESTRETCHMODE_UNIFORM;
default:
case winrt::Windows::UI::Xaml::Media::Stretch::UniformToFill:
return IMAGESTRETCHMODE_UNIFORMTOFILL;
}
}



// Method Description:
// - Helper function for converting a user-specified cursor style corresponding
// CursorStyle enum value
Expand Down
6 changes: 6 additions & 0 deletions src/cascadia/TerminalApp/Profile.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ class TerminalApp::Profile final
static std::wstring EvaluateStartingDirectory(const std::wstring& directory);

static winrt::Microsoft::Terminal::Settings::ScrollbarState ParseScrollbarState(const std::wstring& scrollbarState);
static winrt::Windows::UI::Xaml::Media::Stretch ParseImageStretchMode(const std::wstring& imageStretchMode);
static std::wstring_view SerializeImageStretchMode(const winrt::Windows::UI::Xaml::Media::Stretch imageStretchMode);
static winrt::Microsoft::Terminal::Settings::CursorStyle _ParseCursorShape(const std::wstring& cursorShapeString);
static std::wstring_view _SerializeCursorStyle(const winrt::Microsoft::Terminal::Settings::CursorStyle cursorShape);

Expand All @@ -84,6 +86,10 @@ class TerminalApp::Profile final
double _acrylicTransparency;
bool _useAcrylic;

std::optional<std::wstring> _backgroundImage;
std::optional<double> _backgroundImageOpacity;
std::optional<winrt::Windows::UI::Xaml::Media::Stretch> _backgroundImageStretchMode;

std::optional<std::wstring> _scrollbarState;
bool _closeOnExit;
std::wstring _padding;
Expand Down
121 changes: 111 additions & 10 deletions src/cascadia/TerminalControl/TermControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// - Style our UI elements based on the values in our _settings, and set up
// other control-specific settings. This method will be called whenever
// the settings are reloaded.
// * Calls _InitializeBackgroundBrush to set up the Xaml brush responsible
// for the control's background
// * Calls _BackgroundColorChanged to style the background of the control
// - Core settings will be passed to the terminal in _InitializeTerminal
// Arguments:
Expand All @@ -166,6 +168,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// - <none>
void TermControl::_ApplyUISettings()
{
_InitializeBackgroundBrush();

uint32_t bg = _settings.DefaultBackground();
_BackgroundColorChanged(bg);

Expand All @@ -186,9 +190,96 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
_desiredFont = { _actualFont };
}

// Method Description:
// - Set up the brush used to display the control's background.
// - Respects the settings for acrylic, background image and opacity from
// _settings.
// * Prioritizes the acrylic background if chosen, respecting acrylicOpacity
// from _settings.
// * If acrylic is not enabled and a backgroundImage is present, it is used,
// respecting the opacity and stretch mode settings from _settings.
// * Falls back to a solid color background from _settings if acrylic is not
// enabled and no background image is present.
// - Avoids image flickering and acrylic brush redraw if settings are changed
// but the appropriate brush is still in place.
// - Does not apply background color; _BackgroundColorChanged must be called
// to do so.
// Arguments:
// - <none>
// Return Value:
// - <none>
void TermControl::_InitializeBackgroundBrush()
{
if (_settings.UseAcrylic())
{
// See if we've already got an acrylic background brush
// to avoid the flicker when setting up a new one
auto acrylic = _root.Background().try_as<Media::AcrylicBrush>();

// Instantiate a brush if there's not already one there
if (acrylic == nullptr)
{
acrylic = Media::AcrylicBrush{};
acrylic.BackgroundSource(Media::AcrylicBackgroundSource::HostBackdrop);
}

// Apply brush settings
acrylic.TintOpacity(_settings.TintOpacity());

// Apply brush to control if it's not already there
if (_root.Background() != acrylic)
{
_root.Background(acrylic);
}
}
else if (!_settings.BackgroundImage().empty())
{
Windows::Foundation::Uri imageUri{ _settings.BackgroundImage() };

// Check if the existing brush is an image brush, and if not
// construct a new one
auto brush = _root.Background().try_as<Media::ImageBrush>();

if (brush == nullptr)
{
brush = Media::ImageBrush{};
}

// Check if the image brush is already pointing to the image
// in the modified settings; if it isn't (or isn't there),
// set a new image source for the brush
auto imageSource = brush.ImageSource().try_as<Media::Imaging::BitmapImage>();

if (imageSource == nullptr || imageSource.UriSource() == nullptr
|| imageSource.UriSource().RawUri() != imageUri.RawUri())
{
// Note that BitmapImage handles the image load asynchronously,
// which is especially important since the image
// may well be both large and somewhere out on the
// internet.
Media::Imaging::BitmapImage image(imageUri);
brush.ImageSource(image);
}

// Apply stretch and opacity settings
brush.Stretch(_settings.BackgroundImageStretchMode());
brush.Opacity(_settings.BackgroundImageOpacity());

// Apply brush if it isn't already there
if (_root.Background() != brush)
{
_root.Background(brush);
}
}
else
{
Media::SolidColorBrush solidColor{};
_root.Background(solidColor);
}
}

// Method Description:
// - Style the background of the control with the provided background color
// - Respects the settings for acrylic and opacity from _settings
// Arguments:
// - color: The background color to use as a uint32 (aka DWORD COLORREF)
// Return Value:
Expand All @@ -208,23 +299,33 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation

if (_settings.UseAcrylic())
{
Media::AcrylicBrush acrylic{};
acrylic.BackgroundSource(Media::AcrylicBackgroundSource::HostBackdrop);
acrylic.FallbackColor(bgColor);
acrylic.TintColor(bgColor);
acrylic.TintOpacity(_settings.TintOpacity());
_root.Background(acrylic);
if (auto acrylic = _root.Background().try_as<Media::AcrylicBrush>())
{
acrylic.FallbackColor(bgColor);
acrylic.TintColor(bgColor);
}

// If we're acrylic, we want to make sure that the default BG color
// is transparent, so we can see the acrylic effect on text with the
// default BG color.
_settings.DefaultBackground(ARGB(0, R, G, B));
}
else if (!_settings.BackgroundImage().empty())
{
// This currently applies no changes to the image background
// brush itself.

// Set the default background as transparent to prevent the
// DX layer from overwriting the background image
_settings.DefaultBackground(ARGB(0, R, G, B));
}
else
{
Media::SolidColorBrush solidColor{};
solidColor.Color(bgColor);
_root.Background(solidColor);
if (auto solidColor = _root.Background().try_as<Media::SolidColorBrush>())
{
solidColor.Color(bgColor);
}

_settings.DefaultBackground(RGB(R, G, B));
}
});
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalControl/TermControl.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation

void _Create();
void _ApplyUISettings();
void _InitializeBackgroundBrush();
void _BackgroundColorChanged(const uint32_t color);
void _ApplyConnectionSettings();
void _InitializeTerminal();
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalControl/pch.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <winrt/Windows.UI.Xaml.Controls.h>
#include <winrt/Windows.UI.Xaml.Controls.Primitives.h>
#include <winrt/Windows.ui.xaml.media.h>
#include <winrt/Windows.ui.xaml.media.imaging.h>
#include <winrt/Windows.ui.xaml.input.h>

#include <windows.ui.xaml.media.dxinterop.h>
Expand Down
3 changes: 3 additions & 0 deletions src/cascadia/TerminalSettings/IControlSettings.idl
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,8 @@ namespace Microsoft.Terminal.Settings
String StartingDirectory;
String EnvironmentVariables;

String BackgroundImage;
Double BackgroundImageOpacity;
Windows.UI.Xaml.Media.Stretch BackgroundImageStretchMode;
};
}
Loading