Compare commits

..

8 Commits

Author SHA1 Message Date
Dustin L. Howett
8fe6c21ef8 Keep the font size delta across settings reloads (#20230)
This is a trivial fix for an issue we get a somewhat outsized number of
complaints about.

When the user has adjusted the font size in any direction, we'll just
keep track of the magnitude and apply it every time the settings change.

Yes, this means that if you zoom once and then change your real font
size it's going to zoom even more.

That probably doesn't matter.

Refs #11522
2026-05-15 13:56:48 -05:00
aarushi singh
abeac1b135 Use PlaySoundW for profile bell sounds (#20031)
We believed that this would fix an issue on Windows 10, where the volume
mixer would forget Windows Terminal after every relaunch. It turns out
that it does not.

Still, this code is much more concise and doesn't require yet another
WinRT object. Story of our lives.

Refs #17733
2026-05-15 20:51:01 +02:00
aarushi singh
12e3455bb2 Make DECSTR cursor restore behave like xterm in alt screen buffer (#20032)
There is some disagreement among terminal emulators as to whether DECSTR
(and therefore, soft reset) restores the cursor in the active buffer or
in all buffers.

We had previously chosen to restore the cursor in all buffers.

xterm restores the cursor only in the active buffer.

Closes #19918
2026-05-14 17:04:58 +00:00
Lucas Trzesniewski
fb71a0462e Add safeUriSchemes setting (#20207)
This adds a `safeUriSchemes` global setting which lets you define
hyperlink URI schemes which the user considers safe. No confirmation
dialog will be shown when trying to open hyperlinks which use these
schemes.

- This solves the root issue, but doesn't introduce any UI or
  documentation changes. I wanted to validate the approach and
  implementation with you first.
- I closely followed the code handling the `disabledProfileSources`
  setting, which is of the same type.
- This feature does not change the behavior of `http`, `https` and
  `file` schemes.

Validation

I ran the dev terminal, and tested the behavior by clicking on `vscode`
hyperlinks generated by ripgrep with various `safeUriSchemes` settings:

- Setting not defined - asks for confirmation
- `["vscode"]` - does not ask for confirmation
- `["foo", "vscode"]` - does not ask for confirmation
- `["foo"]` - asks for confirmation
- `null` - asks for confirmation
- `[]` - asks for confirmation
- `[""]` - asks for confirmation
- `[{"foo": "bar"}]` - fails to deserialize (as expected)

A few uinit tests failed, but they seem unrelated to these changes:
- `KeyBindingTests` in `UnitTests_SettingsModel`, probably because I use
  an AZERTY keyboard.
- A few `Conhost` tests, but I didn't touch this part

Refs #20065
Closes #20191
2026-05-12 21:32:56 +00:00
Dustin L. Howett
c829d4ca54 sixel: prevent allocating an absurd amount of memory or writing OOB (#20213)
This commit implements two fixes for the integer overflow/out-of-bounds
write reported in #20149.

First, it catches any exception generated in sixel char processing
(which will prevent `out_of_memory` or `bad_alloc` from being ignored
sight-unseen, and will prevent the consumption of further DCS content).

Second, it prevents us from allocating memory for an image which will
never be displayed (because it exceeds the height of the display.)

This supersedes prior work in #20153 for the same issues.

Closes #20149
Closes #20153

Co-authored-by: James Holderness <j4_james@hotmail.com>
2026-05-12 18:15:19 +02:00
Dustin L. Howett
b991eb048e Only set startingTitle once, clear up title fallback handling (#20214)
This commit ensures that we only set the starting title once when we
open a new terminal pane.

It also consolidates all title selection into Terminal::GetConsoleTitle,
which is now used in the TitleChanged event.

TitleChanged no longer stores a separate copy of the starting title if
an application attempts to _clear_ the title; that seems like a poorer
implementation of what we already had.

This supersedes work in #20204.

Closes #19340
Closes #20204

Co-authored-by: imsh <im.shaedar@gmail.com>
2026-05-12 00:08:57 +00:00
Dustin L. Howett
3e3b3ad883 Reject DTTERM Window Manipulation resizes with the current size (#20183)
This may help #20182 and #20112
Closes #19310
2026-05-11 17:42:18 -05:00
Windows Console Service Bot
d3f76e7acf Localization Updates - main - 05/07/2026 03:04:15 (#20196)
Co-authored-by: Console Service Bot <consvc@microsoft.com>
2026-05-07 14:03:13 -05:00
35 changed files with 445 additions and 698 deletions

View File

@@ -1075,6 +1075,7 @@ NOCONTEXTHELP
NOCOPYBITS
nodiscard
NODUP
NODEFAULT
noexcepts
NOFONT
NOHIDDENTEXT
@@ -1105,6 +1106,7 @@ NOSIZE
NOSNAPSHOT
NOTHOUSANDS
NOTICKS
notif
NOTIMEOUTIFNOTHUNG
NOTIMPL
NOTOPMOST
@@ -1560,6 +1562,7 @@ SMARTQUOTE
SMTO
snapcx
snapcy
SND
snk
SOLIDBOX
Solutiondir

View File

@@ -2472,6 +2472,13 @@
},
"type": "array"
},
"safeUriSchemes": {
"description": "Specifies a list of URI schemes that are considered safe. No confirmation will be required to open URIs with these schemes.",
"items": {
"type": "string"
},
"type": "array"
},
"rendering.graphicsAPI": {
"description": "Direct3D 11 provides a more performant and feature-rich experience, whereas Direct2D is more stable. The default option \"Automatic\" will pick the API that best fits your graphics hardware. If you experience significant issues, consider using Direct2D.",
"type": "string",

View File

@@ -496,12 +496,48 @@
<value>第三方通知</value>
<comment>A hyperlink name for the Terminal's third-party notices</comment>
</data>
<data name="ConfirmCloseDialog_Cancel" xml:space="preserve">
<value>取消</value>
</data>
<data name="ConfirmCloseDialog_CloseAllTitle" xml:space="preserve">
<value>是否要关闭所有窗口?</value>
</data>
<data name="ConfirmCloseDialog_CloseAllPrimary" xml:space="preserve">
<value>全部关闭</value>
</data>
<data name="ConfirmCloseDialog_WindowTitle" xml:space="preserve">
<value>是否要关闭所有标签页?</value>
</data>
<data name="ConfirmCloseDialog_WindowPrimary" xml:space="preserve">
<value>全部关闭</value>
</data>
<data name="ConfirmCloseDialog_TabTitle" xml:space="preserve">
<value>是否要关闭此选项卡?</value>
</data>
<data name="ConfirmCloseDialog_TabPrimary" xml:space="preserve">
<value>关闭选项卡</value>
</data>
<data name="ConfirmCloseDialog_PaneTitle" xml:space="preserve">
<value>是否要关闭此窗格?</value>
</data>
<data name="ConfirmCloseDialog_PanePrimary" xml:space="preserve">
<value>关闭窗格</value>
</data>
<data name="ConfirmCloseDialog_MultipleTabsTitle" xml:space="preserve">
<value>是否要关闭这些选项卡?</value>
</data>
<data name="ConfirmCloseDialog_MultipleTabsPrimary" xml:space="preserve">
<value>关闭选项卡</value>
</data>
<data name="ConfirmCloseDialog_MultiplePanesTitle" xml:space="preserve">
<value>是否要关闭这些窗格?</value>
</data>
<data name="ConfirmCloseDialog_MultiplePanesPrimary" xml:space="preserve">
<value>关闭窗格</value>
</data>
<data name="DontAskAgainCheckBox.Content" xml:space="preserve">
<value>不再询问</value>
</data>
<data name="CloseReadOnlyDialog.CloseButtonText" xml:space="preserve">
<value>取消</value>
</data>

View File

@@ -3307,13 +3307,15 @@ namespace winrt::TerminalApp::implementation
return true;
}
bool TerminalPage::_IsUriConsideredSomewhatSafe(const winrt::Windows::Foundation::Uri& parsedUri)
bool TerminalPage::_IsUriConsideredSomewhatSafe(const winrt::Windows::Foundation::Uri& parsedUri) const
{
if (parsedUri.SchemeName() == L"http" || parsedUri.SchemeName() == L"https")
const auto& schemeName = parsedUri.SchemeName();
if (schemeName == L"http" || schemeName == L"https")
{
return true;
}
if (parsedUri.SchemeName() == L"file")
if (schemeName == L"file")
{
static const auto pathext{ wil::TryGetEnvironmentVariableW<std::wstring>(L"PATHEXT") };
const auto filename = parsedUri.Path();
@@ -3327,6 +3329,16 @@ namespace winrt::TerminalApp::implementation
return true;
}
if (const auto& safeSchemes = _settings.GlobalSettings().SafeUriSchemes())
{
for (const auto& scheme : safeSchemes)
{
if (til::equals_insensitive_ascii(schemeName, scheme))
{
return true;
}
}
}
return false;
}

View File

@@ -438,7 +438,7 @@ namespace winrt::TerminalApp::implementation
safe_void_coroutine _OpenHyperlinkHandler(const IInspectable sender, const Microsoft::Terminal::Control::OpenHyperlinkEventArgs eventArgs);
static bool _IsUriSupported(const winrt::Windows::Foundation::Uri& parsedUri);
static bool _IsUriConsideredSomewhatSafe(const winrt::Windows::Foundation::Uri& parsedUri);
bool _IsUriConsideredSomewhatSafe(const winrt::Windows::Foundation::Uri& parsedUri) const;
void _ShowCouldNotOpenDialog(winrt::hstring reason, winrt::hstring uri);
bool _CopyText(bool dismissSelection, bool singleLine, bool withControlSequences, Microsoft::Terminal::Control::CopyFormat formats);

View File

@@ -71,18 +71,6 @@ namespace winrt::TerminalApp::implementation
_removeControlEvents();
_control.Close();
// Clear out our media player callbacks, and stop any playing media. This
// will prevent the callback from being triggered after we've closed, and
// also make sure that our sound stops when we're closed.
if (_bellPlayer)
{
_bellPlayer.Pause();
_bellPlayer.Source(nullptr);
_bellPlayer.Close();
_bellPlayer = nullptr;
_bellPlayerCreated = false;
}
}
winrt::hstring TerminalPaneContent::Icon() const
@@ -275,14 +263,15 @@ namespace winrt::TerminalApp::implementation
auto sounds{ _profile.BellSound() };
if (sounds && sounds.Size() > 0)
{
winrt::hstring soundPath{ sounds.GetAt(rand() % sounds.Size()).Resolved() };
winrt::Windows::Foundation::Uri uri{ soundPath };
_playBellSound(uri);
// Sound paths are resolved and validated by CascadiaSettings
// before we reach this point.
auto soundPath{ sounds.GetAt(rand() % sounds.Size()).Resolved() };
PlaySoundW(soundPath.c_str(), nullptr, SND_FILENAME | SND_ASYNC | SND_SENTRY | SND_NODEFAULT);
}
else
{
const auto soundAlias = reinterpret_cast<LPCTSTR>(SND_ALIAS_SYSTEMHAND);
PlaySound(soundAlias, NULL, SND_ALIAS_ID | SND_ASYNC | SND_SENTRY);
const auto soundAlias = reinterpret_cast<LPCWSTR>(SND_ALIAS_SYSTEMHAND);
PlaySoundW(soundAlias, nullptr, SND_ALIAS_ID | SND_ASYNC | SND_SENTRY);
}
}
@@ -300,33 +289,6 @@ namespace winrt::TerminalApp::implementation
}
}
safe_void_coroutine TerminalPaneContent::_playBellSound(winrt::Windows::Foundation::Uri uri)
{
auto weakThis{ get_weak() };
co_await wil::resume_foreground(_control.Dispatcher());
if (auto pane{ weakThis.get() })
{
if (!_bellPlayerCreated)
{
// The MediaPlayer might not exist on Windows N SKU.
try
{
_bellPlayerCreated = true;
_bellPlayer = winrt::Windows::Media::Playback::MediaPlayer();
// GH#12258: The media keys (like play/pause) should have no effect on our bell sound.
_bellPlayer.CommandManager().IsEnabled(false);
}
CATCH_LOG();
}
if (_bellPlayer)
{
const auto source{ winrt::Windows::Media::Core::MediaSource::CreateFromUri(uri) };
const auto item{ winrt::Windows::Media::Playback::MediaPlaybackItem(source) };
_bellPlayer.Source(item);
_bellPlayer.Play();
}
}
}
void TerminalPaneContent::_closeTerminalRequestedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/,
const winrt::Windows::Foundation::IInspectable& /*args*/)
{

View File

@@ -76,9 +76,6 @@ namespace winrt::TerminalApp::implementation
std::shared_ptr<TerminalSettingsCache> _cache{};
bool _isDefTermSession{ false };
winrt::Windows::Media::Playback::MediaPlayer _bellPlayer{ nullptr };
bool _bellPlayerCreated{ false };
struct ControlEventTokens
{
winrt::Microsoft::Terminal::Control::TermControl::ConnectionStateChanged_revoker _ConnectionStateChanged;
@@ -96,8 +93,6 @@ namespace winrt::TerminalApp::implementation
void _setupControlEvents();
void _removeControlEvents();
safe_void_coroutine _playBellSound(winrt::Windows::Foundation::Uri uri);
safe_void_coroutine _controlConnectionStateChangedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& /*args*/);
void _controlWarningBellHandler(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable& e);

View File

@@ -925,7 +925,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// Manually turn off acrylic if they turn off transparency.
_runtimeUseAcrylic = _settings.Opacity() < 1.0 && _settings.UseAcrylic();
const auto sizeChanged = _setFontSizeUnderLock(_settings.FontSize());
const auto sizeChanged = _setFontSizeUnderLock(_settings.FontSize() + _accumulatedFontSizeDelta);
// Update the terminal core with its new Core settings
_terminal->UpdateSettings(_settings);
@@ -1163,11 +1163,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - none
void ControlCore::ResetFontSize()
{
const auto lock = _terminal->LockForWriting();
if (_setFontSizeUnderLock(_settings.FontSize()))
if (std::exchange(_accumulatedFontSizeDelta, 0.f) != 0.f)
{
_refreshSizeUnderLock();
// No point in doing this if there was no delta.
AdjustFontSize(0);
}
}
@@ -1177,9 +1176,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - fontSizeDelta: The amount to increase or decrease the font size by.
void ControlCore::AdjustFontSize(float fontSizeDelta)
{
_accumulatedFontSizeDelta += fontSizeDelta;
const auto lock = _terminal->LockForWriting();
if (_setFontSizeUnderLock(_desiredFont.GetFontSize() + fontSizeDelta))
if (_setFontSizeUnderLock(_settings.FontSize() + _accumulatedFontSizeDelta))
{
_refreshSizeUnderLock();
}

View File

@@ -391,6 +391,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
bool _colorGlyphs = true;
CSSLengthPercentage _cellWidth;
CSSLengthPercentage _cellHeight;
float _accumulatedFontSizeDelta = 0.f; // Preserved across reloads to prevent user zoom from being overwritten.
// Rendering stuff.
winrt::handle _lastSwapChainHandle{ nullptr };

View File

@@ -98,7 +98,6 @@ void Terminal::UpdateSettings(ICoreSettings settings)
_answerbackMessage = settings.AnswerbackMessage();
_wordDelimiters = settings.WordDelimiters();
_suppressApplicationTitle = settings.SuppressApplicationTitle();
_startingTitle = settings.StartingTitle();
_trimBlockSelection = settings.TrimBlockSelection();
_autoMarkPrompts = settings.AutoMarkPrompts();
_rainbowSuggestions = settings.RainbowSuggestions();
@@ -124,6 +123,11 @@ void Terminal::UpdateSettings(ICoreSettings settings)
// Save the changes made above and in UpdateAppearance as the new default render settings.
GetRenderSettings().SaveDefaultSettings();
if (!_startingTitle)
{
_startingTitle = settings.StartingTitle();
}
if (!_startingTabColor && settings.StartingTabColor())
{
_startingTabColor = settings.StartingTabColor().Value();

View File

@@ -349,7 +349,7 @@ private:
::Microsoft::Console::VirtualTerminal::TerminalInput _terminalInput;
std::optional<std::wstring> _title;
std::wstring _startingTitle;
std::optional<std::wstring> _startingTitle;
std::optional<til::color> _startingTabColor;
std::vector<til::point_span> _searchHighlights;

View File

@@ -91,8 +91,12 @@ void Terminal::SetWindowTitle(const std::wstring_view title)
_assertLocked();
if (!_suppressApplicationTitle)
{
_title.emplace(title.empty() ? _startingTitle : title);
_pfnTitleChanged(_title.value());
_title.reset();
if (!title.empty())
{
_title.emplace(title);
}
_pfnTitleChanged(GetConsoleTitle());
}
}
@@ -112,6 +116,13 @@ bool Terminal::ResizeWindow(const til::CoordType width, const til::CoordType hei
return false;
}
const auto currentDimensions = _GetMutableViewport().Dimensions();
if (width == currentDimensions.width && height == currentDimensions.height)
{
return false;
}
if (_pfnWindowSizeChanged)
{
_pfnWindowSizeChanged(width, height);

View File

@@ -184,11 +184,18 @@ void Terminal::SelectNewRegion(const til::point coordStart, const til::point coo
std::wstring_view Terminal::GetConsoleTitle() const noexcept
{
_assertLocked();
if (_title.has_value())
if (_title)
{
return *_title;
}
return _startingTitle;
if (_startingTitle)
{
return *_startingTitle;
}
return {};
}
// Method Description:

View File

@@ -17,7 +17,6 @@
#include "ColorSchemes.h"
#include "EditColorScheme.h"
#include "AddProfile.h"
#include "Profiles.h"
#include "InteractionViewModel.h"
#include "LaunchViewModel.h"
#include "NewTabMenuViewModel.h"
@@ -103,7 +102,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
MainPage::MainPage(const CascadiaSettings& settings) :
_settingsSource{ settings },
_settingsClone{ settings.Copy() },
_profilesPageVM{ winrt::make<ProfilesPageViewModel>() }
_profileVMs{ single_threaded_observable_vector<Editor::ProfileViewModel>() }
{
InitializeComponent();
_UpdateBackgroundForMica();
@@ -157,10 +156,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
}
});
_SetupProfilesPageEventHandling();
// Make sure to initialize the profiles _after_ we have initialized the color schemes page VM, because we pass
// that VM into the appearance VMs within the profiles. The Profiles VM owns the per-profile list itself.
// that VM into the appearance VMs within the profiles
_InitializeProfilesList();
// Apply icons and tooltips (GH#19688, long names may be truncated) to static nav items
@@ -208,24 +205,28 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
_UpdateBackgroundForMica();
// Capture data about where we are right now, so we can re-navigate to the same
// place after we rebuild all the settings.
IInspectable destination{ nullptr };
auto subPage = BreadcrumbSubPage::None;
hstring parentNavTag{};
if (const auto size = _breadcrumbs.Size(); size > 0)
// Deduce information about the currently selected item
IInspectable lastBreadcrumb;
const auto size = _breadcrumbs.Size();
if (size > 0)
{
const auto& crumb = _breadcrumbs.GetAt(size - 1).as<Breadcrumb>();
destination = crumb->Tag();
subPage = crumb->SubPage();
// If we were inside the Profiles section, remember that so we can restore
// the "Profiles " prefix on the rebuilt navigation.
if (_RootCrumbIsProfilesBreadcrumb())
{
parentNavTag = hstring{ profilesTag };
}
lastBreadcrumb = _breadcrumbs.GetAt(size - 1);
}
// Collect only the first items out of the menu item source, the static
// ones that we don't want to regenerate.
//
// By manipulating a MenuItemsSource this way, rather than manipulating the
// MenuItems directly, we avoid a crash in WinUI.
//
// By making the vector only _originalNumItems big to start, GetMany
// will only fill that number of elements out of the current source.
std::vector<IInspectable> menuItemsSTL(_originalNumItems, nullptr);
_menuItemSource.GetMany(0, menuItemsSTL);
// now, just stick them back in.
_menuItemSource.ReplaceAll(menuItemsSTL);
// Repopulate profile-related menu items
_InitializeProfilesList();
// Update the Nav State with the new version of the settings
@@ -235,43 +236,64 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
_extensionsVM.UpdateSettings(_settingsClone, _colorSchemesPageVM);
_profileDefaultsVM = nullptr; // Lazy-loaded upon navigation
if (const auto& profileTag{ destination.try_as<Editor::ProfileViewModel>() })
// Now that the menuItems are repopulated,
// refresh the current page using the breadcrumb data we collected before the refresh
if (const auto& crumb{ lastBreadcrumb.try_as<Breadcrumb>() }; crumb && crumb->Tag())
{
// Find the new profile VM by guid
if (const auto newProfileVM = _FindProfileViewModelByGuid(profileTag.OriginalProfileGuid()))
bool foundNavigationParams = false;
auto destination = crumb->Tag();
auto subPage = crumb->SubPage();
for (const auto& item : _menuItemSource)
{
destination = newProfileVM;
}
else
{
// Fall back to the Profiles landing page
destination = box_value(profilesTag);
subPage = BreadcrumbSubPage::None;
parentNavTag = {};
}
}
else if (destination.try_as<Editor::FolderEntryViewModel>())
{
destination = box_value(newTabMenuTag);
subPage = BreadcrumbSubPage::NewTabMenu_Folder;
}
else if (destination.try_as<Editor::ExtensionPackageViewModel>())
{
destination = box_value(extensionsTag);
subPage = BreadcrumbSubPage::Extensions_Extension;
}
else if (!destination.try_as<hstring>())
{
// Couldn't find a meaningful previous page. Fall back to the first menu item.
if (_menuItemSource && _menuItemSource.Size() > 0)
{
destination = _menuItemSource.GetAt(0).as<MUX::Controls::NavigationViewItem>().Tag();
subPage = BreadcrumbSubPage::None;
parentNavTag = {};
const auto menuItem = item.try_as<MUX::Controls::NavigationViewItem>();
if (!menuItem)
{
continue;
}
const auto& tag = menuItem.Tag();
if (const auto& stringTag{ tag.try_as<hstring>() })
{
if (const auto& destString{ destination.try_as<hstring>() })
{
foundNavigationParams = (*stringTag == *destString);
}
else if (destination.try_as<Editor::FolderEntryViewModel>() && *stringTag == newTabMenuTag)
{
foundNavigationParams = true;
subPage = BreadcrumbSubPage::NewTabMenu_Folder;
}
else if (destination.try_as<Editor::ExtensionPackageViewModel>() && *stringTag == extensionsTag)
{
foundNavigationParams = true;
subPage = BreadcrumbSubPage::Extensions_Extension;
}
}
else if (const auto& profileTag{ tag.try_as<ProfileViewModel>() })
{
const auto destProfile = destination.try_as<ProfileViewModel>();
if (destProfile && profileTag->OriginalProfileGuid() == destProfile->OriginalProfileGuid())
{
// Use the new profile VM from the refreshed menu items
destination = tag;
foundNavigationParams = true;
}
}
if (foundNavigationParams)
{
// found the one that was selected before the refresh
_Navigate(destination, subPage);
return;
}
}
}
_Navigate(destination, subPage, {}, parentNavTag);
// Couldn't find the selected item, fall back to first menu item
// This happens when the selected item was a profile which doesn't exist in the new configuration
// We can use menuItemsSTL here because the only things they miss are profile entries.
const auto& firstItem{ _menuItemSource.GetAt(0).as<MUX::Controls::NavigationViewItem>() };
_Navigate(firstItem.Tag(), BreadcrumbSubPage::None);
_UpdateSearchIndex();
}
@@ -303,6 +325,12 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
// can be empty to indicate that we should create a fresh profile
void MainPage::_AddProfileHandler(winrt::guid profileGuid)
{
uint32_t insertIndex;
auto selectedItem{ SettingsNav().SelectedItem() };
if (_menuItemSource)
{
_menuItemSource.IndexOf(selectedItem, insertIndex);
}
if (profileGuid != winrt::guid{})
{
// if we were given a non-empty guid, we want to duplicate the corresponding profile
@@ -310,13 +338,13 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
if (profile)
{
const auto duplicated = _settingsClone.DuplicateProfile(profile);
_CreateAndNavigateToNewProfile(duplicated);
_CreateAndNavigateToNewProfile(insertIndex, duplicated);
}
}
else
{
// we were given an empty guid, create a new profile
_CreateAndNavigateToNewProfile(nullptr);
_CreateAndNavigateToNewProfile(insertIndex, nullptr);
}
}
@@ -423,9 +451,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
}
else if (_colorSchemesPageVM.CurrentPage() == ColorSchemesSubPage::Base)
{
// Preserve the current root context so that "Profiles > Color schemes" still shows the Profiles prefix.
const hstring inheritedParentNavTag = _RootCrumbIsProfilesBreadcrumb() ? hstring{ profilesTag } : hstring{};
_Navigate(boxedTag, BreadcrumbSubPage::None, {}, inheritedParentNavTag);
_Navigate(boxedTag, BreadcrumbSubPage::None);
}
}
else if (settingName == L"CurrentSchemeName")
@@ -495,7 +521,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
if (currentPage == ProfileSubPage::Base)
{
_breadcrumbs.Clear();
_AppendProfilesRootCrumb();
_breadcrumbs.Append(winrt::make<Breadcrumb>(breadcrumbTag, breadcrumbText, BreadcrumbSubPage::None));
}
_NavigateToProfileSubPage(profile, currentPage, breadcrumbTag, {});
@@ -510,10 +535,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
// a view model object (i.e. ProfileViewModel, ColorSchemeViewModel, etc.) for dynamic pages
// - subPage: the sub page to navigate to, used for pages that have multiple sub pages (i.e. Profile > Appearance/Terminal/Advanced)
// - elementToFocus: the name of the element to focus on the target page
// - parentNavTag: optional nav tag of the parent page when this navigation is invoked from inside another
// landing page. Used by the Profiles landing page to keep itself selected and to prepend a
// "Profiles" breadcrumb when entering Color schemes / a profile / Defaults / Add Profile from there.
void MainPage::_Navigate(const IInspectable& vm, BreadcrumbSubPage subPage, hstring elementToFocus, const hstring& parentNavTag)
void MainPage::_Navigate(const IInspectable& vm, BreadcrumbSubPage subPage, hstring elementToFocus)
{
_PreNavigateHelper();
@@ -594,15 +616,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
_breadcrumbs.Append(winrt::make<Breadcrumb>(vm, RS_(L"Nav_Extensions/Content"), BreadcrumbSubPage::None));
}
}
else if (*clickedItemTag == profilesTag)
{
contentFrame().Navigate(xaml_typename<Editor::Profiles>(), winrt::make<NavigateToPageArgs>(_profilesPageVM, *this, elementToFocus));
_breadcrumbs.Append(winrt::make<Breadcrumb>(vm, RS_(L"Nav_Profiles/Content"), BreadcrumbSubPage::None));
}
else if (*clickedItemTag == globalProfileTag)
{
_AppendProfilesRootCrumb();
// lazy load profile defaults VM
if (!_profileDefaultsVM)
{
@@ -621,20 +636,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
// Register handler for future user-driven sub-page changes
_SetupProfileEventHandling(_profileDefaultsVM);
// Keep the Profiles nav item selected.
selectedNavTag = profilesTag;
}
else if (*clickedItemTag == colorSchemesTag)
{
// Color Schemes page is accessible from root level and within Profiles,
// so we need to prepend the root crumb in the second case.
if (parentNavTag == profilesTag)
{
_AppendProfilesRootCrumb();
selectedNavTag = profilesTag;
}
_breadcrumbs.Append(winrt::make<Breadcrumb>(vm, RS_(L"Nav_ColorSchemes/Content"), BreadcrumbSubPage::None));
contentFrame().Navigate(xaml_typename<Editor::ColorSchemes>(), winrt::make<NavigateToPageArgs>(_colorSchemesPageVM, *this, elementToFocus));
@@ -650,62 +654,47 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
}
else if (*clickedItemTag == addProfileTag)
{
_AppendProfilesRootCrumb();
auto addProfileState{ winrt::make<AddProfilePageNavigationState>(_settingsClone) };
addProfileState.AddNew({ get_weak(), &MainPage::_AddProfileHandler });
contentFrame().Navigate(xaml_typename<Editor::AddProfile>(), winrt::make<NavigateToPageArgs>(addProfileState, *this, elementToFocus));
_breadcrumbs.Append(winrt::make<Breadcrumb>(vm, RS_(L"Nav_AddNewProfile/Content"), BreadcrumbSubPage::None));
// Keep the Profiles nav item selected.
selectedNavTag = profilesTag;
}
}
else if (const auto& profile = vm.try_as<Editor::ProfileViewModel>())
{
_AppendProfilesRootCrumb();
selectedNavTag = profilesTag;
if (profile.Orphaned())
{
contentFrame().Navigate(xaml_typename<Editor::Profiles_Base_Orphaned>(), winrt::make<NavigateToPageArgs>(profile, *this, elementToFocus));
_breadcrumbs.Append(winrt::make<Breadcrumb>(vm, profile.Name(), BreadcrumbSubPage::None));
profile.CurrentPage(ProfileSubPage::Base);
_SetupProfileEventHandling(profile);
return;
}
else
// Set CurrentPage before registering the handler to avoid double-navigation
const ProfileSubPage profileSubPage = ProfileSubPageFromBreadcrumb(subPage);
profile.CurrentPage(profileSubPage);
// Navigate directly to the correct sub-page
_breadcrumbs.Append(winrt::make<Breadcrumb>(vm, profile.Name(), BreadcrumbSubPage::None));
_NavigateToProfileSubPage(profile, profileSubPage, vm, elementToFocus);
if (const auto profileNavItem = _FindProfileNavItem(profile.OriginalProfileGuid()))
{
// Set CurrentPage before registering the handler to avoid double-navigation
const ProfileSubPage profileSubPage = ProfileSubPageFromBreadcrumb(subPage);
profile.CurrentPage(profileSubPage);
// Navigate directly to the correct sub-page
_breadcrumbs.Append(winrt::make<Breadcrumb>(vm, profile.Name(), BreadcrumbSubPage::None));
_NavigateToProfileSubPage(profile, profileSubPage, vm, elementToFocus);
// Register handler for future user-driven sub-page changes
_SetupProfileEventHandling(profile);
SettingsNav().SelectedItem(profileNavItem);
}
// Register handler for future user-driven sub-page changes
_SetupProfileEventHandling(profile);
}
else if (const auto& colorSchemeVM = vm.try_as<Editor::ColorSchemeViewModel>())
{
selectedNavTag = colorSchemesTag;
const auto boxedColorSchemesTag = box_value(colorSchemesTag);
// Suppress the handler to avoid double-navigation
_colorSchemesPageViewModelChangedRevoker.revoke();
// Color Schemes page is accessible from within Profiles and root level,
// so we need to prepend the root crumb in the first case.
if (parentNavTag == profilesTag)
{
_AppendProfilesRootCrumb();
selectedNavTag = profilesTag;
}
else
{
selectedNavTag = colorSchemesTag;
}
_breadcrumbs.Append(winrt::make<Breadcrumb>(boxedColorSchemesTag, RS_(L"Nav_ColorSchemes/Content"), BreadcrumbSubPage::None));
if (subPage == BreadcrumbSubPage::None)
@@ -824,9 +813,26 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
}
// Select the appropriate nav item
// NOTE: profiles are special in that they have their own nav item, so those are handled in the profile branch above
if (!selectedNavTag.empty())
{
_SelectNavItemByTag(selectedNavTag);
for (auto&& menuItem : _menuItemSource)
{
if (const auto& navViewItem{ menuItem.try_as<MUX::Controls::NavigationViewItem>() })
{
if (const auto& tag{ navViewItem.Tag() })
{
if (const auto& stringTag{ tag.try_as<hstring>() })
{
if (*stringTag == selectedNavTag)
{
SettingsNav().SelectedItem(navViewItem);
break;
}
}
}
}
}
}
}
@@ -849,11 +855,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
if (gsl::narrow_cast<uint32_t>(args.Index()) < (_breadcrumbs.Size() - 1))
{
const auto crumb = args.Item().as<Breadcrumb>();
// If the breadcrumb chain is rooted at the Profiles landing page, preserve
// that context so navigating to sub pages (i.e. Color schemes) via a back-breadcrumb
// keeps the "Profiles " prefix.
const hstring parentNavTag = _RootCrumbIsProfilesBreadcrumb() ? hstring{ profilesTag } : hstring{};
_Navigate(crumb->Tag(), crumb->SubPage(), {}, parentNavTag);
_Navigate(crumb->Tag(), crumb->SubPage());
}
}
@@ -870,20 +872,35 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
_MoveXamlParsedNavItemsIntoItemSource();
}
// Populate the per-profile view models on the Profiles VM. The Profiles landing
// page (and the search index) read this same list back through the VM.
const auto& profileVMs = _profilesPageVM.Profiles();
profileVMs.Clear();
// Manually create a NavigationViewItem and view model for each profile
// and keep a reference to them in a map so that we
// can easily modify the correct one when the associated
// profile changes.
_profileVMs.Clear();
for (const auto& profile : _settingsClone.AllProfiles())
{
if (!profile.Deleted())
{
auto profileVM = _viewModelForProfile(profile, _settingsClone, Dispatcher());
profileVM.SetupAppearances(_colorSchemesPageVM.AllColorSchemes());
profileVM.DeleteProfileRequested({ this, &MainPage::_DeleteProfile });
profileVMs.Append(profileVM);
auto navItem = _CreateProfileNavViewItem(profileVM);
_menuItemSource.Append(navItem);
}
}
// Top off (the end of the nav view) with the Add Profile item
MUX::Controls::NavigationViewItem addProfileItem;
const auto addProfileText = RS_(L"Nav_AddNewProfile/Content");
addProfileItem.Content(box_value(addProfileText));
addProfileItem.Tag(box_value(addProfileTag));
WUX::Controls::ToolTipService::SetToolTip(addProfileItem, box_value(addProfileText));
FontIcon icon;
// This is the "Add" symbol
icon.Glyph(NavTagIconMap[addProfileTag]);
addProfileItem.Icon(icon);
_menuItemSource.Append(addProfileItem);
}
// BODGY
@@ -919,17 +936,79 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
SettingsNav().MenuItemsSource(_menuItemSource);
}
void MainPage::_CreateAndNavigateToNewProfile(const Model::Profile& profile)
void MainPage::_CreateAndNavigateToNewProfile(const uint32_t index, const Model::Profile& profile)
{
const auto newProfile{ profile ? profile : _settingsClone.CreateNewProfile() };
const auto profileViewModel{ _viewModelForProfile(newProfile, _settingsClone, Dispatcher()) };
profileViewModel.SetupAppearances(_colorSchemesPageVM.AllColorSchemes());
profileViewModel.DeleteProfileRequested({ this, &MainPage::_DeleteProfile });
const auto navItem{ _CreateProfileNavViewItem(profileViewModel) };
_profilesPageVM.Profiles().Append(profileViewModel);
if (_menuItemSource)
{
_menuItemSource.InsertAt(index, navItem);
}
// Select and navigate to the new profile
_Navigate(profileViewModel);
_Navigate(profileViewModel, BreadcrumbSubPage::None);
}
static MUX::Controls::InfoBadge _createGlyphIconBadge(wil::zwstring_view glyph)
{
MUX::Controls::InfoBadge badge;
MUX::Controls::FontIconSource icon;
icon.FontFamily(winrt::Windows::UI::Xaml::Media::FontFamily{ L"Segoe Fluent Icons, Segoe MDL2 Assets" });
icon.FontSize(12);
icon.Glyph(glyph);
badge.IconSource(icon);
return badge;
}
MUX::Controls::NavigationViewItem MainPage::_CreateProfileNavViewItem(const Editor::ProfileViewModel& profile)
{
MUX::Controls::NavigationViewItem profileNavItem;
profileNavItem.Content(box_value(profile.Name()));
profileNavItem.Tag(box_value<Editor::ProfileViewModel>(profile));
profileNavItem.Icon(UI::IconPathConverter::IconWUX(profile.EvaluatedIcon()));
WUX::Controls::ToolTipService::SetToolTip(profileNavItem, box_value(profile.Name()));
if (profile.Orphaned())
{
profileNavItem.InfoBadge(_createGlyphIconBadge(L"\xE7BA") /* Warning Triangle */);
}
else if (profile.Hidden())
{
profileNavItem.InfoBadge(_createGlyphIconBadge(L"\xED1A") /* Hide */);
}
// Update the menu item when the icon/name changes
auto weakMenuItem{ make_weak(profileNavItem) };
profile.PropertyChanged([weakMenuItem](const auto&, const WUX::Data::PropertyChangedEventArgs& args) {
if (auto menuItem{ weakMenuItem.get() })
{
const auto& tag{ menuItem.Tag().as<Editor::ProfileViewModel>() };
if (args.PropertyName() == L"Icon")
{
menuItem.Icon(UI::IconPathConverter::IconWUX(tag.EvaluatedIcon()));
}
else if (args.PropertyName() == L"Name")
{
menuItem.Content(box_value(tag.Name()));
WUX::Controls::ToolTipService::SetToolTip(menuItem, box_value(tag.Name()));
}
else if (args.PropertyName() == L"Hidden")
{
menuItem.InfoBadge(tag.Hidden() ? _createGlyphIconBadge(L"\xED1A") /* Hide */ : nullptr);
}
}
});
// Add an event handler for when the user wants to delete a profile.
profile.DeleteProfileRequested({ this, &MainPage::_DeleteProfile });
// Register the VM so that it appears in the search index
_profileVMs.Append(profile);
return profileNavItem;
}
void MainPage::_DeleteProfile(const IInspectable /*sender*/, const Editor::DeleteProfileEventArgs& args)
@@ -946,20 +1025,33 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
}
}
// Remove the profile VM
const auto& profileVMs = _profilesPageVM.Profiles();
for (uint32_t i = 0; i < profileVMs.Size(); ++i)
// remove selected item
uint32_t index;
auto selectedItem{ SettingsNav().SelectedItem() };
if (_menuItemSource)
{
if (profileVMs.GetAt(i).OriginalProfileGuid() == guid)
{
profileVMs.RemoveAt(i);
break;
}
}
_menuItemSource.IndexOf(selectedItem, index);
_menuItemSource.RemoveAt(index);
// Go back to the Profiles landing page
_Navigate(box_value(profilesTag));
SettingsMainPage_ScrollViewer().ChangeView(nullptr, 0.0, nullptr);
// Remove it from the list of VMs
auto profileVM = selectedItem.as<MUX::Controls::NavigationViewItem>().Tag().as<Editor::ProfileViewModel>();
uint32_t vmIndex;
if (_menuItemSource.IndexOf(profileVM, vmIndex))
{
_profileVMs.RemoveAt(vmIndex);
}
// navigate to the profile next to this one
const auto newSelectedItem{ _menuItemSource.GetAt(index < _menuItemSource.Size() - 1 ? index : index - 1) };
const auto newTag = newSelectedItem.as<MUX::Controls::NavigationViewItem>().Tag();
if (const auto profileViewModel = newTag.try_as<ProfileViewModel>())
{
profileViewModel->FocusDeleteButton(true);
}
_Navigate(newTag, BreadcrumbSubPage::None);
// Since we are navigating to a new profile after deletion, scroll up to the top
SettingsMainPage_ScrollViewer().ChangeView(nullptr, 0.0, nullptr);
}
}
IObservableVector<IInspectable> MainPage::Breadcrumbs() noexcept
@@ -969,20 +1061,29 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void MainPage::_NavigateToProfileHandler(const IInspectable& /*sender*/, winrt::guid profileGuid)
{
if (const auto profileVM = _FindProfileViewModelByGuid(profileGuid))
if (const auto profileNavItem = _FindProfileNavItem(profileGuid))
{
_Navigate(profileVM);
_Navigate(profileNavItem.Tag(), BreadcrumbSubPage::None);
}
// Silently fail if the profile wasn't found
}
Editor::ProfileViewModel MainPage::_FindProfileViewModelByGuid(winrt::guid profileGuid) const
MUX::Controls::NavigationViewItem MainPage::_FindProfileNavItem(winrt::guid profileGuid) const
{
for (const auto& profileVM : _profilesPageVM.Profiles())
for (auto&& menuItem : _menuItemSource)
{
if (profileVM.OriginalProfileGuid() == profileGuid)
if (const auto& navViewItem{ menuItem.try_as<MUX::Controls::NavigationViewItem>() })
{
return profileVM;
if (const auto& tag{ navViewItem.Tag() })
{
if (const auto& profileTag{ tag.try_as<ProfileViewModel>() })
{
if (profileTag->OriginalProfileGuid() == profileGuid)
{
return navViewItem;
}
}
}
}
}
return nullptr;
@@ -993,62 +1094,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
_Navigate(box_value(hstring{ colorSchemesTag }), BreadcrumbSubPage::ColorSchemes_Edit);
}
void MainPage::_AppendProfilesRootCrumb()
{
_breadcrumbs.Append(winrt::make<Breadcrumb>(box_value(profilesTag), RS_(L"Nav_Profiles/Content"), BreadcrumbSubPage::None));
}
bool MainPage::_RootCrumbIsProfilesBreadcrumb() const
{
if (_breadcrumbs.Size() == 0)
{
return false;
}
const auto rootTag = _breadcrumbs.GetAt(0).as<Breadcrumb>()->Tag().try_as<hstring>();
return rootTag && *rootTag == profilesTag;
}
void MainPage::_SelectNavItemByTag(std::wstring_view tag)
{
if (!_menuItemSource)
{
return;
}
for (auto&& menuItem : _menuItemSource)
{
if (const auto& navViewItem{ menuItem.try_as<MUX::Controls::NavigationViewItem>() })
{
if (const auto& itemTag{ navViewItem.Tag() })
{
if (const auto& stringTag{ itemTag.try_as<hstring>() })
{
if (*stringTag == tag)
{
SettingsNav().SelectedItem(navViewItem);
return;
}
}
}
}
}
}
void MainPage::_SetupProfilesPageEventHandling()
{
_profilesPageVM.OpenDefaultsRequested([this](const auto&, const auto&) {
_Navigate(box_value(globalProfileTag));
});
_profilesPageVM.OpenColorSchemesRequested([this](const auto&, const auto&) {
_Navigate(box_value(colorSchemesTag), {}, {}, hstring{ profilesTag });
});
_profilesPageVM.AddProfileRequested([this](const auto&, const auto&) {
_Navigate(box_value(addProfileTag));
});
_profilesPageVM.OpenProfileRequested([this](const auto&, const Editor::ProfileViewModel& profile) {
_Navigate(profile);
});
}
winrt::Windows::UI::Xaml::Media::Brush MainPage::BackgroundBrush()
{
return SettingsNav().Background();
@@ -1151,7 +1196,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
co_return;
}
_currentSearch = SearchIndex::Instance().SearchAsync(sanitizedQuery,
_profilesPageVM.Profiles().GetView(),
_profileVMs.GetView(),
get_self<implementation::NewTabMenuViewModel>(_newTabMenuPageVM)->FolderTreeFlatList().GetView(),
_colorSchemesPageVM.AllColorSchemes().GetView(),
_extensionsVM.ExtensionPackages().GetView(),

View File

@@ -84,25 +84,21 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
std::optional<HWND> _hostingHwnd;
void _InitializeProfilesList();
void _CreateAndNavigateToNewProfile(const Model::Profile& profile);
void _CreateAndNavigateToNewProfile(const uint32_t index, const Model::Profile& profile);
winrt::Microsoft::UI::Xaml::Controls::NavigationViewItem _CreateProfileNavViewItem(const Editor::ProfileViewModel& profile);
void _DeleteProfile(const Windows::Foundation::IInspectable sender, const Editor::DeleteProfileEventArgs& args);
void _AddProfileHandler(const winrt::guid profileGuid);
void _SetupProfileEventHandling(const winrt::Microsoft::Terminal::Settings::Editor::ProfileViewModel profile);
void _SetupColorSchemesEventHandling();
void _SetupActionsEventHandling();
void _SetupProfilesPageEventHandling();
void _NavigateToProfileSubPage(const Editor::ProfileViewModel& profile, ProfileSubPage page, const IInspectable& breadcrumbTag, const hstring& elementToFocus);
void _PreNavigateHelper();
void _Navigate(const IInspectable& vm, BreadcrumbSubPage subPage = BreadcrumbSubPage::None, hstring elementToFocus = {}, const hstring& parentNavTag = {});
void _Navigate(const IInspectable& vm, BreadcrumbSubPage subPage, hstring elementToFocus = {});
void _NavigateToProfileHandler(const IInspectable& sender, winrt::guid profileGuid);
void _NavigateToColorSchemeHandler(const IInspectable& sender, const IInspectable& args);
Editor::ProfileViewModel _FindProfileViewModelByGuid(winrt::guid profileGuid) const;
void _AppendProfilesRootCrumb();
bool _RootCrumbIsProfilesBreadcrumb() const;
void _SelectNavItemByTag(std::wstring_view tag);
Microsoft::UI::Xaml::Controls::NavigationViewItem _FindProfileNavItem(winrt::guid profileGuid) const;
void _UpdateBackgroundForMica();
void _MoveXamlParsedNavItemsIntoItemSource();
@@ -110,11 +106,11 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
safe_void_coroutine _UpdateSearchIndex();
winrt::Microsoft::Terminal::Settings::Editor::ProfileViewModel _profileDefaultsVM{ nullptr };
Windows::Foundation::Collections::IVector<winrt::Microsoft::Terminal::Settings::Editor::ProfileViewModel> _profileVMs{ nullptr };
winrt::Microsoft::Terminal::Settings::Editor::ColorSchemesPageViewModel _colorSchemesPageVM{ nullptr };
winrt::Microsoft::Terminal::Settings::Editor::ActionsViewModel _actionsVM{ nullptr };
winrt::Microsoft::Terminal::Settings::Editor::NewTabMenuViewModel _newTabMenuPageVM{ nullptr };
winrt::Microsoft::Terminal::Settings::Editor::ExtensionsViewModel _extensionsVM{ nullptr };
winrt::Microsoft::Terminal::Settings::Editor::ProfilesPageViewModel _profilesPageVM{ nullptr };
Windows::Foundation::IAsyncOperation<Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable>> _currentSearch{ nullptr };

View File

@@ -192,9 +192,11 @@
x:Uid="Nav_Extensions"
Tag="Extensions_Nav" />
<muxc:NavigationViewItem x:Name="ProfilesNavItem"
x:Uid="Nav_Profiles"
Tag="Profiles_Nav" />
<muxc:NavigationViewItemHeader x:Uid="Nav_Profiles" />
<muxc:NavigationViewItem x:Name="BaseLayerMenuItem"
x:Uid="Nav_ProfileDefaults"
Tag="GlobalProfile_Nav" />
</muxc:NavigationView.MenuItems>
<muxc:NavigationView.FooterMenuItems>

View File

@@ -119,10 +119,6 @@
<DependentUpon>ColorSchemesPageViewModel.idl</DependentUpon>
<SubType>Code</SubType>
</ClInclude>
<ClInclude Include="Profiles.h">
<DependentUpon>Profiles.xaml</DependentUpon>
<SubType>Code</SubType>
</ClInclude>
<ClInclude Include="RenderingViewModel.h">
<DependentUpon>RenderingViewModel.idl</DependentUpon>
<SubType>Code</SubType>
@@ -235,9 +231,6 @@
<Page Include="Profiles_Base.xaml">
<SubType>Designer</SubType>
</Page>
<Page Include="Profiles.xaml">
<SubType>Designer</SubType>
</Page>
<Page Include="Profiles_Base_Orphaned.xaml">
<SubType>Designer</SubType>
</Page>
@@ -338,10 +331,6 @@
<DependentUpon>ColorSchemesPageViewModel.idl</DependentUpon>
<SubType>Code</SubType>
</ClCompile>
<ClCompile Include="Profiles.cpp">
<DependentUpon>Profiles.xaml</DependentUpon>
<SubType>Code</SubType>
</ClCompile>
<ClCompile Include="RenderingViewModel.cpp">
<DependentUpon>RenderingViewModel.idl</DependentUpon>
<SubType>Code</SubType>
@@ -467,10 +456,6 @@
<Midl Include="TerminalColorConverters.idl" />
<Midl Include="ColorSchemeViewModel.idl" />
<Midl Include="ColorSchemesPageViewModel.idl" />
<Midl Include="Profiles.idl">
<DependentUpon>Profiles.xaml</DependentUpon>
<SubType>Code</SubType>
</Midl>
<Midl Include="RenderingViewModel.idl" />
<Midl Include="InteractionViewModel.idl" />
<Midl Include="GlobalAppearanceViewModel.idl" />

View File

@@ -57,6 +57,5 @@
<Page Include="NullableColorPicker.xaml" />
<Page Include="IconPicker.xaml" />
<Page Include="NewTabMenu.xaml" />
<Page Include="Profiles.xaml" />
</ItemGroup>
</Project>

View File

@@ -17,7 +17,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
inline constexpr std::wstring_view actionsTag{ L"Actions_Nav" };
inline constexpr std::wstring_view newTabMenuTag{ L"NewTabMenu_Nav" };
inline constexpr std::wstring_view extensionsTag{ L"Extensions_Nav" };
inline constexpr std::wstring_view profilesTag{ L"Profiles_Nav" };
inline constexpr std::wstring_view globalProfileTag{ L"GlobalProfile_Nav" };
inline constexpr std::wstring_view addProfileTag{ L"AddProfile" };
inline constexpr std::wstring_view colorSchemesTag{ L"ColorSchemes_Nav" };
@@ -34,7 +33,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
std::pair{ actionsTag, L"\xE765" }, /* Keyboard Classic */
std::pair{ newTabMenuTag, L"\xE71D" }, /* All Apps */
std::pair{ extensionsTag, L"\xEA86" }, /* Puzzle */
std::pair{ profilesTag, L"\xE7EE" }, /* Other User */
std::pair{ globalProfileTag, L"\xE81E" }, /* Map Layers */
std::pair{ addProfileTag, L"\xE710" }, /* Add */
std::pair{ openJsonTag, L"\xE713" }, /* Settings */

View File

@@ -1,94 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "Profiles.h"
#include "Profiles.g.cpp"
#include "ProfilesPageViewModel.g.cpp"
#include "ProfileViewModel.h"
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::Foundation::Collections;
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Xaml::Controls;
using namespace winrt::Windows::UI::Xaml::Navigation;
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
Profiles::Profiles()
{
InitializeComponent();
Automation::AutomationProperties::SetName(DefaultsNavigator(), RS_(L"Profiles_DefaultsNavigator_Title/Text"));
Automation::AutomationProperties::SetName(ColorSchemesNavigator(), RS_(L"Profiles_ColorSchemesNavigator_Title/Text"));
Automation::AutomationProperties::SetName(AddProfileButton(), RS_(L"Profiles_AddProfileButton/Text"));
}
void Profiles::OnNavigatedTo(const NavigationEventArgs& e)
{
const auto args = e.Parameter().as<Editor::NavigateToPageArgs>();
_ViewModel = args.ViewModel().as<Editor::ProfilesPageViewModel>();
BringIntoViewWhenLoaded(args.ElementToFocus());
TraceLoggingWrite(
g_hTerminalSettingsEditorProvider,
"NavigatedToPage",
TraceLoggingDescription("Event emitted when the user navigates to a page in the settings UI"),
TraceLoggingValue("profilesLanding", "PageId", "The identifier of the page that was navigated to"),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
}
void Profiles::Defaults_Click(const IInspectable& /*sender*/, const RoutedEventArgs& /*args*/)
{
_ViewModel.RequestOpenDefaults();
}
void Profiles::ColorSchemes_Click(const IInspectable& /*sender*/, const RoutedEventArgs& /*args*/)
{
_ViewModel.RequestOpenColorSchemes();
}
void Profiles::AddProfile_Click(const IInspectable& /*sender*/, const RoutedEventArgs& /*args*/)
{
_ViewModel.RequestAddProfile();
}
void Profiles::Profile_Click(const IInspectable& sender, const RoutedEventArgs& /*args*/)
{
// Profile navigators are buttons whose DataContext is the bound ProfileViewModel.
if (const auto element = sender.try_as<FrameworkElement>())
{
if (const auto profile = element.DataContext().try_as<Editor::ProfileViewModel>())
{
_ViewModel.RequestOpenProfile(profile);
}
}
}
ProfilesPageViewModel::ProfilesPageViewModel()
{
_setProfiles(single_threaded_observable_vector<Editor::ProfileViewModel>());
}
void ProfilesPageViewModel::RequestOpenDefaults()
{
OpenDefaultsRequested.raise(*this, nullptr);
}
void ProfilesPageViewModel::RequestOpenColorSchemes()
{
OpenColorSchemesRequested.raise(*this, nullptr);
}
void ProfilesPageViewModel::RequestAddProfile()
{
AddProfileRequested.raise(*this, nullptr);
}
void ProfilesPageViewModel::RequestOpenProfile(const Editor::ProfileViewModel& profile)
{
OpenProfileRequested.raise(*this, profile);
}
}

View File

@@ -1,67 +0,0 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- Profiles.h
Abstract:
- The Profiles landing page in the Settings UI. The page hosts entry points to
the list of individual profiles among other profile-related settings.
--*/
#pragma once
#include "Profiles.g.h"
#include "ProfilesPageViewModel.g.h"
#include "ProfileViewModel.h"
#include "Utils.h"
#include "ViewModelHelpers.h"
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
struct ProfilesPageViewModel : ProfilesPageViewModelT<ProfilesPageViewModel>, ViewModelHelper<ProfilesPageViewModel>
{
public:
ProfilesPageViewModel();
void RequestOpenDefaults();
void RequestOpenColorSchemes();
void RequestAddProfile();
void RequestOpenProfile(const Editor::ProfileViewModel& profile);
// DON'T YOU DARE ADD A `WINRT_CALLBACK(PropertyChanged` TO A CLASS DERIVED FROM ViewModelHelper. Do this instead:
using ViewModelHelper<ProfilesPageViewModel>::PropertyChanged;
WINRT_OBSERVABLE_PROPERTY(Windows::Foundation::Collections::IObservableVector<Editor::ProfileViewModel>, Profiles, _propertyChangedHandlers, nullptr);
public:
til::typed_event<Windows::Foundation::IInspectable, Windows::Foundation::IInspectable> OpenDefaultsRequested;
til::typed_event<Windows::Foundation::IInspectable, Windows::Foundation::IInspectable> OpenColorSchemesRequested;
til::typed_event<Windows::Foundation::IInspectable, Windows::Foundation::IInspectable> AddProfileRequested;
til::typed_event<Windows::Foundation::IInspectable, Editor::ProfileViewModel> OpenProfileRequested;
};
struct Profiles : public HasScrollViewer<Profiles>, ProfilesT<Profiles>
{
public:
Profiles();
void OnNavigatedTo(const winrt::Windows::UI::Xaml::Navigation::NavigationEventArgs& e);
void Defaults_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args);
void ColorSchemes_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args);
void AddProfile_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args);
void Profile_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args);
til::property_changed_event PropertyChanged;
WINRT_PROPERTY(Editor::ProfilesPageViewModel, ViewModel, nullptr);
};
}
namespace winrt::Microsoft::Terminal::Settings::Editor::factory_implementation
{
BASIC_FACTORY(Profiles);
BASIC_FACTORY(ProfilesPageViewModel);
}

View File

@@ -1,31 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import "ProfileViewModel.idl";
import "ColorSchemeViewModel.idl";
namespace Microsoft.Terminal.Settings.Editor
{
runtimeclass ProfilesPageViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged
{
ProfilesPageViewModel();
Windows.Foundation.Collections.IObservableVector<ProfileViewModel> Profiles { get; };
void RequestOpenDefaults();
void RequestOpenColorSchemes();
void RequestAddProfile();
void RequestOpenProfile(ProfileViewModel profile);
event Windows.Foundation.TypedEventHandler<Object, IInspectable> OpenDefaultsRequested;
event Windows.Foundation.TypedEventHandler<Object, IInspectable> OpenColorSchemesRequested;
event Windows.Foundation.TypedEventHandler<Object, IInspectable> AddProfileRequested;
event Windows.Foundation.TypedEventHandler<Object, ProfileViewModel> OpenProfileRequested;
}
[default_interface] runtimeclass Profiles : Windows.UI.Xaml.Controls.Page
{
Profiles();
ProfilesPageViewModel ViewModel { get; };
}
}

View File

@@ -1,136 +0,0 @@
<!--
Copyright (c) Microsoft Corporation. All rights reserved. Licensed under
the MIT License. See LICENSE in the project root for license information.
-->
<Page x:Class="Microsoft.Terminal.Settings.Editor.Profiles"
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">
<Page.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="CommonResources.xaml" />
</ResourceDictionary.MergedDictionaries>
<DataTemplate x:Key="ProfileNavigatorTemplate"
x:DataType="local:ProfileViewModel">
<Button HorizontalAlignment="Stretch"
AutomationProperties.Name="{x:Bind Name, Mode=OneWay}"
Click="Profile_Click"
Style="{StaticResource NavigatorButtonStyle}">
<Grid ColumnSpacing="12">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="16" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ContentControl Grid.Column="0"
Width="16"
Height="16"
VerticalAlignment="Center"
Content="{x:Bind IconPreview, Mode=OneWay}"
IsTabStop="False" />
<TextBlock Grid.Column="1"
VerticalAlignment="Center"
Style="{StaticResource BodyStrongTextBlockStyle}"
Text="{x:Bind Name, Mode=OneWay}"
TextTrimming="CharacterEllipsis" />
<muxc:InfoBadge Grid.Column="2"
Margin="0,0,8,0"
VerticalAlignment="Center"
Visibility="{x:Bind Hidden, Mode=OneWay}">
<muxc:InfoBadge.IconSource>
<muxc:FontIconSource FontFamily="Segoe Fluent Icons, Segoe MDL2 Assets"
FontSize="12"
Glyph="&#xED1A;" />
</muxc:InfoBadge.IconSource>
</muxc:InfoBadge>
<muxc:InfoBadge Grid.Column="2"
Margin="0,0,8,0"
VerticalAlignment="Center"
Visibility="{x:Bind Orphaned, Mode=OneWay}">
<muxc:InfoBadge.IconSource>
<muxc:FontIconSource FontFamily="Segoe Fluent Icons, Segoe MDL2 Assets"
FontSize="12"
Glyph="&#xE7BA;" />
</muxc:InfoBadge.IconSource>
</muxc:InfoBadge>
</Grid>
</Button>
</DataTemplate>
</ResourceDictionary>
</Page.Resources>
<StackPanel Style="{StaticResource SettingsStackStyle}">
<!-- General Profile Settings -->
<TextBlock x:Uid="Profiles_GeneralSettingsHeader"
Margin="0,0,0,4"
Style="{StaticResource TextBlockSubHeaderStyle}" />
<!-- Defaults navigator -->
<Button x:Name="DefaultsNavigator"
Click="Defaults_Click"
Style="{StaticResource NavigatorButtonStyle}">
<StackPanel Orientation="Vertical">
<TextBlock x:Uid="Profiles_DefaultsNavigator_Title"
Style="{StaticResource BodyStrongTextBlockStyle}" />
<TextBlock x:Uid="Profiles_DefaultsNavigator_Description"
Margin="0,2,0,0"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Style="{StaticResource CaptionTextBlockStyle}"
TextWrapping="Wrap" />
</StackPanel>
</Button>
<!-- Color schemes navigator -->
<Button x:Name="ColorSchemesNavigator"
Click="ColorSchemes_Click"
Style="{StaticResource NavigatorButtonStyle}">
<StackPanel Orientation="Vertical">
<TextBlock x:Uid="Profiles_ColorSchemesNavigator_Title"
Style="{StaticResource BodyStrongTextBlockStyle}" />
<TextBlock x:Uid="Profiles_ColorSchemesNavigator_Description"
Margin="0,2,0,0"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Style="{StaticResource CaptionTextBlockStyle}"
TextWrapping="Wrap" />
</StackPanel>
</Button>
<!-- Terminal profiles section -->
<TextBlock x:Uid="Profiles_TerminalProfilesHeader"
Style="{StaticResource TextBlockSubHeaderStyle}" />
<!-- Add Profile accent button -->
<Button x:Name="AddProfileButton"
Margin="0,4,0,0"
Click="AddProfile_Click"
Style="{StaticResource AccentButtonStyle}">
<StackPanel Orientation="Horizontal">
<FontIcon FontSize="{StaticResource StandardIconSize}"
Glyph="&#xE710;" />
<TextBlock x:Uid="Profiles_AddProfileButton"
Style="{StaticResource IconButtonTextBlockStyle}" />
</StackPanel>
</Button>
<!-- List of profiles. Each is a NavigatorButtonStyle button rendered by ProfileNavigatorTemplate. -->
<ItemsControl Margin="0,0,0,16"
ItemTemplate="{StaticResource ProfileNavigatorTemplate}"
ItemsSource="{x:Bind ViewModel.Profiles, Mode=OneWay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</StackPanel>
</Page>

View File

@@ -1291,39 +1291,7 @@
</data>
<data name="Nav_Profiles.Content" xml:space="preserve">
<value>Profiles</value>
<comment>Header for the "profiles" menu item. Navigates to a landing page that lists profile-related settings (defaults, color schemes, the list of terminal profiles, and the add-profile button).</comment>
</data>
<data name="Nav_Profiles.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
<value>Profiles</value>
<comment>Tooltip for the "profiles" menu item. Navigates to a landing page that lists profile-related settings.</comment>
</data>
<data name="Profiles_GeneralSettingsHeader.Text" xml:space="preserve">
<value>General Profile Settings</value>
<comment>Section header on the Profiles landing page. Introduces general-purpose profile settings such as the defaults shared by all profiles and the color schemes that profiles can use.</comment>
</data>
<data name="Profiles_TerminalProfilesHeader.Text" xml:space="preserve">
<value>Terminal profiles</value>
<comment>Section header on the Profiles landing page. Introduces the list of individual terminal profiles configured by the user.</comment>
</data>
<data name="Profiles_DefaultsNavigator_Title.Text" xml:space="preserve">
<value>Defaults</value>
<comment>Title for the "Defaults" navigator on the Profiles landing page. Clicking this opens the page where defaults shared by all profiles can be edited.</comment>
</data>
<data name="Profiles_DefaultsNavigator_Description.Text" xml:space="preserve">
<value>Changes made here apply to all profiles unless overridden by a specific profile. These settings do not affect the Windows Terminal appearance.</value>
<comment>Help text describing the "Defaults" navigator on the Profiles landing page. Clarifies that the defaults apply to every profile but are not the same as the global Windows Terminal appearance settings.</comment>
</data>
<data name="Profiles_ColorSchemesNavigator_Title.Text" xml:space="preserve">
<value>Color schemes</value>
<comment>Title for the "Color schemes" navigator on the Profiles landing page. Clicking this opens the same page reachable from the top-level Color schemes nav item.</comment>
</data>
<data name="Profiles_ColorSchemesNavigator_Description.Text" xml:space="preserve">
<value>Add, edit, or remove the color schemes that your profiles can use.</value>
<comment>Help text describing the "Color schemes" navigator on the Profiles landing page.</comment>
</data>
<data name="Profiles_AddProfileButton.Text" xml:space="preserve">
<value>Add a new profile</value>
<comment>Text on the accent-colored button on the Profiles landing page. Clicking this opens the page where users can add a new profile.</comment>
<comment>Header for the "profiles" menu items. This acts as a divider to introduce profile-related menu items.</comment>
</data>
<data name="Globals_LaunchModeFocus.Content" xml:space="preserve">
<value>Focus</value>

View File

@@ -2201,10 +2201,26 @@
<value>关闭多个选项卡时发出警告</value>
<comment>Header for a control to toggle whether to show a confirm dialog box when closing the application with multiple tabs open.</comment>
</data>
<data name="Globals_ConfirmOnClose.Header" xml:space="preserve">
<value>关闭时发出警告</value>
<comment>Header for a dropdown controlling when to show a confirmation dialog before closing.</comment>
</data>
<data name="Globals_ConfirmOnClose.HelpText" xml:space="preserve">
<value>控制在关闭选项卡或窗口之前何时显示确认对话框。“始终”在关闭任何窗格时显示对话框。</value>
<comment>Help text associated with Globals_ConfirmOnClose. "Always" refers to Globals_ConfirmOnCloseAlways.Content.</comment>
</data>
<data name="Globals_ConfirmOnCloseNever.Content" xml:space="preserve">
<value>从不</value>
<comment>Option associated with Globals_ConfirmOnClose. "Never" means that the system will never display a warning when closing.</comment>
</data>
<data name="Globals_ConfirmOnCloseAlways.Content" xml:space="preserve">
<value>始终</value>
<comment>Option associated with Globals_ConfirmOnClose. "Always" means that the system will always display a warning when closing.</comment>
</data>
<data name="Globals_ConfirmOnCloseAutomatic.Content" xml:space="preserve">
<value>多个选项卡或窗格</value>
<comment>Option associated with Globals_ConfirmOnClose. The system will display a warning when multiple tabs or panes are present.</comment>
</data>
<data name="Globals_InputServiceWarning.Header" xml:space="preserve">
<value>禁用“触摸键盘和手写面板服务”时发出警告</value>
</data>

View File

@@ -66,8 +66,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
hstring runtimeObjContext{};
if (const auto profileVM = runtimeObj.try_as<Editor::ProfileViewModel>())
{
// No runtimeObjContext: profile name and icon should be enough
runtimeObjLabel = profileVM.Name();
runtimeObjContext = RS_(L"Nav_Profiles/Content");
}
else if (const auto colorSchemeVM = runtimeObj.try_as<Editor::ColorSchemeViewModel>())
{
@@ -258,10 +258,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
localizedEntry.DisplayTextNeutral = EnglishOnlyResourceLoader().GetLocalizedString(entry.ResourceName);
}
if (!entry.SecondaryLabelResourceName.empty())
{
localizedEntry.SecondaryLabelLocalized = GetLibraryResourceString(entry.SecondaryLabelResourceName);
}
localizedIndex.emplace_back(std::move(localizedEntry));
}
return localizedIndex;
@@ -346,7 +342,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
if (bestScore >= MinimumMatchScore)
{
scoredResults.emplace_back(bestScore, winrt::make<FilteredSearchResult>(index, &entry, nullptr, std::nullopt, entry.SecondaryLabelLocalized));
scoredResults.emplace_back(bestScore, winrt::make<FilteredSearchResult>(index, &entry));
}
}

View File

@@ -20,10 +20,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
winrt::hstring DisplayTextLocalized;
std::optional<winrt::hstring> DisplayTextNeutral = std::nullopt;
// No "Neutral" copy of SecondaryLabelLocalized: unlike DisplayText, the secondary
// label is display-only (rendered in the search result row's caption). It is never
// passed to the fuzzy matcher, so a single localized string is sufficient.
winrt::hstring SecondaryLabelLocalized;
const IndexEntry* Entry = nullptr;
std::array<std::pair<std::optional<winrt::hstring>, int>, 2> GetSearchableFields() const;

View File

@@ -102,6 +102,14 @@ winrt::com_ptr<GlobalAppSettings> GlobalAppSettings::Copy() const
globals->_DisabledProfileSources->Append(src);
}
}
if (_SafeUriSchemes)
{
globals->_SafeUriSchemes = winrt::single_threaded_vector<hstring>();
for (const auto& src : *_SafeUriSchemes)
{
globals->_SafeUriSchemes->Append(src);
}
}
for (const auto& parent : _parents)
{

View File

@@ -114,6 +114,7 @@ namespace Microsoft.Terminal.Settings.Model
INHERITABLE_SETTING(Boolean, EnableUnfocusedAcrylic);
INHERITABLE_SETTING(Boolean, AllowHeadless);
INHERITABLE_SETTING(String, SearchWebDefaultQueryUrl);
INHERITABLE_SETTING(IVector<String>, SafeUriSchemes);
Windows.Foundation.Collections.IMapView<String, ColorScheme> ColorSchemes();
void AddColorScheme(ColorScheme scheme);

View File

@@ -63,6 +63,7 @@ Author(s):
X(bool, MinimizeToNotificationArea, "minimizeToNotificationArea", false) \
X(bool, AlwaysShowNotificationIcon, "alwaysShowNotificationIcon", false) \
X(winrt::Windows::Foundation::Collections::IVector<winrt::hstring>, DisabledProfileSources, "disabledProfileSources", nullptr) \
X(winrt::Windows::Foundation::Collections::IVector<winrt::hstring>, SafeUriSchemes, "safeUriSchemes", nullptr) \
X(bool, ShowAdminShield, "showAdminShield", true) \
X(bool, TrimPaste, "trimPaste", true) \
X(bool, EnableColorSelection, "experimental.enableColorSelection", false) \

View File

@@ -467,6 +467,7 @@ namespace SettingsModelUnitTests
"$schema" : "https://aka.ms/terminal-profiles-schema",
"defaultProfile": "{61c54bbd-1111-5271-96e7-009a87ff44bf}",
"disabledProfileSources": [ "Windows.Terminal.Wsl" ],
"safeUriSchemes": [ "vscode" ],
"newTabMenu":
[
{

View File

@@ -123,6 +123,7 @@ class ScreenBufferTests
TEST_METHOD(VtResizePreservingAttributes);
TEST_METHOD(VtSoftResetCursorPosition);
TEST_METHOD(VtSoftResetAltBufferCursorState);
TEST_METHOD(VtScrollMarginsNewlineColor);
@@ -1510,6 +1511,30 @@ void ScreenBufferTests::VtSoftResetCursorPosition()
VERIFY_ARE_EQUAL(til::point(1, 1), cursor.GetPosition());
}
void ScreenBufferTests::VtSoftResetAltBufferCursorState()
{
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
gci.LockConsole(); // Lock must be taken to manipulate buffer.
auto unlock = wil::scope_exit([&] { gci.UnlockConsole(); });
auto& si = gci.GetActiveOutputBuffer();
auto& stateMachine = si.GetStateMachine();
Log::Comment(L"Move cursor on the main buffer.");
stateMachine.ProcessString(L"\x1b[4;7H");
VERIFY_ARE_EQUAL(til::point(6, 3), si.GetTextBuffer().GetCursor().GetPosition());
Log::Comment(L"Enter alt buffer, soft reset, and return to main buffer.");
stateMachine.ProcessString(L"\x1b[?1049h");
VERIFY_IS_TRUE(gci.GetActiveOutputBuffer()._IsAltBuffer());
stateMachine.ProcessString(L"\x1b[!p");
stateMachine.ProcessString(L"\x1b[?1049l");
VERIFY_IS_FALSE(gci.GetActiveOutputBuffer()._IsAltBuffer());
Log::Comment(L"Returning from alt buffer should restore the main cursor position.");
VERIFY_ARE_EQUAL(til::point(6, 3), gci.GetActiveOutputBuffer().GetTextBuffer().GetCursor().GetPosition());
}
void ScreenBufferTests::VtScrollMarginsNewlineColor()
{
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();

View File

@@ -90,7 +90,15 @@ std::function<bool(wchar_t)> SixelParser::DefineImage(const VTInt macroParameter
_state = States::Normal;
_parameters.clear();
return [&](const auto ch) {
_parseCommandChar(ch);
try
{
_parseCommandChar(ch);
}
catch (...)
{
// Ignore all further content.
return false;
}
return true;
};
}
@@ -234,10 +242,18 @@ void SixelParser::_executeNextLine()
_executeCarriageReturn();
_imageLineCount++;
_maybeFlushImageBuffer();
_imageCursor.y += _sixelHeight;
_availablePixelHeight -= _sixelHeight;
_resizeImageBuffer(_sixelHeight);
_fillImageBackgroundWhenScrolled();
// If we don't have any available pixel height, that means the image has
// extended beyond the bottom of the display and we haven't triggered a
// a scroll (because sixel display mode is enabled). In this state, there
// is no point in extending the image any further, because the additional
// content will never be seen, so we'll just be wasting memory.
if (_availablePixelHeight > 0)
{
_imageCursor.y += _sixelHeight;
_availablePixelHeight -= _sixelHeight;
_resizeImageBuffer(_sixelHeight);
_fillImageBackgroundWhenScrolled();
}
}
void SixelParser::_executeMoveToHome()

View File

@@ -3002,17 +3002,15 @@ void AdaptDispatch::SoftReset()
SetGraphicsRendition({}); // Normal rendition.
SetCharacterProtectionAttribute({}); // Default (unprotected)
// Reset the saved cursor state.
// Note that XTerm only resets the main buffer state, but that
// seems likely to be a bug. Most other terminals reset both.
_savedCursorState.at(0) = {}; // Main buffer
_savedCursorState.at(1) = {}; // Alt buffer
// Reset only the active saved cursor state.
// This matches xterm behavior when DECSTR is processed while using
// the alternate screen buffer (GH#19918).
_savedCursorState.at(_usingAltBuffer ? 1 : 0) = {};
// The TerminalOutput state in these buffers must be reset to
// The TerminalOutput state in this buffer must be reset to
// the same state as the _termOutput instance, which is not
// necessarily equivalent to a full reset.
_savedCursorState.at(0).TermOutput = _termOutput;
_savedCursorState.at(1).TermOutput = _termOutput;
_savedCursorState.at(_usingAltBuffer ? 1 : 0).TermOutput = _termOutput;
// Soft reset the Sixel parser if in use.
if (_sixelParser)

View File

@@ -85,7 +85,6 @@ $ClassMap = @{
ResourceName = "Nav_ProfileDefaults/Content"
NavigationParam = "GlobalProfile_Nav"
SubPage = "BreadcrumbSubPage::None"
SecondaryLabel = "Nav_Profiles/Content"
}
"Microsoft::Terminal::Settings::Editor::Profiles_Appearance" = @{
ResourceName = "Nav_ProfileDefaults/Content"
@@ -106,12 +105,6 @@ $ClassMap = @{
ResourceName = "Nav_AddNewProfile/Content"
NavigationParam = "AddProfile"
SubPage = "BreadcrumbSubPage::None"
SecondaryLabel = "Nav_Profiles/Content"
}
"Microsoft::Terminal::Settings::Editor::Profiles" = @{
ResourceName = "Nav_Profiles/Content"
NavigationParam = "Profiles_Nav"
SubPage = "BreadcrumbSubPage::None"
}
}
@@ -163,7 +156,6 @@ foreach ($xamlFile in Get-ChildItem -Path $SourceDir -Filter *.xaml)
NavigationParam = $ClassMap[$pageClass].NavigationParam
SubPage = $ClassMap[$pageClass].SubPage
ElementName = $null # No specific element to navigate to, for the page itself
SecondaryLabel = $ClassMap[$pageClass].SecondaryLabel # Resource name for the result's sub-text (i.e. parent page name); $null if none
File = $filename
}
}
@@ -197,7 +189,6 @@ foreach ($xamlFile in Get-ChildItem -Path $SourceDir -Filter *.xaml)
NavigationParam = $ClassMap[$pageClass].NavigationParam
SubPage = $ClassMap[$pageClass].SubPage
ElementName = "AddNewButton"
SecondaryLabel = $ClassMap[$pageClass].SecondaryLabel
File = $filename
}
}
@@ -294,9 +285,8 @@ function FormatEntry($e)
$formattedResourceName = 'USES_RESOURCE(L"{0}")' -f $e.ResourceName
$formattedNavigationParam = 'L"{0}"' -f $e.NavigationParam # null Navigation param resolves to empty string
$formattedElementName = 'L"{0}"' -f $e.ElementName
$formattedSecondaryLabel = [string]::IsNullOrEmpty($e.SecondaryLabel) ? 'L""' : ('USES_RESOURCE(L"{0}")' -f $e.SecondaryLabel)
return " IndexEntry{{ {0}, {1}, {2}, {3}, {4} }}, // {5}" -f ($formattedResourceName, $formattedNavigationParam, $e.SubPage, $formattedElementName, $formattedSecondaryLabel, $e.File)
return " IndexEntry{{ {0}, {1}, {2}, {3} }}, // {4}" -f ($formattedResourceName, $formattedNavigationParam, $e.SubPage, $formattedElementName, $e.File)
}
function FormatEntries($es) {
@@ -304,7 +294,7 @@ function FormatEntries($es) {
}
# Sort and remove duplicates
$entries = $entries | Sort-Object ResourceName, ParentPage, NavigationParam, SubPage, ElementName, SecondaryLabel, File -Unique
$entries = $entries | Sort-Object ResourceName, ParentPage, NavigationParam, SubPage, ElementName, File -Unique
$buildTimeEntries = @()
$profileEntries = @()
@@ -360,11 +350,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
// x:Name of the SettingContainer to navigate to on the page (i.e. "DefaultProfile")
wil::zwstring_view ElementName;
// Resource name of the search result's secondary label (i.e. parent page name like "Nav_Profiles/Content").
// Empty if the entry has no secondary label.
// NOTE: wrapped in USES_RESOURCE() like ResourceName when non-empty.
wil::zwstring_view SecondaryLabelResourceName;
};
const std::array<IndexEntry, $($buildTimeEntries.Count)>& LoadBuildTimeIndex();