Add input scope startup setting (#17953)

This adds a "defaultInputScope" setting, hooks it up to our TSF,
and exposes it as a setting in the UI under the startup page.
In order to stay close with the other language setting, I moved that
one from the appearance to the startup page as well.
20 out of the 26 files in this PR are boilerplate unfortunately.

Closes #17816

## Validation Steps Performed
* Install and use the Chinese IME
* Launch WT
* Chinese input 
* Change setting to `alphanumericHalfWidth`
* Restart WT
* English input 
This commit is contained in:
Leonard Hecker 2024-09-24 23:14:31 +02:00 committed by GitHub
parent 4259ce535f
commit fc606d2bae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 399 additions and 255 deletions

View File

@ -864,6 +864,7 @@ INLINEPREFIX
inproc
Inputkeyinfo
Inputreadhandledata
INPUTSCOPE
INSERTMODE
INTERACTIVITYBASE
INTERCEPTCOPYPASTE
@ -1290,6 +1291,7 @@ parms
PATCOPY
pathcch
PATTERNID
pbstr
pcb
pcch
PCCHAR
@ -1375,9 +1377,11 @@ POSTCHARBREAKS
POSX
POSXSCROLL
POSYSCROLL
ppbstr
PPEB
ppf
ppidl
pprg
PPROC
ppropvar
ppsi
@ -1693,6 +1697,7 @@ srcsrv
SRCSRVTRG
srctool
srect
SRGS
srvinit
srvpipe
ssa
@ -1839,7 +1844,6 @@ triaging
TRIMZEROHEADINGS
trx
tsa
tsattrs
tsgr
tsm
TStr
@ -1967,10 +1971,8 @@ vswhere
vtapp
VTE
VTID
vtio
vtmode
vtpipeterm
vtpt
VTRGB
VTRGBTo
vtseq

View File

@ -25,6 +25,12 @@ namespace Microsoft.Terminal.Control
Console,
};
enum DefaultInputScope
{
Default,
AlphanumericHalfWidth,
};
runtimeclass FontSizeChangedArgs
{
Int32 Width { get; };

View File

@ -63,6 +63,7 @@ namespace Microsoft.Terminal.Control
Boolean DisablePartialInvalidation { get; };
Boolean SoftwareRendering { get; };
Microsoft.Terminal.Control.TextMeasurement TextMeasurement { get; };
Microsoft.Terminal.Control.DefaultInputScope DefaultInputScope { get; };
Boolean ShowMarks { get; };
Boolean UseBackgroundImageForWindow { get; };
Boolean RightClickContextMenu { get; };

View File

@ -862,6 +862,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
_interactivity.UpdateSettings();
{
const auto inputScope = settings.DefaultInputScope();
const auto alpha = inputScope == DefaultInputScope::AlphanumericHalfWidth;
::Microsoft::Console::TSF::Handle::SetDefaultScopeAlphanumericHalfWidth(alpha);
}
if (_automationPeer)
{
_automationPeer.SetControlPadding(Core::Padding{

View File

@ -27,20 +27,6 @@
</Page.Resources>
<StackPanel Style="{StaticResource SettingsStackStyle}">
<!-- Language -->
<local:SettingContainer x:Uid="Globals_Language"
Visibility="{x:Bind ViewModel.LanguageSelectorAvailable}">
<ComboBox ItemsSource="{x:Bind ViewModel.LanguageList}"
SelectedItem="{x:Bind ViewModel.CurrentLanguage, Mode=TwoWay}"
Style="{StaticResource ComboBoxSettingStyle}">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="x:String">
<TextBlock Text="{x:Bind local:GlobalAppearanceViewModel.LanguageDisplayConverter((x:String))}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</local:SettingContainer>
<!-- Theme -->
<local:SettingContainer x:Uid="Globals_Theme">
<ComboBox AutomationProperties.AccessibilityView="Content"

View File

@ -7,7 +7,6 @@
#include "EnumEntry.h"
#include <LibraryResources.h>
#include <WtExeUtils.h>
using namespace winrt;
using namespace winrt::Windows::UI::Xaml;
@ -18,28 +17,6 @@ using namespace winrt::Windows::Foundation::Collections;
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
// For ComboBox an empty SelectedItem string denotes no selection.
// What we want instead is for "Use system language" to be selected by default.
// --> "und" is synonymous for "Use system language".
constexpr std::wstring_view systemLanguageTag{ L"und" };
static constexpr std::array appLanguageTags{
L"en-US",
L"de-DE",
L"es-ES",
L"fr-FR",
L"it-IT",
L"ja",
L"ko",
L"pt-BR",
L"qps-PLOC",
L"qps-PLOCA",
L"qps-PLOCM",
L"ru",
L"zh-Hans",
L"zh-Hant",
};
constexpr std::wstring_view systemThemeName{ L"system" };
constexpr std::wstring_view darkThemeName{ L"dark" };
constexpr std::wstring_view lightThemeName{ L"light" };
@ -56,146 +33,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
_UpdateThemeList();
}
winrt::hstring GlobalAppearanceViewModel::LanguageDisplayConverter(const winrt::hstring& tag)
{
if (tag == systemLanguageTag)
{
return RS_(L"Globals_LanguageDefault");
}
winrt::Windows::Globalization::Language language{ tag };
return language.NativeName();
}
// Returns whether the language selector is available/shown.
//
// winrt::Windows::Globalization::ApplicationLanguages::PrimaryLanguageOverride()
// doesn't work for unpackaged applications. The corresponding code in TerminalApp is disabled.
// It would be confusing for our users if we presented a dysfunctional language selector.
bool GlobalAppearanceViewModel::LanguageSelectorAvailable()
{
return IsPackaged();
}
// Returns the list of languages the user may override the application language with.
// The returned list are BCP 47 language tags like {"und", "en-US", "de-DE", "es-ES", ...}.
// "und" is short for "undefined" and is synonymous for "Use system language" in this code.
winrt::Windows::Foundation::Collections::IObservableVector<winrt::hstring> GlobalAppearanceViewModel::LanguageList()
{
if (_languageList)
{
return _languageList;
}
if (!LanguageSelectorAvailable())
{
_languageList = {};
return _languageList;
}
// In order to return the language list this code does the following:
// [1] Get all possible languages we want to allow the user to choose.
// We have to acquire languages from multiple sources, creating duplicates. See below at [1].
// [2] Sort languages by their ASCII tags, forcing the UI in a consistent/stable order.
// I wanted to sort the localized language names initially, but it turned out to be complex.
// [3] Remove potential duplicates in our language list from [1].
// We don't want to have en-US twice in the list, do we?
// [4] Optionally remove unwanted language tags (like pseudo-localizations).
std::vector<winrt::hstring> tags;
// [1]:
{
// ManifestLanguages contains languages the app ships with.
// Unfortunately, we cannot use this source. Our manifest must contain the
// ~100 languages that are localized for the shell extension and start menu
// presentation so we align with Windows display languages for those surfaces.
// However, the actual content of our application is limited to a much smaller
// subset of approximately 14 languages. As such, we will code the limited
// subset of languages that we support for selection within the Settings
// dropdown to steer users towards the ones that we can display in the app.
// As per the function definition, the first item
// is always "Use system language" ("und").
tags.emplace_back(systemLanguageTag);
// Add our hard-coded languages after the system definition.
for (const auto& v : appLanguageTags)
{
tags.push_back(v);
}
}
// NOTE: The size of tags is always >0, due to tags[0] being hard-coded to "und".
const auto tagsBegin = ++tags.begin();
const auto tagsEnd = tags.end();
// [2]:
std::sort(tagsBegin, tagsEnd);
// I'd love for both, std::unique and std::remove_if, to occur in a single loop,
// but the code turned out to be complex and even less maintainable, so I gave up.
{
// [3] part 1:
auto it = std::unique(tagsBegin, tagsEnd);
// The qps- languages are useful for testing ("pseudo-localization").
// --> Leave them in if debug features are enabled.
if (!_GlobalSettings.DebugFeaturesEnabled())
{
// [4] part 1:
it = std::remove_if(tagsBegin, it, [](const winrt::hstring& tag) -> bool {
return til::starts_with(tag, L"qps-");
});
}
// [3], [4] part 2 (completing the so called "erase-remove idiom"):
tags.erase(it, tagsEnd);
}
_languageList = winrt::single_threaded_observable_vector(std::move(tags));
return _languageList;
}
winrt::Windows::Foundation::IInspectable GlobalAppearanceViewModel::CurrentLanguage()
{
if (_currentLanguage)
{
return _currentLanguage;
}
if (!LanguageSelectorAvailable())
{
_currentLanguage = {};
return _currentLanguage;
}
// NOTE: PrimaryLanguageOverride throws if this instance is unpackaged.
auto currentLanguage = winrt::Windows::Globalization::ApplicationLanguages::PrimaryLanguageOverride();
if (currentLanguage.empty())
{
currentLanguage = systemLanguageTag;
}
_currentLanguage = winrt::box_value(currentLanguage);
return _currentLanguage;
}
void GlobalAppearanceViewModel::CurrentLanguage(const winrt::Windows::Foundation::IInspectable& tag)
{
_currentLanguage = tag;
const auto currentLanguage = winrt::unbox_value<winrt::hstring>(_currentLanguage);
if (currentLanguage == systemLanguageTag)
{
_GlobalSettings.ClearLanguage();
}
else
{
_GlobalSettings.Language(currentLanguage);
}
}
// Function Description:
// - Updates the list of all themes available to choose from.
void GlobalAppearanceViewModel::_UpdateThemeList()

View File

@ -22,16 +22,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
GETSET_BINDABLE_ENUM_SETTING(TabWidthMode, winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode, _GlobalSettings.TabWidthMode);
public:
// LanguageDisplayConverter maps the given BCP 47 tag to a localized string.
// For instance "en-US" produces "English (United States)", while "de-DE" produces
// "Deutsch (Deutschland)". This works independently of the user's locale.
static winrt::hstring LanguageDisplayConverter(const winrt::hstring& tag);
bool LanguageSelectorAvailable();
winrt::Windows::Foundation::Collections::IObservableVector<winrt::hstring> LanguageList();
winrt::Windows::Foundation::IInspectable CurrentLanguage();
void CurrentLanguage(const winrt::Windows::Foundation::IInspectable& tag);
winrt::Windows::Foundation::IInspectable CurrentTheme();
void CurrentTheme(const winrt::Windows::Foundation::IInspectable& tag);
static winrt::hstring ThemeNameConverter(const Model::Theme& theme);
@ -52,8 +42,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
private:
Model::GlobalAppSettings _GlobalSettings;
winrt::Windows::Foundation::Collections::IObservableVector<winrt::hstring> _languageList;
winrt::Windows::Foundation::IInspectable _currentLanguage;
winrt::Windows::Foundation::IInspectable _currentTheme;
void _UpdateThemeList();

View File

@ -11,11 +11,6 @@ namespace Microsoft.Terminal.Settings.Editor
{
GlobalAppearanceViewModel(Microsoft.Terminal.Settings.Model.GlobalAppSettings globalSettings);
static String LanguageDisplayConverter(String tag);
Boolean LanguageSelectorAvailable { get; };
Windows.Foundation.Collections.IObservableVector<String> LanguageList { get; };
IInspectable CurrentLanguage;
IInspectable CurrentTheme;
static String ThemeNameConverter(Microsoft.Terminal.Settings.Model.Theme theme);
Windows.Foundation.Collections.IObservableVector<Microsoft.Terminal.Settings.Model.Theme> ThemeList { get; };

View File

@ -139,6 +139,29 @@
</ComboBox>
</local:SettingContainer>
<!-- Language -->
<local:SettingContainer x:Uid="Globals_Language"
Visibility="{x:Bind ViewModel.LanguageSelectorAvailable}">
<ComboBox ItemsSource="{x:Bind ViewModel.LanguageList}"
SelectedItem="{x:Bind ViewModel.CurrentLanguage, Mode=TwoWay}"
Style="{StaticResource ComboBoxSettingStyle}">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="x:String">
<TextBlock Text="{x:Bind local:LaunchViewModel.LanguageDisplayConverter((x:String))}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</local:SettingContainer>
<!-- Language -->
<local:SettingContainer x:Uid="Globals_DefaultInputScope">
<ComboBox AutomationProperties.AccessibilityView="Content"
ItemTemplate="{StaticResource EnumComboBoxTemplate}"
ItemsSource="{x:Bind ViewModel.DefaultInputScopeList}"
SelectedItem="{x:Bind ViewModel.CurrentDefaultInputScope, Mode=TwoWay}"
Style="{StaticResource ComboBoxSettingStyle}" />
</local:SettingContainer>
<!-- Start on User Login -->
<local:SettingContainer x:Uid="Globals_StartOnUserLogin">
<ToggleSwitch IsOn="{x:Bind ViewModel.StartOnUserLogin, Mode=TwoWay}"

View File

@ -5,7 +5,9 @@
#include "LaunchViewModel.h"
#include "LaunchViewModel.g.cpp"
#include "EnumEntry.h"
#include <LibraryResources.h>
#include <WtExeUtils.h>
using namespace winrt::Windows::UI::Xaml::Navigation;
using namespace winrt::Windows::Foundation;
@ -14,11 +16,34 @@ using namespace winrt::Windows::UI::Xaml::Data;
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
// For ComboBox an empty SelectedItem string denotes no selection.
// What we want instead is for "Use system language" to be selected by default.
// --> "und" is synonymous for "Use system language".
constexpr std::wstring_view systemLanguageTag{ L"und" };
static constexpr std::array appLanguageTags{
L"en-US",
L"de-DE",
L"es-ES",
L"fr-FR",
L"it-IT",
L"ja",
L"ko",
L"pt-BR",
L"qps-PLOC",
L"qps-PLOCA",
L"qps-PLOCM",
L"ru",
L"zh-Hans",
L"zh-Hant",
};
LaunchViewModel::LaunchViewModel(Model::CascadiaSettings settings) :
_Settings{ settings }
{
_useDefaultLaunchPosition = isnan(InitialPosX()) && isnan(InitialPosY());
INITIALIZE_BINDABLE_ENUM_SETTING(DefaultInputScope, DefaultInputScope, winrt::Microsoft::Terminal::Control::DefaultInputScope, L"Globals_DefaultInputScope", L"Content");
INITIALIZE_BINDABLE_ENUM_SETTING(FirstWindowPreference, FirstWindowPreference, FirstWindowPreference, L"Globals_FirstWindowPreference", L"Content");
INITIALIZE_BINDABLE_ENUM_SETTING(LaunchMode, LaunchMode, LaunchMode, L"Globals_LaunchMode", L"Content");
// More options were added to the JSON mapper when the enum was made into [Flags]
@ -40,6 +65,146 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
});
}
winrt::hstring LaunchViewModel::LanguageDisplayConverter(const winrt::hstring& tag)
{
if (tag == systemLanguageTag)
{
return RS_(L"Globals_LanguageDefault");
}
winrt::Windows::Globalization::Language language{ tag };
return language.NativeName();
}
// Returns whether the language selector is available/shown.
//
// winrt::Windows::Globalization::ApplicationLanguages::PrimaryLanguageOverride()
// doesn't work for unpackaged applications. The corresponding code in TerminalApp is disabled.
// It would be confusing for our users if we presented a dysfunctional language selector.
bool LaunchViewModel::LanguageSelectorAvailable()
{
return IsPackaged();
}
// Returns the list of languages the user may override the application language with.
// The returned list are BCP 47 language tags like {"und", "en-US", "de-DE", "es-ES", ...}.
// "und" is short for "undefined" and is synonymous for "Use system language" in this code.
winrt::Windows::Foundation::Collections::IObservableVector<winrt::hstring> LaunchViewModel::LanguageList()
{
if (_languageList)
{
return _languageList;
}
if (!LanguageSelectorAvailable())
{
_languageList = {};
return _languageList;
}
// In order to return the language list this code does the following:
// [1] Get all possible languages we want to allow the user to choose.
// We have to acquire languages from multiple sources, creating duplicates. See below at [1].
// [2] Sort languages by their ASCII tags, forcing the UI in a consistent/stable order.
// I wanted to sort the localized language names initially, but it turned out to be complex.
// [3] Remove potential duplicates in our language list from [1].
// We don't want to have en-US twice in the list, do we?
// [4] Optionally remove unwanted language tags (like pseudo-localizations).
std::vector<winrt::hstring> tags;
// [1]:
{
// ManifestLanguages contains languages the app ships with.
// Unfortunately, we cannot use this source. Our manifest must contain the
// ~100 languages that are localized for the shell extension and start menu
// presentation so we align with Windows display languages for those surfaces.
// However, the actual content of our application is limited to a much smaller
// subset of approximately 14 languages. As such, we will code the limited
// subset of languages that we support for selection within the Settings
// dropdown to steer users towards the ones that we can display in the app.
// As per the function definition, the first item
// is always "Use system language" ("und").
tags.emplace_back(systemLanguageTag);
// Add our hard-coded languages after the system definition.
for (const auto& v : appLanguageTags)
{
tags.push_back(v);
}
}
// NOTE: The size of tags is always >0, due to tags[0] being hard-coded to "und".
const auto tagsBegin = ++tags.begin();
const auto tagsEnd = tags.end();
// [2]:
std::sort(tagsBegin, tagsEnd);
// I'd love for both, std::unique and std::remove_if, to occur in a single loop,
// but the code turned out to be complex and even less maintainable, so I gave up.
{
// [3] part 1:
auto it = std::unique(tagsBegin, tagsEnd);
// The qps- languages are useful for testing ("pseudo-localization").
// --> Leave them in if debug features are enabled.
if (!_Settings.GlobalSettings().DebugFeaturesEnabled())
{
// [4] part 1:
it = std::remove_if(tagsBegin, it, [](const winrt::hstring& tag) -> bool {
return til::starts_with(tag, L"qps-");
});
}
// [3], [4] part 2 (completing the so called "erase-remove idiom"):
tags.erase(it, tagsEnd);
}
_languageList = winrt::single_threaded_observable_vector(std::move(tags));
return _languageList;
}
winrt::Windows::Foundation::IInspectable LaunchViewModel::CurrentLanguage()
{
if (_currentLanguage)
{
return _currentLanguage;
}
if (!LanguageSelectorAvailable())
{
_currentLanguage = {};
return _currentLanguage;
}
// NOTE: PrimaryLanguageOverride throws if this instance is unpackaged.
auto currentLanguage = winrt::Windows::Globalization::ApplicationLanguages::PrimaryLanguageOverride();
if (currentLanguage.empty())
{
currentLanguage = systemLanguageTag;
}
_currentLanguage = winrt::box_value(currentLanguage);
return _currentLanguage;
}
void LaunchViewModel::CurrentLanguage(const winrt::Windows::Foundation::IInspectable& tag)
{
_currentLanguage = tag;
const auto currentLanguage = winrt::unbox_value<winrt::hstring>(_currentLanguage);
if (currentLanguage == systemLanguageTag)
{
_Settings.GlobalSettings().ClearLanguage();
}
else
{
_Settings.GlobalSettings().Language(currentLanguage);
}
}
winrt::hstring LaunchViewModel::LaunchParametersCurrentValue()
{
const auto launchModeString = CurrentLaunchMode().as<EnumEntry>()->EnumName();

View File

@ -14,6 +14,16 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
public:
LaunchViewModel(Model::CascadiaSettings settings);
// LanguageDisplayConverter maps the given BCP 47 tag to a localized string.
// For instance "en-US" produces "English (United States)", while "de-DE" produces
// "Deutsch (Deutschland)". This works independently of the user's locale.
static winrt::hstring LanguageDisplayConverter(const winrt::hstring& tag);
bool LanguageSelectorAvailable();
winrt::Windows::Foundation::Collections::IObservableVector<winrt::hstring> LanguageList();
winrt::Windows::Foundation::IInspectable CurrentLanguage();
void CurrentLanguage(const winrt::Windows::Foundation::IInspectable& tag);
winrt::hstring LaunchParametersCurrentValue();
double InitialPosX();
double InitialPosY();
@ -35,6 +45,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void CurrentLaunchMode(const winrt::Windows::Foundation::IInspectable& enumEntry);
winrt::Windows::Foundation::Collections::IObservableVector<winrt::Microsoft::Terminal::Settings::Editor::EnumEntry> LaunchModeList();
GETSET_BINDABLE_ENUM_SETTING(DefaultInputScope, winrt::Microsoft::Terminal::Control::DefaultInputScope, _Settings.GlobalSettings().DefaultInputScope);
GETSET_BINDABLE_ENUM_SETTING(FirstWindowPreference, Model::FirstWindowPreference, _Settings.GlobalSettings().FirstWindowPreference);
GETSET_BINDABLE_ENUM_SETTING(WindowingBehavior, Model::WindowingMode, _Settings.GlobalSettings().WindowingBehavior);
@ -45,6 +56,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
private:
Model::CascadiaSettings _Settings;
winrt::Windows::Foundation::Collections::IObservableVector<winrt::hstring> _languageList;
winrt::Windows::Foundation::IInspectable _currentLanguage;
bool _useDefaultLaunchPosition;
winrt::Windows::Foundation::Collections::IObservableVector<winrt::Microsoft::Terminal::Settings::Editor::EnumEntry> _LaunchModeList;

View File

@ -11,6 +11,14 @@ namespace Microsoft.Terminal.Settings.Editor
{
LaunchViewModel(Microsoft.Terminal.Settings.Model.CascadiaSettings settings);
static String LanguageDisplayConverter(String tag);
Boolean LanguageSelectorAvailable { get; };
Windows.Foundation.Collections.IObservableVector<String> LanguageList { get; };
IInspectable CurrentLanguage;
IInspectable CurrentDefaultInputScope;
IObservableVector<Microsoft.Terminal.Settings.Editor.EnumEntry> DefaultInputScopeList { get; };
String LaunchParametersCurrentValue { get; };
Double InitialPosX;
Double InitialPosY;

View File

@ -246,9 +246,22 @@
<comment>A description explaining how this control changes the app's language. {Locked="Windows"}</comment>
</data>
<data name="Globals_LanguageDefault" xml:space="preserve">
<value>Use system default</value>
<value>Default</value>
<comment>The app contains a control allowing users to choose the app's language. If this value is chosen, the language preference list of the system settings is used instead.</comment>
</data>
<data name="Globals_DefaultInputScope.Header" xml:space="preserve">
<value>Default IME input mode</value>
<comment>The "input scope" of an IME tells it what type of characters should be available for input. A Chinese IME for instance may show English letters instead of Chinese ones. It's a technical term. We're using "input mode" instead of "input scope" to make it easier to understand for users.</comment>
</data>
<data name="Globals_DefaultInputScope.HelpText" xml:space="preserve">
<value>Informs the IME what language to use by default. For languages that rely on an IME and don't use Latin characters by default, this setting can be used to force English input on startup.</value>
</data>
<data name="Globals_DefaultInputScopeDefault.Content" xml:space="preserve">
<value>Default</value>
</data>
<data name="Globals_DefaultInputScopeAlphanumericHalfWidth.Content" xml:space="preserve">
<value>Alphanumeric Half-Width (English)</value>
</data>
<data name="Globals_AlwaysShowTabs.Header" xml:space="preserve">
<value>Always show tabs</value>
<comment>Header for a control to toggle if the app should always show the tabs (similar to a website browser).</comment>

View File

@ -33,6 +33,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
DEFINE_ENUM_MAP(winrt::Windows::UI::Xaml::ElementTheme, ElementTheme);
DEFINE_ENUM_MAP(Model::NewTabPosition, NewTabPosition);
DEFINE_ENUM_MAP(winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode, TabViewWidthMode);
DEFINE_ENUM_MAP(Microsoft::Terminal::Control::DefaultInputScope, DefaultInputScope);
DEFINE_ENUM_MAP(Model::FirstWindowPreference, FirstWindowPreference);
DEFINE_ENUM_MAP(Model::LaunchMode, LaunchMode);
DEFINE_ENUM_MAP(Model::TabSwitcherMode, TabSwitcherMode);

View File

@ -29,6 +29,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Windows::UI::Xaml::ElementTheme> ElementTheme();
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, NewTabPosition> NewTabPosition();
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode> TabViewWidthMode();
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Microsoft::Terminal::Control::DefaultInputScope> DefaultInputScope();
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, FirstWindowPreference> FirstWindowPreference();
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, LaunchMode> LaunchMode();
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, TabSwitcherMode> TabSwitcherMode();

View File

@ -11,6 +11,7 @@ namespace Microsoft.Terminal.Settings.Model
static Windows.Foundation.Collections.IMap<String, Windows.UI.Xaml.ElementTheme> ElementTheme { get; };
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Settings.Model.NewTabPosition> NewTabPosition { get; };
static Windows.Foundation.Collections.IMap<String, Microsoft.UI.Xaml.Controls.TabViewWidthMode> TabViewWidthMode { get; };
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Control.DefaultInputScope> DefaultInputScope { get; };
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Settings.Model.FirstWindowPreference> FirstWindowPreference { get; };
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Settings.Model.LaunchMode> LaunchMode { get; };
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Settings.Model.TabSwitcherMode> TabSwitcherMode { get; };

View File

@ -269,6 +269,10 @@ Json::Value GlobalAppSettings::ToJson()
{
_TextMeasurement.reset();
}
if (_DefaultInputScope == Control::DefaultInputScope::Default)
{
_DefaultInputScope.reset();
}
if (_DisablePartialInvalidation == false)
{

View File

@ -73,6 +73,7 @@ namespace Microsoft.Terminal.Settings.Model
INHERITABLE_SETTING(Boolean, TrimPaste);
INHERITABLE_SETTING(LaunchPosition, InitialPosition);
INHERITABLE_SETTING(Boolean, CenterOnLaunch);
INHERITABLE_SETTING(Microsoft.Terminal.Control.DefaultInputScope, DefaultInputScope);
INHERITABLE_SETTING(FirstWindowPreference, FirstWindowPreference);
INHERITABLE_SETTING(LaunchMode, LaunchMode);
INHERITABLE_SETTING(Boolean, SnapToGridOnResize);

View File

@ -28,6 +28,7 @@ Author(s):
X(bool, DisablePartialInvalidation, "rendering.disablePartialInvalidation", false) \
X(bool, SoftwareRendering, "rendering.software", false) \
X(winrt::Microsoft::Terminal::Control::TextMeasurement, TextMeasurement, "compatibility.textMeasurement") \
X(winrt::Microsoft::Terminal::Control::DefaultInputScope, DefaultInputScope, "defaultInputScope") \
X(bool, UseBackgroundImageForWindow, "experimental.useBackgroundImageForWindow", false) \
X(bool, ForceVTInput, "experimental.input.forceVT", false) \
X(bool, TrimBlockSelection, "trimBlockSelection", true) \

View File

@ -368,6 +368,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
_DisablePartialInvalidation = globalSettings.DisablePartialInvalidation();
_SoftwareRendering = globalSettings.SoftwareRendering();
_TextMeasurement = globalSettings.TextMeasurement();
_DefaultInputScope = globalSettings.DefaultInputScope();
_UseBackgroundImageForWindow = globalSettings.UseBackgroundImageForWindow();
_ForceVTInput = globalSettings.ForceVTInput();
_TrimBlockSelection = globalSettings.TrimBlockSelection();

View File

@ -161,6 +161,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
INHERITABLE_SETTING(Model::TerminalSettings, bool, DisablePartialInvalidation, false);
INHERITABLE_SETTING(Model::TerminalSettings, bool, SoftwareRendering, false);
INHERITABLE_SETTING(Model::TerminalSettings, Microsoft::Terminal::Control::TextMeasurement, TextMeasurement);
INHERITABLE_SETTING(Model::TerminalSettings, Microsoft::Terminal::Control::DefaultInputScope, DefaultInputScope);
INHERITABLE_SETTING(Model::TerminalSettings, bool, UseBackgroundImageForWindow, false);
INHERITABLE_SETTING(Model::TerminalSettings, bool, ForceVTInput, false);

View File

@ -782,3 +782,11 @@ JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Control::TextMeasurement)
pair_type{ "console", ValueType::Console },
};
};
JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Control::DefaultInputScope)
{
JSON_MAPPINGS(2) = {
pair_type{ "default", ValueType::Default },
pair_type{ "alphanumericHalfWidth", ValueType::AlphanumericHalfWidth },
};
};

View File

@ -79,6 +79,7 @@
X(bool, DisablePartialInvalidation, false) \
X(bool, SoftwareRendering, false) \
X(winrt::Microsoft::Terminal::Control::TextMeasurement, TextMeasurement) \
X(winrt::Microsoft::Terminal::Control::DefaultInputScope, DefaultInputScope, winrt::Microsoft::Terminal::Control::DefaultInputScope::Default) \
X(bool, UseBackgroundImageForWindow, false) \
X(bool, ShowMarks, false) \
X(winrt::Microsoft::Terminal::Control::CopyFormat, CopyFormatting, 0) \

View File

@ -16,6 +16,11 @@ Handle Handle::Create()
return handle;
}
void Handle::SetDefaultScopeAlphanumericHalfWidth(bool enable)
{
Implementation::SetDefaultScopeAlphanumericHalfWidth(enable);
}
Handle::~Handle()
{
if (_impl)

View File

@ -33,6 +33,7 @@ namespace Microsoft::Console::TSF
struct Handle
{
static Handle Create();
static void SetDefaultScopeAlphanumericHalfWidth(bool enable);
Handle() = default;
~Handle();

View File

@ -27,6 +27,11 @@ static void TfPropertyvalClose(TF_PROPERTYVAL* val)
}
using unique_tf_propertyval = wil::unique_struct<TF_PROPERTYVAL, decltype(&TfPropertyvalClose), &TfPropertyvalClose>;
void Implementation::SetDefaultScopeAlphanumericHalfWidth(bool enable) noexcept
{
_wantsAnsiInputScope.store(enable, std::memory_order_relaxed);
}
void Implementation::Initialize()
{
_categoryMgr = wil::CoCreateInstance<ITfCategoryMgr>(CLSID_TF_CategoryMgr, CLSCTX_INPROC_SERVER);
@ -41,6 +46,8 @@ void Implementation::Initialize()
TfEditCookie ecTextStore;
THROW_IF_FAILED(_documentMgr->CreateContext(_clientId, 0, static_cast<ITfContextOwnerCompositionSink*>(this), _context.addressof(), &ecTextStore));
_ownerCompositionServices = _context.try_query<ITfContextOwnerCompositionServices>();
_contextSource = _context.query<ITfSource>();
THROW_IF_FAILED(_contextSource->AdviseSink(IID_ITfContextOwner, static_cast<ITfContextOwner*>(this), &_cookieContextOwner));
THROW_IF_FAILED(_contextSource->AdviseSink(IID_ITfTextEditSink, static_cast<ITfTextEditSink*>(this), &_cookieTextEditSink));
@ -155,12 +162,9 @@ void Implementation::Unfocus(IDataProvider* provider)
_provider.reset();
if (_compositions > 0)
if (_compositions > 0 && _ownerCompositionServices)
{
if (const auto svc = _context.try_query<ITfContextOwnerCompositionServices>())
{
svc->TerminateComposition(nullptr);
}
std::ignore = _ownerCompositionServices->TerminateComposition(nullptr);
}
}
@ -229,9 +233,7 @@ STDMETHODIMP Implementation::GetACPFromPoint(const POINT* ptScreen, DWORD dwFlag
return E_NOTIMPL;
}
// This returns rectangle of current command line edit area.
// When a user types in East Asian language, candidate window is shown at this position.
// Emoji and more panel (Win+.) is shown at the position, too.
// The returned rectangle is used to position the TSF candidate window.
STDMETHODIMP Implementation::GetTextExt(LONG acpStart, LONG acpEnd, RECT* prc, BOOL* pfClipped) noexcept
try
{
@ -249,8 +251,7 @@ try
}
CATCH_RETURN()
// This returns Rectangle of the text box of whole console.
// When a user taps inside the rectangle while hardware keyboard is not available, touch keyboard is invoked.
// The returned rectangle is used to activate the touch keyboard.
STDMETHODIMP Implementation::GetScreenExt(RECT* prc) noexcept
try
{
@ -306,7 +307,16 @@ STDMETHODIMP Implementation::GetWnd(HWND* phwnd) noexcept
STDMETHODIMP Implementation::GetAttribute(REFGUID rguidAttribute, VARIANT* pvarValue) noexcept
{
return E_NOTIMPL;
if (_wantsAnsiInputScope.load(std::memory_order_relaxed) && IsEqualGUID(rguidAttribute, GUID_PROP_INPUTSCOPE))
{
_ansiInputScope.AddRef();
pvarValue->vt = VT_UNKNOWN;
pvarValue->punkVal = &_ansiInputScope;
return S_OK;
}
pvarValue->vt = VT_EMPTY;
return S_OK;
}
#pragma endregion ITfContextOwner
@ -403,13 +413,89 @@ STDMETHODIMP Implementation::EditSessionProxyBase::QueryInterface(REFIID riid, v
ULONG STDMETHODCALLTYPE Implementation::EditSessionProxyBase::AddRef() noexcept
{
return InterlockedIncrement(&referenceCount);
InterlockedIncrement(&referenceCount);
return self->AddRef();
}
ULONG STDMETHODCALLTYPE Implementation::EditSessionProxyBase::Release() noexcept
{
FAIL_FAST_IF(referenceCount == 0);
return InterlockedDecrement(&referenceCount);
InterlockedDecrement(&referenceCount);
return self->Release();
}
Implementation::AnsiInputScope::AnsiInputScope(Implementation* self) noexcept :
self{ self }
{
}
HRESULT Implementation::AnsiInputScope::QueryInterface(const IID& riid, void** ppvObj) noexcept
{
if (!ppvObj)
{
return E_POINTER;
}
if (IsEqualGUID(riid, IID_ITfInputScope))
{
*ppvObj = static_cast<ITfInputScope*>(this);
}
else if (IsEqualGUID(riid, IID_IUnknown))
{
*ppvObj = static_cast<IUnknown*>(this);
}
else
{
*ppvObj = nullptr;
return E_NOINTERFACE;
}
AddRef();
return S_OK;
}
ULONG Implementation::AnsiInputScope::AddRef() noexcept
{
return self->AddRef();
}
ULONG Implementation::AnsiInputScope::Release() noexcept
{
return self->Release();
}
HRESULT Implementation::AnsiInputScope::GetInputScopes(InputScope** pprgInputScopes, UINT* pcCount) noexcept
{
const auto scopes = static_cast<InputScope*>(CoTaskMemAlloc(1 * sizeof(InputScope)));
if (!scopes)
{
return E_OUTOFMEMORY;
}
scopes[0] = IS_ALPHANUMERIC_HALFWIDTH;
*pprgInputScopes = scopes;
*pcCount = 1;
return S_OK;
}
HRESULT Implementation::AnsiInputScope::GetPhrase(BSTR** ppbstrPhrases, UINT* pcCount) noexcept
{
return E_NOTIMPL;
}
HRESULT Implementation::AnsiInputScope::GetRegularExpression(BSTR* pbstrRegExp) noexcept
{
return E_NOTIMPL;
}
HRESULT Implementation::AnsiInputScope::GetSRGS(BSTR* pbstrSRGS) noexcept
{
return E_NOTIMPL;
}
HRESULT Implementation::AnsiInputScope::GetXML(BSTR* pbstrXML) noexcept
{
return E_NOTIMPL;
}
[[nodiscard]] HRESULT Implementation::_request(EditSessionProxyBase& session, DWORD flags) const

View File

@ -16,6 +16,8 @@ namespace Microsoft::Console::TSF
struct Implementation : ITfContextOwner, ITfContextOwnerCompositionSink, ITfTextEditSink
{
static void SetDefaultScopeAlphanumericHalfWidth(bool enable) noexcept;
virtual ~Implementation() = default;
void Initialize();
@ -82,6 +84,26 @@ namespace Microsoft::Console::TSF
}
};
struct AnsiInputScope : ITfInputScope
{
explicit AnsiInputScope(Implementation* self) noexcept;
virtual ~AnsiInputScope() = default;
// IUnknown methods
STDMETHODIMP QueryInterface(REFIID riid, void** ppvObj) noexcept override;
ULONG STDMETHODCALLTYPE AddRef() noexcept override;
ULONG STDMETHODCALLTYPE Release() noexcept override;
// ITfInputScope methods
STDMETHODIMP GetInputScopes(InputScope** pprgInputScopes, UINT* pcCount) noexcept override;
STDMETHODIMP GetPhrase(BSTR** ppbstrPhrases, UINT* pcCount) noexcept override;
STDMETHODIMP GetRegularExpression(BSTR* pbstrRegExp) noexcept override;
STDMETHODIMP GetSRGS(BSTR* pbstrSRGS) noexcept override;
STDMETHODIMP GetXML(BSTR* pbstrXML) noexcept override;
Implementation* self = nullptr;
};
[[nodiscard]] HRESULT _request(EditSessionProxyBase& session, DWORD flags) const;
void _doCompositionUpdate(TfEditCookie ec);
TextAttribute _textAttributeFromAtom(TfGuidAtom atom) const;
@ -97,6 +119,7 @@ namespace Microsoft::Console::TSF
wil::com_ptr<ITfThreadMgrEx> _threadMgrEx;
wil::com_ptr<ITfDocumentMgr> _documentMgr;
wil::com_ptr<ITfContext> _context;
wil::com_ptr<ITfContextOwnerCompositionServices> _ownerCompositionServices;
wil::com_ptr<ITfSource> _contextSource;
DWORD _cookieContextOwner = TF_INVALID_COOKIE;
DWORD _cookieTextEditSink = TF_INVALID_COOKIE;
@ -104,5 +127,8 @@ namespace Microsoft::Console::TSF
EditSessionProxy<&Implementation::_doCompositionUpdate> _editSessionCompositionUpdate{ this };
int _compositions = 0;
AnsiInputScope _ansiInputScope{ this };
inline static std::atomic<bool> _wantsAnsiInputScope{ false };
};
}

View File

@ -1,47 +1,11 @@
/*++
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
Copyright (c) Microsoft Corporation.
Licensed under the MIT license.
#pragma once
Module Name:
#define INITGUID
precomp.h
Abstract:
This file precompiled header file.
Author:
Revision History:
Notes:
--*/
#define NOMINMAX
#define _OLEAUT32_
#include <ole2.h>
#include <windows.h>
extern "C" {
#include <winuser.h>
#include <ime.h>
#include <intsafe.h>
#include <strsafe.h>
}
#include <climits>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string_view>
#include <span>
#include <msctf.h> // Cicero header
#include <tsattrs.h> // ITextStore standard attributes
// This includes support libraries from the CRT, STL, WIL, and GSL
#include "LibraryIncludes.h"
#include <msctf.h>
#include <InputScope.h>