2021-10-01 20:33:22 +02:00
|
|
|
// Copyright (c) Microsoft Corporation.
|
2020-12-11 22:34:21 +01:00
|
|
|
// Licensed under the MIT license.
|
|
|
|
|
|
|
|
#include "pch.h"
|
|
|
|
#include "Profiles.h"
|
2021-05-17 04:26:47 +02:00
|
|
|
#include "PreviewConnection.h"
|
2020-12-11 22:34:21 +01:00
|
|
|
#include "Profiles.g.cpp"
|
|
|
|
#include "EnumEntry.h"
|
|
|
|
|
|
|
|
#include <LibraryResources.h>
|
2021-10-01 20:33:22 +02:00
|
|
|
#include "..\WinRTUtils\inc\Utils.h"
|
2020-12-11 22:34:21 +01:00
|
|
|
|
|
|
|
using namespace winrt::Windows::UI::Text;
|
|
|
|
using namespace winrt::Windows::UI::Xaml;
|
2020-12-18 00:14:07 +01:00
|
|
|
using namespace winrt::Windows::UI::Xaml::Controls;
|
2021-02-08 19:04:43 +01:00
|
|
|
using namespace winrt::Windows::UI::Xaml::Data;
|
2020-12-11 22:34:21 +01:00
|
|
|
using namespace winrt::Windows::UI::Xaml::Navigation;
|
|
|
|
using namespace winrt::Windows::Foundation;
|
|
|
|
using namespace winrt::Windows::Foundation::Collections;
|
|
|
|
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
|
|
|
|
|
|
|
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|
|
|
{
|
2021-03-08 17:45:12 +01:00
|
|
|
Windows::Foundation::Collections::IObservableVector<Editor::Font> ProfileViewModel::_MonospaceFontList{ nullptr };
|
|
|
|
Windows::Foundation::Collections::IObservableVector<Editor::Font> ProfileViewModel::_FontList{ nullptr };
|
|
|
|
|
2021-05-17 04:26:47 +02:00
|
|
|
ProfileViewModel::ProfileViewModel(const Model::Profile& profile, const Model::CascadiaSettings& appSettings) :
|
2021-03-08 17:45:12 +01:00
|
|
|
_profile{ profile },
|
2021-07-09 22:43:58 +02:00
|
|
|
_defaultAppearanceViewModel{ winrt::make<implementation::AppearanceViewModel>(profile.DefaultAppearance().try_as<AppearanceConfig>()) },
|
2021-05-21 19:34:25 +02:00
|
|
|
_originalProfileGuid{ profile.Guid() },
|
2021-07-14 01:33:22 +02:00
|
|
|
_appSettings{ appSettings },
|
|
|
|
_unfocusedAppearanceViewModel{ nullptr }
|
2021-01-18 23:34:07 +01:00
|
|
|
{
|
|
|
|
// Add a property changed handler to our own property changed event.
|
2021-03-08 17:45:12 +01:00
|
|
|
// This propagates changes from the settings model to anybody listening to our
|
|
|
|
// unique view model members.
|
2021-02-08 19:04:43 +01:00
|
|
|
PropertyChanged([this](auto&&, const PropertyChangedEventArgs& args) {
|
|
|
|
const auto viewModelProperty{ args.PropertyName() };
|
2021-07-09 22:43:58 +02:00
|
|
|
if (viewModelProperty == L"IsBaseLayer")
|
2021-01-18 23:34:07 +01:00
|
|
|
{
|
2021-03-08 17:45:12 +01:00
|
|
|
// we _always_ want to show the background image settings in base layer
|
2021-01-18 23:34:07 +01:00
|
|
|
_NotifyChanges(L"BackgroundImageSettingsVisible");
|
|
|
|
}
|
2021-02-08 19:04:43 +01:00
|
|
|
else if (viewModelProperty == L"StartingDirectory")
|
2021-01-23 00:46:41 +01:00
|
|
|
{
|
2021-03-08 17:45:12 +01:00
|
|
|
// notify listener that all starting directory related values might have changed
|
|
|
|
// NOTE: this is similar to what is done with BackgroundImagePath above
|
2021-02-08 19:04:43 +01:00
|
|
|
_NotifyChanges(L"UseParentProcessDirectory", L"UseCustomStartingDirectory");
|
2021-01-23 00:46:41 +01:00
|
|
|
}
|
2021-01-18 23:34:07 +01:00
|
|
|
});
|
|
|
|
|
2021-01-23 00:46:41 +01:00
|
|
|
// Do the same for the starting directory
|
|
|
|
if (!StartingDirectory().empty())
|
|
|
|
{
|
|
|
|
_lastStartingDirectoryPath = StartingDirectory();
|
|
|
|
}
|
2021-03-08 17:45:12 +01:00
|
|
|
|
|
|
|
// generate the font list, if we don't have one
|
|
|
|
if (!_FontList || !_MonospaceFontList)
|
|
|
|
{
|
|
|
|
UpdateFontList();
|
|
|
|
}
|
2021-07-14 01:33:22 +02:00
|
|
|
|
|
|
|
if (profile.HasUnfocusedAppearance())
|
|
|
|
{
|
|
|
|
_unfocusedAppearanceViewModel = winrt::make<implementation::AppearanceViewModel>(profile.UnfocusedAppearance().try_as<AppearanceConfig>());
|
|
|
|
}
|
|
|
|
|
|
|
|
_defaultAppearanceViewModel.IsDefault(true);
|
2021-03-08 17:45:12 +01:00
|
|
|
}
|
|
|
|
|
2021-05-17 04:26:47 +02:00
|
|
|
Model::TerminalSettings ProfileViewModel::TermSettings() const
|
|
|
|
{
|
Reintroduce the Defaults page and the Reset buttons (#10588)
This pull request brings back the "Base Layer" page, now renamed to
"Defaults", and the "Reset to inherited value" buttons. The scope of
inheritance for which buttons will display has been widened.
The button will be visible in the following cases:
The user has set a setting for the current profile, and it overrides...
1. ... something in profiles.defaults.
2. ... something in a Fragment Extension profile.
3. ... something from a Dynamic Profile Generator.
4. ... something from the compiled-in defaults.
Compared to the original implementation of reset arrows, cases (1), (3)
and (4) are new. Rationale:
(1) The user can see a setting on the Defaults page, and they need a way
to reset back to it.
(3) Dynamic profiles are not meaningfully different from fragments, and
users may need a way to reset back to the default value generated
for WSL or PowerShell.
(4) The user can see a setting on the Defaults page, **BUT** they are
not the one who created it. They *still* need a way to get back to
it.
To support this, I've introduced another origin tag, "User", and renamed
"Custom" to "None". Due to the way origin/override detection works¹, we
cannot otherwise disambiguate between settings that came from the user
and settings that came from the compiled-in defaults.
Changes were required in TerminalSettings such that we could construct a
settings object with a profile that does not have a GUID. In making this
change, I fixed a bit of silliness where we took a profile, extracted
its guid, and used that guid to look up the same profile object. Oops.
I also fixed the PropertyChanged notifier to include the
XxxOverrideSource property.
The presence of the page and the reset arrows is restricted to
Preview- or Dev-branded builds. Stable builds will retain their current
behavior.
¹ `XxxOverrideSource` returns the profile *above* the current profile
that holds a value for setting `Xxx`. When the value is the
compiled-in value, `XxxOverrideSource` will be `null`. Since it's
supposed to be the profile above the current profile, it will also be
`null` if the profile contains a setting at this layer.
In short, `null` means "user specified" *or* "compiled in". Oops.
Fixes #10430
Validation
----------
* [x] Tested Release build to make sure it's mostly arrow-free (apart from fragments)
2021-07-10 00:03:41 +02:00
|
|
|
return Model::TerminalSettings::CreateWithProfile(_appSettings, _profile, nullptr).DefaultSettings();
|
2021-05-17 04:26:47 +02:00
|
|
|
}
|
|
|
|
|
2021-03-08 17:45:12 +01:00
|
|
|
// Method Description:
|
|
|
|
// - Updates the lists of fonts and sorts them alphabetically
|
|
|
|
void ProfileViewModel::UpdateFontList() noexcept
|
|
|
|
try
|
|
|
|
{
|
|
|
|
// initialize font list
|
|
|
|
std::vector<Editor::Font> fontList;
|
|
|
|
std::vector<Editor::Font> monospaceFontList;
|
|
|
|
|
|
|
|
// get a DWriteFactory
|
|
|
|
com_ptr<IDWriteFactory> factory;
|
|
|
|
THROW_IF_FAILED(DWriteCreateFactory(
|
|
|
|
DWRITE_FACTORY_TYPE_SHARED,
|
|
|
|
__uuidof(IDWriteFactory),
|
|
|
|
reinterpret_cast<::IUnknown**>(factory.put())));
|
|
|
|
|
|
|
|
// get the font collection; subscribe to updates
|
|
|
|
com_ptr<IDWriteFontCollection> fontCollection;
|
|
|
|
THROW_IF_FAILED(factory->GetSystemFontCollection(fontCollection.put(), TRUE));
|
|
|
|
|
|
|
|
for (UINT32 i = 0; i < fontCollection->GetFontFamilyCount(); ++i)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
// get the font family
|
|
|
|
com_ptr<IDWriteFontFamily> fontFamily;
|
|
|
|
THROW_IF_FAILED(fontCollection->GetFontFamily(i, fontFamily.put()));
|
|
|
|
|
|
|
|
// get the font's localized names
|
|
|
|
com_ptr<IDWriteLocalizedStrings> localizedFamilyNames;
|
|
|
|
THROW_IF_FAILED(fontFamily->GetFamilyNames(localizedFamilyNames.put()));
|
|
|
|
|
|
|
|
// construct a font entry for tracking
|
|
|
|
if (const auto fontEntry{ _GetFont(localizedFamilyNames) })
|
|
|
|
{
|
|
|
|
// check if the font is monospaced
|
|
|
|
try
|
|
|
|
{
|
|
|
|
com_ptr<IDWriteFont> font;
|
|
|
|
THROW_IF_FAILED(fontFamily->GetFirstMatchingFont(DWRITE_FONT_WEIGHT::DWRITE_FONT_WEIGHT_NORMAL,
|
|
|
|
DWRITE_FONT_STRETCH::DWRITE_FONT_STRETCH_NORMAL,
|
|
|
|
DWRITE_FONT_STYLE::DWRITE_FONT_STYLE_NORMAL,
|
|
|
|
font.put()));
|
|
|
|
|
|
|
|
// add the font name to our list of monospace fonts
|
|
|
|
const auto castedFont{ font.try_as<IDWriteFont1>() };
|
|
|
|
if (castedFont && castedFont->IsMonospacedFont())
|
|
|
|
{
|
|
|
|
monospaceFontList.emplace_back(fontEntry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CATCH_LOG();
|
|
|
|
|
|
|
|
// add the font name to our list of all fonts
|
|
|
|
fontList.emplace_back(std::move(fontEntry));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CATCH_LOG();
|
|
|
|
}
|
|
|
|
|
|
|
|
// sort and save the lists
|
2021-03-30 00:36:28 +02:00
|
|
|
std::sort(begin(fontList), end(fontList), FontComparator());
|
2021-03-08 17:45:12 +01:00
|
|
|
_FontList = single_threaded_observable_vector<Editor::Font>(std::move(fontList));
|
|
|
|
|
2021-03-30 00:36:28 +02:00
|
|
|
std::sort(begin(monospaceFontList), end(monospaceFontList), FontComparator());
|
2021-03-08 17:45:12 +01:00
|
|
|
_MonospaceFontList = single_threaded_observable_vector<Editor::Font>(std::move(monospaceFontList));
|
|
|
|
}
|
|
|
|
CATCH_LOG();
|
|
|
|
|
|
|
|
Editor::Font ProfileViewModel::_GetFont(com_ptr<IDWriteLocalizedStrings> localizedFamilyNames)
|
|
|
|
{
|
|
|
|
// used for the font's name as an identifier (i.e. text block's font family property)
|
|
|
|
std::wstring nameID;
|
|
|
|
UINT32 nameIDIndex;
|
|
|
|
|
|
|
|
// used for the font's localized name
|
|
|
|
std::wstring localizedName;
|
|
|
|
UINT32 localizedNameIndex;
|
|
|
|
|
|
|
|
// use our current locale to find the localized name
|
|
|
|
BOOL exists{ FALSE };
|
|
|
|
HRESULT hr;
|
|
|
|
wchar_t localeName[LOCALE_NAME_MAX_LENGTH];
|
|
|
|
if (GetUserDefaultLocaleName(localeName, LOCALE_NAME_MAX_LENGTH))
|
|
|
|
{
|
|
|
|
hr = localizedFamilyNames->FindLocaleName(localeName, &localizedNameIndex, &exists);
|
|
|
|
}
|
|
|
|
if (SUCCEEDED(hr) && !exists)
|
|
|
|
{
|
|
|
|
// if we can't find the font for our locale, fallback to the en-us one
|
|
|
|
// Source: https://docs.microsoft.com/en-us/windows/win32/api/dwrite/nf-dwrite-idwritelocalizedstrings-findlocalename
|
|
|
|
hr = localizedFamilyNames->FindLocaleName(L"en-us", &localizedNameIndex, &exists);
|
|
|
|
}
|
|
|
|
if (!exists)
|
|
|
|
{
|
|
|
|
// failed to find the correct locale, using the first one
|
|
|
|
localizedNameIndex = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// get the localized name
|
|
|
|
UINT32 nameLength;
|
|
|
|
THROW_IF_FAILED(localizedFamilyNames->GetStringLength(localizedNameIndex, &nameLength));
|
|
|
|
|
|
|
|
localizedName.resize(nameLength);
|
|
|
|
THROW_IF_FAILED(localizedFamilyNames->GetString(localizedNameIndex, localizedName.data(), nameLength + 1));
|
|
|
|
|
|
|
|
// now get the nameID
|
|
|
|
hr = localizedFamilyNames->FindLocaleName(L"en-us", &nameIDIndex, &exists);
|
|
|
|
if (FAILED(hr) || !exists)
|
|
|
|
{
|
|
|
|
// failed to find it, using the first one
|
|
|
|
nameIDIndex = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// get the nameID
|
|
|
|
THROW_IF_FAILED(localizedFamilyNames->GetStringLength(nameIDIndex, &nameLength));
|
|
|
|
nameID.resize(nameLength);
|
|
|
|
THROW_IF_FAILED(localizedFamilyNames->GetString(nameIDIndex, nameID.data(), nameLength + 1));
|
|
|
|
|
|
|
|
if (!nameID.empty() && !localizedName.empty())
|
|
|
|
{
|
|
|
|
return make<Font>(nameID, localizedName);
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
IObservableVector<Editor::Font> ProfileViewModel::CompleteFontList() const noexcept
|
|
|
|
{
|
|
|
|
return _FontList;
|
|
|
|
}
|
|
|
|
|
|
|
|
IObservableVector<Editor::Font> ProfileViewModel::MonospaceFontList() const noexcept
|
|
|
|
{
|
|
|
|
return _MonospaceFontList;
|
|
|
|
}
|
|
|
|
|
2021-05-21 19:34:25 +02:00
|
|
|
winrt::guid ProfileViewModel::OriginalProfileGuid() const noexcept
|
|
|
|
{
|
|
|
|
return _originalProfileGuid;
|
|
|
|
}
|
|
|
|
|
2020-12-18 00:14:07 +01:00
|
|
|
bool ProfileViewModel::CanDeleteProfile() const
|
|
|
|
{
|
2021-08-24 00:00:08 +02:00
|
|
|
return !IsBaseLayer();
|
2020-12-18 00:14:07 +01:00
|
|
|
}
|
|
|
|
|
2021-07-09 22:43:58 +02:00
|
|
|
Editor::AppearanceViewModel ProfileViewModel::DefaultAppearance()
|
2021-01-18 23:34:07 +01:00
|
|
|
{
|
2021-07-09 22:43:58 +02:00
|
|
|
return _defaultAppearanceViewModel;
|
2021-01-18 23:34:07 +01:00
|
|
|
}
|
|
|
|
|
2021-07-14 01:33:22 +02:00
|
|
|
bool ProfileViewModel::HasUnfocusedAppearance()
|
|
|
|
{
|
|
|
|
return _profile.HasUnfocusedAppearance();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ProfileViewModel::EditableUnfocusedAppearance()
|
|
|
|
{
|
|
|
|
if constexpr (Feature_EditableUnfocusedAppearance::IsEnabled())
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ProfileViewModel::ShowUnfocusedAppearance()
|
|
|
|
{
|
|
|
|
return EditableUnfocusedAppearance() && HasUnfocusedAppearance();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProfileViewModel::CreateUnfocusedAppearance(const Windows::Foundation::Collections::IMapView<hstring, Model::ColorScheme>& schemes,
|
|
|
|
const IHostedInWindow& windowRoot)
|
|
|
|
{
|
|
|
|
_profile.CreateUnfocusedAppearance();
|
|
|
|
|
|
|
|
_unfocusedAppearanceViewModel = winrt::make<implementation::AppearanceViewModel>(_profile.UnfocusedAppearance().try_as<AppearanceConfig>());
|
|
|
|
_unfocusedAppearanceViewModel.Schemes(schemes);
|
|
|
|
_unfocusedAppearanceViewModel.WindowRoot(windowRoot);
|
|
|
|
|
2021-07-16 19:18:40 +02:00
|
|
|
_NotifyChanges(L"UnfocusedAppearance", L"HasUnfocusedAppearance", L"ShowUnfocusedAppearance");
|
2021-07-14 01:33:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void ProfileViewModel::DeleteUnfocusedAppearance()
|
|
|
|
{
|
|
|
|
_profile.DeleteUnfocusedAppearance();
|
|
|
|
|
|
|
|
_unfocusedAppearanceViewModel = nullptr;
|
|
|
|
|
2021-07-16 19:18:40 +02:00
|
|
|
_NotifyChanges(L"UnfocusedAppearance", L"HasUnfocusedAppearance", L"ShowUnfocusedAppearance");
|
2021-07-14 01:33:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Editor::AppearanceViewModel ProfileViewModel::UnfocusedAppearance()
|
|
|
|
{
|
|
|
|
return _unfocusedAppearanceViewModel;
|
|
|
|
}
|
|
|
|
|
2021-01-23 00:46:41 +01:00
|
|
|
bool ProfileViewModel::UseParentProcessDirectory()
|
|
|
|
{
|
|
|
|
return StartingDirectory().empty();
|
|
|
|
}
|
|
|
|
|
|
|
|
// This function simply returns the opposite of UseParentProcessDirectory.
|
|
|
|
// We bind the 'IsEnabled' parameters of the textbox and browse button
|
|
|
|
// to this because it needs to be the reverse of UseParentProcessDirectory
|
|
|
|
// but we don't want to create a whole new converter for inverting a boolean
|
|
|
|
bool ProfileViewModel::UseCustomStartingDirectory()
|
|
|
|
{
|
|
|
|
return !UseParentProcessDirectory();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProfileViewModel::UseParentProcessDirectory(const bool useParent)
|
|
|
|
{
|
|
|
|
if (useParent)
|
|
|
|
{
|
|
|
|
// Stash the current value of StartingDirectory. If the user
|
|
|
|
// checks and un-checks the "Use parent process directory" button, we want
|
|
|
|
// the path that we display in the text box to remain unchanged.
|
|
|
|
//
|
|
|
|
// Only stash this value if it's not empty
|
|
|
|
if (!StartingDirectory().empty())
|
|
|
|
{
|
|
|
|
_lastStartingDirectoryPath = StartingDirectory();
|
|
|
|
}
|
|
|
|
StartingDirectory(L"");
|
|
|
|
}
|
2021-04-21 12:53:41 +02:00
|
|
|
else
|
2021-01-23 00:46:41 +01:00
|
|
|
{
|
|
|
|
// Restore the path we had previously cached as long as it wasn't empty
|
|
|
|
// If it was empty, set the starting directory to %USERPROFILE%
|
|
|
|
// (we need to set it to something non-empty otherwise we will automatically
|
|
|
|
// disable the text box)
|
|
|
|
if (_lastStartingDirectoryPath.empty())
|
|
|
|
{
|
|
|
|
StartingDirectory(L"%USERPROFILE%");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
StartingDirectory(_lastStartingDirectoryPath);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-18 00:14:07 +01:00
|
|
|
void ProfilePageNavigationState::DeleteProfile()
|
|
|
|
{
|
|
|
|
auto deleteProfileArgs{ winrt::make_self<DeleteProfileEventArgs>(_Profile.Guid()) };
|
|
|
|
_DeleteProfileHandlers(*this, *deleteProfileArgs);
|
|
|
|
}
|
|
|
|
|
2021-07-14 01:33:22 +02:00
|
|
|
void ProfilePageNavigationState::CreateUnfocusedAppearance()
|
|
|
|
{
|
|
|
|
_Profile.CreateUnfocusedAppearance(_Schemes, _WindowRoot);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProfilePageNavigationState::DeleteUnfocusedAppearance()
|
|
|
|
{
|
|
|
|
_Profile.DeleteUnfocusedAppearance();
|
|
|
|
}
|
|
|
|
|
2020-12-11 22:34:21 +01:00
|
|
|
Profiles::Profiles() :
|
2021-05-17 04:26:47 +02:00
|
|
|
_previewControl{ Control::TermControl(Model::TerminalSettings{}, make<PreviewConnection>()) }
|
2020-12-11 22:34:21 +01:00
|
|
|
{
|
|
|
|
InitializeComponent();
|
|
|
|
|
2021-03-17 21:47:24 +01:00
|
|
|
INITIALIZE_BINDABLE_ENUM_SETTING(AntiAliasingMode, TextAntialiasingMode, winrt::Microsoft::Terminal::Control::TextAntialiasingMode, L"Profile_AntialiasingMode", L"Content");
|
2021-01-14 12:47:33 +01:00
|
|
|
INITIALIZE_BINDABLE_ENUM_SETTING_REVERSE_ORDER(CloseOnExitMode, CloseOnExitMode, winrt::Microsoft::Terminal::Settings::Model::CloseOnExitMode, L"Profile_CloseOnExit", L"Content");
|
2021-03-17 21:47:24 +01:00
|
|
|
INITIALIZE_BINDABLE_ENUM_SETTING(ScrollState, ScrollbarState, winrt::Microsoft::Terminal::Control::ScrollbarState, L"Profile_ScrollbarVisibility", L"Content");
|
2020-12-11 22:34:21 +01:00
|
|
|
|
2021-02-08 19:04:43 +01:00
|
|
|
const auto startingDirCheckboxTooltip{ ToolTipService::GetToolTip(StartingDirectoryUseParentCheckbox()) };
|
|
|
|
Automation::AutomationProperties::SetFullDescription(StartingDirectoryUseParentCheckbox(), unbox_value<hstring>(startingDirCheckboxTooltip));
|
|
|
|
|
|
|
|
Automation::AutomationProperties::SetName(DeleteButton(), RS_(L"Profile_DeleteButton/Text"));
|
2021-05-17 04:26:47 +02:00
|
|
|
|
|
|
|
_previewControl.IsEnabled(false);
|
|
|
|
_previewControl.AllowFocusWhenDisabled(false);
|
|
|
|
ControlPreview().Child(_previewControl);
|
2020-12-11 22:34:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Profiles::OnNavigatedTo(const NavigationEventArgs& e)
|
|
|
|
{
|
|
|
|
_State = e.Parameter().as<Editor::ProfilePageNavigationState>();
|
|
|
|
|
2021-03-08 17:45:12 +01:00
|
|
|
// generate the font list, if we don't have one
|
|
|
|
if (!_State.Profile().CompleteFontList() || !_State.Profile().MonospaceFontList())
|
|
|
|
{
|
|
|
|
ProfileViewModel::UpdateFontList();
|
|
|
|
}
|
|
|
|
|
2021-01-14 20:06:10 +01:00
|
|
|
// Check the use parent directory box if the starting directory is empty
|
|
|
|
if (_State.Profile().StartingDirectory().empty())
|
|
|
|
{
|
|
|
|
StartingDirectoryUseParentCheckbox().IsChecked(true);
|
|
|
|
}
|
2021-01-26 00:06:33 +01:00
|
|
|
|
2021-02-08 19:04:43 +01:00
|
|
|
// Subscribe to some changes in the view model
|
|
|
|
// These changes should force us to update our own set of "Current<Setting>" members,
|
|
|
|
// and propagate those changes to the UI
|
|
|
|
_ViewModelChangedRevoker = _State.Profile().PropertyChanged(winrt::auto_revoke, [=](auto&&, const PropertyChangedEventArgs& args) {
|
|
|
|
const auto settingName{ args.PropertyName() };
|
2021-07-09 22:43:58 +02:00
|
|
|
if (settingName == L"AntialiasingMode")
|
2021-02-08 19:04:43 +01:00
|
|
|
{
|
|
|
|
_PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"CurrentAntiAliasingMode" });
|
|
|
|
}
|
|
|
|
else if (settingName == L"CloseOnExit")
|
|
|
|
{
|
|
|
|
_PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"CurrentCloseOnExitMode" });
|
|
|
|
}
|
|
|
|
else if (settingName == L"BellStyle")
|
|
|
|
{
|
2021-05-25 00:51:03 +02:00
|
|
|
_PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"IsBellStyleFlagSet" });
|
2021-02-08 19:04:43 +01:00
|
|
|
}
|
|
|
|
else if (settingName == L"ScrollState")
|
|
|
|
{
|
|
|
|
_PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"CurrentScrollState" });
|
|
|
|
}
|
2021-07-09 22:43:58 +02:00
|
|
|
_previewControl.Settings(_State.Profile().TermSettings());
|
|
|
|
_previewControl.UpdateSettings();
|
|
|
|
});
|
|
|
|
|
|
|
|
// The Appearances object handles updating the values in the settings UI, but
|
|
|
|
// we still need to listen to the changes here just to update the preview control
|
|
|
|
_AppearanceViewModelChangedRevoker = _State.Profile().DefaultAppearance().PropertyChanged(winrt::auto_revoke, [=](auto&&, const PropertyChangedEventArgs& /*args*/) {
|
2021-05-17 04:26:47 +02:00
|
|
|
_previewControl.Settings(_State.Profile().TermSettings());
|
|
|
|
_previewControl.UpdateSettings();
|
2021-02-08 19:04:43 +01:00
|
|
|
});
|
|
|
|
|
2021-01-26 00:06:33 +01:00
|
|
|
// Navigate to the pivot in the provided navigation state
|
|
|
|
ProfilesPivot().SelectedIndex(static_cast<int>(_State.LastActivePivot()));
|
2021-05-17 04:26:47 +02:00
|
|
|
|
|
|
|
_previewControl.Settings(_State.Profile().TermSettings());
|
|
|
|
// There is a possibility that the control has not fully initialized yet,
|
|
|
|
// so wait for it to initialize before updating the settings (so we know
|
|
|
|
// that the renderer is set up)
|
|
|
|
_previewControl.Initialized([&](auto&& /*s*/, auto&& /*e*/) {
|
|
|
|
_previewControl.UpdateSettings();
|
|
|
|
});
|
2020-12-11 22:34:21 +01:00
|
|
|
}
|
|
|
|
|
2021-02-08 19:04:43 +01:00
|
|
|
void Profiles::OnNavigatedFrom(const NavigationEventArgs& /*e*/)
|
|
|
|
{
|
|
|
|
_ViewModelChangedRevoker.revoke();
|
2021-07-09 22:43:58 +02:00
|
|
|
_AppearanceViewModelChangedRevoker.revoke();
|
2020-12-11 22:34:21 +01:00
|
|
|
}
|
|
|
|
|
2021-05-25 00:51:03 +02:00
|
|
|
bool Profiles::IsBellStyleFlagSet(const uint32_t flag)
|
|
|
|
{
|
|
|
|
return (WI_EnumValue(_State.Profile().BellStyle()) & flag) == flag;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Profiles::SetBellStyleAudible(winrt::Windows::Foundation::IReference<bool> on)
|
|
|
|
{
|
|
|
|
auto currentStyle = State().Profile().BellStyle();
|
|
|
|
WI_UpdateFlag(currentStyle, Model::BellStyle::Audible, winrt::unbox_value<bool>(on));
|
|
|
|
State().Profile().BellStyle(currentStyle);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Profiles::SetBellStyleWindow(winrt::Windows::Foundation::IReference<bool> on)
|
|
|
|
{
|
|
|
|
auto currentStyle = State().Profile().BellStyle();
|
|
|
|
WI_UpdateFlag(currentStyle, Model::BellStyle::Window, winrt::unbox_value<bool>(on));
|
|
|
|
State().Profile().BellStyle(currentStyle);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Profiles::SetBellStyleTaskbar(winrt::Windows::Foundation::IReference<bool> on)
|
|
|
|
{
|
|
|
|
auto currentStyle = State().Profile().BellStyle();
|
|
|
|
WI_UpdateFlag(currentStyle, Model::BellStyle::Taskbar, winrt::unbox_value<bool>(on));
|
|
|
|
State().Profile().BellStyle(currentStyle);
|
|
|
|
}
|
|
|
|
|
2020-12-18 00:14:07 +01:00
|
|
|
void Profiles::DeleteConfirmation_Click(IInspectable const& /*sender*/, RoutedEventArgs const& /*e*/)
|
|
|
|
{
|
|
|
|
auto state{ winrt::get_self<ProfilePageNavigationState>(_State) };
|
|
|
|
state->DeleteProfile();
|
|
|
|
}
|
|
|
|
|
2021-07-14 01:33:22 +02:00
|
|
|
void Profiles::CreateUnfocusedAppearance_Click(IInspectable const& /*sender*/, RoutedEventArgs const& /*e*/)
|
|
|
|
{
|
|
|
|
_State.CreateUnfocusedAppearance();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Profiles::DeleteUnfocusedAppearance_Click(IInspectable const& /*sender*/, RoutedEventArgs const& /*e*/)
|
|
|
|
{
|
|
|
|
_State.DeleteUnfocusedAppearance();
|
|
|
|
}
|
|
|
|
|
2020-12-11 22:34:21 +01:00
|
|
|
fire_and_forget Profiles::Icon_Click(IInspectable const&, RoutedEventArgs const&)
|
|
|
|
{
|
|
|
|
auto lifetime = get_strong();
|
|
|
|
|
2021-04-12 18:55:51 +02:00
|
|
|
const auto parentHwnd{ reinterpret_cast<HWND>(_State.WindowRoot().GetHostingWindow()) };
|
|
|
|
auto file = co_await OpenImagePicker(parentHwnd);
|
2021-04-12 15:12:08 +02:00
|
|
|
if (!file.empty())
|
2020-12-11 22:34:21 +01:00
|
|
|
{
|
2021-04-12 15:12:08 +02:00
|
|
|
_State.Profile().Icon(file);
|
2020-12-11 22:34:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fire_and_forget Profiles::Commandline_Click(IInspectable const&, RoutedEventArgs const&)
|
|
|
|
{
|
|
|
|
auto lifetime = get_strong();
|
|
|
|
|
2021-04-12 15:12:08 +02:00
|
|
|
static constexpr COMDLG_FILTERSPEC supportedFileTypes[] = {
|
|
|
|
{ L"Executable Files (*.exe, *.cmd, *.bat)", L"*.exe;*.cmd;*.bat" },
|
|
|
|
{ L"All Files (*.*)", L"*.*" }
|
|
|
|
};
|
2020-12-11 22:34:21 +01:00
|
|
|
|
2021-04-12 15:12:08 +02:00
|
|
|
static constexpr winrt::guid clientGuidExecutables{ 0x2E7E4331, 0x0800, 0x48E6, { 0xB0, 0x17, 0xA1, 0x4C, 0xD8, 0x73, 0xDD, 0x58 } };
|
2021-04-12 18:55:51 +02:00
|
|
|
const auto parentHwnd{ reinterpret_cast<HWND>(_State.WindowRoot().GetHostingWindow()) };
|
|
|
|
auto path = co_await OpenFilePicker(parentHwnd, [](auto&& dialog) {
|
2021-04-12 15:12:08 +02:00
|
|
|
THROW_IF_FAILED(dialog->SetClientGuid(clientGuidExecutables));
|
|
|
|
try
|
|
|
|
{
|
|
|
|
auto folderShellItem{ winrt::capture<IShellItem>(&SHGetKnownFolderItem, FOLDERID_ComputerFolder, KF_FLAG_DEFAULT, nullptr) };
|
|
|
|
dialog->SetDefaultFolder(folderShellItem.get());
|
|
|
|
}
|
|
|
|
CATCH_LOG(); // non-fatal
|
|
|
|
THROW_IF_FAILED(dialog->SetFileTypes(ARRAYSIZE(supportedFileTypes), supportedFileTypes));
|
|
|
|
THROW_IF_FAILED(dialog->SetFileTypeIndex(1)); // the array is 1-indexed
|
|
|
|
THROW_IF_FAILED(dialog->SetDefaultExtension(L"exe;cmd;bat"));
|
|
|
|
});
|
2020-12-11 22:34:21 +01:00
|
|
|
|
2021-04-12 15:12:08 +02:00
|
|
|
if (!path.empty())
|
2020-12-11 22:34:21 +01:00
|
|
|
{
|
2021-04-12 15:12:08 +02:00
|
|
|
_State.Profile().Commandline(path);
|
2020-12-11 22:34:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fire_and_forget Profiles::StartingDirectory_Click(IInspectable const&, RoutedEventArgs const&)
|
|
|
|
{
|
|
|
|
auto lifetime = get_strong();
|
2021-04-12 18:55:51 +02:00
|
|
|
const auto parentHwnd{ reinterpret_cast<HWND>(_State.WindowRoot().GetHostingWindow()) };
|
|
|
|
auto folder = co_await OpenFilePicker(parentHwnd, [](auto&& dialog) {
|
2021-04-12 15:12:08 +02:00
|
|
|
static constexpr winrt::guid clientGuidFolderPicker{ 0xAADAA433, 0xB04D, 0x4BAE, { 0xB1, 0xEA, 0x1E, 0x6C, 0xD1, 0xCD, 0xA6, 0x8B } };
|
|
|
|
THROW_IF_FAILED(dialog->SetClientGuid(clientGuidFolderPicker));
|
|
|
|
try
|
|
|
|
{
|
|
|
|
auto folderShellItem{ winrt::capture<IShellItem>(&SHGetKnownFolderItem, FOLDERID_ComputerFolder, KF_FLAG_DEFAULT, nullptr) };
|
|
|
|
dialog->SetDefaultFolder(folderShellItem.get());
|
|
|
|
}
|
|
|
|
CATCH_LOG(); // non-fatal
|
|
|
|
|
|
|
|
DWORD flags{};
|
|
|
|
THROW_IF_FAILED(dialog->GetOptions(&flags));
|
|
|
|
THROW_IF_FAILED(dialog->SetOptions(flags | FOS_PICKFOLDERS)); // folders only
|
|
|
|
});
|
|
|
|
|
|
|
|
if (!folder.empty())
|
|
|
|
{
|
|
|
|
_State.Profile().StartingDirectory(folder);
|
2020-12-11 22:34:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-26 00:06:33 +01:00
|
|
|
void Profiles::Pivot_SelectionChanged(Windows::Foundation::IInspectable const& /*sender*/,
|
2021-02-08 19:04:43 +01:00
|
|
|
RoutedEventArgs const& /*e*/)
|
2021-01-26 00:06:33 +01:00
|
|
|
{
|
|
|
|
_State.LastActivePivot(static_cast<Editor::ProfilesPivots>(ProfilesPivot().SelectedIndex()));
|
|
|
|
}
|
2020-12-11 22:34:21 +01:00
|
|
|
}
|