Add nullable colors and improve Profile.Icon in settings UI (#17870)

## Summary of the Pull Request
Adds some pre-existing settings ($profile.foreground,
$profile.background, $profile.selectionBackground, $profile.cursorColor)
to the settings UI. This was accomplished by introducing a new control:
NullableColorPicker. This control allows the user to pick a color from
the color scheme, set the color to null, and select a color from an
advanced color picker.

Improves the UI for the Profile.Icon setting by adding an "Icon Type"
combo box. This allows the user to pick from multiple options:
- None: sets the icon to "none" which is interpreted as no icon
- Built-in Icon: presents a combo box that enumerates the Segoe MDL 2
assets
- Emoji: presents a text box with a hint to open the emoji picker
- File: presents a text box to input the path of the image to use

Additionally, the rendered icon is displayed in the setting container.
If "none", "none" is presented to the user (localized).

## References and Relevant Issues
#10000

## Detailed Description of the Pull Request / Additional comments
- NullableColorPicker control
- includes a built-in NullColorButton to set the current value to null
- includes a "More colors..." button to display an advanced color picker
- uses data templates on data templates (data templates squared?) to
convert the current color scheme into a grid of color chips
- color chips display a checkmark (similar to Windows settings
personalization). This automatically updates its color to stay compliant
with color contrast.
- color chips are added to a list so we can (un)check them when a new
color is selected
- SettingsContainer changes
- Forked `ExpanderSettingContainerStyle` to allow for a custom preview
template. This way, we can display the current value in the expander and
we're not just limited to text.
- changed type of `CurrentValue` property from `String` to
`IInspectable`
- added `CurrentValueTemplate` property to control how to display the
current value
- Miscellaneous:
- Added a few converters (`BooleanToVisibility`, `ColorToString`,
`ColorToBrush`)
- Added `NameWithHexCode` to `ColorTableEntry` to expose a color as `Red
#RRGGBB` (used for tooltips and a11y)
- Added `ForegroundPreview` (and equivalent for other colors) to
AppearanceViewModel to deduce the color that will be used

## Validation Steps Performed
- [X] a11y pass (NVDA, keyboard)
- [X] set the color to one of the color chips
- [X] set the color to null
- [X] set the color to a value from the integrated color picker
- [X] control updates properly when a new color scheme is selected
- [X] control updates properly when a color scheme has multiple colors
of the same value

## Follow-ups
- [A11y] Screen readers don't read expander's preview text
- Add Tab Color to settings UI
- Update CursorColor preview to display #FFFFFF as "invert"
- Use Leonard's font picker UI, with the Segoe icon picker, so that you
can filter the list
This commit is contained in:
Carlos Zamora 2024-12-12 12:30:54 -08:00 committed by GitHub
parent bfab5fde4d
commit 5132f9c553
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
34 changed files with 3068 additions and 215 deletions

View File

@ -99,6 +99,7 @@ Resources/(?!en)
^NOTICE.md
^oss/
^samples/PixelShaders/Screenshots/
^src/cascadia/TerminalSettingsEditor/SegoeFluentIconList.h$
^src/interactivity/onecore/BgfxEngine\.
^src/renderer/atlas/
^src/renderer/wddmcon/WddmConRenderer\.

View File

@ -221,6 +221,30 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
// box, prevent it from ever being changed again.
_NotifyChanges(L"UseDesktopBGImage", L"BackgroundImageSettingsVisible");
}
else if (viewModelProperty == L"Foreground")
{
_NotifyChanges(L"ForegroundPreview");
}
else if (viewModelProperty == L"Background")
{
_NotifyChanges(L"BackgroundPreview");
}
else if (viewModelProperty == L"SelectionBackground")
{
_NotifyChanges(L"SelectionBackgroundPreview");
}
else if (viewModelProperty == L"CursorColor")
{
_NotifyChanges(L"CursorColorPreview");
}
else if (viewModelProperty == L"DarkColorSchemeName" || viewModelProperty == L"LightColorSchemeName")
{
_NotifyChanges(L"CurrentColorScheme");
}
else if (viewModelProperty == L"CurrentColorScheme")
{
_NotifyChanges(L"ForegroundPreview", L"BackgroundPreview", L"SelectionBackgroundPreview", L"CursorColorPreview");
}
});
// Cache the original BG image path. If the user clicks "Use desktop
@ -928,7 +952,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
_NotifyChanges(L"CurrentColorScheme");
}
Editor::ColorSchemeViewModel AppearanceViewModel::CurrentColorScheme()
Editor::ColorSchemeViewModel AppearanceViewModel::CurrentColorScheme() const
{
const auto schemeName{ DarkColorSchemeName() };
const auto allSchemes{ SchemesList() };
@ -950,6 +974,42 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
LightColorSchemeName(val.Name());
}
static inline Windows::UI::Color _getColorPreview(const IReference<Microsoft::Terminal::Core::Color>& modelVal, Windows::UI::Color deducedVal)
{
if (modelVal)
{
// user defined an override value
return Windows::UI::Color{
.A = 255,
.R = modelVal.Value().R,
.G = modelVal.Value().G,
.B = modelVal.Value().B
};
}
// set to null --> deduce value from color scheme
return deducedVal;
}
Windows::UI::Color AppearanceViewModel::ForegroundPreview() const
{
return _getColorPreview(_appearance.Foreground(), CurrentColorScheme().ForegroundColor().Color());
}
Windows::UI::Color AppearanceViewModel::BackgroundPreview() const
{
return _getColorPreview(_appearance.Background(), CurrentColorScheme().BackgroundColor().Color());
}
Windows::UI::Color AppearanceViewModel::SelectionBackgroundPreview() const
{
return _getColorPreview(_appearance.SelectionBackground(), CurrentColorScheme().SelectionBackgroundColor().Color());
}
Windows::UI::Color AppearanceViewModel::CursorColorPreview() const
{
return _getColorPreview(_appearance.CursorColor(), CurrentColorScheme().CursorColor().Color());
}
DependencyProperty Appearances::_AppearanceProperty{ nullptr };
Appearances::Appearances()

View File

@ -127,9 +127,14 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void SetBackgroundImagePath(winrt::hstring path);
void ClearColorScheme();
Editor::ColorSchemeViewModel CurrentColorScheme();
Editor::ColorSchemeViewModel CurrentColorScheme() const;
void CurrentColorScheme(const Editor::ColorSchemeViewModel& val);
Windows::UI::Color ForegroundPreview() const;
Windows::UI::Color BackgroundPreview() const;
Windows::UI::Color SelectionBackgroundPreview() const;
Windows::UI::Color CursorColorPreview() const;
WINRT_PROPERTY(bool, IsDefault, false);
// These settings are not defined in AppearanceConfig, so we grab them
@ -153,6 +158,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
OBSERVABLE_PROJECTED_SETTING(_appearance, BackgroundImageAlignment);
OBSERVABLE_PROJECTED_SETTING(_appearance, IntenseTextStyle);
OBSERVABLE_PROJECTED_SETTING(_appearance, AdjustIndistinguishableColors);
OBSERVABLE_PROJECTED_SETTING(_appearance, Foreground);
OBSERVABLE_PROJECTED_SETTING(_appearance, Background);
OBSERVABLE_PROJECTED_SETTING(_appearance, SelectionBackground);
OBSERVABLE_PROJECTED_SETTING(_appearance, CursorColor);
WINRT_OBSERVABLE_PROPERTY(Windows::Foundation::Collections::IObservableVector<Editor::ColorSchemeViewModel>, SchemesList, _propertyChangedHandlers, nullptr);
private:

View File

@ -45,6 +45,11 @@ namespace Microsoft.Terminal.Settings.Editor
ColorSchemeViewModel CurrentColorScheme;
IObservableVector<ColorSchemeViewModel> SchemesList;
Windows.UI.Color ForegroundPreview { get; };
Windows.UI.Color BackgroundPreview { get; };
Windows.UI.Color SelectionBackgroundPreview { get; };
Windows.UI.Color CursorColorPreview { get; };
String MissingFontFaces { get; };
String ProportionalFontFaces { get; };
Boolean HasPowerlineCharacters { get; };
@ -78,6 +83,11 @@ namespace Microsoft.Terminal.Settings.Editor
OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Microsoft.Terminal.Settings.Model.ConvergedAlignment, BackgroundImageAlignment);
OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Microsoft.Terminal.Settings.Model.IntenseStyle, IntenseTextStyle);
OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Microsoft.Terminal.Core.AdjustTextMode, AdjustIndistinguishableColors);
OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Windows.Foundation.IReference<Microsoft.Terminal.Core.Color>, Foreground);
OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Windows.Foundation.IReference<Microsoft.Terminal.Core.Color>, Background);
OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Windows.Foundation.IReference<Microsoft.Terminal.Core.Color>, SelectionBackground);
OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Windows.Foundation.IReference<Microsoft.Terminal.Core.Color>, CursorColor);
}
[default_interface] runtimeclass Appearances : Windows.UI.Xaml.Controls.UserControl, Windows.UI.Xaml.Data.INotifyPropertyChanged

View File

@ -69,138 +69,188 @@
<!-- Grouping: Text -->
<TextBlock x:Uid="Profile_TextHeader"
Style="{StaticResource TextBlockSubHeaderStyle}" />
<!-- Color Scheme -->
<!-- This currently only display the Dark color scheme, even if the user has a pair of schemes set. -->
<local:SettingContainer x:Uid="Profile_ColorScheme"
ClearSettingValue="{x:Bind Appearance.ClearColorScheme}"
HasSettingValue="{x:Bind Appearance.HasDarkColorSchemeName, Mode=OneWay}"
SettingOverrideSource="{x:Bind Appearance.DarkColorSchemeNameOverrideSource, Mode=OneWay}">
<ComboBox Padding="4"
ItemsSource="{x:Bind Appearance.SchemesList, Mode=OneWay}"
SelectedItem="{x:Bind Appearance.CurrentColorScheme, Mode=TwoWay}"
Style="{StaticResource ComboBoxSettingStyle}">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="local:ColorSchemeViewModel">
<Grid Grid.Column="0"
Padding="8"
VerticalAlignment="Center"
Background="{x:Bind mtu:Converters.ColorToBrush(BackgroundColor.Color), Mode=OneWay}"
ColumnSpacing="1"
CornerRadius="2"
RowSpacing="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ContentControl Grid.Row="0"
Grid.Column="0"
Content="{x:Bind ColorEntryAt(0), Mode=OneWay}"
ContentTemplate="{StaticResource ColorChipTemplate}"
IsTabStop="False" />
<ContentControl Grid.Row="0"
Grid.Column="1"
Content="{x:Bind ColorEntryAt(1), Mode=OneWay}"
ContentTemplate="{StaticResource ColorChipTemplate}"
IsTabStop="False" />
<ContentControl Grid.Row="0"
Grid.Column="2"
Content="{x:Bind ColorEntryAt(2), Mode=OneWay}"
ContentTemplate="{StaticResource ColorChipTemplate}"
IsTabStop="False" />
<ContentControl Grid.Row="0"
Grid.Column="3"
Content="{x:Bind ColorEntryAt(3), Mode=OneWay}"
ContentTemplate="{StaticResource ColorChipTemplate}"
IsTabStop="False" />
<ContentControl Grid.Row="0"
Grid.Column="4"
Content="{x:Bind ColorEntryAt(4), Mode=OneWay}"
ContentTemplate="{StaticResource ColorChipTemplate}"
IsTabStop="False" />
<ContentControl Grid.Row="0"
Grid.Column="5"
Content="{x:Bind ColorEntryAt(5), Mode=OneWay}"
ContentTemplate="{StaticResource ColorChipTemplate}"
IsTabStop="False" />
<ContentControl Grid.Row="0"
Grid.Column="6"
Content="{x:Bind ColorEntryAt(6), Mode=OneWay}"
ContentTemplate="{StaticResource ColorChipTemplate}"
IsTabStop="False" />
<ContentControl Grid.Row="0"
Grid.Column="7"
Content="{x:Bind ColorEntryAt(7), Mode=OneWay}"
ContentTemplate="{StaticResource ColorChipTemplate}"
IsTabStop="False" />
<ContentControl Grid.Row="1"
Grid.Column="0"
Content="{x:Bind ColorEntryAt(8), Mode=OneWay}"
ContentTemplate="{StaticResource ColorChipTemplate}"
IsTabStop="False" />
<ContentControl Grid.Row="1"
Grid.Column="1"
Content="{x:Bind ColorEntryAt(9), Mode=OneWay}"
ContentTemplate="{StaticResource ColorChipTemplate}"
IsTabStop="False" />
<ContentControl Grid.Row="1"
Grid.Column="2"
Content="{x:Bind ColorEntryAt(10), Mode=OneWay}"
ContentTemplate="{StaticResource ColorChipTemplate}"
IsTabStop="False" />
<ContentControl Grid.Row="1"
Grid.Column="3"
Content="{x:Bind ColorEntryAt(11), Mode=OneWay}"
ContentTemplate="{StaticResource ColorChipTemplate}"
IsTabStop="False" />
<ContentControl Grid.Row="1"
Grid.Column="4"
Content="{x:Bind ColorEntryAt(12), Mode=OneWay}"
ContentTemplate="{StaticResource ColorChipTemplate}"
IsTabStop="False" />
<ContentControl Grid.Row="1"
Grid.Column="5"
Content="{x:Bind ColorEntryAt(13), Mode=OneWay}"
ContentTemplate="{StaticResource ColorChipTemplate}"
IsTabStop="False" />
<ContentControl Grid.Row="1"
Grid.Column="6"
Content="{x:Bind ColorEntryAt(14), Mode=OneWay}"
ContentTemplate="{StaticResource ColorChipTemplate}"
IsTabStop="False" />
<ContentControl Grid.Row="1"
Grid.Column="7"
Content="{x:Bind ColorEntryAt(15), Mode=OneWay}"
ContentTemplate="{StaticResource ColorChipTemplate}"
IsTabStop="False" />
<TextBlock Grid.RowSpan="2"
Grid.Column="8"
MaxWidth="192"
Margin="4,0,4,0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
AutomationProperties.AccessibilityView="Raw"
FontFamily="Cascadia Code"
Foreground="{x:Bind mtu:Converters.ColorToBrush(ForegroundColor.Color), Mode=OneWay}"
Text="{x:Bind Name, Mode=OneWay}"
TextTrimming="WordEllipsis" />
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</local:SettingContainer>
SettingOverrideSource="{x:Bind Appearance.DarkColorSchemeNameOverrideSource, Mode=OneWay}"
Style="{StaticResource ExpanderSettingContainerStyleWithComplexPreview}">
<local:SettingContainer.CurrentValue>
<ComboBox Padding="4"
ItemsSource="{x:Bind Appearance.SchemesList, Mode=OneWay}"
SelectedItem="{x:Bind Appearance.CurrentColorScheme, Mode=TwoWay}"
Style="{StaticResource ComboBoxSettingStyle}">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="local:ColorSchemeViewModel">
<Grid Grid.Column="0"
Padding="8"
VerticalAlignment="Center"
Background="{x:Bind mtu:Converters.ColorToBrush(BackgroundColor.Color), Mode=OneWay}"
ColumnSpacing="1"
CornerRadius="2"
RowSpacing="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ContentControl Grid.Row="0"
Grid.Column="0"
Content="{x:Bind ColorEntryAt(0), Mode=OneWay}"
ContentTemplate="{StaticResource ColorChipTemplate}"
IsTabStop="False" />
<ContentControl Grid.Row="0"
Grid.Column="1"
Content="{x:Bind ColorEntryAt(1), Mode=OneWay}"
ContentTemplate="{StaticResource ColorChipTemplate}"
IsTabStop="False" />
<ContentControl Grid.Row="0"
Grid.Column="2"
Content="{x:Bind ColorEntryAt(2), Mode=OneWay}"
ContentTemplate="{StaticResource ColorChipTemplate}"
IsTabStop="False" />
<ContentControl Grid.Row="0"
Grid.Column="3"
Content="{x:Bind ColorEntryAt(3), Mode=OneWay}"
ContentTemplate="{StaticResource ColorChipTemplate}"
IsTabStop="False" />
<ContentControl Grid.Row="0"
Grid.Column="4"
Content="{x:Bind ColorEntryAt(4), Mode=OneWay}"
ContentTemplate="{StaticResource ColorChipTemplate}"
IsTabStop="False" />
<ContentControl Grid.Row="0"
Grid.Column="5"
Content="{x:Bind ColorEntryAt(5), Mode=OneWay}"
ContentTemplate="{StaticResource ColorChipTemplate}"
IsTabStop="False" />
<ContentControl Grid.Row="0"
Grid.Column="6"
Content="{x:Bind ColorEntryAt(6), Mode=OneWay}"
ContentTemplate="{StaticResource ColorChipTemplate}"
IsTabStop="False" />
<ContentControl Grid.Row="0"
Grid.Column="7"
Content="{x:Bind ColorEntryAt(7), Mode=OneWay}"
ContentTemplate="{StaticResource ColorChipTemplate}"
IsTabStop="False" />
<ContentControl Grid.Row="1"
Grid.Column="0"
Content="{x:Bind ColorEntryAt(8), Mode=OneWay}"
ContentTemplate="{StaticResource ColorChipTemplate}"
IsTabStop="False" />
<ContentControl Grid.Row="1"
Grid.Column="1"
Content="{x:Bind ColorEntryAt(9), Mode=OneWay}"
ContentTemplate="{StaticResource ColorChipTemplate}"
IsTabStop="False" />
<ContentControl Grid.Row="1"
Grid.Column="2"
Content="{x:Bind ColorEntryAt(10), Mode=OneWay}"
ContentTemplate="{StaticResource ColorChipTemplate}"
IsTabStop="False" />
<ContentControl Grid.Row="1"
Grid.Column="3"
Content="{x:Bind ColorEntryAt(11), Mode=OneWay}"
ContentTemplate="{StaticResource ColorChipTemplate}"
IsTabStop="False" />
<ContentControl Grid.Row="1"
Grid.Column="4"
Content="{x:Bind ColorEntryAt(12), Mode=OneWay}"
ContentTemplate="{StaticResource ColorChipTemplate}"
IsTabStop="False" />
<ContentControl Grid.Row="1"
Grid.Column="5"
Content="{x:Bind ColorEntryAt(13), Mode=OneWay}"
ContentTemplate="{StaticResource ColorChipTemplate}"
IsTabStop="False" />
<ContentControl Grid.Row="1"
Grid.Column="6"
Content="{x:Bind ColorEntryAt(14), Mode=OneWay}"
ContentTemplate="{StaticResource ColorChipTemplate}"
IsTabStop="False" />
<ContentControl Grid.Row="1"
Grid.Column="7"
Content="{x:Bind ColorEntryAt(15), Mode=OneWay}"
ContentTemplate="{StaticResource ColorChipTemplate}"
IsTabStop="False" />
<TextBlock Grid.RowSpan="2"
Grid.Column="8"
MaxWidth="192"
Margin="4,0,4,0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
AutomationProperties.AccessibilityView="Raw"
FontFamily="Cascadia Code"
Foreground="{x:Bind mtu:Converters.ColorToBrush(ForegroundColor.Color), Mode=OneWay}"
Text="{x:Bind Name, Mode=OneWay}"
TextTrimming="WordEllipsis" />
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</local:SettingContainer.CurrentValue>
<local:SettingContainer.Content>
<StackPanel>
<!-- Foreground Color -->
<local:SettingContainer x:Name="Foreground"
x:Uid="Profile_Foreground"
ClearSettingValue="{x:Bind Appearance.ClearForeground}"
CurrentValue="{x:Bind Appearance.ForegroundPreview, Mode=OneWay}"
CurrentValueTemplate="{StaticResource ColorPreviewTemplate}"
HasSettingValue="{x:Bind Appearance.HasForeground, Mode=OneWay}"
SettingOverrideSource="{x:Bind Appearance.ForegroundOverrideSource, Mode=OneWay}"
Style="{StaticResource ExpanderSettingContainerStyleWithComplexPreview}">
<local:NullableColorPicker x:Uid="Profile_Foreground_NullableColorPicker"
ColorSchemeVM="{x:Bind Appearance.CurrentColorScheme, Mode=OneWay}"
CurrentColor="{x:Bind Appearance.Foreground, Mode=TwoWay}"
NullColorPreview="{x:Bind Appearance.CurrentColorScheme.ForegroundColor.Color, Mode=OneWay}" />
</local:SettingContainer>
<!-- Background Color -->
<local:SettingContainer x:Name="Background"
x:Uid="Profile_Background"
ClearSettingValue="{x:Bind Appearance.ClearBackground}"
CurrentValue="{x:Bind Appearance.BackgroundPreview, Mode=OneWay}"
CurrentValueTemplate="{StaticResource ColorPreviewTemplate}"
HasSettingValue="{x:Bind Appearance.HasBackground, Mode=OneWay}"
SettingOverrideSource="{x:Bind Appearance.BackgroundOverrideSource, Mode=OneWay}"
Style="{StaticResource ExpanderSettingContainerStyleWithComplexPreview}">
<local:NullableColorPicker x:Uid="Profile_Background_NullableColorPicker"
ColorSchemeVM="{x:Bind Appearance.CurrentColorScheme, Mode=OneWay}"
CurrentColor="{x:Bind Appearance.Background, Mode=TwoWay}"
NullColorPreview="{x:Bind Appearance.CurrentColorScheme.BackgroundColor.Color, Mode=OneWay}" />
</local:SettingContainer>
<!-- Selection Background Color -->
<local:SettingContainer x:Name="SelectionBackground"
x:Uid="Profile_SelectionBackground"
ClearSettingValue="{x:Bind Appearance.ClearSelectionBackground}"
CurrentValue="{x:Bind Appearance.SelectionBackgroundPreview, Mode=OneWay}"
CurrentValueTemplate="{StaticResource ColorPreviewTemplate}"
HasSettingValue="{x:Bind Appearance.HasSelectionBackground, Mode=OneWay}"
SettingOverrideSource="{x:Bind Appearance.SelectionBackgroundOverrideSource, Mode=OneWay}"
Style="{StaticResource ExpanderSettingContainerStyleWithComplexPreview}">
<local:NullableColorPicker x:Uid="Profile_SelectionBackground_NullableColorPicker"
ColorSchemeVM="{x:Bind Appearance.CurrentColorScheme, Mode=OneWay}"
CurrentColor="{x:Bind Appearance.SelectionBackground, Mode=TwoWay}"
NullColorPreview="{x:Bind Appearance.CurrentColorScheme.SelectionBackgroundColor.Color, Mode=OneWay}" />
</local:SettingContainer>
</StackPanel>
</local:SettingContainer.Content>
</local:SettingContainer>
<!-- Font Face -->
<local:SettingContainer x:Name="FontFaceContainer"
x:Uid="Profile_FontFace"
@ -452,6 +502,21 @@
Text="{Binding ElementName=CursorHeightSlider, Path=Value, Mode=OneWay}" />
</Grid>
</local:SettingContainer>
<!-- Cursor Color -->
<local:SettingContainer x:Name="CursorColor"
x:Uid="Profile_CursorColor"
ClearSettingValue="{x:Bind Appearance.ClearCursorColor}"
CurrentValue="{x:Bind Appearance.CursorColorPreview, Mode=OneWay}"
CurrentValueTemplate="{StaticResource ColorPreviewTemplate}"
HasSettingValue="{x:Bind Appearance.HasCursorColor, Mode=OneWay}"
SettingOverrideSource="{x:Bind Appearance.CursorColorOverrideSource, Mode=OneWay}"
Style="{StaticResource ExpanderSettingContainerStyleWithComplexPreview}">
<local:NullableColorPicker x:Uid="Profile_CursorColor_NullableColorPicker"
ColorSchemeVM="{x:Bind Appearance.CurrentColorScheme, Mode=OneWay}"
CurrentColor="{x:Bind Appearance.CursorColor, Mode=TwoWay}"
NullColorPreview="{x:Bind Appearance.CurrentColorScheme.CursorColor.Color, Mode=OneWay}" />
</local:SettingContainer>
</StackPanel>
<!-- Grouping: Background -->

View File

@ -69,6 +69,11 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
return hstring{ fmt::format(FMT_COMPILE(L"{} RGB({}, {}, {})"), _Name, _Color.R, _Color.G, _Color.B) };
}
hstring NameWithHexCode() const
{
return hstring{ fmt::format(FMT_COMPILE(L"{} #{:02X}{:02X}{:02X}"), _Name, _Color.R, _Color.G, _Color.B) };
}
til::property_changed_event PropertyChanged;
WINRT_OBSERVABLE_PROPERTY(Windows::UI::Color, Color, PropertyChanged.raise);
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, Name, PropertyChanged.raise);

View File

@ -36,5 +36,6 @@ namespace Microsoft.Terminal.Settings.Editor
IInspectable Tag;
Windows.UI.Color Color;
String AccessibleName { get; };
String NameWithHexCode { get; };
}
}

View File

@ -4,6 +4,8 @@
-->
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Microsoft.Terminal.Settings.Editor"
xmlns:mtu="using:Microsoft.Terminal.UI"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls">
<!-- Merge SettingContainerStyle here to give every page access to the SettingContainer -->
@ -60,6 +62,9 @@
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
<local:ColorToBrushConverter x:Key="ColorToBrushConverter" />
<local:ColorToStringConverter x:Key="ColorToStringConverter" />
<Color x:Key="DeleteButtonColor">Firebrick</Color>
<x:Double x:Key="StandardIconSize">14.0</x:Double>
@ -69,6 +74,32 @@
<x:Double x:Key="StandardControlMaxWidth">1000</x:Double>
<Thickness x:Key="SettingStackMargin">13,0,13,48</Thickness>
<!-- We're purposefully not providing a DataType here.
This is expected to be used with an IReference<Microsoft::Terminal::Core::Color>.
We're doing all the data type handling in the used converters.-->
<DataTemplate x:Key="ColorPreviewTemplate">
<Grid ColumnSpacing="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Rectangle Grid.Column="0"
Width="13"
Height="13"
VerticalAlignment="Center"
Fill="{Binding Converter={StaticResource ColorToBrushConverter}}"
RadiusX="2"
RadiusY="2" />
<TextBlock Grid.Column="1"
Margin="0,0,0,0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
FontFamily="Segoe UI, Segoe Fluent Icons, Segoe MDL2 Assets"
Style="{StaticResource SettingsPageItemDescriptionStyle}"
Text="{Binding Converter={StaticResource ColorToStringConverter}}" />
</Grid>
</DataTemplate>
<!--
This is for styling the entire items control used on the
color schemes page
@ -399,6 +430,91 @@
</Setter>
</Style>
<Style x:Key="ColorToggleButtonStyle"
TargetType="ToggleButton">
<Setter Property="BackgroundSizing" Value="OuterBorderEdge" />
<Setter Property="BorderBrush" Value="{ThemeResource ToggleButtonBorderThemeBrush}" />
<Setter Property="BorderThickness" Value="{ThemeResource ToggleButtonBorderThemeThickness}" />
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}" />
<Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}" />
<Setter Property="FocusVisualMargin" Value="-3" />
<Setter Property="Width" Value="32" />
<Setter Property="Height" Value="32" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Grid>
<Border x:Name="ColorButtonBorder"
BorderBrush="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}">
<Grid>
<Border x:Name="ColorButtonBackground"
Background="{TemplateBinding Background}"
BorderThickness="0" />
<!-- We have to use Binding here! We're trying to do a template binding with a converter, so we have to use the verbose syntax. -->
<FontIcon Margin="0,0,-1,-1"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
FontFamily="Segoe Fluent Icons"
FontSize="12"
Foreground="{TemplateBinding BorderBrush}"
Glyph="&#xE73D;"
Visibility="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsChecked, Mode=OneWay}" />
</Grid>
</Border>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled" />
<VisualState x:Name="PointerOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ColorButtonBackground"
Storyboard.TargetProperty="Opacity">
<DiscreteObjectKeyFrame KeyTime="0"
Value="0.7" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ColorButtonBorder"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0"
Value="{Binding Path=BorderBrush, RelativeSource={RelativeSource TemplatedParent}}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ColorButtonBackground"
Storyboard.TargetProperty="Opacity">
<DiscreteObjectKeyFrame KeyTime="0"
Value="0.5" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ColorButtonBorder"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0"
Value="{Binding Path=BorderBrush, RelativeSource={RelativeSource TemplatedParent}}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Checked">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ColorButtonBorder"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0"
Value="{Binding Path=BorderBrush, RelativeSource={RelativeSource TemplatedParent}}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- Slider-Related Styling -->
<Style x:Key="SliderValueLabelStyle"
TargetType="TextBlock">

View File

@ -752,9 +752,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
if (auto menuItem{ weakMenuItem.get() })
{
const auto& tag{ menuItem.Tag().as<Editor::ProfileViewModel>() };
if (args.PropertyName() == L"Icon")
if (args.PropertyName() == L"Icon" || args.PropertyName() == L"EvaluatedIcon")
{
menuItem.Icon(UI::IconPathConverter::IconWUX(tag.Icon()));
menuItem.Icon(UI::IconPathConverter::IconWUX(tag.EvaluatedIcon()));
}
else if (args.PropertyName() == L"Name")
{

View File

@ -57,6 +57,10 @@
<DependentUpon>ColorSchemes.xaml</DependentUpon>
<SubType>Code</SubType>
</ClInclude>
<ClInclude Include="NullableColorPicker.h">
<DependentUpon>NullableColorPicker.xaml</DependentUpon>
<SubType>Code</SubType>
</ClInclude>
<ClInclude Include="EditColorScheme.h">
<DependentUpon>EditColorScheme.xaml</DependentUpon>
<SubType>Code</SubType>
@ -88,6 +92,11 @@
<DependentUpon>ActionsViewModel.idl</DependentUpon>
<SubType>Code</SubType>
</ClInclude>
<ClInclude Include="SegoeFluentIconList.h" />
<ClInclude Include="TerminalColorConverters.h">
<DependentUpon>TerminalColorConverters.idl</DependentUpon>
<SubType>Code</SubType>
</ClInclude>
<ClInclude Include="ColorSchemeViewModel.h">
<DependentUpon>ColorSchemeViewModel.idl</DependentUpon>
<SubType>Code</SubType>
@ -166,6 +175,9 @@
<Page Include="ColorSchemes.xaml">
<SubType>Designer</SubType>
</Page>
<Page Include="NullableColorPicker.xaml">
<SubType>Designer</SubType>
</Page>
<Page Include="EditColorScheme.xaml">
<SubType>Designer</SubType>
</Page>
@ -227,6 +239,10 @@
<DependentUpon>ColorSchemes.xaml</DependentUpon>
<SubType>Code</SubType>
</ClCompile>
<ClCompile Include="NullableColorPicker.cpp">
<DependentUpon>NullableColorPicker.xaml</DependentUpon>
<SubType>Code</SubType>
</ClCompile>
<ClCompile Include="EditColorScheme.cpp">
<DependentUpon>EditColorScheme.xaml</DependentUpon>
<SubType>Code</SubType>
@ -261,6 +277,10 @@
<DependentUpon>ActionsViewModel.idl</DependentUpon>
<SubType>Code</SubType>
</ClCompile>
<ClCompile Include="TerminalColorConverters.cpp">
<DependentUpon>TerminalColorConverters.idl</DependentUpon>
<SubType>Code</SubType>
</ClCompile>
<ClCompile Include="ColorSchemeViewModel.cpp">
<DependentUpon>ColorSchemeViewModel.idl</DependentUpon>
<SubType>Code</SubType>
@ -343,6 +363,10 @@
<DependentUpon>ColorSchemes.xaml</DependentUpon>
<SubType>Code</SubType>
</Midl>
<Midl Include="NullableColorPicker.idl">
<DependentUpon>NullableColorPicker.xaml</DependentUpon>
<SubType>Code</SubType>
</Midl>
<Midl Include="EditColorScheme.idl">
<DependentUpon>EditColorScheme.xaml</DependentUpon>
<SubType>Code</SubType>
@ -376,6 +400,7 @@
</Midl>
<Midl Include="ProfileViewModel.idl" />
<Midl Include="ActionsViewModel.idl" />
<Midl Include="TerminalColorConverters.idl" />
<Midl Include="ColorSchemeViewModel.idl" />
<Midl Include="ColorSchemesPageViewModel.idl" />
<Midl Include="RenderingViewModel.idl" />
@ -439,7 +464,6 @@
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalConnection\TerminalConnection.vcxproj">
<Private>false</Private>
</ProjectReference>
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalControl\dll\TerminalControl.vcxproj">
<!-- Private:false and ReferenceOutputAssembly:false, in combination with
the manual reference to TerminalControl.winmd below make sure that this
@ -452,7 +476,6 @@
<Private>false</Private>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<!-- Manually add a reference to TerminalControl here. We need this so
MDMERGE will know where the TermControl types are defined. However, we need
@ -471,17 +494,13 @@
<CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
</Reference>
</ItemGroup>
<ItemDefinitionGroup>
<Link>
<AdditionalDependencies>shell32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
<!-- This -must- go after cppwinrt.build.post.props because that includes many VS-provided props including appcontainer.common.props, which stomps on what cppwinrt.targets did. -->
<Import Project="$(OpenConsoleDir)src\common.nugetversions.targets" />
<Import Project="$(SolutionDir)build\rules\CollectWildcardResources.targets" />
</Project>
</Project>

View File

@ -2,6 +2,7 @@
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Natvis Include="$(SolutionDir)tools\ConsoleTypes.natvis" />
<Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natvis" />
</ItemGroup>
<ItemGroup>
<PRIResource Include="Resources\en-US\Resources.resw" />
@ -15,6 +16,7 @@
<ClInclude Include="pch.h" />
<ClInclude Include="Utils.h" />
<ClInclude Include="PreviewConnection.h" />
<ClInclude Include="SegoeFluentIconList.h" />
</ItemGroup>
<ItemGroup>
<Midl Include="ProfileViewModel.idl" />
@ -27,10 +29,10 @@
<Midl Include="LaunchViewModel.idl" />
<Midl Include="EnumEntry.idl" />
<Midl Include="SettingContainer.idl" />
<Midl Include="TerminalColorConverters.idl" />
<Midl Include="NewTabMenuViewModel.idl" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<None Include="$(ProjectName).def" />
</ItemGroup>
<ItemGroup>
@ -50,6 +52,7 @@
<Page Include="SettingContainerStyle.xaml" />
<Page Include="AddProfile.xaml" />
<Page Include="KeyChordListener.xaml" />
<Page Include="NullableColorPicker.xaml" />
<Page Include="NewTabMenu.xaml" />
</ItemGroup>
</Project>
</Project>

View File

@ -335,7 +335,8 @@
<!-- Add Profile -->
<local:SettingContainer x:Uid="NewTabMenu_AddProfile"
FontIconGlyph="&#xE756;">
FontIconGlyph="&#xE756;"
Style="{StaticResource SettingContainerWithIcon}">
<StackPanel Orientation="Horizontal"
Spacing="5">
@ -383,7 +384,8 @@
<!-- Add Separator -->
<local:SettingContainer x:Uid="NewTabMenu_AddSeparator"
FontIconGlyph="&#xE76f;">
FontIconGlyph="&#xE76f;"
Style="{StaticResource SettingContainerWithIcon}">
<Button x:Name="AddSeparatorButton"
x:Uid="NewTabMenu_AddSeparatorButton"
HorizontalAlignment="Stretch"
@ -398,7 +400,8 @@
<!-- Add Folder -->
<local:SettingContainer x:Uid="NewTabMenu_AddFolder"
FontIconGlyph="&#xF12B;">
FontIconGlyph="&#xF12B;"
Style="{StaticResource SettingContainerWithIcon}">
<StackPanel Orientation="Horizontal"
Spacing="5">
<TextBox x:Name="FolderNameTextBox"
@ -424,7 +427,7 @@
<!-- Add Match Profiles -->
<local:SettingContainer x:Uid="NewTabMenu_AddMatchProfiles"
FontIconGlyph="&#xE748;"
Style="{StaticResource ExpanderSettingContainerStyle}">
Style="{StaticResource ExpanderSettingContainerStyleWithIcon}">
<StackPanel Spacing="10">
<TextBox x:Uid="NewTabMenu_AddMatchProfiles_Name"
Text="{x:Bind ViewModel.ProfileMatcherName, Mode=TwoWay}" />
@ -448,7 +451,8 @@
<!-- Add Remaining Profiles -->
<local:SettingContainer x:Uid="NewTabMenu_AddRemainingProfiles"
FontIconGlyph="&#xE902;">
FontIconGlyph="&#xE902;"
Style="{StaticResource SettingContainerWithIcon}">
<Button x:Name="AddRemainingProfilesButton"
x:Uid="NewTabMenu_AddRemainingProfilesButton"
HorizontalAlignment="Stretch"

View File

@ -0,0 +1,226 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "NullableColorPicker.h"
#include "NullableColorPicker.g.cpp"
#include <LibraryResources.h>
using namespace winrt;
using namespace winrt::Windows::UI;
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Xaml::Navigation;
using namespace winrt::Windows::UI::Xaml::Controls;
using namespace winrt::Windows::UI::Xaml::Media;
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::Foundation::Collections;
using namespace winrt::Microsoft::UI::Xaml::Controls;
namespace winrt
{
namespace MUX = Microsoft::UI::Xaml;
namespace WUX = Windows::UI::Xaml;
}
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
static constexpr bool equalsColor(Windows::UI::Color a, Microsoft::Terminal::Core::Color b)
{
return a.R == b.R && a.G == b.G && a.B == b.B;
}
DependencyProperty NullableColorPicker::_ColorSchemeVMProperty{ nullptr };
DependencyProperty NullableColorPicker::_CurrentColorProperty{ nullptr };
DependencyProperty NullableColorPicker::_ShowNullColorButtonProperty{ nullptr };
DependencyProperty NullableColorPicker::_NullColorButtonLabelProperty{ nullptr };
DependencyProperty NullableColorPicker::_NullColorPreviewProperty{ nullptr };
NullableColorPicker::NullableColorPicker()
{
_InitializeProperties();
InitializeComponent();
}
void NullableColorPicker::_InitializeProperties()
{
// Initialize any dependency properties here.
// This performs a lazy load on these properties, instead of
// initializing them when the DLL loads.
if (!_ColorSchemeVMProperty)
{
_ColorSchemeVMProperty =
DependencyProperty::Register(
L"ColorSchemeVM",
xaml_typename<Editor::ColorSchemeViewModel>(),
xaml_typename<Editor::NullableColorPicker>(),
PropertyMetadata{ nullptr });
}
if (!_CurrentColorProperty)
{
_CurrentColorProperty =
DependencyProperty::Register(
L"CurrentColor",
xaml_typename<Windows::Foundation::IReference<Microsoft::Terminal::Core::Color>>(),
xaml_typename<Editor::NullableColorPicker>(),
PropertyMetadata{ nullptr, PropertyChangedCallback{ &NullableColorPicker::_OnCurrentColorValueChanged } });
}
if (!_ShowNullColorButtonProperty)
{
_ShowNullColorButtonProperty =
DependencyProperty::Register(
L"ShowNullColorButton",
xaml_typename<bool>(),
xaml_typename<Editor::NullableColorPicker>(),
PropertyMetadata{ box_value(true) });
}
if (!_NullColorButtonLabelProperty)
{
_NullColorButtonLabelProperty =
DependencyProperty::Register(
L"NullColorButtonLabel",
xaml_typename<hstring>(),
xaml_typename<Editor::NullableColorPicker>(),
PropertyMetadata{ nullptr });
}
if (!_NullColorPreviewProperty)
{
_NullColorPreviewProperty =
DependencyProperty::Register(
L"NullColorPreview",
xaml_typename<Windows::UI::Color>(),
xaml_typename<Editor::NullableColorPicker>(),
PropertyMetadata{ box_value(Windows::UI::Colors::Transparent()) });
}
}
void NullableColorPicker::_OnCurrentColorValueChanged(const DependencyObject& d, const DependencyPropertyChangedEventArgs& /*e*/)
{
const auto& obj{ d.try_as<Editor::NullableColorPicker>() };
get_self<NullableColorPicker>(obj)->_UpdateColorChips();
}
void NullableColorPicker::_UpdateColorChips()
{
const auto& currentColor = CurrentColor();
for (const auto& colorChip : _colorChips)
{
const auto& chipColor = colorChip.DataContext().as<Editor::ColorTableEntry>().Color();
colorChip.IsChecked(currentColor ?
equalsColor(chipColor, currentColor.Value()) :
false);
}
}
SolidColorBrush NullableColorPicker::CalculateBorderBrush(const Windows::UI::Color& color)
{
static constexpr auto isColorLight = [](const winrt::Windows::UI::Color& clr) -> bool {
return (((5 * clr.G) + (2 * clr.R) + clr.B) > (8 * 128));
};
if (isColorLight(color))
{
return SolidColorBrush(Colors::Black());
}
else
{
return SolidColorBrush(Colors::White());
}
}
void NullableColorPicker::ColorChip_Clicked(const IInspectable& sender, const RoutedEventArgs& /*args*/)
{
const auto& btn = sender.as<Windows::UI::Xaml::Controls::Primitives::ToggleButton>();
const auto& colorEntryColor = btn.DataContext().as<Editor::ColorTableEntry>().Color();
const Microsoft::Terminal::Core::Color terminalColor{ colorEntryColor.R, colorEntryColor.G, colorEntryColor.B, colorEntryColor.A };
CurrentColor(terminalColor);
btn.IsChecked(true);
}
void NullableColorPicker::ColorChip_DataContextChanged(const IInspectable& sender, const DataContextChangedEventArgs& args)
{
if (const auto& toggleBtn = sender.try_as<Controls::Primitives::ToggleButton>())
{
if (const auto& currentColor = CurrentColor())
{
const auto& currentColorVal = currentColor.Value();
const auto& newChipColor = args.NewValue().as<Editor::ColorTableEntry>().Color();
toggleBtn.IsChecked(equalsColor(newChipColor, currentColorVal));
}
}
}
bool NullableColorPicker::IsNull(IReference<Microsoft::Terminal::Core::Color> color)
{
return color == nullptr;
}
void NullableColorPicker::NullColorButton_Clicked(const IInspectable& /*sender*/, const RoutedEventArgs& /*args*/)
{
CurrentColor(nullptr);
}
safe_void_coroutine NullableColorPicker::MoreColors_Clicked(const IInspectable& /*sender*/, const RoutedEventArgs& /*args*/)
{
co_await ColorPickerDialog().ShowAsync();
}
void NullableColorPicker::ColorPickerDialog_Opened(const IInspectable& /*sender*/, const ContentDialogOpenedEventArgs& /*args*/)
{
// Initialize color picker with current color
if (CurrentColor())
{
const auto& terminalColor = CurrentColor().Value();
const Windows::UI::Color winuiColor{
.A = terminalColor.A,
.R = terminalColor.R,
.G = terminalColor.G,
.B = terminalColor.B
};
ColorPickerControl().Color(winuiColor);
}
else
{
// No current color (null), use the deduced value for null
ColorPickerControl().Color(NullColorPreview());
}
}
void NullableColorPicker::ColorPickerDialog_PrimaryButtonClick(const IInspectable& /*sender*/, const ContentDialogButtonClickEventArgs& /*args*/)
{
const auto& selectedColor = ColorPickerControl().Color();
const Microsoft::Terminal::Core::Color terminalColor{ selectedColor.R, selectedColor.G, selectedColor.B, selectedColor.A };
CurrentColor(terminalColor);
}
void NullableColorPicker::ColorChip_Loaded(const IInspectable& sender, const RoutedEventArgs& /*args*/)
{
if (const auto& toggleBtn = sender.try_as<Controls::Primitives::ToggleButton>())
{
if (const auto& currentColor = CurrentColor())
{
const auto& currentColorVal = currentColor.Value();
const auto& chipColor = toggleBtn.DataContext().as<Editor::ColorTableEntry>().Color();
if (equalsColor(chipColor, currentColorVal))
{
toggleBtn.IsChecked(true);
}
}
_colorChips.push_back(toggleBtn);
}
}
void NullableColorPicker::ColorChip_Unloaded(const IInspectable& sender, const RoutedEventArgs& /*args*/)
{
if (const auto& toggleBtn = sender.try_as<Controls::Primitives::ToggleButton>())
{
for (auto it = _colorChips.begin(); it != _colorChips.end(); ++it)
{
if (*it == toggleBtn)
{
_colorChips.erase(it);
break;
}
}
}
}
}

View File

@ -0,0 +1,49 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include "NullableColorPicker.g.h"
#include "Utils.h"
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
struct NullableColorPicker : public HasScrollViewer<NullableColorPicker>, NullableColorPickerT<NullableColorPicker>
{
public:
NullableColorPicker();
static winrt::Windows::UI::Xaml::Media::SolidColorBrush CalculateBorderBrush(const Windows::UI::Color& color);
static bool IsNull(Windows::Foundation::IReference<Microsoft::Terminal::Core::Color> color);
void ColorChip_Loaded(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::RoutedEventArgs& args);
void ColorChip_Unloaded(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::RoutedEventArgs& args);
void ColorChip_Clicked(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::RoutedEventArgs& args);
void ColorChip_DataContextChanged(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::DataContextChangedEventArgs& args);
void NullColorButton_Clicked(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::RoutedEventArgs& args);
safe_void_coroutine MoreColors_Clicked(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::RoutedEventArgs& args);
void ColorPickerDialog_Opened(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::Controls::ContentDialogOpenedEventArgs& args);
void ColorPickerDialog_PrimaryButtonClick(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::Controls::ContentDialogButtonClickEventArgs& args);
DEPENDENCY_PROPERTY(Editor::ColorSchemeViewModel, ColorSchemeVM);
DEPENDENCY_PROPERTY(Windows::Foundation::IReference<Microsoft::Terminal::Core::Color>, CurrentColor);
DEPENDENCY_PROPERTY(bool, ShowNullColorButton);
DEPENDENCY_PROPERTY(hstring, NullColorButtonLabel);
DEPENDENCY_PROPERTY(Windows::UI::Color, NullColorPreview);
private:
static void _InitializeProperties();
static void _OnCurrentColorValueChanged(const Windows::UI::Xaml::DependencyObject& d, const Windows::UI::Xaml::DependencyPropertyChangedEventArgs& e);
void _UpdateColorChips();
std::vector<Windows::UI::Xaml::Controls::Primitives::ToggleButton> _colorChips;
};
}
namespace winrt::Microsoft::Terminal::Settings::Editor::factory_implementation
{
BASIC_FACTORY(NullableColorPicker);
}

View File

@ -0,0 +1,30 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import "ColorSchemeViewModel.idl";
namespace Microsoft.Terminal.Settings.Editor
{
[default_interface] runtimeclass NullableColorPicker : Windows.UI.Xaml.Controls.UserControl
{
NullableColorPicker();
ColorSchemeViewModel ColorSchemeVM;
static Windows.UI.Xaml.DependencyProperty ColorSchemeVMProperty { get; };
Windows.Foundation.IReference<Microsoft.Terminal.Core.Color> CurrentColor;
static Windows.UI.Xaml.DependencyProperty CurrentColorProperty { get; };
Boolean ShowNullColorButton;
static Windows.UI.Xaml.DependencyProperty ShowNullColorButtonProperty { get; };
String NullColorButtonLabel;
static Windows.UI.Xaml.DependencyProperty NullColorButtonLabelProperty { get; };
Windows.UI.Color NullColorPreview;
static Windows.UI.Xaml.DependencyProperty NullColorPreviewProperty { get; };
static Windows.UI.Xaml.Media.SolidColorBrush CalculateBorderBrush(Windows.UI.Color color);
static Boolean IsNull(Windows.Foundation.IReference<Microsoft.Terminal.Core.Color> color);
}
}

View File

@ -0,0 +1,155 @@
<!--
Copyright (c) Microsoft Corporation. All rights reserved. Licensed under
the MIT License. See LICENSE in the project root for license information.
-->
<UserControl x:Class="Microsoft.Terminal.Settings.Editor.NullableColorPicker"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:Microsoft.Terminal.Settings.Editor"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:model="using:Microsoft.Terminal.Settings.Model"
xmlns:mtu="using:Microsoft.Terminal.UI"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
mc:Ignorable="d">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="CommonResources.xaml" />
</ResourceDictionary.MergedDictionaries>
<DataTemplate x:Key="ColorPreviewChipTemplate"
x:DataType="local:ColorTableEntry">
<ToggleButton Margin="0,0,4,4"
AutomationProperties.Name="{x:Bind NameWithHexCode}"
Background="{x:Bind mtu:Converters.ColorToBrush(Color)}"
BorderBrush="{x:Bind local:NullableColorPicker.CalculateBorderBrush(Color), Mode=OneWay}"
BorderThickness="2"
Click="ColorChip_Clicked"
DataContextChanged="ColorChip_DataContextChanged"
Loaded="ColorChip_Loaded"
Style="{StaticResource ColorToggleButtonStyle}"
ToolTipService.ToolTip="{x:Bind NameWithHexCode}"
Unloaded="ColorChip_Unloaded" />
</DataTemplate>
<DataTemplate x:Key="ColorSchemeTemplate"
x:DataType="local:ColorSchemeViewModel">
<VariableSizedWrapGrid Name="ColorSchemeGrid"
HorizontalAlignment="Left"
MaximumRowsOrColumns="4"
Orientation="Horizontal"
XYFocusKeyboardNavigation="Enabled">
<ContentControl Content="{x:Bind ColorEntryAt(0), Mode=OneWay}"
ContentTemplate="{StaticResource ColorPreviewChipTemplate}"
IsTabStop="False" />
<ContentControl Content="{x:Bind ColorEntryAt(1), Mode=OneWay}"
ContentTemplate="{StaticResource ColorPreviewChipTemplate}"
IsTabStop="False" />
<ContentControl Content="{x:Bind ColorEntryAt(2), Mode=OneWay}"
ContentTemplate="{StaticResource ColorPreviewChipTemplate}"
IsTabStop="False" />
<ContentControl Content="{x:Bind ColorEntryAt(3), Mode=OneWay}"
ContentTemplate="{StaticResource ColorPreviewChipTemplate}"
IsTabStop="False" />
<ContentControl Content="{x:Bind ColorEntryAt(4), Mode=OneWay}"
ContentTemplate="{StaticResource ColorPreviewChipTemplate}"
IsTabStop="False" />
<ContentControl Content="{x:Bind ColorEntryAt(5), Mode=OneWay}"
ContentTemplate="{StaticResource ColorPreviewChipTemplate}"
IsTabStop="False" />
<ContentControl Content="{x:Bind ColorEntryAt(6), Mode=OneWay}"
ContentTemplate="{StaticResource ColorPreviewChipTemplate}"
IsTabStop="False" />
<ContentControl Content="{x:Bind ColorEntryAt(7), Mode=OneWay}"
ContentTemplate="{StaticResource ColorPreviewChipTemplate}"
IsTabStop="False" />
<ContentControl Content="{x:Bind ColorEntryAt(8), Mode=OneWay}"
ContentTemplate="{StaticResource ColorPreviewChipTemplate}"
IsTabStop="False" />
<ContentControl Content="{x:Bind ColorEntryAt(9), Mode=OneWay}"
ContentTemplate="{StaticResource ColorPreviewChipTemplate}"
IsTabStop="False" />
<ContentControl Content="{x:Bind ColorEntryAt(10), Mode=OneWay}"
ContentTemplate="{StaticResource ColorPreviewChipTemplate}"
IsTabStop="False" />
<ContentControl Content="{x:Bind ColorEntryAt(11), Mode=OneWay}"
ContentTemplate="{StaticResource ColorPreviewChipTemplate}"
IsTabStop="False" />
<ContentControl Content="{x:Bind ColorEntryAt(12), Mode=OneWay}"
ContentTemplate="{StaticResource ColorPreviewChipTemplate}"
IsTabStop="False" />
<ContentControl Content="{x:Bind ColorEntryAt(13), Mode=OneWay}"
ContentTemplate="{StaticResource ColorPreviewChipTemplate}"
IsTabStop="False" />
<ContentControl Content="{x:Bind ColorEntryAt(14), Mode=OneWay}"
ContentTemplate="{StaticResource ColorPreviewChipTemplate}"
IsTabStop="False" />
<ContentControl Content="{x:Bind ColorEntryAt(15), Mode=OneWay}"
ContentTemplate="{StaticResource ColorPreviewChipTemplate}"
IsTabStop="False" />
</VariableSizedWrapGrid>
</DataTemplate>
</ResourceDictionary>
</UserControl.Resources>
<Grid ColumnSpacing="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ContentDialog x:Name="ColorPickerDialog"
x:Uid="NullableColorPicker_ColorPickerContentDialog"
DefaultButton="Primary"
Opened="ColorPickerDialog_Opened"
PrimaryButtonClick="ColorPickerDialog_PrimaryButtonClick"
TabFocusNavigation="Cycle">
<muxc:ColorPicker x:Name="ColorPickerControl"
Margin="0,0,0,-40"
ColorSpectrumShape="Box"
IsAlphaEnabled="False"
IsAlphaSliderVisible="True"
IsAlphaTextInputVisible="True"
IsColorChannelTextInputVisible="False"
IsColorSliderVisible="False"
IsHexInputVisible="False"
IsMoreButtonVisible="False"
Orientation="Horizontal" />
</ContentDialog>
<ContentPresenter Grid.Column="0"
Content="{x:Bind ColorSchemeVM, Mode=OneWay}"
ContentTemplate="{StaticResource ColorSchemeTemplate}" />
<StackPanel Grid.Column="1"
Spacing="5">
<ToggleButton AutomationProperties.Name="{x:Bind NullColorButtonLabel}"
Click="NullColorButton_Clicked"
IsChecked="{x:Bind IsNull(CurrentColor), Mode=OneWay}">
<Grid ColumnSpacing="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Border Grid.Column="0"
Width="20"
Height="20"
Background="{x:Bind mtu:Converters.ColorToBrush(NullColorPreview), Mode=OneWay}"
BorderThickness="1"
CornerRadius="{ThemeResource ControlCornerRadius}" />
<TextBlock Grid.Column="1"
Text="{x:Bind NullColorButtonLabel}" />
</Grid>
</ToggleButton>
<Button x:Uid="NullableColorPicker_MoreColorsButton"
HorizontalAlignment="Stretch"
Click="MoreColors_Clicked" />
</StackPanel>
</Grid>
</UserControl>

View File

@ -10,6 +10,7 @@
#include <LibraryResources.h>
#include "../WinRTUtils/inc/Utils.h"
#include "../../renderer/base/FontCache.h"
#include "SegoeFluentIconList.h"
using namespace winrt::Windows::UI::Text;
using namespace winrt::Windows::UI::Xaml;
@ -26,6 +27,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
Windows::Foundation::Collections::IObservableVector<Editor::Font> ProfileViewModel::_MonospaceFontList{ nullptr };
Windows::Foundation::Collections::IObservableVector<Editor::Font> ProfileViewModel::_FontList{ nullptr };
Windows::Foundation::Collections::IVector<IInspectable> ProfileViewModel::_BuiltInIcons{ nullptr };
static constexpr std::wstring_view HideIconValue{ L"none" };
@ -41,6 +43,17 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
INITIALIZE_BINDABLE_ENUM_SETTING(ScrollState, ScrollbarState, winrt::Microsoft::Terminal::Control::ScrollbarState, L"Profile_ScrollbarVisibility", L"Content");
INITIALIZE_BINDABLE_ENUM_SETTING(PathTranslationStyle, PathTranslationStyle, winrt::Microsoft::Terminal::Control::PathTranslationStyle, L"Profile_PathTranslationStyle", L"Content");
// set up IconTypes
std::vector<IInspectable> iconTypes;
iconTypes.reserve(4);
iconTypes.emplace_back(make<EnumEntry>(RS_(L"Profile_IconTypeNone"), box_value(IconType::None)));
iconTypes.emplace_back(make<EnumEntry>(RS_(L"Profile_IconTypeFontIcon"), box_value(IconType::FontIcon)));
iconTypes.emplace_back(make<EnumEntry>(RS_(L"Profile_IconTypeEmoji"), box_value(IconType::Emoji)));
iconTypes.emplace_back(make<EnumEntry>(RS_(L"Profile_IconTypeImage"), box_value(IconType::Image)));
_IconTypes = winrt::single_threaded_vector<IInspectable>(std::move(iconTypes));
_DeduceCurrentIconType();
_DeduceCurrentBuiltInIcon();
// Add a property changed handler to our own property changed event.
// This propagates changes from the settings model to anybody listening to our
// unique view model members.
@ -75,7 +88,29 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
}
else if (viewModelProperty == L"Icon")
{
_NotifyChanges(L"HideIcon");
// _DeduceCurrentIconType() ends with a "CurrentIconType" notification
// so we don't need to call _UpdateIconPreview() here
_DeduceCurrentIconType();
}
else if (viewModelProperty == L"CurrentIconType")
{
// "Using*" handles the visibility of the IconType-related UI.
// The others propagate the rendered icon into a preview (i.e. nav view, container item)
_NotifyChanges(L"UsingNoIcon",
L"UsingBuiltInIcon",
L"UsingEmojiIcon",
L"UsingImageIcon",
L"LocalizedIcon",
L"IconPreview",
L"EvaluatedIcon");
}
else if (viewModelProperty == L"CurrentBuiltInIcon")
{
Icon(unbox_value<hstring>(_CurrentBuiltInIcon.as<Editor::EnumEntry>().EnumValue()));
}
else if (viewModelProperty == L"CurrentEmojiIcon")
{
Icon(CurrentEmojiIcon());
}
else if (viewModelProperty == L"PathTranslationStyle")
{
@ -107,6 +142,61 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
_defaultAppearanceViewModel.IsDefault(true);
}
void ProfileViewModel::_UpdateBuiltInIcons()
{
std::vector<IInspectable> builtInIcons;
for (auto& [val, name] : s_SegoeFluentIcons)
{
builtInIcons.emplace_back(make<EnumEntry>(hstring{ name }, box_value(val)));
}
_BuiltInIcons = single_threaded_vector<IInspectable>(std::move(builtInIcons));
}
void ProfileViewModel::_DeduceCurrentIconType()
{
const auto& profileIcon = _profile.Icon();
if (profileIcon == HideIconValue)
{
_currentIconType = _IconTypes.GetAt(0);
}
else if (L"\uE700" <= profileIcon && profileIcon <= L"\uF8B3")
{
_currentIconType = _IconTypes.GetAt(1);
_DeduceCurrentBuiltInIcon();
}
else if (profileIcon.size() <= 2)
{
// We already did a range check for MDL2 Assets in the previous one,
// so if we're out of that range but still short, assume we're an emoji
_currentIconType = _IconTypes.GetAt(2);
}
else
{
_currentIconType = _IconTypes.GetAt(3);
}
_NotifyChanges(L"CurrentIconType");
}
void ProfileViewModel::_DeduceCurrentBuiltInIcon()
{
if (!_BuiltInIcons)
{
_UpdateBuiltInIcons();
}
const auto& profileIcon = Icon();
for (uint32_t i = 0; i < _BuiltInIcons.Size(); i++)
{
const auto& builtIn = _BuiltInIcons.GetAt(i);
if (profileIcon == unbox_value<hstring>(builtIn.as<Editor::EnumEntry>().EnumValue()))
{
_CurrentBuiltInIcon = builtIn;
return;
}
}
_CurrentBuiltInIcon = _BuiltInIcons.GetAt(0);
_NotifyChanges(L"CurrentBuiltInIcon");
}
void ProfileViewModel::LeftPadding(double value) noexcept
{
const hstring& padding = _GetNewPadding(PaddingDirection::Left, value);
@ -483,26 +573,103 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
}
}
bool ProfileViewModel::HideIcon()
winrt::hstring ProfileViewModel::LocalizedIcon() const
{
return Icon() == HideIconValue;
if (_currentIconType && unbox_value<IconType>(_currentIconType.as<Editor::EnumEntry>().EnumValue()) == IconType::None)
{
return RS_(L"Profile_IconTypeNone");
}
return Icon();
}
void ProfileViewModel::HideIcon(const bool hide)
Windows::UI::Xaml::Controls::IconElement ProfileViewModel::IconPreview() const
{
if (hide)
// IconWUX sets the icon width/height to 32 by default
auto icon = Microsoft::Terminal::UI::IconPathConverter::IconWUX(EvaluatedIcon());
icon.Width(16);
icon.Height(16);
return icon;
}
void ProfileViewModel::CurrentIconType(const Windows::Foundation::IInspectable& value)
{
if (_currentIconType != value)
{
// Stash the current value of Icon. If the user
// checks and un-checks the "Hide Icon" checkbox, we want
// the path that we display in the text box to remain unchanged.
_lastIcon = Icon();
Icon(HideIconValue);
}
else
{
Icon(_lastIcon);
// Switching from...
if (_currentIconType && unbox_value<IconType>(_currentIconType.as<Editor::EnumEntry>().EnumValue()) == IconType::Image)
{
// Stash the current value of Icon. If the user
// switches out of then back to IconType::Image, we want
// the path that we display in the text box to remain unchanged.
_lastIconPath = Icon();
}
// Set the member here instead of after setting Icon() below!
// We have an Icon property changed handler defined for when we discard changes.
// Inadvertently, that means that we call this setter again.
// Setting the member here means that we early exit at the beginning of the function
// because _currentIconType == value.
_currentIconType = value;
// Switched to...
switch (unbox_value<IconType>(value.as<Editor::EnumEntry>().EnumValue()))
{
case IconType::None:
{
_profile.Icon(HideIconValue);
break;
}
case IconType::Image:
{
if (!_lastIconPath.empty())
{
// Conversely, if we switch to Image,
// retrieve that saved value and apply it
_profile.Icon(_lastIconPath);
}
break;
}
case IconType::FontIcon:
{
if (_CurrentBuiltInIcon)
{
_profile.Icon(unbox_value<hstring>(_CurrentBuiltInIcon.as<Editor::EnumEntry>().EnumValue()));
}
break;
}
case IconType::Emoji:
{
// Don't set Icon here!
// Clear out the text box so we direct the user to use the emoji picker.
CurrentEmojiIcon({});
}
}
// We're not using the VM's Icon() setter above,
// so notify HasIcon changed manually
_NotifyChanges(L"CurrentIconType", L"HasIcon");
}
}
bool ProfileViewModel::UsingNoIcon() const
{
return _currentIconType == _IconTypes.GetAt(0);
}
bool ProfileViewModel::UsingBuiltInIcon() const
{
return _currentIconType == _IconTypes.GetAt(1);
}
bool ProfileViewModel::UsingEmojiIcon() const
{
return _currentIconType == _IconTypes.GetAt(2);
}
bool ProfileViewModel::UsingImageIcon() const
{
return _currentIconType == _IconTypes.GetAt(3);
}
bool ProfileViewModel::IsBellStyleFlagSet(const uint32_t flag)
{
return (WI_EnumValue(BellStyle()) & flag) == flag;

View File

@ -33,6 +33,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
static void UpdateFontList() noexcept;
static Windows::Foundation::Collections::IObservableVector<Editor::Font> CompleteFontList() noexcept { return _FontList; };
static Windows::Foundation::Collections::IObservableVector<Editor::Font> MonospaceFontList() noexcept { return _MonospaceFontList; };
static Windows::Foundation::Collections::IVector<IInspectable> BuiltInIcons() noexcept { return _BuiltInIcons; };
ProfileViewModel(const Model::Profile& profile, const Model::CascadiaSettings& settings);
Model::TerminalSettings TermSettings() const;
@ -64,16 +65,23 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
return _profile.EvaluatedIcon();
}
Windows::Foundation::IInspectable CurrentIconType() const noexcept
{
return _currentIconType;
}
Windows::UI::Xaml::Controls::IconElement IconPreview() const;
winrt::hstring LocalizedIcon() const;
void CurrentIconType(const Windows::Foundation::IInspectable& value);
bool UsingNoIcon() const;
bool UsingBuiltInIcon() const;
bool UsingEmojiIcon() const;
bool UsingImageIcon() const;
// starting directory
bool UseParentProcessDirectory();
void UseParentProcessDirectory(const bool useParent);
bool UseCustomStartingDirectory();
// icon
bool HideIcon();
void HideIcon(const bool hide);
// general profile knowledge
winrt::guid OriginalProfileGuid() const noexcept;
bool CanDeleteProfile() const;
@ -94,6 +102,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
til::typed_event<Editor::ProfileViewModel, Editor::DeleteProfileEventArgs> DeleteProfileRequested;
VIEW_MODEL_OBSERVABLE_PROPERTY(ProfileSubPage, CurrentPage);
VIEW_MODEL_OBSERVABLE_PROPERTY(Windows::Foundation::IInspectable, CurrentBuiltInIcon);
VIEW_MODEL_OBSERVABLE_PROPERTY(hstring, CurrentEmojiIcon);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_profile, Guid);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_profile, ConnectionType);
@ -110,10 +120,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
OBSERVABLE_PROJECTED_SETTING(_profile, Commandline);
OBSERVABLE_PROJECTED_SETTING(_profile, StartingDirectory);
OBSERVABLE_PROJECTED_SETTING(_profile, AntialiasingMode);
OBSERVABLE_PROJECTED_SETTING(_profile.DefaultAppearance(), Foreground);
OBSERVABLE_PROJECTED_SETTING(_profile.DefaultAppearance(), Background);
OBSERVABLE_PROJECTED_SETTING(_profile.DefaultAppearance(), SelectionBackground);
OBSERVABLE_PROJECTED_SETTING(_profile.DefaultAppearance(), CursorColor);
OBSERVABLE_PROJECTED_SETTING(_profile.DefaultAppearance(), Opacity);
OBSERVABLE_PROJECTED_SETTING(_profile.DefaultAppearance(), UseAcrylic);
OBSERVABLE_PROJECTED_SETTING(_profile, HistorySize);
@ -134,6 +140,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
WINRT_PROPERTY(bool, IsBaseLayer, false);
WINRT_PROPERTY(bool, FocusDeleteButton, false);
WINRT_PROPERTY(Windows::Foundation::Collections::IVector<Windows::Foundation::IInspectable>, IconTypes);
GETSET_BINDABLE_ENUM_SETTING(AntiAliasingMode, Microsoft::Terminal::Control::TextAntialiasingMode, AntialiasingMode);
GETSET_BINDABLE_ENUM_SETTING(CloseOnExitMode, Microsoft::Terminal::Settings::Model::CloseOnExitMode, CloseOnExit);
GETSET_BINDABLE_ENUM_SETTING(ScrollState, Microsoft::Terminal::Control::ScrollbarState, ScrollState);
@ -144,11 +151,13 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
winrt::guid _originalProfileGuid{};
winrt::hstring _lastBgImagePath;
winrt::hstring _lastStartingDirectoryPath;
winrt::hstring _lastIcon;
winrt::hstring _lastIconPath;
Windows::Foundation::IInspectable _currentIconType{};
Editor::AppearanceViewModel _defaultAppearanceViewModel;
static Windows::Foundation::Collections::IObservableVector<Editor::Font> _MonospaceFontList;
static Windows::Foundation::Collections::IObservableVector<Editor::Font> _FontList;
static Windows::Foundation::Collections::IVector<Windows::Foundation::IInspectable> _BuiltInIcons;
Model::CascadiaSettings _appSettings;
Editor::AppearanceViewModel _unfocusedAppearanceViewModel;
@ -163,6 +172,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
winrt::hstring _GetNewPadding(PaddingDirection paddingDirection, double newPaddingValue) const;
double _GetPaddingValue(PaddingDirection paddingDirection) const;
void _UpdateBuiltInIcons();
void _DeduceCurrentIconType();
void _DeduceCurrentBuiltInIcon();
};
struct DeleteProfileEventArgs :

View File

@ -33,6 +33,14 @@ namespace Microsoft.Terminal.Settings.Editor
Advanced = 3
};
enum IconType
{
None = 0,
FontIcon,
Image,
Emoji
};
runtimeclass ProfileViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged
{
Microsoft.Terminal.Settings.Model.TerminalSettings TermSettings { get; };
@ -71,7 +79,6 @@ namespace Microsoft.Terminal.Settings.Editor
ProfileSubPage CurrentPage;
Boolean UseParentProcessDirectory;
Boolean UseCustomStartingDirectory { get; };
Boolean HideIcon;
AppearanceViewModel DefaultAppearance { get; };
Guid OriginalProfileGuid { get; };
Boolean HasUnfocusedAppearance { get; };
@ -83,7 +90,19 @@ namespace Microsoft.Terminal.Settings.Editor
Boolean AutoMarkPromptsAvailable { get; };
Boolean RepositionCursorWithMouseAvailable { get; };
Windows.UI.Xaml.Controls.IconElement IconPreview { get; };
String EvaluatedIcon { get; };
String LocalizedIcon { get; };
String CurrentEmojiIcon;
IInspectable CurrentIconType;
Windows.Foundation.Collections.IVector<IInspectable> IconTypes { get; };
Boolean UsingNoIcon { get; };
Boolean UsingBuiltInIcon { get; };
Boolean UsingEmojiIcon { get; };
Boolean UsingImageIcon { get; };
IInspectable CurrentBuiltInIcon;
Windows.Foundation.Collections.IVector<IInspectable> BuiltInIcons { get; };
void CreateUnfocusedAppearance();
void DeleteUnfocusedAppearance();
@ -106,10 +125,6 @@ namespace Microsoft.Terminal.Settings.Editor
OBSERVABLE_PROJECTED_PROFILE_SETTING(String, Commandline);
OBSERVABLE_PROJECTED_PROFILE_SETTING(String, StartingDirectory);
OBSERVABLE_PROJECTED_PROFILE_SETTING(Microsoft.Terminal.Control.TextAntialiasingMode, AntialiasingMode);
OBSERVABLE_PROJECTED_PROFILE_SETTING(Windows.Foundation.IReference<Microsoft.Terminal.Core.Color>, Foreground);
OBSERVABLE_PROJECTED_PROFILE_SETTING(Windows.Foundation.IReference<Microsoft.Terminal.Core.Color>, Background);
OBSERVABLE_PROJECTED_PROFILE_SETTING(Windows.Foundation.IReference<Microsoft.Terminal.Core.Color>, SelectionBackground);
OBSERVABLE_PROJECTED_PROFILE_SETTING(Windows.Foundation.IReference<Microsoft.Terminal.Core.Color>, CursorColor);
OBSERVABLE_PROJECTED_PROFILE_SETTING(Int32, HistorySize);
OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, SnapOnInput);
OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, AltGrAliasing);

View File

@ -148,4 +148,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
_Profile.StartingDirectory(folder);
}
}
Windows::UI::Xaml::Controls::IconSource Profiles_Base::BuiltInIconConverter(const IInspectable& iconVal)
{
return Microsoft::Terminal::UI::IconPathConverter::IconSourceWUX(unbox_value<hstring>(iconVal));
}
}

View File

@ -25,6 +25,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void Advanced_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e);
void DeleteConfirmation_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e);
static Windows::UI::Xaml::Controls::IconSource BuiltInIconConverter(const Windows::Foundation::IInspectable& iconVal);
til::property_changed_event PropertyChanged;
WINRT_PROPERTY(Editor::ProfileViewModel, Profile, nullptr);

View File

@ -9,5 +9,7 @@ namespace Microsoft.Terminal.Settings.Editor
{
Profiles_Base();
ProfileViewModel Profile { get; };
static Windows.UI.Xaml.Controls.IconSource BuiltInIconConverter(IInspectable iconVal);
}
}

View File

@ -102,28 +102,98 @@
<!-- Icon -->
<local:SettingContainer x:Uid="Profile_Icon"
ClearSettingValue="{x:Bind Profile.ClearIcon}"
CurrentValue="{x:Bind Profile.Icon, Mode=OneWay}"
HasSettingValue="{x:Bind Profile.HasIcon, Mode=OneWay}"
SettingOverrideSource="{x:Bind Profile.IconOverrideSource, Mode=OneWay}"
Style="{StaticResource ExpanderSettingContainerStyle}">
<StackPanel>
<TextBox x:Uid="Profile_IconBox"
FontFamily="Segoe UI, Segoe Fluent Icons, Segoe MDL2 Assets"
IsEnabled="{x:Bind mtu:Converters.InvertBoolean(Profile.HideIcon), Mode=OneWay}"
IsSpellCheckEnabled="False"
Style="{StaticResource TextBoxSettingStyle}"
Text="{x:Bind Profile.Icon, Mode=TwoWay}"
Visibility="{x:Bind mtu:Converters.InvertedBooleanToVisibility(Profile.HideIcon), Mode=OneWay}" />
<Button x:Uid="Profile_IconBrowse"
Margin="0,10,0,0"
Click="Icon_Click"
Style="{StaticResource BrowseButtonStyle}"
Visibility="{x:Bind mtu:Converters.InvertedBooleanToVisibility(Profile.HideIcon), Mode=OneWay}" />
<CheckBox x:Name="HideIconCheckbox"
x:Uid="Profile_HideIconCheckbox"
Margin="0,5,0,0"
IsChecked="{x:Bind Profile.HideIcon, Mode=TwoWay}" />
</StackPanel>
Style="{StaticResource ExpanderSettingContainerStyleWithComplexPreview}">
<local:SettingContainer.CurrentValue>
<Grid>
<ContentControl Width="16"
Height="16"
Content="{x:Bind Profile.IconPreview, Mode=OneWay}"
Visibility="{x:Bind mtu:Converters.InvertedBooleanToVisibility(Profile.UsingNoIcon), Mode=OneWay}" />
<TextBlock Margin="0,0,0,0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
FontFamily="Segoe UI, Segoe Fluent Icons, Segoe MDL2 Assets"
Style="{StaticResource SettingsPageItemDescriptionStyle}"
Text="{x:Bind Profile.LocalizedIcon, Mode=OneWay}"
Visibility="{x:Bind Profile.UsingNoIcon, Mode=OneWay}" />
</Grid>
</local:SettingContainer.CurrentValue>
<local:SettingContainer.Content>
<Grid ColumnSpacing="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<!-- Icon Type -->
<ComboBox x:Uid="Profile_IconType"
Grid.Column="0"
ItemsSource="{x:Bind Profile.IconTypes}"
SelectedItem="{x:Bind Profile.CurrentIconType, Mode=TwoWay}">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="local:EnumEntry">
<TextBlock Text="{x:Bind EnumName}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<!-- Built-In Icon -->
<ComboBox x:Uid="Profile_BuiltInIcon"
Grid.Column="1"
ItemsSource="{x:Bind Profile.BuiltInIcons}"
SelectedItem="{x:Bind Profile.CurrentBuiltInIcon, Mode=TwoWay}"
Visibility="{x:Bind Profile.UsingBuiltInIcon, Mode=OneWay}">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="local:EnumEntry">
<Grid ColumnSpacing="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="16" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<IconSourceElement Grid.Column="0"
Width="16"
Height="16"
IconSource="{x:Bind local:Profiles_Base.BuiltInIconConverter(EnumValue), Mode=OneTime}" />
<TextBlock Grid.Column="1"
Text="{x:Bind EnumName}" />
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<!-- Image (File) Icon -->
<TextBox x:Uid="Profile_IconBox"
Grid.Column="1"
MaxWidth="Infinity"
HorizontalAlignment="Stretch"
FontFamily="Segoe UI, Segoe Fluent Icons, Segoe MDL2 Assets"
IsSpellCheckEnabled="False"
Style="{StaticResource TextBoxSettingStyle}"
Text="{x:Bind Profile.Icon, Mode=TwoWay}"
Visibility="{x:Bind Profile.UsingImageIcon, Mode=OneWay}" />
<Button x:Uid="Profile_IconBrowse"
Grid.Column="2"
Margin="0"
VerticalAlignment="Top"
Click="Icon_Click"
Style="{StaticResource BrowseButtonStyle}"
Visibility="{x:Bind Profile.UsingImageIcon, Mode=OneWay}" />
<!-- Emoji Icon -->
<TextBox x:Uid="Profile_IconEmojiBox"
Grid.Column="1"
MaxWidth="Infinity"
FontFamily="Segoe UI, Segoe Fluent Icons, Segoe MDL2 Assets"
IsSpellCheckEnabled="False"
Style="{StaticResource TextBoxSettingStyle}"
Text="{x:Bind Profile.CurrentEmojiIcon, Mode=TwoWay}"
Visibility="{x:Bind Profile.UsingEmojiIcon, Mode=OneWay}" />
</Grid>
</local:SettingContainer.Content>
</local:SettingContainer>
<!-- Tab Title -->

View File

@ -894,6 +894,10 @@
<value>Color scheme</value>
<comment>Header for a control to select the scheme (or set) of colors used in the session. This is selected from a list of options managed by the user.</comment>
</data>
<data name="Profile_ColorScheme.HelpText" xml:space="preserve">
<value>Expand to override the foreground, background, and selection background.</value>
<comment>Help text for a control to select the scheme (or set) of colors used in the session. Directs the user to expand the control to access overrides for certain values in the color scheme.</comment>
</data>
<data name="Profile_Commandline.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Command line</value>
<comment>Name for a control to determine commandline executable (i.e. a .exe file) to run when a terminal session of this profile is launched.</comment>
@ -1121,6 +1125,10 @@
<value>Icon</value>
<comment>Name for a control to determine what icon can be used to represent this profile. This is not necessarily a file path, but can be one.</comment>
</data>
<data name="Profile_IconEmojiBox.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Icon</value>
<comment>Name for a control to determine what icon can be used to represent this profile.</comment>
</data>
<data name="Profile_Icon.HelpText" xml:space="preserve">
<value>Emoji or image file location of the icon used in the profile.</value>
<comment>A description for what the "icon" setting does. Presented near "Profile_Icon".</comment>
@ -1929,6 +1937,102 @@
<value>Non-monospace fonts:</value>
<comment>This is a label that is followed by a list of proportional fonts.</comment>
</data>
<data name="Profile_TabColor.Header" xml:space="preserve">
<value>Tab color</value>
<comment>Header for a control to determine the color of the tab.</comment>
</data>
<data name="Profile_Foreground.Header" xml:space="preserve">
<value>Foreground</value>
<comment>Header for a control to determine the foreground color of text.</comment>
</data>
<data name="Profile_Background.Header" xml:space="preserve">
<value>Background</value>
<comment>Header for a control to determine the background color of text.</comment>
</data>
<data name="Profile_SelectionBackground.Header" xml:space="preserve">
<value>Selection background</value>
<comment>Header for a control to determine the background color of selected text.</comment>
</data>
<data name="Profile_CursorColor.Header" xml:space="preserve">
<value>Cursor color</value>
<comment>Header for a control to determine the color of the cursor.</comment>
</data>
<data name="Profile_Foreground.HelpText" xml:space="preserve">
<value>Overrides the foreground color from the color scheme.</value>
<comment>A description for what the "foreground" setting does. Presented near "Profile_Foreground".</comment>
</data>
<data name="Profile_CursorColor.HelpText" xml:space="preserve">
<value>Overrides the cursor color from the color scheme.</value>
<comment>A description for what the "cursor color" setting does. Presented near "Profile_CursorColor".</comment>
</data>
<data name="Profile_Background.HelpText" xml:space="preserve">
<value>Overrides the background color from the color scheme.</value>
<comment>A description for what the "background" setting does. Presented near "Profile_Background".</comment>
</data>
<data name="Profile_SelectionBackground.HelpText" xml:space="preserve">
<value>Overrides the selection background color from the color scheme.</value>
<comment>A description for what the "selection background" setting does. Presented near "Profile_SelectionBackground".</comment>
</data>
<data name="NullableColorPicker_MoreColorsButton.Content" xml:space="preserve">
<value>More colors...</value>
<comment>Text label for a button that allows the user to select from more colors in a new window. {Locked="..."}</comment>
</data>
<data name="NullableColorPicker_ColorPickerContentDialog.Title" xml:space="preserve">
<value>Pick a color</value>
<comment>Title displayed on a content dialog directing the user to pick a color.</comment>
</data>
<data name="NullableColorPicker_ColorPickerContentDialog.PrimaryButtonText" xml:space="preserve">
<value>OK</value>
<comment>Button label for the color picker content dialog. Used as confirmation to apply the selected color.</comment>
</data>
<data name="NullableColorPicker_ColorPickerContentDialog.SecondaryButtonText" xml:space="preserve">
<value>Cancel</value>
<comment>Text label for secondary button the color picker content dialog. When clicked, the operation of selecting a color is cancelled by the user.</comment>
</data>
<data name="Profile_CursorColor_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Use cursor color from color scheme</value>
<comment>Label for a button directing the user to use the cursor color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_Foreground_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Use foreground color from color scheme</value>
<comment>Label for a button directing the user to use the foreground color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_Background_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Use background color from color scheme</value>
<comment>Label for a button directing the user to use the background color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_SelectionBackground_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Use selection background color from color scheme</value>
<comment>Label for a button directing the user to use the selection background color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_IconTypeNone" xml:space="preserve">
<value>None</value>
<comment>An option to choose from for the "icon style" dropdown. When selected, there will be no icon for the profile.</comment>
</data>
<data name="Profile_IconTypeImage" xml:space="preserve">
<value>File</value>
<comment>An option to choose from for the "icon style" dropdown. When selected, a custom image can set for the profile's icon.</comment>
</data>
<data name="Profile_IconTypeEmoji" xml:space="preserve">
<value>Emoji</value>
<comment>An option to choose from for the "icon style" dropdown. When selected, an emoji can be set for the profile's icon.</comment>
</data>
<data name="Profile_IconTypeFontIcon" xml:space="preserve">
<value>Built-in Icon</value>
<comment>An option to choose from for the "icon style" dropdown. When selected, the user can choose from several preselected options to set the profile's icon.</comment>
</data>
<data name="Profile_IconEmojiBox.PlaceholderText" xml:space="preserve">
<value>Use "Win + period" to open the emoji picker</value>
<comment>"Win + period" refers to the OS key binding to open the emoji picker.</comment>
</data>
<data name="Profile_IconType.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Icon type</value>
<comment>Accessible name for a control allowing the user to select the type of icon they would like to use.</comment>
</data>
<data name="Profile_BuiltInIcon.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Icon</value>
<comment>Accessible name for a control allowing the user to select the icon from a list of built in icons.</comment>
</data>
<data name="Nav_NewTabMenu.Content" xml:space="preserve">
<value>New Tab Menu</value>
<comment>Header for the "new tab menu" menu item. This navigates to a page that lets you see and modify settings related to the app's new tab menu (i.e. profile ordering, nested folders, dividers, etc.)</comment>

File diff suppressed because it is too large Load Diff

View File

@ -14,6 +14,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
DependencyProperty SettingContainer::_HelpTextProperty{ nullptr };
DependencyProperty SettingContainer::_FontIconGlyphProperty{ nullptr };
DependencyProperty SettingContainer::_CurrentValueProperty{ nullptr };
DependencyProperty SettingContainer::_CurrentValueTemplateProperty{ nullptr };
DependencyProperty SettingContainer::_HasSettingValueProperty{ nullptr };
DependencyProperty SettingContainer::_SettingOverrideSourceProperty{ nullptr };
DependencyProperty SettingContainer::_StartExpandedProperty{ nullptr };
@ -60,9 +61,18 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
_CurrentValueProperty =
DependencyProperty::Register(
L"CurrentValue",
xaml_typename<hstring>(),
xaml_typename<IInspectable>(),
xaml_typename<Editor::SettingContainer>(),
PropertyMetadata{ box_value(L"") });
PropertyMetadata{ nullptr });
}
if (!_CurrentValueTemplateProperty)
{
_CurrentValueTemplateProperty =
DependencyProperty::Register(
L"CurrentValueTemplate",
xaml_typename<Windows::UI::Xaml::DataTemplate>(),
xaml_typename<Editor::SettingContainer>(),
PropertyMetadata{ nullptr });
}
if (!_HasSettingValueProperty)
{

View File

@ -36,7 +36,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
DEPENDENCY_PROPERTY(Windows::Foundation::IInspectable, Header);
DEPENDENCY_PROPERTY(hstring, HelpText);
DEPENDENCY_PROPERTY(hstring, FontIconGlyph);
DEPENDENCY_PROPERTY(hstring, CurrentValue);
DEPENDENCY_PROPERTY(Windows::Foundation::IInspectable, CurrentValue);
DEPENDENCY_PROPERTY(Windows::UI::Xaml::DataTemplate, CurrentValueTemplate);
DEPENDENCY_PROPERTY(bool, HasSettingValue);
DEPENDENCY_PROPERTY(bool, StartExpanded);
DEPENDENCY_PROPERTY(IInspectable, SettingOverrideSource);

View File

@ -18,9 +18,12 @@ namespace Microsoft.Terminal.Settings.Editor
String FontIconGlyph;
static Windows.UI.Xaml.DependencyProperty FontIconGlyphProperty { get; };
String CurrentValue;
IInspectable CurrentValue;
static Windows.UI.Xaml.DependencyProperty CurrentValueProperty { get; };
Windows.UI.Xaml.DataTemplate CurrentValueTemplate;
static Windows.UI.Xaml.DependencyProperty CurrentValueTemplateProperty { get; };
Boolean HasSettingValue;
static Windows.UI.Xaml.DependencyProperty HasSettingValueProperty { get; };

View File

@ -178,11 +178,67 @@
<Setter Property="TextWrapping" Value="WrapWholeWords" />
</Style>
<DataTemplate x:Key="ExpanderSettingContainerStringPreviewTemplate">
<TextBlock MaxWidth="250"
Margin="0,0,-16,0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
FontFamily="Segoe UI, Segoe Fluent Icons, Segoe MDL2 Assets"
Style="{StaticResource SettingsPageItemDescriptionStyle}"
Text="{Binding}" />
</DataTemplate>
<!-- A setting container for a setting that has no additional options -->
<Style TargetType="local:SettingContainer">
<Setter Property="Margin" Value="0,4,0,0" />
<Setter Property="IsTabStop" Value="False" />
<Setter Property="MaxWidth" Value="1000" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:SettingContainer">
<Grid AutomationProperties.Name="{TemplateBinding Header}"
Style="{StaticResource NonExpanderGrid}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0"
Style="{StaticResource StackPanelInExpanderStyle}">
<StackPanel Orientation="Horizontal">
<TextBlock Style="{StaticResource SettingsPageItemHeaderStyle}"
Text="{TemplateBinding Header}" />
<Button x:Name="ResetButton"
Style="{StaticResource SettingContainerResetButtonStyle}">
<FontIcon Glyph="&#xE845;"
Style="{StaticResource SettingContainerFontIconStyle}" />
</Button>
</StackPanel>
<TextBlock x:Name="HelpTextBlock"
Style="{StaticResource SettingsPageItemDescriptionStyle}"
Text="{TemplateBinding HelpText}" />
</StackPanel>
<ContentPresenter Grid.Column="1"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Content="{TemplateBinding Content}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!--
A setting container for a setting that has no additional options.
Includes space for an icon on the left side of the header.
XAML applies the margin/padding of the icon regardless of its
existence (which caused inconsistent padding). It's easier to just create
another style and move on.
-->
<Style x:Key="SettingContainerWithIcon"
TargetType="local:SettingContainer">
<Setter Property="Margin" Value="0,4,0,0" />
<Setter Property="IsTabStop" Value="False" />
<Setter Property="MaxWidth" Value="1000" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:SettingContainer">
@ -194,9 +250,9 @@
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<FontIcon Grid.Column="0"
Margin="0,0,10,0"
Glyph="{TemplateBinding FontIconGlyph}" />
<StackPanel Grid.Column="1"
Padding="14,12,0,12"
Style="{StaticResource StackPanelInExpanderStyle}">
<StackPanel Orientation="Horizontal">
<TextBlock Style="{StaticResource SettingsPageItemHeaderStyle}"
@ -238,14 +294,10 @@
<muxc:Expander.Header>
<Grid MinHeight="64">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<FontIcon Grid.Column="0"
Margin="0,0,10,0"
Glyph="{TemplateBinding FontIconGlyph}" />
<StackPanel Grid.Column="1"
<StackPanel Grid.Column="0"
Style="{StaticResource StackPanelInExpanderStyle}">
<StackPanel Orientation="Horizontal">
<TextBlock Style="{StaticResource SettingsPageItemHeaderStyle}"
@ -260,14 +312,114 @@
Style="{StaticResource SettingsPageItemDescriptionStyle}"
Text="{TemplateBinding HelpText}" />
</StackPanel>
<TextBlock Grid.Column="2"
MaxWidth="250"
Margin="0,0,-16,0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
FontFamily="Segoe UI, Segoe Fluent Icons, Segoe MDL2 Assets"
Style="{StaticResource SettingsPageItemDescriptionStyle}"
Text="{TemplateBinding CurrentValue}" />
<ContentPresenter Grid.Column="1"
Content="{TemplateBinding CurrentValue}"
ContentTemplate="{StaticResource ExpanderSettingContainerStringPreviewTemplate}" />
</Grid>
</muxc:Expander.Header>
</muxc:Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!--
A setting container which can expand. Includes space for an icon on the left side of the header.
XAML applies the margin/padding of the icon regardless of its
existence (which caused inconsistent padding). It's easier to just create
another style and move on.
-->
<Style x:Key="ExpanderSettingContainerStyleWithIcon"
TargetType="local:SettingContainer">
<Setter Property="MaxWidth" Value="1000" />
<Setter Property="IsTabStop" Value="False" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:SettingContainer">
<muxc:Expander x:Name="Expander"
Margin="0,4,0,0"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
Content="{TemplateBinding Content}"
IsExpanded="{TemplateBinding StartExpanded}">
<muxc:Expander.Header>
<Grid MinHeight="64">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<FontIcon Grid.Column="0"
Glyph="{TemplateBinding FontIconGlyph}" />
<StackPanel Grid.Column="1"
Padding="14,12,0,12"
Style="{StaticResource StackPanelInExpanderStyle}">
<StackPanel Orientation="Horizontal">
<TextBlock Style="{StaticResource SettingsPageItemHeaderStyle}"
Text="{TemplateBinding Header}" />
<Button x:Name="ResetButton"
Style="{StaticResource SettingContainerResetButtonStyle}">
<FontIcon Glyph="&#xE845;"
Style="{StaticResource SettingContainerFontIconStyle}" />
</Button>
</StackPanel>
<TextBlock x:Name="HelpTextBlock"
Style="{StaticResource SettingsPageItemDescriptionStyle}"
Text="{TemplateBinding HelpText}" />
</StackPanel>
<ContentPresenter Grid.Column="2"
Content="{TemplateBinding CurrentValue}"
ContentTemplate="{StaticResource ExpanderSettingContainerStringPreviewTemplate}" />
</Grid>
</muxc:Expander.Header>
</muxc:Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- A setting container which can expand. Supports data template override for preview -->
<Style x:Key="ExpanderSettingContainerStyleWithComplexPreview"
TargetType="local:SettingContainer">
<Setter Property="MaxWidth" Value="1000" />
<Setter Property="IsTabStop" Value="False" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:SettingContainer">
<muxc:Expander x:Name="Expander"
Margin="0,4,0,0"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
Content="{TemplateBinding Content}"
IsExpanded="{TemplateBinding StartExpanded}">
<muxc:Expander.Header>
<Grid MinHeight="64">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0"
Style="{StaticResource StackPanelInExpanderStyle}">
<StackPanel Orientation="Horizontal">
<TextBlock Style="{StaticResource SettingsPageItemHeaderStyle}"
Text="{TemplateBinding Header}" />
<Button x:Name="ResetButton"
Style="{StaticResource SettingContainerResetButtonStyle}">
<FontIcon Glyph="&#xE845;"
Style="{StaticResource SettingContainerFontIconStyle}" />
</Button>
</StackPanel>
<TextBlock x:Name="HelpTextBlock"
Style="{StaticResource SettingsPageItemDescriptionStyle}"
Text="{TemplateBinding HelpText}" />
</StackPanel>
<ContentPresenter Grid.Column="1"
MaxWidth="250"
Margin="0,0,-16,0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Content="{TemplateBinding CurrentValue}"
ContentTemplate="{TemplateBinding CurrentValueTemplate}" />
</Grid>
</muxc:Expander.Header>
</muxc:Expander>

View File

@ -0,0 +1,35 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "TerminalColorConverters.h"
#include "ColorToBrushConverter.g.cpp"
#include "ColorToStringConverter.g.cpp"
using namespace winrt::Microsoft::Terminal::Settings::Model;
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
Windows::Foundation::IInspectable ColorToBrushConverter::Convert(Windows::Foundation::IInspectable const& value, Windows::UI::Xaml::Interop::TypeName const& /*targetType*/, Windows::Foundation::IInspectable const& /*parameter*/, hstring const& /*language*/)
{
const auto color = value.as<Windows::UI::Color>();
return Microsoft::Terminal::UI::Converters::ColorToBrush(color);
}
Windows::Foundation::IInspectable ColorToBrushConverter::ConvertBack(Windows::Foundation::IInspectable const& /*value*/, Windows::UI::Xaml::Interop::TypeName const& /*targetType*/, Windows::Foundation::IInspectable const& /*parameter*/, hstring const& /*language*/)
{
throw hresult_not_implemented();
}
Windows::Foundation::IInspectable ColorToStringConverter::Convert(Windows::Foundation::IInspectable const& value, Windows::UI::Xaml::Interop::TypeName const& /*targetType*/, Windows::Foundation::IInspectable const& /*parameter*/, hstring const& /*language*/)
{
const auto color = value.as<Windows::UI::Color>();
return winrt::box_value(fmt::format(FMT_COMPILE(L"#{:02X}{:02X}{:02X}"), color.R, color.G, color.B));
}
Windows::Foundation::IInspectable ColorToStringConverter::ConvertBack(Windows::Foundation::IInspectable const& /*value*/, Windows::UI::Xaml::Interop::TypeName const& /*targetType*/, Windows::Foundation::IInspectable const& /*parameter*/, hstring const& /*language*/)
{
throw hresult_not_implemented();
}
}

View File

@ -0,0 +1,33 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include "ColorToBrushConverter.g.h"
#include "ColorToStringConverter.g.h"
#include "Utils.h"
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
struct ColorToBrushConverter : ColorToBrushConverterT<ColorToBrushConverter>
{
ColorToBrushConverter() = default;
Windows::Foundation::IInspectable Convert(Windows::Foundation::IInspectable const& value, Windows::UI::Xaml::Interop::TypeName const& targetType, Windows::Foundation::IInspectable const& parameter, hstring const& language);
Windows::Foundation::IInspectable ConvertBack(Windows::Foundation::IInspectable const& value, Windows::UI::Xaml::Interop::TypeName const& targetType, Windows::Foundation::IInspectable const& parameter, hstring const& language);
};
struct ColorToStringConverter : ColorToStringConverterT<ColorToStringConverter>
{
ColorToStringConverter() = default;
Windows::Foundation::IInspectable Convert(Windows::Foundation::IInspectable const& value, Windows::UI::Xaml::Interop::TypeName const& targetType, Windows::Foundation::IInspectable const& parameter, hstring const& language);
Windows::Foundation::IInspectable ConvertBack(Windows::Foundation::IInspectable const& value, Windows::UI::Xaml::Interop::TypeName const& targetType, Windows::Foundation::IInspectable const& parameter, hstring const& language);
};
};
namespace winrt::Microsoft::Terminal::Settings::Editor::factory_implementation
{
BASIC_FACTORY(ColorToBrushConverter);
BASIC_FACTORY(ColorToStringConverter);
}

View File

@ -0,0 +1,15 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
namespace Microsoft.Terminal.Settings.Editor
{
runtimeclass ColorToBrushConverter : [default] Windows.UI.Xaml.Data.IValueConverter
{
ColorToBrushConverter();
}
runtimeclass ColorToStringConverter : [default] Windows.UI.Xaml.Data.IValueConverter
{
ColorToStringConverter();
}
}

View File

@ -409,6 +409,61 @@ winrt::hstring Profile::_evaluateIcon() const
return winrt::hstring{ cmdline.c_str() };
}
bool Profile::HasIcon() const
{
return _Icon.has_value();
}
winrt::Microsoft::Terminal::Settings::Model::Profile Profile::IconOverrideSource()
{
for (const auto& parent : _parents)
{
if (auto source{ parent->_getIconOverrideSourceImpl() })
{
return source;
}
}
return nullptr;
}
void Profile::ClearIcon()
{
_Icon = std::nullopt;
_evaluatedIcon = std::nullopt;
}
std::optional<winrt::hstring> Profile::_getIconImpl() const
{
if (_Icon)
{
return _Icon;
}
for (const auto& parent : _parents)
{
if (auto val{ parent->_getIconImpl() })
{
return val;
}
}
return std::nullopt;
}
winrt::Microsoft::Terminal::Settings::Model::Profile Profile::_getIconOverrideSourceImpl() const
{
if (_Icon)
{
return *this;
}
for (const auto& parent : _parents)
{
if (auto source{ parent->_getIconOverrideSourceImpl() })
{
return source;
}
}
return nullptr;
}
// Given a commandLine like the following:
// * "C:\WINDOWS\System32\cmd.exe"
// * "pwsh -WorkingDirectory ~"

View File

@ -102,17 +102,22 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
Model::IAppearanceConfig DefaultAppearance();
Model::FontConfig FontInfo();
winrt::hstring EvaluatedIcon();
static std::wstring NormalizeCommandLine(LPCWSTR commandLine);
void _FinalizeInheritance() override;
void LogSettingChanges(std::set<std::string>& changes, const std::string_view& context) const;
// Special fields
// EvaluatedIcon depends on Icon. It allows us to grab the
// icon from an exe path.
// As a result, we can't use the INHERITABLE_SETTING macro for Icon,
// as we manually have to set/unset _evaluatedIcon when Icon changes.
winrt::hstring EvaluatedIcon();
hstring Icon() const;
void Icon(const hstring& value);
bool HasIcon() const;
Model::Profile IconOverrideSource();
void ClearIcon();
WINRT_PROPERTY(bool, Deleted, false);
WINRT_PROPERTY(bool, Orphaned, false);
@ -129,8 +134,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
INHERITABLE_SETTING(Model::Profile, bool, Hidden, false);
INHERITABLE_SETTING(Model::Profile, guid, Guid, _GenerateGuidForProfile(Name(), Source()));
INHERITABLE_SETTING(Model::Profile, hstring, Padding, DEFAULT_PADDING);
// Icon is _very special_ because we want to customize its setter
_BASE_INHERITABLE_SETTING(Model::Profile, std::optional<hstring>, Icon, L"\uE756");
public:
#define PROFILE_SETTINGS_INITIALIZE(type, name, jsonKey, ...) \
@ -142,6 +145,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
Model::IAppearanceConfig _DefaultAppearance{ winrt::make<AppearanceConfig>(weak_ref<Model::Profile>(*this)) };
Model::FontConfig _FontInfo{ winrt::make<FontConfig>(weak_ref<Model::Profile>(*this)) };
std::optional<hstring> _Icon{ std::nullopt };
std::optional<winrt::hstring> _evaluatedIcon{ std::nullopt };
std::set<std::string> _changeLog;
@ -150,6 +154,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
static guid _GenerateGuidForProfile(const std::wstring_view& name, const std::wstring_view& source) noexcept;
winrt::hstring _evaluateIcon() const;
std::optional<hstring> _getIconImpl() const;
Model::Profile _getIconOverrideSourceImpl() const;
void _logSettingSet(const std::string_view& setting);
void _logSettingIfSet(const std::string_view& setting, const bool isSet);