Background image support (#853)

* Initial code check in for background images

* Cleaning up whitespace

* Whitespace cleanup

* Added/fixed comments

* Fixing tabs

* Reverting erroneous file add

* Removing custom enum for image stretching mode and using Windows::UI::Xaml::Media::Stretch instead.

* Removing now-superfluous static_cast when setting stretch mode.

* Updating code to use wstring_view (per #925)

* One last set of wstring -> wstring_view changes

* Split off brush-intialization function from TermControl::_BackgroundColorChanged and added code to prevent flicker on resetting acrylic or image backgrounds.
This commit is contained in:
d-bingham 2019-05-29 13:35:46 -05:00 committed by Mike Griese
parent 2f88c46350
commit 097f7d32a6
8 changed files with 277 additions and 12 deletions

View File

@ -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" };
@ -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" };
@ -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())
{
@ -76,7 +84,10 @@ Profile::Profile(const winrt::guid& guid):
_scrollbarState{ },
_closeOnExit{ true },
_padding{ DEFAULT_PADDING },
_icon{ }
_icon{ },
_backgroundImage{ },
_backgroundImageOpacity{ },
_backgroundImageStretchMode{ }
{
}
@ -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;
}
@ -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;
}
@ -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;
}
@ -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

View File

@ -59,6 +59,8 @@ private:
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);
@ -84,6 +86,10 @@ private:
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;

View File

@ -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:
@ -166,6 +168,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// - <none>
void TermControl::_ApplyUISettings()
{
_InitializeBackgroundBrush();
uint32_t bg = _settings.DefaultBackground();
_BackgroundColorChanged(bg);
@ -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:
@ -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));
}
});

View File

@ -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();

View File

@ -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>

View File

@ -34,5 +34,8 @@ namespace Microsoft.Terminal.Settings
String StartingDirectory;
String EnvironmentVariables;
String BackgroundImage;
Double BackgroundImageOpacity;
Windows.UI.Xaml.Media.Stretch BackgroundImageStretchMode;
};
}

View File

@ -26,6 +26,9 @@ namespace winrt::Microsoft::Terminal::Settings::implementation
_padding{ DEFAULT_PADDING },
_fontFace{ DEFAULT_FONT_FACE },
_fontSize{ DEFAULT_FONT_SIZE },
_backgroundImage{},
_backgroundImageOpacity{ 1.0 },
_backgroundImageStretchMode{ winrt::Windows::UI::Xaml::Media::Stretch::UniformToFill },
_keyBindings{ nullptr },
_scrollbarState{ ScrollbarState::Visible }
{
@ -193,6 +196,36 @@ namespace winrt::Microsoft::Terminal::Settings::implementation
_fontSize = value;
}
void TerminalSettings::BackgroundImage(hstring const& value)
{
_backgroundImage = value;
}
hstring TerminalSettings::BackgroundImage()
{
return _backgroundImage;
}
void TerminalSettings::BackgroundImageOpacity(double value)
{
_backgroundImageOpacity = value;
}
double TerminalSettings::BackgroundImageOpacity()
{
return _backgroundImageOpacity;
}
winrt::Windows::UI::Xaml::Media::Stretch TerminalSettings::BackgroundImageStretchMode()
{
return _backgroundImageStretchMode;
}
void TerminalSettings::BackgroundImageStretchMode(winrt::Windows::UI::Xaml::Media::Stretch value)
{
_backgroundImageStretchMode = value;
}
Settings::IKeyBindings TerminalSettings::KeyBindings()
{
return _keyBindings;

View File

@ -61,6 +61,13 @@ namespace winrt::Microsoft::Terminal::Settings::implementation
int32_t FontSize();
void FontSize(int32_t value);
hstring BackgroundImage();
void BackgroundImage(hstring const& value);
double BackgroundImageOpacity();
void BackgroundImageOpacity(double value);
winrt::Windows::UI::Xaml::Media::Stretch BackgroundImageStretchMode();
void BackgroundImageStretchMode(winrt::Windows::UI::Xaml::Media::Stretch value);
winrt::Microsoft::Terminal::Settings::IKeyBindings KeyBindings();
void KeyBindings(winrt::Microsoft::Terminal::Settings::IKeyBindings const& value);
@ -94,6 +101,9 @@ namespace winrt::Microsoft::Terminal::Settings::implementation
hstring _fontFace;
int32_t _fontSize;
hstring _padding;
hstring _backgroundImage;
double _backgroundImageOpacity;
winrt::Windows::UI::Xaml::Media::Stretch _backgroundImageStretchMode;
hstring _commandline;
hstring _startingDir;
hstring _envVars;