Merge remote-tracking branch 'origin/main' into dev/migrie/f/remoting.dll
This commit is contained in:
commit
fa2df47898
|
@ -1,6 +1,6 @@
|
|||
# Dictionaries are lists of words to accept unconditionally
|
||||
|
||||
While check spelling will complain about a whitelisted word
|
||||
While check spelling will complain about an expected word
|
||||
which is no longer present, you can include things here even if
|
||||
they are not otherwise present in the repository.
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
- Yes, the large majority of the `DEFINE_PROPERTYKEY` defs are the same, it's only the last byte of the guid that changes
|
||||
|
||||
2. Add matching fields to Settings.hpp
|
||||
- add getters, setters, the whole drill.
|
||||
- Add getters, setters, the whole drill.
|
||||
|
||||
3. Add to the propsheet
|
||||
- We need to add it to *reading and writing* the registry from the propsheet, and *reading* the link from the propsheet. Yes, that's weird, but the propsheet is smart enough to re-use ShortcutSerialization::s_SetLinkValues, but not smart enough to do the same with RegistrySerialization.
|
||||
|
|
14
doc/TAEF.md
14
doc/TAEF.md
|
@ -48,3 +48,17 @@ Invoke-OpenConsoleTests
|
|||
```
|
||||
|
||||
`Invoke-OpenConsoleTests` supports a number of options, which you can enumerate by running `Invoke-OpenConsoleTests -?`.
|
||||
|
||||
|
||||
### Debugging Tests
|
||||
|
||||
If you want to debug a test, you can do so by using the TAEF /waitForDebugger flag, such as:
|
||||
|
||||
runut *Tests.dll /name:TextBufferTests::TestInsertCharacter /waitForDebugger
|
||||
|
||||
Replace the test name with the one you want to debug. Then, TAEF will begin executing the test and output something like this:
|
||||
|
||||
TAEF: Waiting for debugger - PID <some PID> @ IP <some IP address>
|
||||
|
||||
You can then attach to that PID in your debugger of choice. In Visual Studio, you can use Debug -> Attach To Process, or you could use WinDbg or whatever you want.
|
||||
Once the debugger attaches, the test will execute and your breakpoints will be hit.
|
||||
|
|
395
doc/cascadia/AddASetting.md
Normal file
395
doc/cascadia/AddASetting.md
Normal file
|
@ -0,0 +1,395 @@
|
|||
# Adding Settings to Windows Terminal
|
||||
|
||||
Adding a setting to Windows Terminal is fairly straightforward. This guide serves as a reference on how to add a setting.
|
||||
|
||||
## 1. Terminal Settings Model
|
||||
|
||||
The Terminal Settings Model (`Microsoft.Terminal.Settings.Model`) is responsible for (de)serializing and exposing settings.
|
||||
|
||||
### `GETSET_SETTING` macro
|
||||
|
||||
The `GETSET_SETTING` macro can be used to implement inheritance for your new setting and store the setting in the settings model. It takes three parameters:
|
||||
- `type`: the type that the setting will be stored as
|
||||
- `name`: the name of the variable for storage
|
||||
- `defaultValue`: the value to use if the user does not define the setting anywhere
|
||||
|
||||
### Adding a Profile setting
|
||||
|
||||
This tutorial will add `CloseOnExitMode CloseOnExit` as a profile setting.
|
||||
|
||||
1. In `Profile.h`, declare/define the setting:
|
||||
|
||||
```c++
|
||||
GETSET_SETTING(CloseOnExitMode, CloseOnExit, CloseOnExitMode::Graceful)
|
||||
```
|
||||
|
||||
2. In `Profile.idl`, expose the setting via WinRT:
|
||||
|
||||
```c++
|
||||
Boolean HasCloseOnExit();
|
||||
void ClearCloseOnExit();
|
||||
CloseOnExitMode CloseOnExit;
|
||||
```
|
||||
|
||||
3. In `Profile.cpp`, add (de)serialization and copy logic:
|
||||
|
||||
```c++
|
||||
// Top of file:
|
||||
// - Add the serialization key
|
||||
static constexpr std::string_view CloseOnExitKey{ "closeOnExit" };
|
||||
|
||||
// CopySettings() or Copy():
|
||||
// - The setting is exposed in the Settings UI
|
||||
profile->_CloseOnExit = source->_CloseOnExit;
|
||||
|
||||
// LayerJson():
|
||||
// - get the value from the JSON
|
||||
JsonUtils::GetValueForKey(json, CloseOnExitKey, _CloseOnExit);
|
||||
|
||||
// ToJson():
|
||||
// - write the value to the JSON
|
||||
JsonUtils::SetValueForKey(json, CloseOnExitKey, _CloseOnExit);
|
||||
```
|
||||
|
||||
- If the setting is not a primitive type, in `TerminalSettingsSerializationHelpers.h` add (de)serialization logic for the accepted values:
|
||||
|
||||
```c++
|
||||
// For enum values...
|
||||
JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::CloseOnExitMode)
|
||||
{
|
||||
JSON_MAPPINGS(3) = {
|
||||
pair_type{ "always", ValueType::Always },
|
||||
pair_type{ "graceful", ValueType::Graceful },
|
||||
pair_type{ "never", ValueType::Never },
|
||||
};
|
||||
};
|
||||
|
||||
// For enum flag values...
|
||||
JSON_FLAG_MAPPER(::winrt::Microsoft::Terminal::TerminalControl::CopyFormat)
|
||||
{
|
||||
JSON_MAPPINGS(5) = {
|
||||
pair_type{ "none", AllClear },
|
||||
pair_type{ "html", ValueType::HTML },
|
||||
pair_type{ "rtf", ValueType::RTF },
|
||||
pair_type{ "all", AllSet },
|
||||
};
|
||||
};
|
||||
|
||||
// NOTE: This is also where you can add functionality for...
|
||||
// - overloaded type support (i.e. accept a bool and an enum)
|
||||
// - custom (de)serialization logic (i.e. coordinates)
|
||||
```
|
||||
|
||||
### Adding a Global setting
|
||||
|
||||
Follow the "adding a Profile setting" instructions above, but do it on the `GlobalAppSettings` files.
|
||||
|
||||
### Adding an Action
|
||||
|
||||
This tutorial will add the `openSettings` action.
|
||||
|
||||
1. In `KeyMapping.idl`, declare the action:
|
||||
|
||||
```c++
|
||||
// Add the action to ShortcutAction
|
||||
enum ShortcutAction
|
||||
{
|
||||
OpenSettings
|
||||
}
|
||||
```
|
||||
|
||||
2. In `ActionAndArgs.cpp`, add serialization logic:
|
||||
|
||||
```c++
|
||||
// Top of file:
|
||||
// - Add the serialization key
|
||||
static constexpr std::string_view OpenSettingsKey{ "openSettings" };
|
||||
|
||||
// ActionKeyNamesMap:
|
||||
// - map the new enum to the json key
|
||||
{ OpenSettingsKey, ShortcutAction::OpenSettings },
|
||||
```
|
||||
|
||||
3. If the action should automatically generate a name when it appears in the Command Palette...
|
||||
```c++
|
||||
// In ActionAndArgs.cpp GenerateName() --> GeneratedActionNames
|
||||
{ ShortcutAction::OpenSettings, RS_(L"OpenSettingsCommandKey") },
|
||||
|
||||
// In Resources.resw for Microsoft.Terminal.Settings.Model.Lib,
|
||||
// add the generated name
|
||||
// NOTE: Visual Studio presents the resw file as a table.
|
||||
// If you choose to edit the file with a text editor,
|
||||
// the code should look something like this...
|
||||
<data name="OpenSettingsCommandKey" xml:space="preserve">
|
||||
<value>Open settings file</value>
|
||||
</data>
|
||||
```
|
||||
|
||||
4. If the action supports arguments...
|
||||
- In `ActionArgs.idl`, declare the arguments
|
||||
```c++
|
||||
[default_interface] runtimeclass OpenSettingsArgs : IActionArgs
|
||||
{
|
||||
// this declares the "target" arg
|
||||
SettingsTarget Target { get; };
|
||||
};
|
||||
```
|
||||
- In `ActionArgs.h`, define the new runtime class
|
||||
```c++
|
||||
struct OpenSettingsArgs : public OpenSettingsArgsT<OpenSettingsArgs>
|
||||
{
|
||||
OpenSettingsArgs() = default;
|
||||
|
||||
// adds a getter/setter for your argument, and defines the json key
|
||||
GETSET_PROPERTY(SettingsTarget, Target, SettingsTarget::SettingsFile);
|
||||
static constexpr std::string_view TargetKey{ "target" };
|
||||
|
||||
public:
|
||||
hstring GenerateName() const;
|
||||
|
||||
bool Equals(const IActionArgs& other)
|
||||
{
|
||||
auto otherAsUs = other.try_as<OpenSettingsArgs>();
|
||||
if (otherAsUs)
|
||||
{
|
||||
return otherAsUs->_Target == _Target;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
static FromJsonResult FromJson(const Json::Value& json)
|
||||
{
|
||||
// LOAD BEARING: Not using make_self here _will_ break you in the future!
|
||||
auto args = winrt::make_self<OpenSettingsArgs>();
|
||||
JsonUtils::GetValueForKey(json, TargetKey, args->_Target);
|
||||
return { *args, {} };
|
||||
}
|
||||
|
||||
IActionArgs Copy() const
|
||||
{
|
||||
auto copy{ winrt::make_self<OpenSettingsArgs>() };
|
||||
copy->_Target = _Target;
|
||||
return *copy;
|
||||
}
|
||||
};
|
||||
```
|
||||
- In `ActionArgs.cpp`, define `GenerateName()`. This is used to automatically generate a name when it appears in the Command Palette.
|
||||
- In `ActionAndArgs.cpp`, add serialization logic:
|
||||
|
||||
```c++
|
||||
// ActionKeyNamesMap --> argParsers
|
||||
{ ShortcutAction::OpenSettings, OpenSettingsArgs::FromJson },
|
||||
```
|
||||
|
||||
### Adding an Action Argument
|
||||
|
||||
Follow step 3 from the "adding an Action" instructions above, but modify the relevant `ActionArgs` files.
|
||||
|
||||
## 2. Setting Functionality
|
||||
|
||||
Now that the Terminal Settings Model is updated, Windows Terminal can read and write to the settings file. This section covers how to add functionality to your newly created setting.
|
||||
|
||||
### App-level settings
|
||||
|
||||
App-level settings are settings that affect the frame of Windows Terminal. Generally, these tend to be global settings. The `TerminalApp` project is responsible for presenting the frame of Windows Terminal. A few files of interest include:
|
||||
- `TerminalPage`: XAML control responsible for the look and feel of Windows Terminal
|
||||
- `AppLogic`: WinRT class responsible for window-related issues (i.e. the titlebar, focus mode, etc...)
|
||||
|
||||
Both have access to a `CascadiaSettings` object, for you to read the loaded setting and update Windows Terminal appropriately.
|
||||
|
||||
### Terminal-level settings
|
||||
|
||||
Terminal-level settings are settings that affect a shell session. Generally, these tend to be profile settings. The `TerminalApp` project is responsible for packaging this settings from the Terminal Settings Model to the terminal instance. There are two kinds of settings here:
|
||||
- `IControlSettings`:
|
||||
- These are settings that affect the `TerminalControl` (a XAML control that hosts a shell session).
|
||||
- Examples include background image customization, interactivity behavior (i.e. selection), acrylic and font customization.
|
||||
- The `TerminalControl` project has access to these settings via a saved `IControlSettings` member.
|
||||
- `ICoreSettings`:
|
||||
- These are settings that affect the `TerminalCore` (a lower level object that interacts with the text buffer).
|
||||
- Examples include initial size, history size, and cursor customization.
|
||||
- The `TerminalCore` project has access to these settings via a saved `ICoreSettings` member.
|
||||
|
||||
`TerminalApp` packages these settings into a `TerminalSettings : IControlSettings, ICoreSettings` object upon creating a new terminal instance. To do so, you must submit the following changes:
|
||||
- Declare the setting in `IControlSettings.idl` or `ICoreSettings.idl` (whichever is relevant to your setting). If your setting is an enum setting, declare the enum here instead of in the `TerminalSettingsModel` project.
|
||||
- In `TerminalSettings.h`, declare/define the setting...
|
||||
```c++
|
||||
// The GETSET_PROPERTY macro declares/defines a getter setter for the setting.
|
||||
// Like GETSET_SETTING, it takes in a type, name, and defaultValue.
|
||||
GETSET_PROPERTY(bool, UseAcrylic, false);
|
||||
```
|
||||
- In `TerminalSettings.cpp`...
|
||||
- update `_ApplyProfileSettings` for profile settings
|
||||
- update `_ApplyGlobalSettings` for global settings
|
||||
- If additional processing is necessary, that would happen here. For example, `backgroundImageAlignment` is stored as a `ConvergedAlignment` in the Terminal Settings Model, but converted into XAML's separate horizontal and vertical alignment enums for packaging.
|
||||
|
||||
### Actions
|
||||
|
||||
Actions are packaged as an `ActionAndArgs` object, then handled in `TerminalApp`. To add functionality for actions...
|
||||
- In the `ShortcutActionDispatch` files, dispatch an event when the action occurs...
|
||||
```c++
|
||||
// ShortcutActionDispatch.idl
|
||||
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> OpenSettings;
|
||||
|
||||
// ShortcutActionDispatch.h
|
||||
TYPED_EVENT(OpenSettings, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs);
|
||||
|
||||
// ShortcutActionDispatch.cpp --> DoAction()
|
||||
// - dispatch the appropriate event
|
||||
case ShortcutAction::OpenSettings:
|
||||
{
|
||||
_OpenSettingsHandlers(*this, eventArgs);
|
||||
break;
|
||||
}
|
||||
```
|
||||
- In `TerminalPage` files, handle the event...
|
||||
```c++
|
||||
// TerminalPage.h
|
||||
// - declare the handler
|
||||
void _HandleOpenSettings(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args);
|
||||
|
||||
// TerminalPage.cpp --> _RegisterActionCallbacks()
|
||||
// - register the handler
|
||||
_actionDispatch->OpenSettings({ this, &TerminalPage::_HandleOpenSettings });
|
||||
|
||||
// AppActionHandlers.cpp
|
||||
// - direct the function to the right place and call a helper function
|
||||
void TerminalPage::_HandleOpenSettings(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
// NOTE: this if-statement can be omitted if the action does not support arguments
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<OpenSettingsArgs>())
|
||||
{
|
||||
_LaunchSettings(realArgs.Target());
|
||||
args.Handled(true);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`AppActionHandlers` vary based on the action you want to perform. A few useful helper functions include:
|
||||
- `_GetFocusedTab()`: retrieves the focused tab
|
||||
- `_GetActiveControl()`: retrieves the active terminal control
|
||||
- `_GetTerminalTabImpl()`: tries to cast the given tab as a `TerminalTab` (a tab that hosts a terminal instance)
|
||||
|
||||
|
||||
## 3. Settings UI
|
||||
|
||||
### Exposing Enum Settings
|
||||
If the new setting supports enums, you need to expose a map of the enum and the respective value in the Terminal Settings Model's `EnumMappings`:
|
||||
|
||||
```c++
|
||||
// EnumMappings.idl
|
||||
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Settings.Model.CloseOnExitMode> CloseOnExitMode { get; };
|
||||
|
||||
// EnumMappings.h
|
||||
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, CloseOnExitMode> CloseOnExitMode();
|
||||
|
||||
// EnumMappings.cpp
|
||||
// - this macro leverages the json enum mapper in TerminalSettingsSerializationHelper to expose
|
||||
// the mapped values across project boundaries
|
||||
DEFINE_ENUM_MAP(Model::CloseOnExitMode, CloseOnExitMode);
|
||||
```
|
||||
|
||||
### Binding and Localizing the Enum Setting
|
||||
|
||||
Find the page in the Settings UI that the new setting fits best in. In this example, we are adding `LaunchMode`.
|
||||
1. In `Launch.idl`, expose the bindable setting...
|
||||
```c++
|
||||
// Expose the current value for the setting
|
||||
IInspectable CurrentLaunchMode;
|
||||
|
||||
// Expose the list of possible values
|
||||
Windows.Foundation.Collections.IObservableVector<Microsoft.Terminal.Settings.Editor.EnumEntry> LaunchModeList { get; };
|
||||
```
|
||||
|
||||
2. In `Launch.h`, declare the bindable enum setting...
|
||||
```c++
|
||||
// the GETSET_BINDABLE_ENUM_SETTING macro accepts...
|
||||
// - name: the name of the setting
|
||||
// - enumType: the type of the setting
|
||||
// - settingsModelName: how to retrieve the setting (use State() to get access to the settings model)
|
||||
// - settingNameInModel: the name of the setting in the terminal settings model
|
||||
GETSET_BINDABLE_ENUM_SETTING(LaunchMode, Model::LaunchMode, State().Settings().GlobalSettings, LaunchMode);
|
||||
```
|
||||
|
||||
3. In `Launch.cpp`, populate these functions...
|
||||
```c++
|
||||
// Constructor (after InitializeComponent())
|
||||
// the INITIALIZE_BINDABLE_ENUM_SETTING macro accepts...
|
||||
// - name: the name of the setting
|
||||
// - enumMappingsName: the name from the TerminalSettingsModel's EnumMappings
|
||||
// - enumType: the type for the enum
|
||||
// - resourceSectionAndType: prefix for the localization
|
||||
// - resourceProperty: postfix for the localization
|
||||
INITIALIZE_BINDABLE_ENUM_SETTING(LaunchMode, LaunchMode, LaunchMode, L"Globals_LaunchMode", L"Content");
|
||||
```
|
||||
|
||||
4. In `Resources.resw` for Microsoft.Terminal.Settings.Editor, add the localized text to expose each enum value. Use the following format: `<SettingGroup>_<SettingName><EnumValue>.Content`
|
||||
- `SettingGroup`:
|
||||
- `Globals` for global settings
|
||||
- `Profile` for profile settings
|
||||
- `SettingName`:
|
||||
- the Pascal-case format for the setting type (i.e. `LaunchMode` for `"launchMode"`)
|
||||
- `EnumValue`:
|
||||
- the json key for the setting value, but with the first letter capitalized (i.e. `Focus` for `"focus"`)
|
||||
- The resulting resw key should look something like this `Globals_LaunchModeFocus.Content`
|
||||
- This is the text that will be used in your control
|
||||
|
||||
### Updating the UI
|
||||
|
||||
#### Enum Settings
|
||||
|
||||
Now, create a XAML control in the relevant XAML file. Use the following tips and tricks to style everything appropriately:
|
||||
- Wrap the control in a `ContentPresenter` adhering to the `SettingContainerStyle` style
|
||||
- Bind `SelectedItem` to the relevant `Current<Setting>` (i.e. `CurrentLaunchMode`). Ensure it's a TwoWay binding
|
||||
- Bind `ItemsSource` to `<Setting>List` (i.e. `LaunchModeList`)
|
||||
- Set the ItemTemplate to the `Enum<ControlType>Template` (i.e. `EnumRadioButtonTemplate` for radio buttons)
|
||||
- Set the style to the appropriate one in `CommonResources.xaml`
|
||||
|
||||
```xml
|
||||
<!--Launch Mode-->
|
||||
<ContentPresenter Style="{StaticResource SettingContainerStyle}">
|
||||
<muxc:RadioButtons x:Uid="Globals_LaunchMode"
|
||||
SelectedItem="{x:Bind CurrentLaunchMode, Mode="TwoWay"}"
|
||||
ItemsSource="{x:Bind LaunchModeList}"
|
||||
ItemTemplate="{StaticResource EnumRadioButtonTemplate}"
|
||||
Style="{StaticResource RadioButtonsSettingStyle}"/>
|
||||
</ContentPresenter>
|
||||
```
|
||||
|
||||
To add any localized text, add a `x:Uid`, and access the relevant property via the Resources.resw file. For example, `Globals_LaunchMode.Header` sets the header for this control. You can also set the tooltip text like this:
|
||||
`Globals_DefaultProfile.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip`.
|
||||
|
||||
#### Non-Enum Settings
|
||||
|
||||
Continue to reference `CommonResources.xaml` for appropriate styling and wrap the control with a similar `ContentPresenter`. However, instead of binding to the `Current<Setting>` and `<Setting>List`, bind directly to the setting via the state. Binding a setting like `altGrAliasing` should look something like this:
|
||||
```xml
|
||||
<!--AltGr Aliasing-->
|
||||
<ContentPresenter Style="{StaticResource SettingContainerStyle}">
|
||||
<CheckBox x:Uid="Profile_AltGrAliasing"
|
||||
IsChecked="{x:Bind State.Profile.AltGrAliasing, Mode=TwoWay}"
|
||||
Style="{StaticResource CheckBoxSettingStyle}"/>
|
||||
</ContentPresenter>
|
||||
```
|
||||
|
||||
#### Profile Settings
|
||||
|
||||
If you are specifically adding a Profile setting, in addition to the steps above, you need to make the setting observable by modifying the `Profiles` files...
|
||||
```c++
|
||||
// Profiles.idl --> ProfileViewModel
|
||||
// - this declares the setting as observable using the type and the name of the setting
|
||||
OBSERVABLE_PROJECTED_SETTING(Microsoft.Terminal.Settings.Model.CloseOnExitMode, CloseOnExit);
|
||||
|
||||
// Profiles.h --> ProfileViewModel
|
||||
// - this defines the setting as observable off of the _profile object
|
||||
OBSERVABLE_PROJECTED_SETTING(_profile, CloseOnExit);
|
||||
|
||||
// Profiles.h --> ProfileViewModel
|
||||
// - if the setting cannot be inherited by another profile (aka missing the Clear() function), use the following macro instead:
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_profile, Guid);
|
||||
```
|
||||
|
||||
The `ProfilePageNavigationState` holds a `ProfileViewModel`, which wraps the `Profile` object from the Terminal Settings Model. The `ProfileViewModel` makes all of the profile settings observable.
|
||||
|
||||
### Actions
|
||||
|
||||
Actions are not yet supported in the Settings UI.
|
|
@ -369,6 +369,13 @@
|
|||
"splitMode": {
|
||||
"default": "duplicate",
|
||||
"description": "Control how the pane splits. Only accepts \"duplicate\" which will duplicate the focused pane's profile into a new pane."
|
||||
},
|
||||
"size": {
|
||||
"default": 0.5,
|
||||
"description": "Specify how large the new pane should be, as a fraction of the current pane's size. 1.0 would be 'all of the current pane', and 0.0 is 'None of the parent'. Accepts floating point values from 0-1 (default 0.5).",
|
||||
"maximum": 1,
|
||||
"minimum": 0,
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,10 +11,16 @@
|
|||
// - attr - the default text attribute
|
||||
// Return Value:
|
||||
// - constructed object
|
||||
// Note: will throw exception if unable to allocate memory for text attribute storage
|
||||
ATTR_ROW::ATTR_ROW(const UINT cchRowWidth, const TextAttribute attr)
|
||||
ATTR_ROW::ATTR_ROW(const UINT cchRowWidth, const TextAttribute attr) noexcept
|
||||
{
|
||||
_list.push_back(TextAttributeRun(cchRowWidth, attr));
|
||||
try
|
||||
{
|
||||
_list.emplace_back(TextAttributeRun(cchRowWidth, attr));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
FAIL_FAST_CAUGHT_EXCEPTION();
|
||||
}
|
||||
_cchRowWidth = cchRowWidth;
|
||||
}
|
||||
|
||||
|
@ -25,7 +31,7 @@ ATTR_ROW::ATTR_ROW(const UINT cchRowWidth, const TextAttribute attr)
|
|||
void ATTR_ROW::Reset(const TextAttribute attr)
|
||||
{
|
||||
_list.clear();
|
||||
_list.push_back(TextAttributeRun(_cchRowWidth, attr));
|
||||
_list.emplace_back(TextAttributeRun(_cchRowWidth, attr));
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
@ -402,7 +408,7 @@ void ATTR_ROW::ReplaceAttrs(const TextAttribute& toBeReplacedAttr, const TextAtt
|
|||
// The original run was 3 long. The insertion run was 1 long. We need 1 more for the
|
||||
// fact that an existing piece of the run was split in half (to hold the latter half).
|
||||
const size_t cNewRun = _list.size() + newAttrs.size() + 1;
|
||||
std::vector<TextAttributeRun> newRun;
|
||||
decltype(_list) newRun;
|
||||
newRun.reserve(cNewRun);
|
||||
|
||||
// We will start analyzing from the beginning of our existing run.
|
||||
|
@ -595,8 +601,7 @@ std::vector<TextAttributeRun> ATTR_ROW::PackAttrs(const std::vector<TextAttribut
|
|||
{
|
||||
if (runs.empty() || runs.back().GetAttributes() != attr)
|
||||
{
|
||||
const TextAttributeRun run(1, attr);
|
||||
runs.push_back(run);
|
||||
runs.emplace_back(TextAttributeRun(1, attr));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -20,6 +20,7 @@ Revision History:
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "boost/container/small_vector.hpp"
|
||||
#include "TextAttributeRun.hpp"
|
||||
#include "AttrRowIterator.hpp"
|
||||
|
||||
|
@ -28,7 +29,16 @@ class ATTR_ROW final
|
|||
public:
|
||||
using const_iterator = typename AttrRowIterator;
|
||||
|
||||
ATTR_ROW(const UINT cchRowWidth, const TextAttribute attr);
|
||||
ATTR_ROW(const UINT cchRowWidth, const TextAttribute attr)
|
||||
noexcept;
|
||||
|
||||
~ATTR_ROW() = default;
|
||||
|
||||
ATTR_ROW(const ATTR_ROW&) = default;
|
||||
ATTR_ROW& operator=(const ATTR_ROW&) = default;
|
||||
ATTR_ROW(ATTR_ROW&&)
|
||||
noexcept = default;
|
||||
ATTR_ROW& operator=(ATTR_ROW&&) noexcept = default;
|
||||
|
||||
void Reset(const TextAttribute attr);
|
||||
|
||||
|
@ -65,7 +75,7 @@ public:
|
|||
friend class AttrRowIterator;
|
||||
|
||||
private:
|
||||
std::vector<TextAttributeRun> _list;
|
||||
boost::container::small_vector<TextAttributeRun, 1> _list;
|
||||
size_t _cchRowWidth;
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
|
|
|
@ -39,19 +39,6 @@ bool AttrRowIterator::operator!=(const AttrRowIterator& it) const noexcept
|
|||
return !(*this == it);
|
||||
}
|
||||
|
||||
AttrRowIterator& AttrRowIterator::operator++() noexcept
|
||||
{
|
||||
_increment(1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
AttrRowIterator AttrRowIterator::operator++(int) noexcept
|
||||
{
|
||||
auto copy = *this;
|
||||
_increment(1);
|
||||
return copy;
|
||||
}
|
||||
|
||||
AttrRowIterator& AttrRowIterator::operator+=(const ptrdiff_t& movement)
|
||||
{
|
||||
if (!_exceeded)
|
||||
|
@ -74,19 +61,6 @@ AttrRowIterator& AttrRowIterator::operator-=(const ptrdiff_t& movement)
|
|||
return this->operator+=(-movement);
|
||||
}
|
||||
|
||||
AttrRowIterator& AttrRowIterator::operator--() noexcept
|
||||
{
|
||||
_decrement(1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
AttrRowIterator AttrRowIterator::operator--(int) noexcept
|
||||
{
|
||||
auto copy = *this;
|
||||
_decrement(1);
|
||||
return copy;
|
||||
}
|
||||
|
||||
const TextAttribute* AttrRowIterator::operator->() const
|
||||
{
|
||||
THROW_HR_IF(E_BOUNDS, _exceeded);
|
||||
|
|
|
@ -15,6 +15,7 @@ Author(s):
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "boost/container/small_vector.hpp"
|
||||
#include "TextAttribute.hpp"
|
||||
#include "TextAttributeRun.hpp"
|
||||
|
||||
|
@ -38,20 +39,38 @@ public:
|
|||
bool operator==(const AttrRowIterator& it) const noexcept;
|
||||
bool operator!=(const AttrRowIterator& it) const noexcept;
|
||||
|
||||
AttrRowIterator& operator++() noexcept;
|
||||
AttrRowIterator operator++(int) noexcept;
|
||||
AttrRowIterator& operator++() noexcept
|
||||
{
|
||||
_increment(1);
|
||||
return *this;
|
||||
}
|
||||
AttrRowIterator operator++(int) noexcept
|
||||
{
|
||||
auto copy = *this;
|
||||
_increment(1);
|
||||
return copy;
|
||||
}
|
||||
|
||||
AttrRowIterator& operator+=(const ptrdiff_t& movement);
|
||||
AttrRowIterator& operator-=(const ptrdiff_t& movement);
|
||||
|
||||
AttrRowIterator& operator--() noexcept;
|
||||
AttrRowIterator operator--(int) noexcept;
|
||||
AttrRowIterator& operator--() noexcept
|
||||
{
|
||||
_decrement(1);
|
||||
return *this;
|
||||
}
|
||||
AttrRowIterator operator--(int) noexcept
|
||||
{
|
||||
auto copy = *this;
|
||||
_decrement(1);
|
||||
return copy;
|
||||
}
|
||||
|
||||
const TextAttribute* operator->() const;
|
||||
const TextAttribute& operator*() const;
|
||||
|
||||
private:
|
||||
std::vector<TextAttributeRun>::const_iterator _run;
|
||||
boost::container::small_vector_base<TextAttributeRun>::const_iterator _run;
|
||||
const ATTR_ROW* _pAttrRow;
|
||||
size_t _currentAttributeIndex; // index of TextAttribute within the current TextAttributeRun
|
||||
bool _exceeded;
|
||||
|
|
|
@ -15,13 +15,16 @@
|
|||
// Return Value:
|
||||
// - instantiated object
|
||||
// Note: will through if unable to allocate char/attribute buffers
|
||||
CharRow::CharRow(size_t rowWidth, ROW* const pParent) :
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 26447) // small_vector's constructor says it can throw but it should not given how we use it. This suppresses this error for the AuditMode build.
|
||||
CharRow::CharRow(size_t rowWidth, ROW* const pParent) noexcept :
|
||||
_wrapForced{ false },
|
||||
_doubleBytePadded{ false },
|
||||
_data(rowWidth, value_type()),
|
||||
_pParent{ FAIL_FAST_IF_NULL(pParent) }
|
||||
{
|
||||
}
|
||||
#pragma warning(pop)
|
||||
|
||||
// Routine Description:
|
||||
// - Sets the wrap status for the current row
|
||||
|
@ -141,7 +144,7 @@ typename CharRow::const_iterator CharRow::cend() const noexcept
|
|||
// - The calculated left boundary of the internal string.
|
||||
size_t CharRow::MeasureLeft() const noexcept
|
||||
{
|
||||
std::vector<value_type>::const_iterator it = _data.cbegin();
|
||||
const_iterator it = _data.cbegin();
|
||||
while (it != _data.cend() && it->IsSpace())
|
||||
{
|
||||
++it;
|
||||
|
@ -155,9 +158,9 @@ size_t CharRow::MeasureLeft() const noexcept
|
|||
// - <none>
|
||||
// Return Value:
|
||||
// - The calculated right boundary of the internal string.
|
||||
size_t CharRow::MeasureRight() const noexcept
|
||||
size_t CharRow::MeasureRight() const
|
||||
{
|
||||
std::vector<value_type>::const_reverse_iterator it = _data.crbegin();
|
||||
const_reverse_iterator it = _data.crbegin();
|
||||
while (it != _data.crend() && it->IsSpace())
|
||||
{
|
||||
++it;
|
||||
|
|
|
@ -24,6 +24,7 @@ Revision History:
|
|||
#include "CharRowCellReference.hpp"
|
||||
#include "CharRowCell.hpp"
|
||||
#include "UnicodeStorage.hpp"
|
||||
#include "boost/container/small_vector.hpp"
|
||||
|
||||
class ROW;
|
||||
|
||||
|
@ -49,11 +50,12 @@ class CharRow final
|
|||
public:
|
||||
using glyph_type = typename wchar_t;
|
||||
using value_type = typename CharRowCell;
|
||||
using iterator = typename std::vector<value_type>::iterator;
|
||||
using const_iterator = typename std::vector<value_type>::const_iterator;
|
||||
using iterator = typename boost::container::small_vector_base<value_type>::iterator;
|
||||
using const_iterator = typename boost::container::small_vector_base<value_type>::const_iterator;
|
||||
using const_reverse_iterator = typename boost::container::small_vector_base<value_type>::const_reverse_iterator;
|
||||
using reference = typename CharRowCellReference;
|
||||
|
||||
CharRow(size_t rowWidth, ROW* const pParent);
|
||||
CharRow(size_t rowWidth, ROW* const pParent) noexcept;
|
||||
|
||||
void SetWrapForced(const bool wrap) noexcept;
|
||||
bool WasWrapForced() const noexcept;
|
||||
|
@ -63,7 +65,7 @@ public:
|
|||
void Reset() noexcept;
|
||||
[[nodiscard]] HRESULT Resize(const size_t newSize) noexcept;
|
||||
size_t MeasureLeft() const noexcept;
|
||||
size_t MeasureRight() const noexcept;
|
||||
size_t MeasureRight() const;
|
||||
void ClearCell(const size_t column);
|
||||
bool ContainsText() const noexcept;
|
||||
const DbcsAttribute& DbcsAttrAt(const size_t column) const;
|
||||
|
@ -101,7 +103,7 @@ protected:
|
|||
bool _doubleBytePadded;
|
||||
|
||||
// storage for glyph data and dbcs attributes
|
||||
std::vector<value_type> _data;
|
||||
boost::container::small_vector<value_type, 120> _data;
|
||||
|
||||
// ROW that this CharRow belongs to
|
||||
ROW* _pParent;
|
||||
|
|
|
@ -8,18 +8,6 @@
|
|||
// default glyph value, used for resetting the character data portion of a cell
|
||||
static constexpr wchar_t DefaultValue = UNICODE_SPACE;
|
||||
|
||||
CharRowCell::CharRowCell() noexcept :
|
||||
_wch{ DefaultValue },
|
||||
_attr{}
|
||||
{
|
||||
}
|
||||
|
||||
CharRowCell::CharRowCell(const wchar_t wch, const DbcsAttribute attr) noexcept :
|
||||
_wch{ wch },
|
||||
_attr{ attr }
|
||||
{
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - "erases" the glyph. really sets it back to the default "empty" value
|
||||
void CharRowCell::EraseChars() noexcept
|
||||
|
|
|
@ -17,6 +17,7 @@ Author(s):
|
|||
#pragma once
|
||||
|
||||
#include "DbcsAttribute.hpp"
|
||||
#include "unicode.hpp"
|
||||
|
||||
#if (defined(_M_IX86) || defined(_M_AMD64))
|
||||
// currently CharRowCell's fields use 3 bytes of memory, leaving the 4th byte in unused. this leads
|
||||
|
@ -27,8 +28,13 @@ Author(s):
|
|||
class CharRowCell final
|
||||
{
|
||||
public:
|
||||
CharRowCell() noexcept;
|
||||
CharRowCell(const wchar_t wch, const DbcsAttribute attr) noexcept;
|
||||
CharRowCell() noexcept = default;
|
||||
CharRowCell(const wchar_t wch, const DbcsAttribute attr) noexcept
|
||||
:
|
||||
_wch(wch),
|
||||
_attr(attr)
|
||||
{
|
||||
}
|
||||
|
||||
void EraseChars() noexcept;
|
||||
void Reset() noexcept;
|
||||
|
@ -44,8 +50,8 @@ public:
|
|||
friend constexpr bool operator==(const CharRowCell& a, const CharRowCell& b) noexcept;
|
||||
|
||||
private:
|
||||
wchar_t _wch;
|
||||
DbcsAttribute _attr;
|
||||
wchar_t _wch{ UNICODE_SPACE };
|
||||
DbcsAttribute _attr{};
|
||||
};
|
||||
|
||||
#if (defined(_M_IX86) || defined(_M_AMD64))
|
||||
|
|
|
@ -32,8 +32,8 @@ public:
|
|||
}
|
||||
|
||||
~CharRowCellReference() = default;
|
||||
CharRowCellReference(const CharRowCellReference&) = default;
|
||||
CharRowCellReference(CharRowCellReference&&) = default;
|
||||
CharRowCellReference(const CharRowCellReference&) noexcept = default;
|
||||
CharRowCellReference(CharRowCellReference&&) noexcept = default;
|
||||
|
||||
void operator=(const CharRowCellReference&) = delete;
|
||||
void operator=(CharRowCellReference&&) = delete;
|
||||
|
|
|
@ -16,50 +16,15 @@
|
|||
// - pParent - the text buffer that this row belongs to
|
||||
// Return Value:
|
||||
// - constructed object
|
||||
ROW::ROW(const SHORT rowId, const short rowWidth, const TextAttribute fillAttribute, TextBuffer* const pParent) :
|
||||
ROW::ROW(const SHORT rowId, const unsigned short rowWidth, const TextAttribute fillAttribute, TextBuffer* const pParent) noexcept :
|
||||
_id{ rowId },
|
||||
_rowWidth{ gsl::narrow<size_t>(rowWidth) },
|
||||
_charRow{ gsl::narrow<size_t>(rowWidth), this },
|
||||
_attrRow{ gsl::narrow<UINT>(rowWidth), fillAttribute },
|
||||
_rowWidth{ rowWidth },
|
||||
_charRow{ rowWidth, this },
|
||||
_attrRow{ rowWidth, fillAttribute },
|
||||
_pParent{ pParent }
|
||||
{
|
||||
}
|
||||
|
||||
size_t ROW::size() const noexcept
|
||||
{
|
||||
return _rowWidth;
|
||||
}
|
||||
|
||||
const CharRow& ROW::GetCharRow() const noexcept
|
||||
{
|
||||
return _charRow;
|
||||
}
|
||||
|
||||
CharRow& ROW::GetCharRow() noexcept
|
||||
{
|
||||
return _charRow;
|
||||
}
|
||||
|
||||
const ATTR_ROW& ROW::GetAttrRow() const noexcept
|
||||
{
|
||||
return _attrRow;
|
||||
}
|
||||
|
||||
ATTR_ROW& ROW::GetAttrRow() noexcept
|
||||
{
|
||||
return _attrRow;
|
||||
}
|
||||
|
||||
SHORT ROW::GetId() const noexcept
|
||||
{
|
||||
return _id;
|
||||
}
|
||||
|
||||
void ROW::SetId(const SHORT id) noexcept
|
||||
{
|
||||
_id = id;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Sets all properties of the ROW to default values
|
||||
// Arguments:
|
||||
|
@ -87,7 +52,7 @@ bool ROW::Reset(const TextAttribute Attr)
|
|||
// - width - the new width, in cells
|
||||
// Return Value:
|
||||
// - S_OK if successful, otherwise relevant error
|
||||
[[nodiscard]] HRESULT ROW::Resize(const size_t width)
|
||||
[[nodiscard]] HRESULT ROW::Resize(const unsigned short width)
|
||||
{
|
||||
RETURN_IF_FAILED(_charRow.Resize(width));
|
||||
try
|
||||
|
@ -113,25 +78,6 @@ void ROW::ClearColumn(const size_t column)
|
|||
_charRow.ClearCell(column);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - gets the text of the row as it would be shown on the screen
|
||||
// Return Value:
|
||||
// - wstring containing text for the row
|
||||
std::wstring ROW::GetText() const
|
||||
{
|
||||
return _charRow.GetText();
|
||||
}
|
||||
|
||||
RowCellIterator ROW::AsCellIter(const size_t startIndex) const
|
||||
{
|
||||
return AsCellIter(startIndex, size() - startIndex);
|
||||
}
|
||||
|
||||
RowCellIterator ROW::AsCellIter(const size_t startIndex, const size_t count) const
|
||||
{
|
||||
return RowCellIterator(*this, startIndex, count);
|
||||
}
|
||||
|
||||
UnicodeStorage& ROW::GetUnicodeStorage() noexcept
|
||||
{
|
||||
return _pParent->GetUnicodeStorage();
|
||||
|
|
|
@ -32,27 +32,28 @@ class TextBuffer;
|
|||
class ROW final
|
||||
{
|
||||
public:
|
||||
ROW(const SHORT rowId, const short rowWidth, const TextAttribute fillAttribute, TextBuffer* const pParent);
|
||||
ROW(const SHORT rowId, const unsigned short rowWidth, const TextAttribute fillAttribute, TextBuffer* const pParent)
|
||||
noexcept;
|
||||
|
||||
size_t size() const noexcept;
|
||||
size_t size() const noexcept { return _rowWidth; }
|
||||
|
||||
const CharRow& GetCharRow() const noexcept;
|
||||
CharRow& GetCharRow() noexcept;
|
||||
const CharRow& GetCharRow() const noexcept { return _charRow; }
|
||||
CharRow& GetCharRow() noexcept { return _charRow; }
|
||||
|
||||
const ATTR_ROW& GetAttrRow() const noexcept;
|
||||
ATTR_ROW& GetAttrRow() noexcept;
|
||||
const ATTR_ROW& GetAttrRow() const noexcept { return _attrRow; }
|
||||
ATTR_ROW& GetAttrRow() noexcept { return _attrRow; }
|
||||
|
||||
SHORT GetId() const noexcept;
|
||||
void SetId(const SHORT id) noexcept;
|
||||
SHORT GetId() const noexcept { return _id; }
|
||||
void SetId(const SHORT id) noexcept { _id = id; }
|
||||
|
||||
bool Reset(const TextAttribute Attr);
|
||||
[[nodiscard]] HRESULT Resize(const size_t width);
|
||||
[[nodiscard]] HRESULT Resize(const unsigned short width);
|
||||
|
||||
void ClearColumn(const size_t column);
|
||||
std::wstring GetText() const;
|
||||
std::wstring GetText() const { return _charRow.GetText(); }
|
||||
|
||||
RowCellIterator AsCellIter(const size_t startIndex) const;
|
||||
RowCellIterator AsCellIter(const size_t startIndex, const size_t count) const;
|
||||
RowCellIterator AsCellIter(const size_t startIndex) const { return AsCellIter(startIndex, size() - startIndex); }
|
||||
RowCellIterator AsCellIter(const size_t startIndex, const size_t count) const { return RowCellIterator(*this, startIndex, count); }
|
||||
|
||||
UnicodeStorage& GetUnicodeStorage() noexcept;
|
||||
const UnicodeStorage& GetUnicodeStorage() const noexcept;
|
||||
|
@ -69,7 +70,7 @@ private:
|
|||
CharRow _charRow;
|
||||
ATTR_ROW _attrRow;
|
||||
SHORT _id;
|
||||
size_t _rowWidth;
|
||||
unsigned short _rowWidth;
|
||||
TextBuffer* _pParent; // non ownership pointer
|
||||
};
|
||||
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "precomp.h"
|
||||
#include "TextAttributeRun.hpp"
|
||||
|
||||
TextAttributeRun::TextAttributeRun() noexcept :
|
||||
_cchLength(0)
|
||||
{
|
||||
SetAttributes(TextAttribute(0));
|
||||
}
|
||||
|
||||
TextAttributeRun::TextAttributeRun(const size_t cchLength, const TextAttribute attr) noexcept :
|
||||
_cchLength(cchLength)
|
||||
{
|
||||
SetAttributes(attr);
|
||||
}
|
||||
|
||||
size_t TextAttributeRun::GetLength() const noexcept
|
||||
{
|
||||
return _cchLength;
|
||||
}
|
||||
|
||||
void TextAttributeRun::SetLength(const size_t cchLength) noexcept
|
||||
{
|
||||
_cchLength = cchLength;
|
||||
}
|
||||
|
||||
void TextAttributeRun::IncrementLength() noexcept
|
||||
{
|
||||
_cchLength++;
|
||||
}
|
||||
|
||||
void TextAttributeRun::DecrementLength() noexcept
|
||||
{
|
||||
_cchLength--;
|
||||
}
|
||||
|
||||
const TextAttribute& TextAttributeRun::GetAttributes() const noexcept
|
||||
{
|
||||
return _attributes;
|
||||
}
|
||||
|
||||
void TextAttributeRun::SetAttributes(const TextAttribute textAttribute) noexcept
|
||||
{
|
||||
_attributes = textAttribute;
|
||||
}
|
|
@ -25,20 +25,24 @@ Revision History:
|
|||
class TextAttributeRun final
|
||||
{
|
||||
public:
|
||||
TextAttributeRun() noexcept;
|
||||
TextAttributeRun(const size_t cchLength, const TextAttribute attr) noexcept;
|
||||
TextAttributeRun() = default;
|
||||
TextAttributeRun(const size_t cchLength, const TextAttribute attr) noexcept :
|
||||
_cchLength(gsl::narrow<unsigned int>(cchLength))
|
||||
{
|
||||
SetAttributes(attr);
|
||||
}
|
||||
|
||||
size_t GetLength() const noexcept;
|
||||
void SetLength(const size_t cchLength) noexcept;
|
||||
void IncrementLength() noexcept;
|
||||
void DecrementLength() noexcept;
|
||||
size_t GetLength() const noexcept { return _cchLength; }
|
||||
void SetLength(const size_t cchLength) noexcept { _cchLength = gsl::narrow<unsigned int>(cchLength); }
|
||||
void IncrementLength() noexcept { _cchLength++; }
|
||||
void DecrementLength() noexcept { _cchLength--; }
|
||||
|
||||
const TextAttribute& GetAttributes() const noexcept;
|
||||
void SetAttributes(const TextAttribute textAttribute) noexcept;
|
||||
const TextAttribute& GetAttributes() const noexcept { return _attributes; }
|
||||
void SetAttributes(const TextAttribute textAttribute) noexcept { _attributes = textAttribute; }
|
||||
|
||||
private:
|
||||
size_t _cchLength;
|
||||
TextAttribute _attributes;
|
||||
unsigned int _cchLength{ 0 };
|
||||
TextAttribute _attributes{ 0 };
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
friend class AttrRowTests;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<RootNamespace>bufferout</RootNamespace>
|
||||
<ProjectName>BufferOut</ProjectName>
|
||||
<TargetName>ConBufferOut</TargetName>
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)src\common.build.pre.props" />
|
||||
<ItemGroup>
|
||||
|
@ -22,7 +22,6 @@
|
|||
<ClCompile Include="..\search.cpp" />
|
||||
<ClCompile Include="..\TextColor.cpp" />
|
||||
<ClCompile Include="..\TextAttribute.cpp" />
|
||||
<ClCompile Include="..\TextAttributeRun.cpp" />
|
||||
<ClCompile Include="..\textBuffer.cpp" />
|
||||
<ClCompile Include="..\textBufferCellIterator.cpp" />
|
||||
<ClCompile Include="..\textBufferTextIterator.cpp" />
|
||||
|
@ -61,4 +60,4 @@
|
|||
</ItemGroup>
|
||||
<!-- Careful reordering these. Some default props (contained in these files) are order sensitive. -->
|
||||
<Import Project="$(SolutionDir)src\common.build.post.props" />
|
||||
</Project>
|
||||
</Project>
|
|
@ -40,7 +40,6 @@ SOURCES= \
|
|||
..\RowCellIterator.cpp \
|
||||
..\TextColor.cpp \
|
||||
..\TextAttribute.cpp \
|
||||
..\TextAttributeRun.cpp \
|
||||
..\textBuffer.cpp \
|
||||
..\textBufferCellIterator.cpp \
|
||||
..\textBufferTextIterator.cpp \
|
||||
|
|
|
@ -42,6 +42,7 @@ TextBuffer::TextBuffer(const COORD screenBufferSize,
|
|||
_currentPatternId{ 0 }
|
||||
{
|
||||
// initialize ROWs
|
||||
_storage.reserve(static_cast<size_t>(screenBufferSize.Y));
|
||||
for (size_t i = 0; i < static_cast<size_t>(screenBufferSize.Y); ++i)
|
||||
{
|
||||
_storage.emplace_back(static_cast<SHORT>(i), screenBufferSize.X, _currentAttributes, this);
|
||||
|
@ -837,11 +838,10 @@ void TextBuffer::Reset()
|
|||
const SHORT TopRowIndex = (GetFirstRowIndex() + TopRow) % currentSize.Y;
|
||||
|
||||
// rotate rows until the top row is at index 0
|
||||
const ROW& newTopRow = _storage.at(TopRowIndex);
|
||||
while (&newTopRow != &_storage.front())
|
||||
for (int i = 0; i < TopRowIndex; i++)
|
||||
{
|
||||
_storage.push_back(std::move(_storage.front()));
|
||||
_storage.pop_front();
|
||||
_storage.emplace_back(std::move(_storage.front()));
|
||||
_storage.erase(_storage.begin());
|
||||
}
|
||||
|
||||
_SetFirstRowIndex(0);
|
||||
|
@ -2410,7 +2410,7 @@ PointTree TextBuffer::GetPatterns(const size_t firstRow, const size_t lastRow) c
|
|||
// all the text into one string and find the patterns in that string
|
||||
for (auto i = firstRow; i <= lastRow; ++i)
|
||||
{
|
||||
auto row = GetRowByOffset(i);
|
||||
auto& row = GetRowByOffset(i);
|
||||
concatAll += row.GetCharRow().GetText();
|
||||
}
|
||||
|
||||
|
|
|
@ -49,6 +49,8 @@ filling in the last row, and updating the screen.
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "cursor.h"
|
||||
#include "Row.hpp"
|
||||
#include "TextAttribute.hpp"
|
||||
|
@ -190,7 +192,7 @@ public:
|
|||
private:
|
||||
void _UpdateSize();
|
||||
Microsoft::Console::Types::Viewport _size;
|
||||
std::deque<ROW> _storage;
|
||||
std::vector<ROW> _storage;
|
||||
Cursor _cursor;
|
||||
|
||||
SHORT _firstRow; // indexes top row (not necessarily 0)
|
||||
|
|
|
@ -37,6 +37,7 @@ namespace SettingsModelLocalTests
|
|||
TEST_METHOD(ManyCommandsSameAction);
|
||||
TEST_METHOD(LayerCommand);
|
||||
TEST_METHOD(TestSplitPaneArgs);
|
||||
TEST_METHOD(TestSplitPaneBadSize);
|
||||
TEST_METHOD(TestResourceKeyName);
|
||||
TEST_METHOD(TestAutogeneratedName);
|
||||
TEST_METHOD(TestLayerOnAutogeneratedName);
|
||||
|
@ -147,7 +148,8 @@ namespace SettingsModelLocalTests
|
|||
{ "name": "command1", "command": { "action": "splitPane", "split": "vertical" } },
|
||||
{ "name": "command2", "command": { "action": "splitPane", "split": "horizontal" } },
|
||||
{ "name": "command4", "command": { "action": "splitPane" } },
|
||||
{ "name": "command5", "command": { "action": "splitPane", "split": "auto" } }
|
||||
{ "name": "command5", "command": { "action": "splitPane", "split": "auto" } },
|
||||
{ "name": "command6", "command": { "action": "splitPane", "size": 0.25 } },
|
||||
])" };
|
||||
|
||||
const auto commands0Json = VerifyParseSucceeded(commands0String);
|
||||
|
@ -156,7 +158,7 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_ARE_EQUAL(0u, commands.Size());
|
||||
auto warnings = implementation::Command::LayerJson(commands, commands0Json);
|
||||
VERIFY_ARE_EQUAL(0u, warnings.size());
|
||||
VERIFY_ARE_EQUAL(4u, commands.Size());
|
||||
VERIFY_ARE_EQUAL(5u, commands.Size());
|
||||
|
||||
{
|
||||
auto command = commands.Lookup(L"command1");
|
||||
|
@ -167,6 +169,7 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
|
||||
VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize());
|
||||
}
|
||||
{
|
||||
auto command = commands.Lookup(L"command2");
|
||||
|
@ -177,6 +180,7 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Horizontal, realArgs.SplitStyle());
|
||||
VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize());
|
||||
}
|
||||
{
|
||||
auto command = commands.Lookup(L"command4");
|
||||
|
@ -187,6 +191,7 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
|
||||
VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize());
|
||||
}
|
||||
{
|
||||
auto command = commands.Lookup(L"command5");
|
||||
|
@ -197,8 +202,51 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
|
||||
VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize());
|
||||
}
|
||||
{
|
||||
auto command = commands.Lookup(L"command6");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
VERIFY_IS_NOT_NULL(command.Action());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.Action().Action());
|
||||
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
|
||||
VERIFY_ARE_EQUAL(0.25, realArgs.SplitSize());
|
||||
}
|
||||
}
|
||||
|
||||
void CommandTests::TestSplitPaneBadSize()
|
||||
{
|
||||
const std::string commands0String{ R"([
|
||||
{ "name": "command1", "command": { "action": "splitPane", "size": 0.25 } },
|
||||
{ "name": "command2", "command": { "action": "splitPane", "size": 1.0 } },
|
||||
{ "name": "command3", "command": { "action": "splitPane", "size": 0 } },
|
||||
{ "name": "command4", "command": { "action": "splitPane", "size": 50 } },
|
||||
])" };
|
||||
|
||||
const auto commands0Json = VerifyParseSucceeded(commands0String);
|
||||
|
||||
IMap<winrt::hstring, Command> commands = winrt::single_threaded_map<winrt::hstring, Command>();
|
||||
VERIFY_ARE_EQUAL(0u, commands.Size());
|
||||
auto warnings = implementation::Command::LayerJson(commands, commands0Json);
|
||||
VERIFY_ARE_EQUAL(3u, warnings.size());
|
||||
VERIFY_ARE_EQUAL(1u, commands.Size());
|
||||
|
||||
{
|
||||
auto command = commands.Lookup(L"command1");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
VERIFY_IS_NOT_NULL(command.Action());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.Action().Action());
|
||||
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
|
||||
VERIFY_ARE_EQUAL(0.25, realArgs.SplitSize());
|
||||
}
|
||||
}
|
||||
|
||||
void CommandTests::TestResourceKeyName()
|
||||
{
|
||||
// This test checks looking up a name from a resource key.
|
||||
|
|
|
@ -486,7 +486,7 @@ namespace TerminalAppLocalTests
|
|||
|
||||
Log::Comment(NoThrowString().Format(L"Duplicate the first pane"));
|
||||
result = RunOnUIThread([&page]() {
|
||||
page->_SplitPane(SplitState::Automatic, SplitType::Duplicate, nullptr);
|
||||
page->_SplitPane(SplitState::Automatic, SplitType::Duplicate, 0.5f, nullptr);
|
||||
|
||||
VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
|
||||
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
||||
|
@ -504,7 +504,7 @@ namespace TerminalAppLocalTests
|
|||
|
||||
Log::Comment(NoThrowString().Format(L"Duplicate the pane, and don't crash"));
|
||||
result = RunOnUIThread([&page]() {
|
||||
page->_SplitPane(SplitState::Automatic, SplitType::Duplicate, nullptr);
|
||||
page->_SplitPane(SplitState::Automatic, SplitType::Duplicate, 0.5f, nullptr);
|
||||
|
||||
VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
|
||||
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
||||
|
|
|
@ -122,7 +122,11 @@ namespace winrt::TerminalApp::implementation
|
|||
}
|
||||
else if (const auto& realArgs = args.ActionArgs().try_as<SplitPaneArgs>())
|
||||
{
|
||||
_SplitPane(realArgs.SplitStyle(), realArgs.SplitMode(), realArgs.TerminalArgs());
|
||||
_SplitPane(realArgs.SplitStyle(),
|
||||
realArgs.SplitMode(),
|
||||
// This is safe, we're already filtering so the value is (0, 1)
|
||||
::base::saturated_cast<float>(realArgs.SplitSize()),
|
||||
realArgs.TerminalArgs());
|
||||
args.Handled(true);
|
||||
}
|
||||
}
|
||||
|
@ -130,23 +134,20 @@ namespace winrt::TerminalApp::implementation
|
|||
void TerminalPage::_HandleTogglePaneZoom(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
if (auto focusedTab = _GetFocusedTab())
|
||||
if (const auto activeTab{ _GetFocusedTabImpl() })
|
||||
{
|
||||
if (auto activeTab = _GetTerminalTabImpl(focusedTab))
|
||||
// Don't do anything if there's only one pane. It's already zoomed.
|
||||
if (activeTab->GetLeafPaneCount() > 1)
|
||||
{
|
||||
// Don't do anything if there's only one pane. It's already zoomed.
|
||||
if (activeTab && activeTab->GetLeafPaneCount() > 1)
|
||||
{
|
||||
// First thing's first, remove the current content from the UI
|
||||
// tree. This is important, because we might be leaving zoom, and if
|
||||
// a pane is zoomed, then it's currently in the UI tree, and should
|
||||
// be removed before it's re-added in Pane::Restore
|
||||
_tabContent.Children().Clear();
|
||||
// First thing's first, remove the current content from the UI
|
||||
// tree. This is important, because we might be leaving zoom, and if
|
||||
// a pane is zoomed, then it's currently in the UI tree, and should
|
||||
// be removed before it's re-added in Pane::Restore
|
||||
_tabContent.Children().Clear();
|
||||
|
||||
// Togging the zoom on the tab will cause the tab to inform us of
|
||||
// the new root Content for this tab.
|
||||
activeTab->ToggleZoom();
|
||||
}
|
||||
// Togging the zoom on the tab will cause the tab to inform us of
|
||||
// the new root Content for this tab.
|
||||
activeTab->ToggleZoom();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -343,19 +344,16 @@ namespace winrt::TerminalApp::implementation
|
|||
args.Handled(false);
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<SetColorSchemeArgs>())
|
||||
{
|
||||
if (auto focusedTab = _GetFocusedTab())
|
||||
if (const auto activeTab{ _GetFocusedTabImpl() })
|
||||
{
|
||||
if (auto activeTab = _GetTerminalTabImpl(focusedTab))
|
||||
if (auto activeControl = activeTab->GetActiveTerminalControl())
|
||||
{
|
||||
if (auto activeControl = activeTab->GetActiveTerminalControl())
|
||||
if (const auto scheme = _settings.GlobalSettings().ColorSchemes().TryLookup(realArgs.SchemeName()))
|
||||
{
|
||||
if (const auto scheme = _settings.GlobalSettings().ColorSchemes().TryLookup(realArgs.SchemeName()))
|
||||
{
|
||||
auto controlSettings = activeControl.Settings().as<TerminalSettings>();
|
||||
controlSettings->ApplyColorScheme(scheme);
|
||||
activeControl.UpdateSettings(*controlSettings);
|
||||
args.Handled(true);
|
||||
}
|
||||
auto controlSettings = activeControl.Settings().as<TerminalSettings>();
|
||||
controlSettings->ApplyColorScheme(scheme);
|
||||
activeControl.UpdateSettings(*controlSettings);
|
||||
args.Handled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -372,18 +370,15 @@ namespace winrt::TerminalApp::implementation
|
|||
tabColor = realArgs.TabColor();
|
||||
}
|
||||
|
||||
if (auto focusedTab = _GetFocusedTab())
|
||||
if (const auto activeTab{ _GetFocusedTabImpl() })
|
||||
{
|
||||
if (auto activeTab = _GetTerminalTabImpl(focusedTab))
|
||||
if (tabColor)
|
||||
{
|
||||
if (tabColor)
|
||||
{
|
||||
activeTab->SetRuntimeTabColor(tabColor.Value());
|
||||
}
|
||||
else
|
||||
{
|
||||
activeTab->ResetRuntimeTabColor();
|
||||
}
|
||||
activeTab->SetRuntimeTabColor(tabColor.Value());
|
||||
}
|
||||
else
|
||||
{
|
||||
activeTab->ResetRuntimeTabColor();
|
||||
}
|
||||
}
|
||||
args.Handled(true);
|
||||
|
@ -392,12 +387,9 @@ namespace winrt::TerminalApp::implementation
|
|||
void TerminalPage::_HandleOpenTabColorPicker(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
if (auto focusedTab = _GetFocusedTab())
|
||||
if (const auto activeTab{ _GetFocusedTabImpl() })
|
||||
{
|
||||
if (auto activeTab = _GetTerminalTabImpl(focusedTab))
|
||||
{
|
||||
activeTab->ActivateColorPicker();
|
||||
}
|
||||
activeTab->ActivateColorPicker();
|
||||
}
|
||||
args.Handled(true);
|
||||
}
|
||||
|
@ -412,18 +404,15 @@ namespace winrt::TerminalApp::implementation
|
|||
title = realArgs.Title();
|
||||
}
|
||||
|
||||
if (auto focusedTab = _GetFocusedTab())
|
||||
if (const auto activeTab{ _GetFocusedTabImpl() })
|
||||
{
|
||||
if (auto activeTab = _GetTerminalTabImpl(focusedTab))
|
||||
if (title.has_value())
|
||||
{
|
||||
if (title.has_value())
|
||||
{
|
||||
activeTab->SetTabText(title.value());
|
||||
}
|
||||
else
|
||||
{
|
||||
activeTab->ResetTabText();
|
||||
}
|
||||
activeTab->SetTabText(title.value());
|
||||
}
|
||||
else
|
||||
{
|
||||
activeTab->ResetTabText();
|
||||
}
|
||||
}
|
||||
args.Handled(true);
|
||||
|
@ -432,12 +421,9 @@ namespace winrt::TerminalApp::implementation
|
|||
void TerminalPage::_HandleOpenTabRenamer(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
if (auto focusedTab = _GetFocusedTab())
|
||||
if (const auto activeTab{ _GetFocusedTabImpl() })
|
||||
{
|
||||
if (auto activeTab = _GetTerminalTabImpl(focusedTab))
|
||||
{
|
||||
activeTab->ActivateTabRenamer();
|
||||
}
|
||||
activeTab->ActivateTabRenamer();
|
||||
}
|
||||
args.Handled(true);
|
||||
}
|
||||
|
|
|
@ -247,6 +247,10 @@ void AppCommandlineArgs::_buildSplitPaneParser()
|
|||
_splitVertical,
|
||||
RS_A(L"CmdSplitPaneVerticalArgDesc"));
|
||||
subcommand._verticalOption->excludes(subcommand._horizontalOption);
|
||||
auto* sizeOpt = subcommand.subcommand->add_option("-s,--size",
|
||||
_splitPaneSize,
|
||||
RS_A(L"CmdSplitPaneSizeArgDesc"));
|
||||
sizeOpt->check(CLI::Range(0.01f, 0.99f));
|
||||
|
||||
// When ParseCommand is called, if this subcommand was provided, this
|
||||
// callback function will be triggered on the same thread. We can be sure
|
||||
|
@ -274,7 +278,7 @@ void AppCommandlineArgs::_buildSplitPaneParser()
|
|||
style = SplitState::Vertical;
|
||||
}
|
||||
}
|
||||
SplitPaneArgs args{ style, terminalArgs };
|
||||
SplitPaneArgs args{ style, _splitPaneSize, terminalArgs };
|
||||
splitPaneActionAndArgs.Args(args);
|
||||
_startupActions.push_back(splitPaneActionAndArgs);
|
||||
});
|
||||
|
|
|
@ -86,6 +86,7 @@ private:
|
|||
|
||||
bool _splitVertical{ false };
|
||||
bool _splitHorizontal{ false };
|
||||
float _splitPaneSize{ 0.5f };
|
||||
|
||||
int _focusTabIndex{ -1 };
|
||||
bool _focusNextTab{ false };
|
||||
|
|
|
@ -42,7 +42,8 @@ static const std::array<std::wstring_view, static_cast<uint32_t>(SettingsLoadWar
|
|||
USES_RESOURCE(L"LegacyGlobalsProperty"),
|
||||
USES_RESOURCE(L"FailedToParseCommandJson"),
|
||||
USES_RESOURCE(L"FailedToWriteToSettings"),
|
||||
USES_RESOURCE(L"InvalidColorSchemeInCmd")
|
||||
USES_RESOURCE(L"InvalidColorSchemeInCmd"),
|
||||
USES_RESOURCE(L"InvalidSplitSize")
|
||||
};
|
||||
static const std::array<std::wstring_view, static_cast<uint32_t>(SettingsLoadErrors::ERRORS_SIZE)> settingsLoadErrorsLabels {
|
||||
USES_RESOURCE(L"NoProfilesText"),
|
||||
|
|
|
@ -291,7 +291,6 @@ namespace winrt::TerminalApp::implementation
|
|||
Windows::UI::Xaml::Input::KeyRoutedEventArgs const& e)
|
||||
{
|
||||
auto key = e.OriginalKey();
|
||||
auto const ctrlDown = WI_IsFlagSet(CoreWindow::GetForCurrentThread().GetKeyState(winrt::Windows::System::VirtualKey::Control), CoreVirtualKeyStates::Down);
|
||||
|
||||
if (key == VirtualKey::Up)
|
||||
{
|
||||
|
@ -691,7 +690,10 @@ namespace winrt::TerminalApp::implementation
|
|||
{
|
||||
if (const auto tabPaletteItem{ filteredCommand.Item().try_as<winrt::TerminalApp::TabPaletteItem>() })
|
||||
{
|
||||
_SwitchToTabRequestedHandlers(*this, tabPaletteItem.Tab());
|
||||
if (const auto tab{ tabPaletteItem.Tab() })
|
||||
{
|
||||
_SwitchToTabRequestedHandlers(*this, tab);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ using namespace TerminalApp;
|
|||
|
||||
static const int PaneBorderSize = 2;
|
||||
static const int CombinedPaneBorderSize = 2 * PaneBorderSize;
|
||||
static const float Half = 0.50f;
|
||||
|
||||
// WARNING: Don't do this! This won't work
|
||||
// Duration duration{ std::chrono::milliseconds{ 200 } };
|
||||
|
@ -1216,32 +1215,6 @@ void Pane::_SetupEntranceAnimation()
|
|||
setupAnimation(secondSize, false);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Determines whether the pane can be split
|
||||
// Arguments:
|
||||
// - splitType: what type of split we want to create.
|
||||
// Return Value:
|
||||
// - True if the pane can be split. False otherwise.
|
||||
bool Pane::CanSplit(SplitState splitType)
|
||||
{
|
||||
if (_IsLeaf())
|
||||
{
|
||||
return _CanSplit(splitType);
|
||||
}
|
||||
|
||||
if (_firstChild->_HasFocusedChild())
|
||||
{
|
||||
return _firstChild->CanSplit(splitType);
|
||||
}
|
||||
|
||||
if (_secondChild->_HasFocusedChild())
|
||||
{
|
||||
return _secondChild->CanSplit(splitType);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - This is a helper to determine if a given Pane can be split, but without
|
||||
// using the ActualWidth() and ActualHeight() methods. This is used during
|
||||
|
@ -1272,12 +1245,15 @@ bool Pane::CanSplit(SplitState splitType)
|
|||
// - This method is highly similar to Pane::PreCalculateAutoSplit
|
||||
std::optional<bool> Pane::PreCalculateCanSplit(const std::shared_ptr<Pane> target,
|
||||
SplitState splitType,
|
||||
const float splitSize,
|
||||
const winrt::Windows::Foundation::Size availableSpace) const
|
||||
{
|
||||
if (_IsLeaf())
|
||||
{
|
||||
if (target.get() == this)
|
||||
{
|
||||
const auto firstPrecent = 1.0f - splitSize;
|
||||
const auto secondPercent = splitSize;
|
||||
// If this pane is a leaf, and it's the pane we're looking for, use
|
||||
// the available space to calculate which direction to split in.
|
||||
const Size minSize = _GetMinSize();
|
||||
|
@ -1290,17 +1266,19 @@ std::optional<bool> Pane::PreCalculateCanSplit(const std::shared_ptr<Pane> targe
|
|||
else if (splitType == SplitState::Vertical)
|
||||
{
|
||||
const auto widthMinusSeparator = availableSpace.Width - CombinedPaneBorderSize;
|
||||
const auto newWidth = widthMinusSeparator * Half;
|
||||
const auto newFirstWidth = widthMinusSeparator * firstPrecent;
|
||||
const auto newSecondWidth = widthMinusSeparator * secondPercent;
|
||||
|
||||
return { newWidth > minSize.Width };
|
||||
return { newFirstWidth > minSize.Width && newSecondWidth > minSize.Width };
|
||||
}
|
||||
|
||||
else if (splitType == SplitState::Horizontal)
|
||||
{
|
||||
const auto heightMinusSeparator = availableSpace.Height - CombinedPaneBorderSize;
|
||||
const auto newHeight = heightMinusSeparator * Half;
|
||||
const auto newFirstHeight = heightMinusSeparator * firstPrecent;
|
||||
const auto newSecondHeight = heightMinusSeparator * secondPercent;
|
||||
|
||||
return { newHeight > minSize.Height };
|
||||
return { newFirstHeight > minSize.Height && newSecondHeight > minSize.Height };
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -1329,8 +1307,8 @@ std::optional<bool> Pane::PreCalculateCanSplit(const std::shared_ptr<Pane> targe
|
|||
(availableSpace.Height - firstHeight) - PaneBorderSize :
|
||||
availableSpace.Height;
|
||||
|
||||
const auto firstResult = _firstChild->PreCalculateCanSplit(target, splitType, { firstWidth, firstHeight });
|
||||
return firstResult.has_value() ? firstResult : _secondChild->PreCalculateCanSplit(target, splitType, { secondWidth, secondHeight });
|
||||
const auto firstResult = _firstChild->PreCalculateCanSplit(target, splitType, splitSize, { firstWidth, firstHeight });
|
||||
return firstResult.has_value() ? firstResult : _secondChild->PreCalculateCanSplit(target, splitType, splitSize, { secondWidth, secondHeight });
|
||||
}
|
||||
|
||||
// We should not possibly be getting here - both the above branches should
|
||||
|
@ -1348,23 +1326,26 @@ std::optional<bool> Pane::PreCalculateCanSplit(const std::shared_ptr<Pane> targe
|
|||
// - control: A TermControl to use in the new pane.
|
||||
// Return Value:
|
||||
// - The two newly created Panes
|
||||
std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::Split(SplitState splitType, const GUID& profile, const TermControl& control)
|
||||
std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::Split(SplitState splitType,
|
||||
const float splitSize,
|
||||
const GUID& profile,
|
||||
const TermControl& control)
|
||||
{
|
||||
if (!_IsLeaf())
|
||||
{
|
||||
if (_firstChild->_HasFocusedChild())
|
||||
{
|
||||
return _firstChild->Split(splitType, profile, control);
|
||||
return _firstChild->Split(splitType, splitSize, profile, control);
|
||||
}
|
||||
else if (_secondChild->_HasFocusedChild())
|
||||
{
|
||||
return _secondChild->Split(splitType, profile, control);
|
||||
return _secondChild->Split(splitType, splitSize, profile, control);
|
||||
}
|
||||
|
||||
return { nullptr, nullptr };
|
||||
}
|
||||
|
||||
return _Split(splitType, profile, control);
|
||||
return _Split(splitType, splitSize, profile, control);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -1392,45 +1373,6 @@ SplitState Pane::_convertAutomaticSplitState(const SplitState& splitType) const
|
|||
return splitType;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Determines whether the pane can be split.
|
||||
// Arguments:
|
||||
// - splitType: what type of split we want to create.
|
||||
// Return Value:
|
||||
// - True if the pane can be split. False otherwise.
|
||||
bool Pane::_CanSplit(SplitState splitType)
|
||||
{
|
||||
const Size actualSize{ gsl::narrow_cast<float>(_root.ActualWidth()),
|
||||
gsl::narrow_cast<float>(_root.ActualHeight()) };
|
||||
|
||||
const Size minSize = _GetMinSize();
|
||||
|
||||
auto actualSplitType = _convertAutomaticSplitState(splitType);
|
||||
|
||||
if (actualSplitType == SplitState::None)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (actualSplitType == SplitState::Vertical)
|
||||
{
|
||||
const auto widthMinusSeparator = actualSize.Width - CombinedPaneBorderSize;
|
||||
const auto newWidth = widthMinusSeparator * Half;
|
||||
|
||||
return newWidth > minSize.Width;
|
||||
}
|
||||
|
||||
if (actualSplitType == SplitState::Horizontal)
|
||||
{
|
||||
const auto heightMinusSeparator = actualSize.Height - CombinedPaneBorderSize;
|
||||
const auto newHeight = heightMinusSeparator * Half;
|
||||
|
||||
return newHeight > minSize.Height;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Does the bulk of the work of creating a new split. Initializes our UI,
|
||||
// creates a new Pane to host the control, registers event handlers.
|
||||
|
@ -1440,7 +1382,10 @@ bool Pane::_CanSplit(SplitState splitType)
|
|||
// - control: A TermControl to use in the new pane.
|
||||
// Return Value:
|
||||
// - The two newly created Panes
|
||||
std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::_Split(SplitState splitType, const GUID& profile, const TermControl& control)
|
||||
std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::_Split(SplitState splitType,
|
||||
const float splitSize,
|
||||
const GUID& profile,
|
||||
const TermControl& control)
|
||||
{
|
||||
if (splitType == SplitState::None)
|
||||
{
|
||||
|
@ -1465,7 +1410,7 @@ std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::_Split(SplitState
|
|||
_gotFocusRevoker.revoke();
|
||||
|
||||
_splitState = actualSplitType;
|
||||
_desiredSplitPosition = Half;
|
||||
_desiredSplitPosition = 1.0f - splitSize;
|
||||
|
||||
// Remove any children we currently have. We can't add the existing
|
||||
// TermControl to a new grid until we do this.
|
||||
|
|
|
@ -58,14 +58,16 @@ public:
|
|||
bool ResizePane(const winrt::Microsoft::Terminal::Settings::Model::ResizeDirection& direction);
|
||||
bool NavigateFocus(const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction);
|
||||
|
||||
bool CanSplit(winrt::Microsoft::Terminal::Settings::Model::SplitState splitType);
|
||||
std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Split(winrt::Microsoft::Terminal::Settings::Model::SplitState splitType,
|
||||
const float splitSize,
|
||||
const GUID& profile,
|
||||
const winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
|
||||
float CalcSnappedDimension(const bool widthOrHeight, const float dimension) const;
|
||||
std::optional<winrt::Microsoft::Terminal::Settings::Model::SplitState> PreCalculateAutoSplit(const std::shared_ptr<Pane> target, const winrt::Windows::Foundation::Size parentSize) const;
|
||||
std::optional<winrt::Microsoft::Terminal::Settings::Model::SplitState> PreCalculateAutoSplit(const std::shared_ptr<Pane> target,
|
||||
const winrt::Windows::Foundation::Size parentSize) const;
|
||||
std::optional<bool> PreCalculateCanSplit(const std::shared_ptr<Pane> target,
|
||||
winrt::Microsoft::Terminal::Settings::Model::SplitState splitType,
|
||||
const float splitSize,
|
||||
const winrt::Windows::Foundation::Size availableSpace) const;
|
||||
void Shutdown();
|
||||
void Close();
|
||||
|
@ -120,8 +122,8 @@ private:
|
|||
bool _HasFocusedChild() const noexcept;
|
||||
void _SetupChildCloseHandlers();
|
||||
|
||||
bool _CanSplit(winrt::Microsoft::Terminal::Settings::Model::SplitState splitType);
|
||||
std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> _Split(winrt::Microsoft::Terminal::Settings::Model::SplitState splitType,
|
||||
const float splitSize,
|
||||
const GUID& profile,
|
||||
const winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
|
||||
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
|
@ -26,36 +26,36 @@
|
|||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
|
@ -249,6 +249,10 @@
|
|||
<value>Found a command with an invalid "colorScheme". This command will be ignored. Make sure that when setting a "colorScheme", the value matches the "name" of a color scheme in the "schemes" list.</value>
|
||||
<comment>{Locked="\"colorScheme\"","\"name\"","\"schemes\""}</comment>
|
||||
</data>
|
||||
<data name="InvalidSplitSize" xml:space="preserve">
|
||||
<value>Found a "splitPane" command with an invalid "size". This command will be ignored. Make sure the size is between 0 and 1, exclusive.</value>
|
||||
<comment>{Locked="\"splitPane\"","\"size\""}</comment>
|
||||
</data>
|
||||
<data name="CmdCommandArgDesc" xml:space="preserve">
|
||||
<value>An optional command, with arguments, to be spawned in the new tab or pane</value>
|
||||
</data>
|
||||
|
@ -268,6 +272,9 @@
|
|||
<data name="CmdFocusTabTargetArgDesc" xml:space="preserve">
|
||||
<value>Move focus the tab at the given index</value>
|
||||
</data>
|
||||
<data name="CmdSplitPaneSizeArgDesc" xml:space="preserve">
|
||||
<value>Specify the size as a percentage of the parent pane. Valid values are between (0,1), exclusive.</value>
|
||||
</data>
|
||||
<data name="CmdNewTabDesc" xml:space="preserve">
|
||||
<value>Create a new tab</value>
|
||||
</data>
|
||||
|
|
|
@ -19,7 +19,7 @@ using namespace winrt::Microsoft::Terminal::Settings::Model;
|
|||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
TabPaletteItem::TabPaletteItem(winrt::TerminalApp::TabBase const& tab) :
|
||||
_Tab(tab)
|
||||
_tab(tab)
|
||||
{
|
||||
Name(tab.Title());
|
||||
Icon(tab.Icon());
|
||||
|
|
|
@ -14,9 +14,13 @@ namespace winrt::TerminalApp::implementation
|
|||
TabPaletteItem() = default;
|
||||
TabPaletteItem(winrt::TerminalApp::TabBase const& tab);
|
||||
|
||||
GETSET_PROPERTY(winrt::TerminalApp::TabBase, Tab, nullptr);
|
||||
winrt::TerminalApp::TabBase Tab() const noexcept
|
||||
{
|
||||
return _tab.get();
|
||||
}
|
||||
|
||||
private:
|
||||
winrt::weak_ref<winrt::TerminalApp::TabBase> _tab;
|
||||
Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _tabChangedRevoker;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -209,6 +209,7 @@ namespace winrt::TerminalApp::implementation
|
|||
{
|
||||
page->_SplitPane(SplitState::Automatic,
|
||||
SplitType::Manual,
|
||||
0.5f,
|
||||
nullptr);
|
||||
}
|
||||
else
|
||||
|
@ -560,6 +561,7 @@ namespace winrt::TerminalApp::implementation
|
|||
{
|
||||
page->_SplitPane(SplitState::Automatic,
|
||||
SplitType::Manual,
|
||||
0.5f,
|
||||
newTerminalArgs);
|
||||
}
|
||||
else
|
||||
|
@ -600,7 +602,9 @@ namespace winrt::TerminalApp::implementation
|
|||
settingsItem.Click({ this, &TerminalPage::_SettingsButtonOnClick });
|
||||
newTabFlyout.Items().Append(settingsItem);
|
||||
|
||||
auto settingsKeyChord = keyBindings.GetKeyBindingForAction(ShortcutAction::OpenSettings);
|
||||
Microsoft::Terminal::Settings::Model::OpenSettingsArgs args{ SettingsTarget::SettingsFile };
|
||||
Microsoft::Terminal::Settings::Model::ActionAndArgs settingsAction{ ShortcutAction::OpenSettings, args };
|
||||
const auto settingsKeyChord{ keyBindings.GetKeyBindingForActionWithArgs(settingsAction) };
|
||||
if (settingsKeyChord)
|
||||
{
|
||||
_SetAcceleratorForMenuItem(settingsItem, settingsKeyChord);
|
||||
|
@ -809,7 +813,7 @@ namespace winrt::TerminalApp::implementation
|
|||
TermControl newControl{ settings, debugConnection };
|
||||
_RegisterTerminalEvents(newControl, *newTabImpl);
|
||||
// Split (auto) with the debug tap.
|
||||
newTabImpl->SplitPane(SplitState::Automatic, profileGuid, newControl);
|
||||
newTabImpl->SplitPane(SplitState::Automatic, 0.5f, profileGuid, newControl);
|
||||
}
|
||||
|
||||
// This kicks off TabView::SelectionChanged, in response to which
|
||||
|
@ -927,6 +931,34 @@ namespace winrt::TerminalApp::implementation
|
|||
_ShowAboutDialog();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// Called when the users pressed keyBindings while CommandPalette is open.
|
||||
// Arguments:
|
||||
// - e: the KeyRoutedEventArgs containing info about the keystroke.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalPage::_KeyDownHandler(Windows::Foundation::IInspectable const& /*sender*/, Windows::UI::Xaml::Input::KeyRoutedEventArgs const& e)
|
||||
{
|
||||
auto key = e.OriginalKey();
|
||||
auto const ctrlDown = WI_IsFlagSet(CoreWindow::GetForCurrentThread().GetKeyState(winrt::Windows::System::VirtualKey::Control), CoreVirtualKeyStates::Down);
|
||||
auto const altDown = WI_IsFlagSet(CoreWindow::GetForCurrentThread().GetKeyState(winrt::Windows::System::VirtualKey::Menu), CoreVirtualKeyStates::Down);
|
||||
auto const shiftDown = WI_IsFlagSet(CoreWindow::GetForCurrentThread().GetKeyState(winrt::Windows::System::VirtualKey::Shift), CoreVirtualKeyStates::Down);
|
||||
|
||||
winrt::Microsoft::Terminal::TerminalControl::KeyChord kc{ ctrlDown, altDown, shiftDown, static_cast<int32_t>(key) };
|
||||
auto setting = AppLogic::CurrentAppSettings();
|
||||
auto keymap = setting.GlobalSettings().KeyMap();
|
||||
const auto actionAndArgs = keymap.TryLookup(kc);
|
||||
if (actionAndArgs)
|
||||
{
|
||||
if (CommandPalette().Visibility() == Visibility::Visible && actionAndArgs.Action() != ShortcutAction::ToggleCommandPalette)
|
||||
{
|
||||
CommandPalette().Visibility(Visibility::Collapsed);
|
||||
}
|
||||
_actionDispatch->DoAction(actionAndArgs);
|
||||
e.Handled(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Configure the AppKeyBindings to use our ShortcutActionDispatch and the updated KeyMapping
|
||||
// as the object to handle dispatching ShortcutAction events.
|
||||
|
@ -1065,34 +1097,31 @@ namespace winrt::TerminalApp::implementation
|
|||
// - Duplicates the current focused tab
|
||||
void TerminalPage::_DuplicateTabViewItem()
|
||||
{
|
||||
if (auto index{ _GetFocusedTabIndex() })
|
||||
if (const auto terminalTab{ _GetFocusedTabImpl() })
|
||||
{
|
||||
if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*index)))
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
// TODO: GH#5047 - In the future, we should get the Profile of
|
||||
// the focused pane, and use that to build a new instance of the
|
||||
// settings so we can duplicate this tab/pane.
|
||||
//
|
||||
// Currently, if the profile doesn't exist anymore in our
|
||||
// settings, we'll silently do nothing.
|
||||
//
|
||||
// In the future, it will be preferable to just duplicate the
|
||||
// current control's settings, but we can't do that currently,
|
||||
// because we won't be able to create a new instance of the
|
||||
// connection without keeping an instance of the original Profile
|
||||
// object around.
|
||||
// TODO: GH#5047 - In the future, we should get the Profile of
|
||||
// the focused pane, and use that to build a new instance of the
|
||||
// settings so we can duplicate this tab/pane.
|
||||
//
|
||||
// Currently, if the profile doesn't exist anymore in our
|
||||
// settings, we'll silently do nothing.
|
||||
//
|
||||
// In the future, it will be preferable to just duplicate the
|
||||
// current control's settings, but we can't do that currently,
|
||||
// because we won't be able to create a new instance of the
|
||||
// connection without keeping an instance of the original Profile
|
||||
// object around.
|
||||
|
||||
const auto& profileGuid = terminalTab->GetFocusedProfile();
|
||||
if (profileGuid.has_value())
|
||||
{
|
||||
const auto settings{ winrt::make<TerminalSettings>(_settings, profileGuid.value(), *_bindings) };
|
||||
_CreateNewTabFromSettings(profileGuid.value(), settings);
|
||||
}
|
||||
const auto& profileGuid = terminalTab->GetFocusedProfile();
|
||||
if (profileGuid.has_value())
|
||||
{
|
||||
const auto settings{ winrt::make<TerminalSettings>(_settings, profileGuid.value(), *_bindings) };
|
||||
_CreateNewTabFromSettings(profileGuid.value(), settings);
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1117,6 +1146,13 @@ namespace winrt::TerminalApp::implementation
|
|||
// - tabIndex: the index of the tab to be removed
|
||||
void TerminalPage::_RemoveTabViewItemByIndex(uint32_t tabIndex)
|
||||
{
|
||||
// We use _removing flag to suppress _OnTabSelectionChanged events
|
||||
// that might get triggered while removing
|
||||
_removing = true;
|
||||
auto unsetRemoving = wil::scope_exit([&]() noexcept { _removing = false; });
|
||||
|
||||
const auto focusedTabIndex{ _GetFocusedTabIndex() };
|
||||
|
||||
// Removing the tab from the collection should destroy its control and disconnect its connection,
|
||||
// but it doesn't always do so. The UI tree may still be holding the control and preventing its destruction.
|
||||
auto tab{ _tabs.GetAt(tabIndex) };
|
||||
|
@ -1137,43 +1173,51 @@ namespace winrt::TerminalApp::implementation
|
|||
{
|
||||
_lastTabClosedHandlers(*this, nullptr);
|
||||
}
|
||||
else if (_isFullscreen || _rearranging || _isInFocusMode)
|
||||
else if (focusedTabIndex.has_value() && focusedTabIndex.value() == gsl::narrow_cast<uint32_t>(tabIndex))
|
||||
{
|
||||
// GH#5799 - If we're fullscreen, the TabView isn't visible. If it's
|
||||
// not Visible, it's _not_ going to raise a SelectionChanged event,
|
||||
// which is what we usually use to focus another tab. Instead, we'll
|
||||
// have to do it manually here.
|
||||
//
|
||||
// GH#7916 - Similarly, TabView isn't visible in focus mode.
|
||||
// We need to focus another tab manually from the same considerations as above.
|
||||
//
|
||||
// GH#5559 Similarly, we suppress _OnTabItemsChanged events during a
|
||||
// rearrange, so if a tab is closed while we're rearranging tabs, do
|
||||
// this manually.
|
||||
//
|
||||
// We can't use
|
||||
// auto selectedIndex = _tabView.SelectedIndex();
|
||||
// Because this will always return -1 in this scenario unfortunately.
|
||||
//
|
||||
// So, what we're going to try to do is move the focus to the tab
|
||||
// to the left, within the bounds of how many tabs we have.
|
||||
//
|
||||
// EX: we have 4 tabs: [A, B, C, D]. If we close:
|
||||
// * A (tabIndex=0): We'll want to focus tab B (now in index 0)
|
||||
// * B (tabIndex=1): We'll want to focus tab A (now in index 0)
|
||||
// * C (tabIndex=2): We'll want to focus tab B (now in index 1)
|
||||
// * D (tabIndex=3): We'll want to focus tab C (now in index 2)
|
||||
const auto newSelectedIndex = std::clamp<int32_t>(tabIndex - 1, 0, _tabs.Size());
|
||||
// _UpdatedSelectedTab will do the work of setting up the new tab as
|
||||
// the focused one, and unfocusing all the others.
|
||||
_UpdatedSelectedTab(newSelectedIndex);
|
||||
// Manually select the new tab to get focus, rather than relying on TabView since:
|
||||
// 1. We want to customize this behavior (e.g., use MRU logic)
|
||||
// 2. In fullscreen (GH#5799) and focus (GH#7916) modes the _OnTabItemsChanged is not fired
|
||||
// 3. When rearranging tabs (GH#7916) _OnTabItemsChanged is suppressed
|
||||
const auto tabSwitchMode = _settings.GlobalSettings().TabSwitcherMode();
|
||||
|
||||
// Also, we need to _manually_ set the SelectedItem of the tabView
|
||||
// here. If we don't, then the TabView will technically not have a
|
||||
// selected item at all, which can make things like ClosePane not
|
||||
// work correctly.
|
||||
auto newSelectedTab{ _tabs.GetAt(newSelectedIndex) };
|
||||
_tabView.SelectedItem(newSelectedTab.TabViewItem());
|
||||
if (tabSwitchMode == TabSwitcherMode::MostRecentlyUsed)
|
||||
{
|
||||
const auto newSelectedTab = _mruTabs.GetAt(0);
|
||||
|
||||
uint32_t newSelectedIndex;
|
||||
if (_tabs.IndexOf(newSelectedTab, newSelectedIndex))
|
||||
{
|
||||
_UpdatedSelectedTab(newSelectedIndex);
|
||||
_tabView.SelectedItem(newSelectedTab.TabViewItem());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We can't use
|
||||
// auto selectedIndex = _tabView.SelectedIndex();
|
||||
// Because this will always return -1 in this scenario unfortunately.
|
||||
//
|
||||
// So, what we're going to try to do is move the focus to the tab
|
||||
// to the left, within the bounds of how many tabs we have.
|
||||
//
|
||||
// EX: we have 4 tabs: [A, B, C, D]. If we close:
|
||||
// * A (tabIndex=0): We'll want to focus tab B (now in index 0)
|
||||
// * B (tabIndex=1): We'll want to focus tab A (now in index 0)
|
||||
// * C (tabIndex=2): We'll want to focus tab B (now in index 1)
|
||||
// * D (tabIndex=3): We'll want to focus tab C (now in index 2)
|
||||
const auto newSelectedIndex = std::clamp<int32_t>(tabIndex - 1, 0, _tabs.Size());
|
||||
// _UpdatedSelectedTab will do the work of setting up the new tab as
|
||||
// the focused one, and unfocusing all the others.
|
||||
_UpdatedSelectedTab(newSelectedIndex);
|
||||
|
||||
// Also, we need to _manually_ set the SelectedItem of the tabView
|
||||
// here. If we don't, then the TabView will technically not have a
|
||||
// selected item at all, which can make things like ClosePane not
|
||||
// work correctly.
|
||||
auto newSelectedTab{ _tabs.GetAt(newSelectedIndex) };
|
||||
_tabView.SelectedItem(newSelectedTab.TabViewItem());
|
||||
}
|
||||
}
|
||||
|
||||
// GH#5559 - If we were in the middle of a drag/drop, end it by clearing
|
||||
|
@ -1373,20 +1417,17 @@ namespace winrt::TerminalApp::implementation
|
|||
// - <none>
|
||||
void TerminalPage::_UnZoomIfNeeded()
|
||||
{
|
||||
if (auto focusedTab = _GetFocusedTab())
|
||||
if (const auto activeTab{ _GetFocusedTabImpl() })
|
||||
{
|
||||
if (auto activeTab = _GetTerminalTabImpl(focusedTab))
|
||||
if (activeTab->IsZoomed())
|
||||
{
|
||||
if (activeTab->IsZoomed())
|
||||
{
|
||||
// Remove the content from the tab first, so Pane::UnZoom can
|
||||
// re-attach the content to the tree w/in the pane
|
||||
_tabContent.Children().Clear();
|
||||
// In ExitZoom, we'll change the Tab's Content(), triggering the
|
||||
// content changed event, which will re-attach the tab's new content
|
||||
// root to the tree.
|
||||
activeTab->ExitZoom();
|
||||
}
|
||||
// Remove the content from the tab first, so Pane::UnZoom can
|
||||
// re-attach the content to the tree w/in the pane
|
||||
_tabContent.Children().Clear();
|
||||
// In ExitZoom, we'll change the Tab's Content(), triggering the
|
||||
// content changed event, which will re-attach the tab's new content
|
||||
// root to the tree.
|
||||
activeTab->ExitZoom();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1401,24 +1442,18 @@ namespace winrt::TerminalApp::implementation
|
|||
// - <none>
|
||||
void TerminalPage::_MoveFocus(const FocusDirection& direction)
|
||||
{
|
||||
if (auto index{ _GetFocusedTabIndex() })
|
||||
if (const auto terminalTab{ _GetFocusedTabImpl() })
|
||||
{
|
||||
if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*index)))
|
||||
{
|
||||
_UnZoomIfNeeded();
|
||||
terminalTab->NavigateFocus(direction);
|
||||
}
|
||||
_UnZoomIfNeeded();
|
||||
terminalTab->NavigateFocus(direction);
|
||||
}
|
||||
}
|
||||
|
||||
TermControl TerminalPage::_GetActiveControl()
|
||||
{
|
||||
if (auto index{ _GetFocusedTabIndex() })
|
||||
if (const auto terminalTab{ _GetFocusedTabImpl() })
|
||||
{
|
||||
if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*index)))
|
||||
{
|
||||
return terminalTab->GetActiveTerminalControl();
|
||||
}
|
||||
return terminalTab->GetActiveTerminalControl();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -1443,7 +1478,7 @@ namespace winrt::TerminalApp::implementation
|
|||
// Method Description:
|
||||
// - returns a com_ptr to the currently focused tab. This might return null,
|
||||
// so make sure to check the result!
|
||||
winrt::TerminalApp::TabBase TerminalPage::_GetFocusedTab()
|
||||
winrt::TerminalApp::TabBase TerminalPage::_GetFocusedTab() const noexcept
|
||||
{
|
||||
if (auto index{ _GetFocusedTabIndex() })
|
||||
{
|
||||
|
@ -1452,6 +1487,18 @@ namespace winrt::TerminalApp::implementation
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - returns a com_ptr to the currently focused tab implementation. This might return null,
|
||||
// so make sure to check the result!
|
||||
winrt::com_ptr<TerminalTab> TerminalPage::_GetFocusedTabImpl() const noexcept
|
||||
{
|
||||
if (auto tab{ _GetFocusedTab() })
|
||||
{
|
||||
return _GetTerminalTabImpl(tab);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - An async method for changing the focused tab on the UI thread. This
|
||||
// method will _only_ set the selected item of the TabView, which will
|
||||
|
@ -1493,13 +1540,10 @@ namespace winrt::TerminalApp::implementation
|
|||
// tab's Closed event.
|
||||
void TerminalPage::_CloseFocusedPane()
|
||||
{
|
||||
if (auto index{ _GetFocusedTabIndex() })
|
||||
if (const auto terminalTab{ _GetFocusedTabImpl() })
|
||||
{
|
||||
if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*index)))
|
||||
{
|
||||
_UnZoomIfNeeded();
|
||||
terminalTab->ClosePane();
|
||||
}
|
||||
_UnZoomIfNeeded();
|
||||
terminalTab->ClosePane();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1542,26 +1586,23 @@ namespace winrt::TerminalApp::implementation
|
|||
// - rowsToScroll: a number of lines to move the viewport. If not provided we will use a system default.
|
||||
void TerminalPage::_Scroll(ScrollDirection scrollDirection, const Windows::Foundation::IReference<uint32_t>& rowsToScroll)
|
||||
{
|
||||
if (auto index{ _GetFocusedTabIndex() })
|
||||
if (const auto terminalTab{ _GetFocusedTabImpl() })
|
||||
{
|
||||
if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*index)))
|
||||
uint32_t realRowsToScroll;
|
||||
if (rowsToScroll == nullptr)
|
||||
{
|
||||
uint32_t realRowsToScroll;
|
||||
if (rowsToScroll == nullptr)
|
||||
{
|
||||
// The magic value of WHEEL_PAGESCROLL indicates that we need to scroll the entire page
|
||||
realRowsToScroll = _systemRowsToScroll == WHEEL_PAGESCROLL ?
|
||||
terminalTab->GetActiveTerminalControl().GetViewHeight() :
|
||||
_systemRowsToScroll;
|
||||
}
|
||||
else
|
||||
{
|
||||
// use the custom value specified in the command
|
||||
realRowsToScroll = rowsToScroll.Value();
|
||||
}
|
||||
auto scrollDelta = _ComputeScrollDelta(scrollDirection, realRowsToScroll);
|
||||
terminalTab->Scroll(scrollDelta);
|
||||
// The magic value of WHEEL_PAGESCROLL indicates that we need to scroll the entire page
|
||||
realRowsToScroll = _systemRowsToScroll == WHEEL_PAGESCROLL ?
|
||||
terminalTab->GetActiveTerminalControl().GetViewHeight() :
|
||||
_systemRowsToScroll;
|
||||
}
|
||||
else
|
||||
{
|
||||
// use the custom value specified in the command
|
||||
realRowsToScroll = rowsToScroll.Value();
|
||||
}
|
||||
auto scrollDelta = _ComputeScrollDelta(scrollDirection, realRowsToScroll);
|
||||
terminalTab->Scroll(scrollDelta);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1578,6 +1619,7 @@ namespace winrt::TerminalApp::implementation
|
|||
// configurations. See CascadiaSettings::BuildSettings for more details.
|
||||
void TerminalPage::_SplitPane(const SplitState splitType,
|
||||
const SplitType splitMode,
|
||||
const float splitSize,
|
||||
const NewTerminalArgs& newTerminalArgs)
|
||||
{
|
||||
// Do nothing if we're requesting no split.
|
||||
|
@ -1586,17 +1628,9 @@ namespace winrt::TerminalApp::implementation
|
|||
return;
|
||||
}
|
||||
|
||||
auto indexOpt = _GetFocusedTabIndex();
|
||||
const auto focusedTab{ _GetFocusedTabImpl() };
|
||||
|
||||
// Do nothing if for some reason, there's no tab in focus. We don't want to crash.
|
||||
if (!indexOpt)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto focusedTab = _GetTerminalTabImpl(_tabs.GetAt(*indexOpt));
|
||||
|
||||
// Do nothing if the focused tab isn't a TerminalTab
|
||||
// Do nothing if no TerminalTab is focused
|
||||
if (!focusedTab)
|
||||
{
|
||||
return;
|
||||
|
@ -1647,7 +1681,7 @@ namespace winrt::TerminalApp::implementation
|
|||
realSplitType = focusedTab->PreCalculateAutoSplit(availableSpace);
|
||||
}
|
||||
|
||||
const auto canSplit = focusedTab->PreCalculateCanSplit(realSplitType, availableSpace);
|
||||
const auto canSplit = focusedTab->PreCalculateCanSplit(realSplitType, splitSize, availableSpace);
|
||||
if (!canSplit)
|
||||
{
|
||||
return;
|
||||
|
@ -1660,7 +1694,7 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
_UnZoomIfNeeded();
|
||||
|
||||
focusedTab->SplitPane(realSplitType, realGuid, newControl);
|
||||
focusedTab->SplitPane(realSplitType, splitSize, realGuid, newControl);
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
|
@ -1675,13 +1709,10 @@ namespace winrt::TerminalApp::implementation
|
|||
// - <none>
|
||||
void TerminalPage::_ResizePane(const ResizeDirection& direction)
|
||||
{
|
||||
if (auto index{ _GetFocusedTabIndex() })
|
||||
if (const auto terminalTab{ _GetFocusedTabImpl() })
|
||||
{
|
||||
if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*index)))
|
||||
{
|
||||
_UnZoomIfNeeded();
|
||||
terminalTab->ResizePane(direction);
|
||||
}
|
||||
_UnZoomIfNeeded();
|
||||
terminalTab->ResizePane(direction);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1692,14 +1723,8 @@ namespace winrt::TerminalApp::implementation
|
|||
// - scrollDirection: ScrollUp will move the viewport up, ScrollDown will move the viewport down
|
||||
void TerminalPage::_ScrollPage(ScrollDirection scrollDirection)
|
||||
{
|
||||
auto indexOpt = _GetFocusedTabIndex();
|
||||
// Do nothing if for some reason, there's no tab in focus. We don't want to crash.
|
||||
if (!indexOpt)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*indexOpt)))
|
||||
// Do nothing if for some reason, there's no terminal tab in focus. We don't want to crash.
|
||||
if (const auto terminalTab{ _GetFocusedTabImpl() })
|
||||
{
|
||||
const auto control = _GetActiveControl();
|
||||
const auto termHeight = control.GetViewHeight();
|
||||
|
@ -1710,13 +1735,10 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
void TerminalPage::_ScrollToBufferEdge(ScrollDirection scrollDirection)
|
||||
{
|
||||
if (const auto indexOpt = _GetFocusedTabIndex())
|
||||
if (const auto terminalTab{ _GetFocusedTabImpl() })
|
||||
{
|
||||
if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*indexOpt)))
|
||||
{
|
||||
auto scrollDelta = _ComputeScrollDelta(scrollDirection, INT_MAX);
|
||||
terminalTab->Scroll(scrollDelta);
|
||||
}
|
||||
auto scrollDelta = _ComputeScrollDelta(scrollDirection, INT_MAX);
|
||||
terminalTab->Scroll(scrollDelta);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1826,12 +1848,9 @@ namespace winrt::TerminalApp::implementation
|
|||
{
|
||||
if (_settings && _settings.GlobalSettings().SnapToGridOnResize())
|
||||
{
|
||||
if (auto index{ _GetFocusedTabIndex() })
|
||||
if (const auto terminalTab{ _GetFocusedTabImpl() })
|
||||
{
|
||||
if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*index)))
|
||||
{
|
||||
return terminalTab->CalcSnappedDimension(widthOrHeight, dimension);
|
||||
}
|
||||
return terminalTab->CalcSnappedDimension(widthOrHeight, dimension);
|
||||
}
|
||||
}
|
||||
return dimension;
|
||||
|
@ -2212,7 +2231,7 @@ namespace winrt::TerminalApp::implementation
|
|||
// - eventArgs: the event's constituent arguments
|
||||
void TerminalPage::_OnTabSelectionChanged(const IInspectable& sender, const WUX::Controls::SelectionChangedEventArgs& /*eventArgs*/)
|
||||
{
|
||||
if (!_rearranging)
|
||||
if (!_rearranging && !_removing)
|
||||
{
|
||||
auto tabView = sender.as<MUX::Controls::TabView>();
|
||||
auto selectedIndex = tabView.SelectedIndex();
|
||||
|
@ -2837,7 +2856,7 @@ namespace winrt::TerminalApp::implementation
|
|||
// Return Value:
|
||||
// - If the tab is a TerminalTab, a com_ptr to the implementation type.
|
||||
// If the tab is not a TerminalTab, nullptr
|
||||
winrt::com_ptr<TerminalTab> TerminalPage::_GetTerminalTabImpl(const TerminalApp::TabBase& tab) const
|
||||
winrt::com_ptr<TerminalTab> TerminalPage::_GetTerminalTabImpl(const TerminalApp::TabBase& tab)
|
||||
{
|
||||
if (auto terminalTab = tab.try_as<TerminalApp::TerminalTab>())
|
||||
{
|
||||
|
|
|
@ -113,7 +113,7 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
Windows::Foundation::Collections::IObservableVector<TerminalApp::TabBase> _tabs;
|
||||
Windows::Foundation::Collections::IVector<TerminalApp::TabBase> _mruTabs;
|
||||
winrt::com_ptr<TerminalTab> _GetTerminalTabImpl(const TerminalApp::TabBase& tab) const;
|
||||
static winrt::com_ptr<TerminalTab> _GetTerminalTabImpl(const TerminalApp::TabBase& tab);
|
||||
|
||||
void _UpdateTabIndices();
|
||||
|
||||
|
@ -126,6 +126,7 @@ namespace winrt::TerminalApp::implementation
|
|||
bool _rearranging;
|
||||
std::optional<int> _rearrangeFrom;
|
||||
std::optional<int> _rearrangeTo;
|
||||
bool _removing{ false };
|
||||
|
||||
uint32_t _systemRowsToScroll{ DefaultRowsToScroll };
|
||||
|
||||
|
@ -157,6 +158,7 @@ namespace winrt::TerminalApp::implementation
|
|||
void _AboutButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
|
||||
void _ThirdPartyNoticesOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
|
||||
|
||||
void _KeyDownHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::KeyRoutedEventArgs const& e);
|
||||
void _HookupKeyBindings(const Microsoft::Terminal::Settings::Model::KeyMapping& keymap) noexcept;
|
||||
void _RegisterActionCallbacks();
|
||||
|
||||
|
@ -182,7 +184,9 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
winrt::Microsoft::Terminal::TerminalControl::TermControl _GetActiveControl();
|
||||
std::optional<uint32_t> _GetFocusedTabIndex() const noexcept;
|
||||
TerminalApp::TabBase _GetFocusedTab();
|
||||
TerminalApp::TabBase _GetFocusedTab() const noexcept;
|
||||
winrt::com_ptr<TerminalTab> _GetFocusedTabImpl() const noexcept;
|
||||
|
||||
winrt::fire_and_forget _SetFocusedTabIndex(const uint32_t tabIndex);
|
||||
void _CloseFocusedTab();
|
||||
void _CloseFocusedPane();
|
||||
|
@ -193,8 +197,13 @@ namespace winrt::TerminalApp::implementation
|
|||
// Todo: add more event implementations here
|
||||
// MSFT:20641986: Add keybindings for New Window
|
||||
void _Scroll(ScrollDirection scrollDirection, const Windows::Foundation::IReference<uint32_t>& rowsToScroll);
|
||||
void _SplitPane(const Microsoft::Terminal::Settings::Model::SplitState splitType, const Microsoft::Terminal::Settings::Model::SplitType splitMode = Microsoft::Terminal::Settings::Model::SplitType::Manual, const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs = nullptr);
|
||||
|
||||
void _SplitPane(const Microsoft::Terminal::Settings::Model::SplitState splitType,
|
||||
const Microsoft::Terminal::Settings::Model::SplitType splitMode = Microsoft::Terminal::Settings::Model::SplitType::Manual,
|
||||
const float splitSize = 0.5f,
|
||||
const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs = nullptr);
|
||||
void _ResizePane(const Microsoft::Terminal::Settings::Model::ResizeDirection& direction);
|
||||
|
||||
void _ScrollPage(ScrollDirection scrollDirection);
|
||||
void _ScrollToBufferEdge(ScrollDirection scrollDirection);
|
||||
void _SetAcceleratorForMenuItem(Windows::UI::Xaml::Controls::MenuFlyoutItem& menuItem, const winrt::Microsoft::Terminal::TerminalControl::KeyChord& keyChord);
|
||||
|
@ -245,12 +254,9 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
void _OpenSettingsUI();
|
||||
|
||||
void _MakeSwitchToTabCommand(const TerminalApp::TabBase& tab, const uint32_t index);
|
||||
|
||||
static int _ComputeScrollDelta(ScrollDirection scrollDirection, const uint32_t rowsToScroll);
|
||||
static uint32_t _ReadSystemRowsToScroll();
|
||||
|
||||
void _UpdateTabSwitcherCommands(const bool mru);
|
||||
void _UpdateMRUTab(const uint32_t index);
|
||||
|
||||
void _TryMoveTab(const uint32_t currentTabIndex, const int32_t suggestedNewTabIndex);
|
||||
|
|
|
@ -95,6 +95,7 @@ the MIT License. See LICENSE in the project root for license information. -->
|
|||
x:Name="CommandPalette"
|
||||
Grid.Row="1"
|
||||
Visibility="Collapsed"
|
||||
PreviewKeyDown ="_KeyDownHandler"
|
||||
VerticalAlignment="Stretch" />
|
||||
|
||||
<mux:InfoBar x:Name="KeyboardWarningInfoBar"
|
||||
|
|
|
@ -50,7 +50,6 @@ namespace winrt::TerminalApp::implementation
|
|||
tab->SetTabText(title);
|
||||
}
|
||||
});
|
||||
|
||||
// Use our header control as the TabViewItem's header
|
||||
TabViewItem().Header(_headerControl);
|
||||
}
|
||||
|
@ -76,6 +75,13 @@ namespace winrt::TerminalApp::implementation
|
|||
_RecalculateAndApplyTabColor();
|
||||
}
|
||||
|
||||
void TerminalTab::_SetToolTip(const winrt::hstring& tabTitle)
|
||||
{
|
||||
WUX::Controls::ToolTip toolTip{};
|
||||
toolTip.Content(winrt::box_value(tabTitle));
|
||||
WUX::Controls::ToolTipService::SetToolTip(TabViewItem(), toolTip);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Returns nullptr if no children of this tab were the last control to be
|
||||
// focused, or the TermControl that _was_ the last control to be focused (if
|
||||
|
@ -213,17 +219,19 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
if (hide)
|
||||
if (tab->_iconHidden != hide)
|
||||
{
|
||||
Icon({});
|
||||
TabViewItem().IconSource(IconPathConverter::IconSourceMUX({}));
|
||||
tab->_iconHidden = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Icon(_lastIconPath);
|
||||
TabViewItem().IconSource(IconPathConverter::IconSourceMUX(_lastIconPath));
|
||||
tab->_iconHidden = false;
|
||||
if (hide)
|
||||
{
|
||||
Icon({});
|
||||
TabViewItem().IconSource(IconPathConverter::IconSourceMUX({}));
|
||||
}
|
||||
else
|
||||
{
|
||||
Icon(_lastIconPath);
|
||||
TabViewItem().IconSource(IconPathConverter::IconSourceMUX(_lastIconPath));
|
||||
}
|
||||
tab->_iconHidden = hide;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -265,6 +273,7 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
// Update the control to reflect the changed title
|
||||
_headerControl.Title(activeTitle);
|
||||
_SetToolTip(activeTitle);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -286,17 +295,6 @@ namespace winrt::TerminalApp::implementation
|
|||
control.ScrollViewport(currentOffset + delta);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Determines whether the focused pane has sufficient space to be split.
|
||||
// Arguments:
|
||||
// - splitType: The type of split we want to create.
|
||||
// Return Value:
|
||||
// - True if the focused pane can be split. False otherwise.
|
||||
bool TerminalTab::CanSplitPane(SplitState splitType)
|
||||
{
|
||||
return _activePane->CanSplit(splitType);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Split the focused pane in our tree of panes, and place the
|
||||
// given TermControl into the newly created pane.
|
||||
|
@ -306,11 +304,14 @@ namespace winrt::TerminalApp::implementation
|
|||
// - control: A TermControl to use in the new pane.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalTab::SplitPane(SplitState splitType, const GUID& profile, TermControl& control)
|
||||
void TerminalTab::SplitPane(SplitState splitType,
|
||||
const float splitSize,
|
||||
const GUID& profile,
|
||||
TermControl& control)
|
||||
{
|
||||
// Make sure to take the ID before calling Split() - Split() will clear out the active pane's ID
|
||||
const auto activePaneId = _activePane->Id();
|
||||
auto [first, second] = _activePane->Split(splitType, profile, control);
|
||||
auto [first, second] = _activePane->Split(splitType, splitSize, profile, control);
|
||||
first->Id(activePaneId);
|
||||
second->Id(_nextPaneId);
|
||||
++_nextPaneId;
|
||||
|
@ -936,9 +937,11 @@ namespace winrt::TerminalApp::implementation
|
|||
return _rootPane->PreCalculateAutoSplit(_activePane, availableSpace).value_or(SplitState::Vertical);
|
||||
}
|
||||
|
||||
bool TerminalTab::PreCalculateCanSplit(SplitState splitType, winrt::Windows::Foundation::Size availableSpace) const
|
||||
bool TerminalTab::PreCalculateCanSplit(SplitState splitType,
|
||||
const float splitSize,
|
||||
winrt::Windows::Foundation::Size availableSpace) const
|
||||
{
|
||||
return _rootPane->PreCalculateCanSplit(_activePane, splitType, availableSpace).value_or(false);
|
||||
return _rootPane->PreCalculateCanSplit(_activePane, splitType, splitSize, availableSpace).value_or(false);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
|
|
@ -30,15 +30,19 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
winrt::fire_and_forget Scroll(const int delta);
|
||||
|
||||
bool CanSplitPane(winrt::Microsoft::Terminal::Settings::Model::SplitState splitType);
|
||||
void SplitPane(winrt::Microsoft::Terminal::Settings::Model::SplitState splitType, const GUID& profile, winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
|
||||
void SplitPane(winrt::Microsoft::Terminal::Settings::Model::SplitState splitType,
|
||||
const float splitSize,
|
||||
const GUID& profile,
|
||||
winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
|
||||
|
||||
winrt::fire_and_forget UpdateIcon(const winrt::hstring iconPath);
|
||||
winrt::fire_and_forget HideIcon(const bool hide);
|
||||
|
||||
float CalcSnappedDimension(const bool widthOrHeight, const float dimension) const;
|
||||
winrt::Microsoft::Terminal::Settings::Model::SplitState PreCalculateAutoSplit(winrt::Windows::Foundation::Size rootSize) const;
|
||||
bool PreCalculateCanSplit(winrt::Microsoft::Terminal::Settings::Model::SplitState splitType, winrt::Windows::Foundation::Size availableSpace) const;
|
||||
bool PreCalculateCanSplit(winrt::Microsoft::Terminal::Settings::Model::SplitState splitType,
|
||||
const float splitSize,
|
||||
winrt::Windows::Foundation::Size availableSpace) const;
|
||||
|
||||
void ResizeContent(const winrt::Windows::Foundation::Size& newSize);
|
||||
void ResizePane(const winrt::Microsoft::Terminal::Settings::Model::ResizeDirection& direction);
|
||||
|
@ -98,6 +102,8 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
void _MakeTabViewItem();
|
||||
|
||||
void _SetToolTip(const winrt::hstring& tabTitle);
|
||||
|
||||
void _CreateContextMenu() override;
|
||||
|
||||
void _RefreshVisualState();
|
||||
|
|
|
@ -1288,7 +1288,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
|||
_terminal->SetSelectionEnd(terminalPosition, mode);
|
||||
_selectionNeedsToBeCopied = true;
|
||||
}
|
||||
else if (mode == ::Terminal::SelectionExpansionMode::Cell)
|
||||
else if (mode == ::Terminal::SelectionExpansionMode::Cell && !shiftEnabled)
|
||||
{
|
||||
// Single Click: reset the selection and begin a new one
|
||||
_terminal->ClearSelection();
|
||||
|
|
|
@ -329,7 +329,7 @@ void Terminal::UpdateSettings(ICoreSettings settings)
|
|||
{
|
||||
try
|
||||
{
|
||||
auto row = newTextBuffer->GetRowByOffset(::base::ClampSub(proposedTop, 1));
|
||||
auto& row = newTextBuffer->GetRowByOffset(::base::ClampSub(proposedTop, 1));
|
||||
if (row.GetCharRow().WasWrapForced())
|
||||
{
|
||||
proposedTop--;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
using namespace winrt;
|
||||
using namespace winrt::Windows::UI;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace winrt::Windows::UI::Xaml::Navigation;
|
||||
using namespace winrt::Windows::UI::Xaml::Controls;
|
||||
using namespace winrt::Windows::UI::Xaml::Media;
|
||||
|
@ -38,8 +39,20 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|||
RS_(L"ColorScheme_BrightWhite/Header")
|
||||
};
|
||||
|
||||
static const std::array<std::wstring, 9> InBoxSchemes = {
|
||||
L"Campbell",
|
||||
L"Campbell Powershell",
|
||||
L"Vintage",
|
||||
L"One Half Dark",
|
||||
L"One Half Light",
|
||||
L"Solarized Dark",
|
||||
L"Solarized Light",
|
||||
L"Tango Dark",
|
||||
L"Tango Light"
|
||||
};
|
||||
|
||||
ColorSchemes::ColorSchemes() :
|
||||
_ColorSchemeList{ single_threaded_observable_vector<hstring>() },
|
||||
_ColorSchemeList{ single_threaded_observable_vector<Model::ColorScheme>() },
|
||||
_CurrentColorTable{ single_threaded_observable_vector<Editor::ColorTableEntry>() }
|
||||
{
|
||||
InitializeComponent();
|
||||
|
@ -73,10 +86,23 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|||
SelectionChangedEventArgs const& args)
|
||||
{
|
||||
// Update the color scheme this page is modifying
|
||||
auto str = winrt::unbox_value<hstring>(args.AddedItems().GetAt(0));
|
||||
auto colorScheme = _State.Globals().ColorSchemes().Lookup(str);
|
||||
const auto colorScheme{ args.AddedItems().GetAt(0).try_as<Model::ColorScheme>() };
|
||||
CurrentColorScheme(colorScheme);
|
||||
_UpdateColorTable(colorScheme);
|
||||
|
||||
// Set the text disclaimer for the text box
|
||||
hstring disclaimer{};
|
||||
const std::wstring schemeName{ colorScheme.Name() };
|
||||
if (std::find(std::begin(InBoxSchemes), std::end(InBoxSchemes), schemeName) != std::end(InBoxSchemes))
|
||||
{
|
||||
// load disclaimer for in-box profiles
|
||||
disclaimer = RS_(L"ColorScheme_DeleteButtonDisclaimerInBox");
|
||||
}
|
||||
DeleteButtonDisclaimer().Text(disclaimer);
|
||||
|
||||
// Update the state of the page
|
||||
_PropertyChangedHandlers(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"CanDeleteCurrentScheme" });
|
||||
IsRenaming(false);
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
|
@ -92,7 +118,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|||
const auto& colorSchemeMap{ _State.Globals().ColorSchemes() };
|
||||
for (const auto& pair : colorSchemeMap)
|
||||
{
|
||||
_ColorSchemeList.Append(pair.Key());
|
||||
_ColorSchemeList.Append(pair.Value());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,6 +146,101 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|||
}
|
||||
}
|
||||
|
||||
bool ColorSchemes::CanDeleteCurrentScheme() const
|
||||
{
|
||||
if (const auto scheme{ CurrentColorScheme() })
|
||||
{
|
||||
// Only allow this color scheme to be deleted if it's not provided in-box
|
||||
const std::wstring myName{ scheme.Name() };
|
||||
return std::find(std::begin(InBoxSchemes), std::end(InBoxSchemes), myName) == std::end(InBoxSchemes);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ColorSchemes::DeleteConfirmation_Click(IInspectable const& /*sender*/, RoutedEventArgs const& /*e*/)
|
||||
{
|
||||
const auto schemeName{ CurrentColorScheme().Name() };
|
||||
_State.Globals().RemoveColorScheme(schemeName);
|
||||
|
||||
const auto removedSchemeIndex{ ColorSchemeComboBox().SelectedIndex() };
|
||||
if (static_cast<uint32_t>(removedSchemeIndex) < _ColorSchemeList.Size() - 1)
|
||||
{
|
||||
// select same index
|
||||
ColorSchemeComboBox().SelectedIndex(removedSchemeIndex + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// select last color scheme (avoid out of bounds error)
|
||||
ColorSchemeComboBox().SelectedIndex(removedSchemeIndex - 1);
|
||||
}
|
||||
_ColorSchemeList.RemoveAt(removedSchemeIndex);
|
||||
DeleteButton().Flyout().Hide();
|
||||
}
|
||||
|
||||
void ColorSchemes::AddNew_Click(IInspectable const& /*sender*/, RoutedEventArgs const& /*e*/)
|
||||
{
|
||||
// Give the new scheme a distinct name
|
||||
const hstring schemeName{ fmt::format(L"Color Scheme {}", _State.Globals().ColorSchemes().Size() + 1) };
|
||||
Model::ColorScheme scheme{ schemeName };
|
||||
|
||||
// Add the new color scheme
|
||||
_State.Globals().AddColorScheme(scheme);
|
||||
|
||||
// Update current page
|
||||
_ColorSchemeList.Append(scheme);
|
||||
ColorSchemeComboBox().SelectedItem(scheme);
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Pre-populates/focuses the name TextBox, updates the UI
|
||||
// Arguments:
|
||||
// - <unused>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void ColorSchemes::Rename_Click(IInspectable const& /*sender*/, RoutedEventArgs const& /*e*/)
|
||||
{
|
||||
NameBox().Text(CurrentColorScheme().Name());
|
||||
IsRenaming(true);
|
||||
NameBox().Focus(FocusState::Programmatic);
|
||||
NameBox().SelectAll();
|
||||
}
|
||||
|
||||
void ColorSchemes::RenameAccept_Click(IInspectable const& /*sender*/, RoutedEventArgs const& /*e*/)
|
||||
{
|
||||
_RenameCurrentScheme(NameBox().Text());
|
||||
}
|
||||
|
||||
void ColorSchemes::RenameCancel_Click(IInspectable const& /*sender*/, RoutedEventArgs const& /*e*/)
|
||||
{
|
||||
IsRenaming(false);
|
||||
}
|
||||
|
||||
void ColorSchemes::NameBox_PreviewKeyDown(IInspectable const& /*sender*/, winrt::Windows::UI::Xaml::Input::KeyRoutedEventArgs const& e)
|
||||
{
|
||||
if (e.OriginalKey() == winrt::Windows::System::VirtualKey::Enter)
|
||||
{
|
||||
_RenameCurrentScheme(NameBox().Text());
|
||||
e.Handled(true);
|
||||
}
|
||||
else if (e.OriginalKey() == winrt::Windows::System::VirtualKey::Escape)
|
||||
{
|
||||
IsRenaming(false);
|
||||
e.Handled(true);
|
||||
}
|
||||
}
|
||||
|
||||
void ColorSchemes::_RenameCurrentScheme(hstring newName)
|
||||
{
|
||||
CurrentColorScheme().Name(newName);
|
||||
IsRenaming(false);
|
||||
|
||||
// The color scheme is renamed appropriately, but the ComboBox still shows the old name (until you open it)
|
||||
// We need to manually force the ComboBox to refresh itself.
|
||||
const auto selectedIndex{ ColorSchemeComboBox().SelectedIndex() };
|
||||
ColorSchemeComboBox().SelectedIndex((selectedIndex + 1) % ColorSchemeList().Size());
|
||||
ColorSchemeComboBox().SelectedIndex(selectedIndex);
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Updates the currently modifiable color table based on the given current color scheme.
|
||||
// Arguments:
|
||||
|
|
|
@ -27,17 +27,28 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|||
|
||||
void ColorSchemeSelectionChanged(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::Controls::SelectionChangedEventArgs const& args);
|
||||
void ColorPickerChanged(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::Controls::ColorChangedEventArgs const& args);
|
||||
void AddNew_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
|
||||
|
||||
void Rename_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
|
||||
void RenameAccept_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
|
||||
void RenameCancel_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
|
||||
void NameBox_PreviewKeyDown(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::Input::KeyRoutedEventArgs const& e);
|
||||
|
||||
bool CanDeleteCurrentScheme() const;
|
||||
void DeleteConfirmation_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
|
||||
|
||||
GETSET_PROPERTY(Editor::ColorSchemesPageNavigationState, State, nullptr);
|
||||
GETSET_PROPERTY(Windows::Foundation::Collections::IObservableVector<winrt::Microsoft::Terminal::Settings::Editor::ColorTableEntry>, CurrentColorTable, nullptr);
|
||||
GETSET_PROPERTY(Windows::Foundation::Collections::IObservableVector<winrt::hstring>, ColorSchemeList, nullptr);
|
||||
GETSET_PROPERTY(Windows::Foundation::Collections::IObservableVector<Model::ColorScheme>, ColorSchemeList, nullptr);
|
||||
|
||||
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::Microsoft::Terminal::Settings::Model::ColorScheme, CurrentColorScheme, _PropertyChangedHandlers, nullptr);
|
||||
OBSERVABLE_GETSET_PROPERTY(bool, IsRenaming, _PropertyChangedHandlers, nullptr);
|
||||
|
||||
private:
|
||||
void _UpdateColorTable(const winrt::Microsoft::Terminal::Settings::Model::ColorScheme& colorScheme);
|
||||
void _UpdateColorSchemeList();
|
||||
void _RenameCurrentScheme(hstring newName);
|
||||
};
|
||||
|
||||
struct ColorTableEntry : ColorTableEntryT<ColorTableEntry>
|
||||
|
|
|
@ -13,9 +13,12 @@ namespace Microsoft.Terminal.Settings.Editor
|
|||
ColorSchemes();
|
||||
ColorSchemesPageNavigationState State { get; };
|
||||
|
||||
Boolean CanDeleteCurrentScheme { get; };
|
||||
Boolean IsRenaming { get; };
|
||||
|
||||
Microsoft.Terminal.Settings.Model.ColorScheme CurrentColorScheme { get; };
|
||||
Windows.Foundation.Collections.IObservableVector<ColorTableEntry> CurrentColorTable;
|
||||
Windows.Foundation.Collections.IObservableVector<String> ColorSchemeList { get; };
|
||||
Windows.Foundation.Collections.IObservableVector<Microsoft.Terminal.Settings.Model.ColorScheme> ColorSchemeList { get; };
|
||||
}
|
||||
|
||||
[default_interface] runtimeclass ColorTableEntry : Windows.UI.Xaml.Data.INotifyPropertyChanged
|
||||
|
|
|
@ -5,6 +5,7 @@ the MIT License. See LICENSE in the project root for license information. -->
|
|||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:Microsoft.Terminal.Settings.Editor"
|
||||
xmlns:model="using:Microsoft.Terminal.Settings.Model"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
@ -15,8 +16,8 @@ the MIT License. See LICENSE in the project root for license information. -->
|
|||
<ResourceDictionary Source="CommonResources.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<Thickness x:Key="ColorSchemesStandardControlMargin">10,0,0,20</Thickness>
|
||||
|
||||
<Thickness x:Key="ColorSchemesStandardControlMargin">0,0,10,20</Thickness>
|
||||
|
||||
<Style x:Key="ColorStackPanelStyle" TargetType="StackPanel">
|
||||
<Setter Property="Margin" Value="{StaticResource ColorSchemesStandardControlMargin}"/>
|
||||
<Setter Property="Height" Value="59"/>
|
||||
|
@ -42,32 +43,85 @@ the MIT License. See LICENSE in the project root for license information. -->
|
|||
|
||||
<local:ColorToBrushConverter x:Key="ColorToBrushConverter"/>
|
||||
<local:ColorToHexConverter x:Key="ColorToHexConverter"/>
|
||||
<local:InvertedBooleanToVisibilityConverter x:Key="InvertedBooleanToVisibilityConverter"/>
|
||||
</ResourceDictionary>
|
||||
</Page.Resources>
|
||||
|
||||
<ScrollViewer>
|
||||
<Grid>
|
||||
<Grid Margin="{StaticResource StandardIndentMargin}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="1*" MaxWidth="480"/>
|
||||
<ColumnDefinition Width="1*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!--Select a color scheme-->
|
||||
<ComboBox x:Name="ColorSchemeComboBox"
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="2"
|
||||
SelectedIndex="0"
|
||||
ItemsSource="{x:Bind ColorSchemeList, Mode=OneWay}"
|
||||
SelectionChanged="ColorSchemeSelectionChanged"
|
||||
Style="{StaticResource ComboBoxSettingStyle}"
|
||||
Margin="{StaticResource ColorSchemesStandardControlMargin}"/>
|
||||
<!--Select Color and Add New Button-->
|
||||
<StackPanel Orientation="Horizontal"
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="2"
|
||||
Margin="{StaticResource StandardControlSpacing}">
|
||||
|
||||
<!-- Color Table -->
|
||||
<StackPanel Orientation="Horizontal"
|
||||
Visibility="{x:Bind IsRenaming, Converter={StaticResource InvertedBooleanToVisibilityConverter}, Mode=OneWay}">
|
||||
<!--Select a color scheme-->
|
||||
<ComboBox x:Name="ColorSchemeComboBox"
|
||||
SelectedIndex="0"
|
||||
ItemsSource="{x:Bind ColorSchemeList, Mode=OneWay}"
|
||||
SelectionChanged="ColorSchemeSelectionChanged"
|
||||
Style="{StaticResource ComboBoxSettingStyle}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate x:DataType="model:ColorScheme">
|
||||
<TextBlock Text="{x:Bind Name, Mode=OneWay}"/>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<!--Rename Button-->
|
||||
<!--Bind IsEnabled to prevent a the color scheme from returning from the dead-->
|
||||
<Button x:Uid="Rename"
|
||||
Style="{StaticResource BrowseButtonStyle}"
|
||||
Click="Rename_Click"
|
||||
IsEnabled="{x:Bind CanDeleteCurrentScheme, Mode=OneWay}">
|
||||
<FontIcon Glyph=""/>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Orientation="Horizontal"
|
||||
Visibility="{x:Bind IsRenaming, Mode=OneWay}">
|
||||
<TextBox x:Name="NameBox"
|
||||
Style="{StaticResource TextBoxSettingStyle}"
|
||||
PreviewKeyDown="NameBox_PreviewKeyDown"/>
|
||||
<Button x:Uid="RenameAccept"
|
||||
Style="{StaticResource AccentBrowseButtonStyle}"
|
||||
Click="RenameAccept_Click">
|
||||
<FontIcon Glyph=""/>
|
||||
</Button>
|
||||
<Button x:Uid="RenameCancel"
|
||||
Style="{StaticResource BrowseButtonStyle}"
|
||||
Click="RenameCancel_Click">
|
||||
<FontIcon Glyph=""/>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
<!--Add new button-->
|
||||
<Button Click="AddNew_Click"
|
||||
Style="{StaticResource BrowseButtonStyle}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<FontIcon Glyph=""
|
||||
FontSize="15"/>
|
||||
<TextBlock x:Uid="ColorScheme_AddNewButton"
|
||||
Margin="10,0,0,0"
|
||||
FontSize="15"/>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Color Table (Left Column)-->
|
||||
<ItemsControl x:Name="ColorTableControl"
|
||||
ItemsSource="{x:Bind CurrentColorTable, Mode=OneWay}"
|
||||
Grid.Row="1"
|
||||
|
@ -98,8 +152,8 @@ the MIT License. See LICENSE in the project root for license information. -->
|
|||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
<!-- Additional Colors -->
|
||||
<ItemsControl Grid.Row="2"
|
||||
<!-- Additional Colors (Right Column) -->
|
||||
<ItemsControl Grid.Row="1"
|
||||
Grid.Column="1">
|
||||
<StackPanel Style="{StaticResource ColorStackPanelStyle}">
|
||||
<TextBlock x:Uid="ColorScheme_Foreground"
|
||||
|
@ -149,6 +203,71 @@ the MIT License. See LICENSE in the project root for license information. -->
|
|||
</Button>
|
||||
</StackPanel>
|
||||
</ItemsControl>
|
||||
|
||||
<!--Delete Button-->
|
||||
<StackPanel Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="2"
|
||||
Margin="{StaticResource StandardControlSpacing}">
|
||||
<Button x:Name="DeleteButton"
|
||||
Style="{StaticResource BaseButtonStyle}"
|
||||
IsEnabled="{x:Bind CanDeleteCurrentScheme, Mode=OneWay}"
|
||||
Margin="0,0,0,10">
|
||||
<Button.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<SolidColorBrush x:Key="ButtonBackground" Color="Firebrick"/>
|
||||
<SolidColorBrush x:Key="ButtonBackgroundPointerOver" Color="#C23232"/>
|
||||
<SolidColorBrush x:Key="ButtonBackgroundPressed" Color="#A21212"/>
|
||||
<SolidColorBrush x:Key="ButtonForeground" Color="White"/>
|
||||
<SolidColorBrush x:Key="ButtonForegroundPointerOver" Color="White"/>
|
||||
<SolidColorBrush x:Key="ButtonForegroundPressed" Color="White"/>
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
<SolidColorBrush x:Key="ButtonBackground" Color="Firebrick"/>
|
||||
<SolidColorBrush x:Key="ButtonBackgroundPointerOver" Color="#C23232"/>
|
||||
<SolidColorBrush x:Key="ButtonBackgroundPressed" Color="#A21212"/>
|
||||
<SolidColorBrush x:Key="ButtonForeground" Color="White"/>
|
||||
<SolidColorBrush x:Key="ButtonForegroundPointerOver" Color="White"/>
|
||||
<SolidColorBrush x:Key="ButtonForegroundPressed" Color="White"/>
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="HighContrast">
|
||||
<SolidColorBrush x:Key="ButtonBackground" Color="{ThemeResource SystemColorButtonFaceColor}"/>
|
||||
<SolidColorBrush x:Key="ButtonBackgroundPointerOver" Color="{ThemeResource SystemColorHighlightColor}"/>
|
||||
<SolidColorBrush x:Key="ButtonBackgroundPressed" Color="{ThemeResource SystemColorHighlightColor}"/>
|
||||
<SolidColorBrush x:Key="ButtonForeground" Color="{ThemeResource SystemColorButtonTextColor}"/>
|
||||
<SolidColorBrush x:Key="ButtonForegroundPointerOver" Color="{ThemeResource SystemColorHighlightTextColor}"/>
|
||||
<SolidColorBrush x:Key="ButtonForegroundPressed" Color="{ThemeResource SystemColorHighlightTextColor}"/>
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Button.Resources>
|
||||
<Button.Content>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<FontIcon Glyph=""
|
||||
FontSize="{StaticResource StandardFontSize}"/>
|
||||
<TextBlock x:Uid="ColorScheme_DeleteButton"
|
||||
FontSize="{StaticResource StandardFontSize}"
|
||||
Margin="10,0,0,0"/>
|
||||
</StackPanel>
|
||||
</Button.Content>
|
||||
<Button.Flyout>
|
||||
<Flyout>
|
||||
<StackPanel>
|
||||
<TextBlock x:Uid="ColorScheme_DeleteConfirmationMessage"
|
||||
Style="{StaticResource CustomFlyoutTextStyle}"/>
|
||||
<Button x:Uid="ColorScheme_DeleteConfirmationButton"
|
||||
Style="{StaticResource BaseButtonStyle}"
|
||||
Click="DeleteConfirmation_Click"/>
|
||||
</StackPanel>
|
||||
</Flyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
<TextBlock x:Name="DeleteButtonDisclaimer"
|
||||
Style="{StaticResource DisclaimerStyle}"
|
||||
VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</Page>
|
||||
|
|
|
@ -5,12 +5,16 @@
|
|||
|
||||
<x:Double x:Key="StandardFontSize">15.0</x:Double>
|
||||
<Thickness x:Key="StandardIndentMargin">20,0,0,0</Thickness>
|
||||
<x:Double x:Key="StandardBoxMinWidth">300</x:Double>
|
||||
<Thickness x:Key="StandardControlSpacing">0,0,0,20</Thickness>
|
||||
<x:Double x:Key="StandardBoxMinWidth">250</x:Double>
|
||||
|
||||
<Thickness x:Key="PivotIndentMargin">10,0,0,0</Thickness>
|
||||
<Thickness x:Key="PivotStackPanelMargin">0,10,0,0</Thickness>
|
||||
|
||||
<!-- This is for easier transition to the SettingsContainer control.
|
||||
The SettingsContainer will wrap a setting with inheritance UI.-->
|
||||
<Style x:Key="SettingContainerStyle" TargetType="ContentPresenter">
|
||||
<Setter Property="Padding" Value="0,0,0,20"/>
|
||||
<Setter Property="Padding" Value="{StaticResource StandardControlSpacing}"/>
|
||||
</Style>
|
||||
|
||||
<!--Used to stack a group of settings-->
|
||||
|
@ -63,6 +67,20 @@
|
|||
<Setter Property="Margin" Value="0,20,0,10"/>
|
||||
</Style>
|
||||
|
||||
<!--Used for disclaimers-->
|
||||
<Style x:Key="DisclaimerStyle" TargetType="TextBlock">
|
||||
<Setter Property="FontSize" Value="{StaticResource StandardFontSize}"/>
|
||||
<Setter Property="FontStyle" Value="Italic"/>
|
||||
<Setter Property="TextWrapping" Value="WrapWholeWords"/>
|
||||
</Style>
|
||||
|
||||
<!--Used for flyout messages-->
|
||||
<Style x:Key="CustomFlyoutTextStyle" TargetType="TextBlock" BasedOn="{StaticResource BaseTextBlockStyle}">
|
||||
<Setter Property="FontSize" Value="{StaticResource StandardFontSize}"/>
|
||||
<Setter Property="FontWeight" Value="Bold"/>
|
||||
<Setter Property="Margin" Value="0,0,0,12"/>
|
||||
</Style>
|
||||
|
||||
<!--Number Box-->
|
||||
<Style x:Key="NumberBoxSettingStyle" TargetType="muxc:NumberBox">
|
||||
<Setter Property="FontSize" Value="{StaticResource StandardFontSize}"/>
|
||||
|
@ -74,6 +92,7 @@
|
|||
<!--Button-Related Styling-->
|
||||
<Style x:Key="BaseButtonStyle" TargetType="Button" BasedOn="{StaticResource DefaultButtonStyle}">
|
||||
<Setter Property="FontSize" Value="{StaticResource StandardFontSize}"/>
|
||||
<Setter Property="ToolTipService.Placement" Value="Mouse"/>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="BrowseButtonStyle" TargetType="Button" BasedOn="{StaticResource BaseButtonStyle}">
|
||||
|
@ -81,6 +100,13 @@
|
|||
<Setter Property="VerticalAlignment" Value="Bottom"/>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="AccentBrowseButtonStyle" TargetType="Button" BasedOn="{StaticResource AccentButtonStyle}">
|
||||
<Setter Property="FontSize" Value="{StaticResource StandardFontSize}"/>
|
||||
<Setter Property="ToolTipService.Placement" Value="Mouse"/>
|
||||
<Setter Property="Margin" Value="10,0,0,0"/>
|
||||
<Setter Property="VerticalAlignment" Value="Bottom"/>
|
||||
</Style>
|
||||
|
||||
<!--Slider-Related Styling-->
|
||||
<Style x:Key="SliderSettingStyle" TargetType="Slider" BasedOn="{StaticResource DefaultSliderStyle}">
|
||||
<Setter Property="FontSize" Value="{StaticResource StandardFontSize}"/>
|
||||
|
@ -105,5 +131,4 @@
|
|||
<Setter Property="Width" Value="300"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Left"/>
|
||||
</Style>
|
||||
|
||||
</ResourceDictionary>
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "InvertedBooleanToVisibilityConverter.h"
|
||||
#include "InvertedBooleanToVisibilityConverter.g.cpp"
|
||||
|
||||
using namespace winrt::Windows;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
Foundation::IInspectable InvertedBooleanToVisibilityConverter::Convert(Foundation::IInspectable const& value,
|
||||
Windows::UI::Xaml::Interop::TypeName const& /* targetType */,
|
||||
Foundation::IInspectable const& /* parameter */,
|
||||
hstring const& /* language */)
|
||||
{
|
||||
return winrt::box_value(winrt::unbox_value<bool>(value) ? Visibility::Collapsed : Visibility::Visible);
|
||||
}
|
||||
|
||||
Foundation::IInspectable InvertedBooleanToVisibilityConverter::ConvertBack(Foundation::IInspectable const& /*value*/,
|
||||
Windows::UI::Xaml::Interop::TypeName const& /* targetType */,
|
||||
Foundation::IInspectable const& /*parameter*/,
|
||||
hstring const& /* language */)
|
||||
{
|
||||
throw hresult_not_implemented();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "InvertedBooleanToVisibilityConverter.g.h"
|
||||
#include "Utils.h"
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
struct InvertedBooleanToVisibilityConverter : InvertedBooleanToVisibilityConverterT<InvertedBooleanToVisibilityConverter>
|
||||
{
|
||||
InvertedBooleanToVisibilityConverter() = default;
|
||||
|
||||
Windows::Foundation::IInspectable Convert(Windows::Foundation::IInspectable const& value,
|
||||
Windows::UI::Xaml::Interop::TypeName const& targetType,
|
||||
Windows::Foundation::IInspectable const& parameter,
|
||||
hstring const& language);
|
||||
|
||||
Windows::Foundation::IInspectable ConvertBack(Windows::Foundation::IInspectable const& value,
|
||||
Windows::UI::Xaml::Interop::TypeName const& targetType,
|
||||
Windows::Foundation::IInspectable const& parameter,
|
||||
hstring const& language);
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::factory_implementation
|
||||
{
|
||||
BASIC_FACTORY(InvertedBooleanToVisibilityConverter);
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Microsoft.Terminal.Settings.Editor
|
||||
{
|
||||
runtimeclass InvertedBooleanToVisibilityConverter : [default] Windows.UI.Xaml.Data.IValueConverter
|
||||
{
|
||||
InvertedBooleanToVisibilityConverter();
|
||||
};
|
||||
}
|
|
@ -10,6 +10,7 @@
|
|||
#include "Profiles.h"
|
||||
#include "GlobalAppearance.h"
|
||||
#include "ColorSchemes.h"
|
||||
#include "..\types\inc\utils.hpp"
|
||||
|
||||
#include <LibraryResources.h>
|
||||
|
||||
|
@ -75,7 +76,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|||
{
|
||||
if (const auto tag{ navViewItem.Tag() })
|
||||
{
|
||||
if (tag.try_as<Model::Profile>())
|
||||
if (tag.try_as<Editor::ProfileViewModel>())
|
||||
{
|
||||
// hide NavViewItem pointing to a Profile
|
||||
navViewItem.Visibility(Visibility::Collapsed);
|
||||
|
@ -103,13 +104,13 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|||
{
|
||||
if (const auto tag{ selectedItem.as<MUX::Controls::NavigationViewItem>().Tag() })
|
||||
{
|
||||
if (const auto profile{ tag.try_as<Model::Profile>() })
|
||||
if (const auto oldProfile{ tag.try_as<Editor::ProfileViewModel>() })
|
||||
{
|
||||
// check if the profile still exists
|
||||
if (_settingsClone.FindProfile(profile.Guid()))
|
||||
if (const auto profile{ _settingsClone.FindProfile(oldProfile.Guid()) })
|
||||
{
|
||||
// Navigate to the page with the given profile
|
||||
contentFrame().Navigate(xaml_typename<Editor::Profiles>(), winrt::make<ProfilePageNavigationState>(_viewModelForProfile(profile), _settingsClone.GlobalSettings().ColorSchemes(), *this));
|
||||
_Navigate(_viewModelForProfile(profile));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -125,7 +126,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|||
// could not find the page we were on, fallback to first menu item
|
||||
const auto firstItem{ navigationMenu.MenuItems().GetAt(0) };
|
||||
navigationMenu.SelectedItem(firstItem);
|
||||
if (const auto tag{ navigationMenu.SelectedItem().as<NavigationViewItem>().Tag() })
|
||||
if (const auto tag{ navigationMenu.SelectedItem().as<MUX::Controls::NavigationViewItem>().Tag() })
|
||||
{
|
||||
_Navigate(unbox_value<hstring>(tag));
|
||||
}
|
||||
|
@ -197,7 +198,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|||
else if (const auto profile = clickedItemContainer.Tag().try_as<Editor::ProfileViewModel>())
|
||||
{
|
||||
// Navigate to a page with the given profile
|
||||
contentFrame().Navigate(xaml_typename<Editor::Profiles>(), winrt::make<ProfilePageNavigationState>(profile, _settingsClone.GlobalSettings().ColorSchemes(), *this));
|
||||
_Navigate(profile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -218,7 +219,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|||
}
|
||||
else if (clickedItemTag == globalProfileTag)
|
||||
{
|
||||
contentFrame().Navigate(xaml_typename<Editor::Profiles>(), winrt::make<ProfilePageNavigationState>(_viewModelForProfile(_settingsClone.ProfileDefaults()), _settingsClone.GlobalSettings().ColorSchemes(), *this));
|
||||
auto profileVM{ _viewModelForProfile(_settingsClone.ProfileDefaults()) };
|
||||
profileVM.IsBaseLayer(true);
|
||||
contentFrame().Navigate(xaml_typename<Editor::Profiles>(), winrt::make<ProfilePageNavigationState>(profileVM, _settingsClone.GlobalSettings().ColorSchemes(), *this));
|
||||
}
|
||||
else if (clickedItemTag == colorSchemesTag)
|
||||
{
|
||||
|
@ -230,6 +233,16 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|||
}
|
||||
}
|
||||
|
||||
void MainPage::_Navigate(const Editor::ProfileViewModel& profile)
|
||||
{
|
||||
auto state{ winrt::make<ProfilePageNavigationState>(profile, _settingsClone.GlobalSettings().ColorSchemes(), *this) };
|
||||
|
||||
// Add an event handler for when the user wants to delete a profile.
|
||||
state.DeleteProfile({ this, &MainPage::_DeleteProfile });
|
||||
|
||||
contentFrame().Navigate(xaml_typename<Editor::Profiles>(), state);
|
||||
}
|
||||
|
||||
void MainPage::OpenJsonTapped(IInspectable const& /*sender*/, Windows::UI::Xaml::Input::TappedRoutedEventArgs const& /*args*/)
|
||||
{
|
||||
const CoreWindow window = CoreWindow::GetForCurrentThread();
|
||||
|
@ -297,7 +310,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|||
|
||||
// Select and navigate to the new profile
|
||||
SettingsNav().SelectedItem(navItem);
|
||||
contentFrame().Navigate(xaml_typename<Editor::Profiles>(), winrt::make<ProfilePageNavigationState>(profileViewModel, _settingsClone.GlobalSettings().ColorSchemes(), *this));
|
||||
_Navigate(profileViewModel);
|
||||
}
|
||||
|
||||
MUX::Controls::NavigationViewItem MainPage::_CreateProfileNavViewItem(const Editor::ProfileViewModel& profile)
|
||||
|
@ -313,4 +326,31 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|||
|
||||
return profileNavItem;
|
||||
}
|
||||
|
||||
void MainPage::_DeleteProfile(const IInspectable /*sender*/, const Editor::DeleteProfileEventArgs& args)
|
||||
{
|
||||
// Delete profile from settings model
|
||||
const auto guid{ args.ProfileGuid() };
|
||||
auto profileList{ _settingsClone.AllProfiles() };
|
||||
for (uint32_t i = 0; i < profileList.Size(); ++i)
|
||||
{
|
||||
if (profileList.GetAt(i).Guid() == guid)
|
||||
{
|
||||
profileList.RemoveAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// remove selected item
|
||||
uint32_t index;
|
||||
auto selectedItem{ SettingsNav().SelectedItem() };
|
||||
auto menuItems{ SettingsNav().MenuItems() };
|
||||
menuItems.IndexOf(selectedItem, index);
|
||||
menuItems.RemoveAt(index);
|
||||
|
||||
// navigate to the profile next to this one
|
||||
const auto newSelectedItem{ menuItems.GetAt(index < menuItems.Size() - 1 ? index : index - 1) };
|
||||
SettingsNav().SelectedItem(newSelectedItem);
|
||||
_Navigate(newSelectedItem.try_as<MUX::Controls::NavigationViewItem>().Tag().try_as<Editor::ProfileViewModel>());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,9 +35,11 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|||
|
||||
void _InitializeProfilesList();
|
||||
void _CreateAndNavigateToNewProfile(const uint32_t index);
|
||||
winrt::Microsoft::UI::Xaml::Controls::NavigationViewItem _CreateProfileNavViewItem(const ProfileViewModel& profile);
|
||||
winrt::Microsoft::UI::Xaml::Controls::NavigationViewItem _CreateProfileNavViewItem(const Editor::ProfileViewModel& profile);
|
||||
void _DeleteProfile(const Windows::Foundation::IInspectable sender, const Editor::DeleteProfileEventArgs& args);
|
||||
|
||||
void _Navigate(hstring clickedItemTag);
|
||||
void _Navigate(const Editor::ProfileViewModel& profile);
|
||||
void _RefreshCurrentPage();
|
||||
};
|
||||
}
|
||||
|
|
|
@ -44,6 +44,9 @@
|
|||
<ClInclude Include="FontWeightConverter.h">
|
||||
<DependentUpon>FontWeightConverter.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="InvertedBooleanToVisibilityConverter.h">
|
||||
<DependentUpon>InvertedBooleanToVisibilityConverter.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="PercentageConverter.h">
|
||||
<DependentUpon>PercentageConverter.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
|
@ -114,6 +117,9 @@
|
|||
<ClCompile Include="FontWeightConverter.cpp">
|
||||
<DependentUpon>FontWeightConverter.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="InvertedBooleanToVisibilityConverter.cpp">
|
||||
<DependentUpon>InvertedBooleanToVisibilityConverter.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="PercentageConverter.cpp">
|
||||
<DependentUpon>PercentageConverter.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
|
@ -151,6 +157,7 @@
|
|||
<Midl Include="ColorToBrushConverter.idl" />
|
||||
<Midl Include="ColorToHexConverter.idl" />
|
||||
<Midl Include="FontWeightConverter.idl" />
|
||||
<Midl Include="InvertedBooleanToVisibilityConverter.idl" />
|
||||
<Midl Include="PercentageConverter.idl" />
|
||||
<Midl Include="EnumEntry.idl" />
|
||||
<Midl Include="GlobalAppearance.idl">
|
||||
|
|
|
@ -25,6 +25,9 @@
|
|||
<Midl Include="FontWeightConverter.idl">
|
||||
<Filter>Converters</Filter>
|
||||
</Midl>
|
||||
<Midl Include="InvertedBooleanToVisibilityConverter.idl">
|
||||
<Filter>Converters</Filter>
|
||||
</Midl>
|
||||
<Midl Include="PercentageConverter.idl">
|
||||
<Filter>Converters</Filter>
|
||||
</Midl>
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
using namespace winrt::Windows::UI::Text;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace winrt::Windows::UI::Xaml::Controls;
|
||||
using namespace winrt::Windows::UI::Xaml::Navigation;
|
||||
using namespace winrt::Windows::Foundation;
|
||||
using namespace winrt::Windows::Foundation::Collections;
|
||||
|
@ -18,8 +19,42 @@ using namespace winrt::Windows::Storage::AccessCache;
|
|||
using namespace winrt::Windows::Storage::Pickers;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
|
||||
static const std::array<winrt::guid, 2> InBoxProfileGuids{
|
||||
winrt::guid{ 0x61c54bbd, 0xc2c6, 0x5271, { 0x96, 0xe7, 0x00, 0x9a, 0x87, 0xff, 0x44, 0xbf } }, // Windows Powershell
|
||||
winrt::guid{ 0x0caa0dad, 0x35be, 0x5f56, { 0xa8, 0xff, 0xaf, 0xce, 0xee, 0xaa, 0x61, 0x01 } } // Command Prompt
|
||||
};
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
bool ProfileViewModel::CanDeleteProfile() const
|
||||
{
|
||||
const auto guid{ Guid() };
|
||||
if (IsBaseLayer())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (std::find(std::begin(InBoxProfileGuids), std::end(InBoxProfileGuids), guid) != std::end(InBoxProfileGuids))
|
||||
{
|
||||
// in-box profile
|
||||
return false;
|
||||
}
|
||||
else if (!Source().empty())
|
||||
{
|
||||
// dynamic profile
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void ProfilePageNavigationState::DeleteProfile()
|
||||
{
|
||||
auto deleteProfileArgs{ winrt::make_self<DeleteProfileEventArgs>(_Profile.Guid()) };
|
||||
_DeleteProfileHandlers(*this, *deleteProfileArgs);
|
||||
}
|
||||
|
||||
Profiles::Profiles() :
|
||||
_ColorSchemeList{ single_threaded_observable_vector<ColorScheme>() }
|
||||
{
|
||||
|
@ -64,6 +99,21 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|||
{
|
||||
biButton.IsChecked(biButton.Tag().as<int32_t>() == biAlignmentVal);
|
||||
}
|
||||
|
||||
// Set the text disclaimer for the text box
|
||||
hstring disclaimer{};
|
||||
const auto guid{ _State.Profile().Guid() };
|
||||
if (std::find(std::begin(InBoxProfileGuids), std::end(InBoxProfileGuids), guid) != std::end(InBoxProfileGuids))
|
||||
{
|
||||
// load disclaimer for in-box profiles
|
||||
disclaimer = RS_(L"Profile_DeleteButtonDisclaimerInBox");
|
||||
}
|
||||
else if (!_State.Profile().Source().empty())
|
||||
{
|
||||
// load disclaimer for dynamic profiles
|
||||
disclaimer = RS_(L"Profile_DeleteButtonDisclaimerDynamic");
|
||||
}
|
||||
DeleteButtonDisclaimer().Text(disclaimer);
|
||||
}
|
||||
|
||||
ColorScheme Profiles::CurrentColorScheme()
|
||||
|
@ -86,6 +136,12 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|||
_State.Profile().ColorSchemeName(val.Name());
|
||||
}
|
||||
|
||||
void Profiles::DeleteConfirmation_Click(IInspectable const& /*sender*/, RoutedEventArgs const& /*e*/)
|
||||
{
|
||||
auto state{ winrt::get_self<ProfilePageNavigationState>(_State) };
|
||||
state->DeleteProfile();
|
||||
}
|
||||
|
||||
fire_and_forget Profiles::BackgroundImage_Click(IInspectable const&, RoutedEventArgs const&)
|
||||
{
|
||||
auto lifetime = get_strong();
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include "Profiles.g.h"
|
||||
#include "ProfilePageNavigationState.g.h"
|
||||
#include "DeleteProfileEventArgs.g.h"
|
||||
#include "ProfileViewModel.g.h"
|
||||
#include "Utils.h"
|
||||
#include "ViewModelHelpers.h"
|
||||
|
@ -17,6 +18,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|||
ProfileViewModel(const Model::Profile& profile) :
|
||||
_profile{ profile } {}
|
||||
|
||||
bool CanDeleteProfile() const;
|
||||
GETSET_PROPERTY(bool, IsBaseLayer, false);
|
||||
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_profile, Guid);
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_profile, ConnectionType);
|
||||
OBSERVABLE_PROJECTED_SETTING(_profile, Name);
|
||||
|
@ -60,6 +64,19 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|||
Model::Profile _profile;
|
||||
};
|
||||
|
||||
struct DeleteProfileEventArgs :
|
||||
public DeleteProfileEventArgsT<DeleteProfileEventArgs>
|
||||
{
|
||||
public:
|
||||
DeleteProfileEventArgs(guid profileGuid) :
|
||||
_ProfileGuid(profileGuid) {}
|
||||
|
||||
guid ProfileGuid() const noexcept { return _ProfileGuid; }
|
||||
|
||||
private:
|
||||
guid _ProfileGuid{};
|
||||
};
|
||||
|
||||
struct ProfilePageNavigationState : ProfilePageNavigationStateT<ProfilePageNavigationState>
|
||||
{
|
||||
public:
|
||||
|
@ -70,9 +87,12 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|||
{
|
||||
}
|
||||
|
||||
void DeleteProfile();
|
||||
|
||||
Windows::Foundation::Collections::IMapView<hstring, Model::ColorScheme> Schemes() { return _Schemes; }
|
||||
void Schemes(const Windows::Foundation::Collections::IMapView<hstring, Model::ColorScheme>& val) { _Schemes = val; }
|
||||
|
||||
TYPED_EVENT(DeleteProfile, Editor::ProfilePageNavigationState, Editor::DeleteProfileEventArgs);
|
||||
GETSET_PROPERTY(IHostedInWindow, WindowRoot, nullptr);
|
||||
GETSET_PROPERTY(Editor::ProfileViewModel, Profile, nullptr);
|
||||
|
||||
|
@ -95,6 +115,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|||
fire_and_forget StartingDirectory_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
|
||||
fire_and_forget Icon_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
|
||||
void BIAlignment_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
|
||||
void DeleteConfirmation_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
|
||||
|
||||
// CursorShape visibility logic
|
||||
void CursorShape_Changed(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e);
|
||||
|
|
|
@ -10,6 +10,9 @@ namespace Microsoft.Terminal.Settings.Editor
|
|||
{
|
||||
runtimeclass ProfileViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged
|
||||
{
|
||||
Boolean CanDeleteProfile { get; };
|
||||
Boolean IsBaseLayer;
|
||||
|
||||
OBSERVABLE_PROJECTED_SETTING(String, Name);
|
||||
OBSERVABLE_PROJECTED_SETTING(Guid, Guid);
|
||||
OBSERVABLE_PROJECTED_SETTING(String, Source);
|
||||
|
@ -50,12 +53,19 @@ namespace Microsoft.Terminal.Settings.Editor
|
|||
OBSERVABLE_PROJECTED_SETTING(Microsoft.Terminal.Settings.Model.BellStyle, BellStyle);
|
||||
}
|
||||
|
||||
runtimeclass DeleteProfileEventArgs
|
||||
{
|
||||
Guid ProfileGuid { get; };
|
||||
}
|
||||
|
||||
runtimeclass ProfilePageNavigationState
|
||||
{
|
||||
Windows.Foundation.Collections.IMapView<String, Microsoft.Terminal.Settings.Model.ColorScheme> Schemes;
|
||||
IHostedInWindow WindowRoot; // necessary to send the right HWND into the file picker dialogs.
|
||||
|
||||
ProfileViewModel Profile { get; };
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<ProfilePageNavigationState, DeleteProfileEventArgs> DeleteProfile;
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass Profiles : Windows.UI.Xaml.Controls.Page, Windows.UI.Xaml.Data.INotifyPropertyChanged
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -19,9 +19,8 @@ the MIT License. See LICENSE in the project root for license information. -->
|
|||
<ScrollViewer>
|
||||
<StackPanel Style="{StaticResource SettingsStackStyle}">
|
||||
<TextBlock x:Uid="Globals_RenderingDisclaimer"
|
||||
FontSize="{StaticResource StandardFontSize}"
|
||||
FontStyle="Italic"
|
||||
Margin="0,0,0,20"/>
|
||||
Style="{StaticResource DisclaimerStyle}"
|
||||
Margin="{StaticResource StandardControlSpacing}"/>
|
||||
|
||||
<!--Force Full Repaint-->
|
||||
<ContentPresenter Style="{StaticResource SettingContainerStyle}">
|
||||
|
|
|
@ -722,6 +722,27 @@
|
|||
<data name="Profile_BellStyleVisual.Content" xml:space="preserve">
|
||||
<value>Visual (Flash Taskbar)</value>
|
||||
</data>
|
||||
<data name="ColorScheme_AddNewButton.Text" xml:space="preserve">
|
||||
<value>Add new</value>
|
||||
</data>
|
||||
<data name="ColorScheme_DeleteButton.Text" xml:space="preserve">
|
||||
<value>Delete color scheme</value>
|
||||
</data>
|
||||
<data name="ColorScheme_Name.Header" xml:space="preserve">
|
||||
<value>Name</value>
|
||||
</data>
|
||||
<data name="ColorScheme_Name.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
|
||||
<value>The name of the color scheme.</value>
|
||||
</data>
|
||||
<data name="Profile_DeleteButton.Text" xml:space="preserve">
|
||||
<value>Delete profile</value>
|
||||
</data>
|
||||
<data name="Profile_Name.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
|
||||
<value>The name of the profile that appears in the dropdown.</value>
|
||||
</data>
|
||||
<data name="Profile_Name.Header" xml:space="preserve">
|
||||
<value>Name</value>
|
||||
</data>
|
||||
<data name="Profile_AcrylicHeader.Text" xml:space="preserve">
|
||||
<value>Acrylic</value>
|
||||
</data>
|
||||
|
@ -741,4 +762,40 @@
|
|||
<value>Open your settings.json file. Alt+Click to open your defaults.json file.</value>
|
||||
<comment>{Locked="settings.json"}, {Locked="defaults.json"}</comment>
|
||||
</data>
|
||||
<data name="ColorScheme_DeleteButton.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
|
||||
<value>Delete the current color scheme.</value>
|
||||
</data>
|
||||
<data name="Rename.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
|
||||
<value>Rename</value>
|
||||
</data>
|
||||
<data name="Profile_DeleteButtonDisclaimerInBox" xml:space="preserve">
|
||||
<value>This profile cannot be deleted because it is included by default.</value>
|
||||
</data>
|
||||
<data name="Profile_DeleteButtonDisclaimerDynamic" xml:space="preserve">
|
||||
<value>This profile cannot be deleted because it is automatically generated.</value>
|
||||
</data>
|
||||
<data name="ColorScheme_DeleteButtonDisclaimerInBox" xml:space="preserve">
|
||||
<value>This color scheme cannot be deleted or renamed because it is included by default.</value>
|
||||
</data>
|
||||
<data name="ColorScheme_DeleteConfirmationButton.Content" xml:space="preserve">
|
||||
<value>Yes, delete color scheme</value>
|
||||
</data>
|
||||
<data name="ColorScheme_DeleteConfirmationMessage.Text" xml:space="preserve">
|
||||
<value>Are you sure you want to delete this color scheme?</value>
|
||||
</data>
|
||||
<data name="RenameAccept.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
|
||||
<value>Accept rename</value>
|
||||
</data>
|
||||
<data name="RenameCancel.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
|
||||
<value>Cancel rename</value>
|
||||
</data>
|
||||
<data name="Profile_BaseLayerDisclaimer.Text" xml:space="preserve">
|
||||
<value>Settings defined here will apply to all profiles unless they are overridden by a profile's settings.</value>
|
||||
</data>
|
||||
<data name="Profile_DeleteConfirmationButton.Content" xml:space="preserve">
|
||||
<value>Yes, delete profile</value>
|
||||
</data>
|
||||
<data name="Profile_DeleteConfirmationMessage.Text" xml:space="preserve">
|
||||
<value>Are you sure you want to delete this profile?</value>
|
||||
</data>
|
||||
</root>
|
|
@ -231,8 +231,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
winrt::hstring SplitPaneArgs::GenerateName() const
|
||||
{
|
||||
// The string will be similar to the following:
|
||||
// * "Duplicate pane[, split: <direction>][, new terminal arguments...]"
|
||||
// * "Split pane[, split: <direction>][, new terminal arguments...]"
|
||||
// * "Duplicate pane[, split: <direction>][, size: <size>%][, new terminal arguments...]"
|
||||
// * "Split pane[, split: <direction>][, size: <size>%][, new terminal arguments...]"
|
||||
//
|
||||
// Direction will only be added to the string if the split direction is
|
||||
// not "auto".
|
||||
|
@ -262,6 +262,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
break;
|
||||
}
|
||||
|
||||
if (_SplitSize != .5f)
|
||||
{
|
||||
ss << L"size: " << (_SplitSize * 100) << L"%, ";
|
||||
}
|
||||
|
||||
winrt::hstring newTerminalArgsStr;
|
||||
if (_TerminalArgs)
|
||||
{
|
||||
|
@ -287,6 +292,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
return RS_(L"OpenDefaultSettingsCommandKey");
|
||||
case SettingsTarget::AllFiles:
|
||||
return RS_(L"OpenBothSettingsFilesCommandKey");
|
||||
case SettingsTarget::SettingsUI:
|
||||
return RS_(L"OpenSettingsUICommandKey");
|
||||
default:
|
||||
return RS_(L"OpenSettingsCommandKey");
|
||||
}
|
||||
|
|
|
@ -379,6 +379,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
struct SplitPaneArgs : public SplitPaneArgsT<SplitPaneArgs>
|
||||
{
|
||||
SplitPaneArgs() = default;
|
||||
SplitPaneArgs(SplitState style, double size, const Model::NewTerminalArgs& terminalArgs) :
|
||||
_SplitStyle{ style },
|
||||
_SplitSize{ size },
|
||||
_TerminalArgs{ terminalArgs } {};
|
||||
SplitPaneArgs(SplitState style, const Model::NewTerminalArgs& terminalArgs) :
|
||||
_SplitStyle{ style },
|
||||
_TerminalArgs{ terminalArgs } {};
|
||||
|
@ -387,9 +391,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
GETSET_PROPERTY(SplitState, SplitStyle, SplitState::Automatic);
|
||||
GETSET_PROPERTY(Model::NewTerminalArgs, TerminalArgs, nullptr);
|
||||
GETSET_PROPERTY(SplitType, SplitMode, SplitType::Manual);
|
||||
GETSET_PROPERTY(double, SplitSize, .5);
|
||||
|
||||
static constexpr std::string_view SplitKey{ "split" };
|
||||
static constexpr std::string_view SplitModeKey{ "splitMode" };
|
||||
static constexpr std::string_view SplitSizeKey{ "size" };
|
||||
|
||||
public:
|
||||
hstring GenerateName() const;
|
||||
|
@ -402,6 +408,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
return otherAsUs->_SplitStyle == _SplitStyle &&
|
||||
(otherAsUs->_TerminalArgs ? otherAsUs->_TerminalArgs.Equals(_TerminalArgs) :
|
||||
otherAsUs->_TerminalArgs == _TerminalArgs) &&
|
||||
otherAsUs->_SplitSize == _SplitSize &&
|
||||
otherAsUs->_SplitMode == _SplitMode;
|
||||
}
|
||||
return false;
|
||||
|
@ -413,6 +420,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
args->_TerminalArgs = NewTerminalArgs::FromJson(json);
|
||||
JsonUtils::GetValueForKey(json, SplitKey, args->_SplitStyle);
|
||||
JsonUtils::GetValueForKey(json, SplitModeKey, args->_SplitMode);
|
||||
JsonUtils::GetValueForKey(json, SplitSizeKey, args->_SplitSize);
|
||||
if (args->_SplitSize >= 1 || args->_SplitSize <= 0)
|
||||
{
|
||||
return { nullptr, { SettingsLoadWarnings::InvalidSplitSize } };
|
||||
}
|
||||
return { *args, {} };
|
||||
}
|
||||
IActionArgs Copy() const
|
||||
|
@ -421,6 +433,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
copy->_SplitStyle = _SplitStyle;
|
||||
copy->_TerminalArgs = _TerminalArgs.Copy();
|
||||
copy->_SplitMode = _SplitMode;
|
||||
copy->_SplitSize = _SplitSize;
|
||||
return *copy;
|
||||
}
|
||||
};
|
||||
|
@ -428,6 +441,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
struct OpenSettingsArgs : public OpenSettingsArgsT<OpenSettingsArgs>
|
||||
{
|
||||
OpenSettingsArgs() = default;
|
||||
OpenSettingsArgs(const SettingsTarget& target) :
|
||||
_Target{ target } {}
|
||||
GETSET_PROPERTY(SettingsTarget, Target, SettingsTarget::SettingsFile);
|
||||
|
||||
static constexpr std::string_view TargetKey{ "target" };
|
||||
|
@ -837,4 +852,5 @@ namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
|
|||
BASIC_FACTORY(CloseOtherTabsArgs);
|
||||
BASIC_FACTORY(CloseTabsAfterArgs);
|
||||
BASIC_FACTORY(MoveTabArgs);
|
||||
BASIC_FACTORY(OpenSettingsArgs);
|
||||
}
|
||||
|
|
|
@ -135,16 +135,19 @@ namespace Microsoft.Terminal.Settings.Model
|
|||
|
||||
[default_interface] runtimeclass SplitPaneArgs : IActionArgs
|
||||
{
|
||||
SplitPaneArgs(SplitState split, Double size, NewTerminalArgs terminalArgs);
|
||||
SplitPaneArgs(SplitState split, NewTerminalArgs terminalArgs);
|
||||
SplitPaneArgs(SplitType splitMode);
|
||||
|
||||
SplitState SplitStyle { get; };
|
||||
NewTerminalArgs TerminalArgs { get; };
|
||||
SplitType SplitMode { get; };
|
||||
Double SplitSize { get; };
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass OpenSettingsArgs : IActionArgs
|
||||
{
|
||||
OpenSettingsArgs(SettingsTarget target);
|
||||
SettingsTarget Target { get; };
|
||||
};
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "ColorScheme.h"
|
||||
#include "DefaultSettings.h"
|
||||
#include "../../types/inc/Utils.hpp"
|
||||
#include "../../types/inc/colorTable.hpp"
|
||||
#include "Utils.h"
|
||||
#include "JsonUtils.h"
|
||||
|
||||
|
@ -41,14 +42,18 @@ static constexpr std::array<std::string_view, 16> TableColors = {
|
|||
};
|
||||
|
||||
ColorScheme::ColorScheme() :
|
||||
_Foreground{ DEFAULT_FOREGROUND_WITH_ALPHA },
|
||||
_Background{ DEFAULT_BACKGROUND_WITH_ALPHA },
|
||||
_SelectionBackground{ DEFAULT_FOREGROUND },
|
||||
_CursorColor{ DEFAULT_CURSOR_COLOR }
|
||||
ColorScheme(L"", DEFAULT_FOREGROUND_WITH_ALPHA, DEFAULT_BACKGROUND_WITH_ALPHA, DEFAULT_CURSOR_COLOR)
|
||||
{
|
||||
Utils::InitializeCampbellColorTable(_table);
|
||||
}
|
||||
|
||||
ColorScheme::ColorScheme(winrt::hstring name, Color defaultFg, Color defaultBg, Color cursorColor) :
|
||||
ColorScheme::ColorScheme(winrt::hstring name) :
|
||||
ColorScheme(name, DEFAULT_FOREGROUND_WITH_ALPHA, DEFAULT_BACKGROUND_WITH_ALPHA, DEFAULT_CURSOR_COLOR)
|
||||
{
|
||||
Utils::InitializeCampbellColorTable(_table);
|
||||
}
|
||||
|
||||
ColorScheme::ColorScheme(winrt::hstring name, COLORREF defaultFg, COLORREF defaultBg, COLORREF cursorColor) :
|
||||
_Name{ name },
|
||||
_Foreground{ defaultFg },
|
||||
_Background{ defaultBg },
|
||||
|
|
|
@ -33,7 +33,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
{
|
||||
public:
|
||||
ColorScheme();
|
||||
ColorScheme(hstring name, Windows::UI::Color defaultFg, Windows::UI::Color defaultBg, Windows::UI::Color cursorColor);
|
||||
ColorScheme(hstring name);
|
||||
ColorScheme(hstring name, COLORREF defaultFg, COLORREF defaultBg, COLORREF cursorColor);
|
||||
com_ptr<ColorScheme> Copy() const;
|
||||
|
||||
static com_ptr<ColorScheme> FromJson(const Json::Value& json);
|
||||
|
@ -60,3 +61,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
friend class SettingsModelLocalTests::ColorSchemeTests;
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
|
||||
{
|
||||
BASIC_FACTORY(ColorScheme);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
namespace Microsoft.Terminal.Settings.Model
|
||||
{
|
||||
[default_interface] runtimeclass ColorScheme {
|
||||
ColorScheme(String name);
|
||||
|
||||
String Name;
|
||||
|
||||
Windows.UI.Color Foreground;
|
||||
|
|
|
@ -334,6 +334,11 @@ void GlobalAppSettings::AddColorScheme(const Model::ColorScheme& scheme)
|
|||
_colorSchemes.Insert(scheme.Name(), scheme);
|
||||
}
|
||||
|
||||
void GlobalAppSettings::RemoveColorScheme(hstring schemeName)
|
||||
{
|
||||
_colorSchemes.TryRemove(schemeName);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Return the warnings that we've collected during parsing the JSON for the
|
||||
// keybindings. It's possible that the user provided keybindings have some
|
||||
|
|
|
@ -40,6 +40,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
|
||||
Windows::Foundation::Collections::IMapView<hstring, Model::ColorScheme> ColorSchemes() noexcept;
|
||||
void AddColorScheme(const Model::ColorScheme& scheme);
|
||||
void RemoveColorScheme(hstring schemeName);
|
||||
|
||||
Model::KeyMapping KeyMap() const noexcept;
|
||||
|
||||
|
|
|
@ -137,6 +137,7 @@ namespace Microsoft.Terminal.Settings.Model
|
|||
|
||||
Windows.Foundation.Collections.IMapView<String, ColorScheme> ColorSchemes();
|
||||
void AddColorScheme(ColorScheme scheme);
|
||||
void RemoveColorScheme(String schemeName);
|
||||
|
||||
KeyMapping KeyMap();
|
||||
|
||||
|
|
|
@ -363,4 +363,7 @@
|
|||
<data name="BreakIntoDebuggerCommandKey" xml:space="preserve">
|
||||
<value>Break into the debugger</value>
|
||||
</data>
|
||||
<data name="OpenSettingsUICommandKey" xml:space="preserve">
|
||||
<value>Open Settings...</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
|
@ -19,6 +19,7 @@ namespace Microsoft.Terminal.Settings.Model
|
|||
FailedToParseCommandJson = 9,
|
||||
FailedToWriteToSettings = 10,
|
||||
InvalidColorSchemeInCmd = 11,
|
||||
InvalidSplitSize = 12,
|
||||
WARNINGS_SIZE // IMPORTANT: This MUST be the last value in this enum. It's an unused placeholder.
|
||||
};
|
||||
|
||||
|
|
|
@ -3529,7 +3529,7 @@ void ConptyRoundtripTests::HyperlinkIdConsistency()
|
|||
|
||||
auto verifyData = [](TextBuffer& tb) {
|
||||
// Check that all the linked cells still have the same ID
|
||||
auto attrRow = tb.GetRowByOffset(0).GetAttrRow();
|
||||
auto& attrRow = tb.GetRowByOffset(0).GetAttrRow();
|
||||
auto id = attrRow.GetAttrByColumn(0).GetHyperlinkId();
|
||||
for (auto i = 1; i < 4; ++i)
|
||||
{
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="AuditMode|Win32">
|
||||
<ProjectConfiguration Include="AuditMode|Win32">
|
||||
<Configuration>AuditMode</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
|
@ -90,7 +90,7 @@
|
|||
<PrecompiledHeaderFile>precomp.h</PrecompiledHeaderFile>
|
||||
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\src\inc;$(SolutionDir)\dep;$(SolutionDir)\dep\Console;$(SolutionDir)\dep\Win32K;$(SolutionDir)\dep\gsl\include;$(SolutionDir)\dep\wil\include;$(SolutionDir)\oss\chromium;$(SolutionDir)\oss\fmt\include;$(SolutionDir)\oss\dynamic_bitset;$(SolutionDir)\oss\libpopcnt;$(SolutionDir)\oss\interval_tree;%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\src\inc;$(SolutionDir)\dep;$(SolutionDir)\dep\Console;$(SolutionDir)\dep\Win32K;$(SolutionDir)\dep\gsl\include;$(SolutionDir)\dep\wil\include;$(SolutionDir)\oss\chromium;$(SolutionDir)\oss\fmt\include;$(SolutionDir)\oss\dynamic_bitset;$(SolutionDir)\oss\libpopcnt;$(SolutionDir)\oss\interval_tree;$(SolutionDir)\oss\boost\boost_1_73_0;%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
|
|
|
@ -88,6 +88,8 @@ TRACELOGGING_DECLARE_PROVIDER(g_hConhostV2EventTraceProvider);
|
|||
#include "../inc/operators.hpp"
|
||||
#include "../inc/conattrs.hpp"
|
||||
|
||||
#include "boost/container/small_vector.hpp"
|
||||
|
||||
// TODO: MSFT 9355094 Find a better way of doing this. http://osgvsowi/9355094
|
||||
[[nodiscard]] inline NTSTATUS NTSTATUS_FROM_HRESULT(HRESULT hr)
|
||||
{
|
||||
|
|
|
@ -235,6 +235,24 @@ class AttrRowTests
|
|||
return NoThrowString().Format(L"%wc%d", run.GetAttributes().GetLegacyAttributes(), run.GetLength());
|
||||
}
|
||||
|
||||
void LogChain(_In_ PCWSTR pwszPrefix,
|
||||
boost::container::small_vector_base<TextAttributeRun>& chain)
|
||||
{
|
||||
NoThrowString str(pwszPrefix);
|
||||
|
||||
if (chain.size() > 0)
|
||||
{
|
||||
str.Append(LogRunElement(chain[0]));
|
||||
|
||||
for (size_t i = 1; i < chain.size(); i++)
|
||||
{
|
||||
str.AppendFormat(L"->%s", (const wchar_t*)(LogRunElement(chain[i])));
|
||||
}
|
||||
}
|
||||
|
||||
Log::Comment(str);
|
||||
}
|
||||
|
||||
void LogChain(_In_ PCWSTR pwszPrefix,
|
||||
std::vector<TextAttributeRun>& chain)
|
||||
{
|
||||
|
|
|
@ -80,19 +80,10 @@ public:
|
|||
|
||||
typedef struct _RegPropertyMap
|
||||
{
|
||||
_RegPropertyType const propertyType;
|
||||
_RegPropertyType propertyType;
|
||||
PCWSTR pwszValueName;
|
||||
DWORD const dwFieldOffset;
|
||||
size_t const cbFieldSize;
|
||||
_RegPropertyMap(
|
||||
_RegPropertyType const propertyType,
|
||||
PCWSTR pwszValueName,
|
||||
DWORD const dwFieldOffset,
|
||||
size_t const cbFieldSize) :
|
||||
propertyType(propertyType),
|
||||
pwszValueName(pwszValueName),
|
||||
dwFieldOffset(dwFieldOffset),
|
||||
cbFieldSize(cbFieldSize){};
|
||||
DWORD dwFieldOffset;
|
||||
size_t cbFieldSize;
|
||||
|
||||
_RegPropertyMap& operator=(const _RegPropertyMap&) { return *this; }
|
||||
} RegPropertyMap;
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "precomp.h"
|
||||
|
||||
#include "CustomTextLayout.h"
|
||||
#include "CustomTextRenderer.h"
|
||||
|
||||
#include <wrl.h>
|
||||
#include <wrl/client.h>
|
||||
|
@ -19,19 +20,27 @@ using namespace Microsoft::Console::Render;
|
|||
// - factory - DirectWrite factory reference in case we need other DirectWrite objects for our layout
|
||||
// - analyzer - DirectWrite text analyzer from the factory that has been cached at a level above this layout (expensive to create)
|
||||
// - format - The DirectWrite format object representing the size and other text properties to be applied (by default) to a layout
|
||||
// - formatItalic - The italic variant of the format object representing the size and other text properties for italic text
|
||||
// - font - The DirectWrite font face to use while calculating layout (by default, will fallback if necessary)
|
||||
// - fontItalic - The italic variant of the font face to use while calculating layout for italic text
|
||||
// - width - The count of pixels available per column (the expected pixel width of every column)
|
||||
// - boxEffect - Box drawing scaling effects that are cached for the base font across layouts.
|
||||
CustomTextLayout::CustomTextLayout(gsl::not_null<IDWriteFactory1*> const factory,
|
||||
gsl::not_null<IDWriteTextAnalyzer1*> const analyzer,
|
||||
gsl::not_null<IDWriteTextFormat*> const format,
|
||||
gsl::not_null<IDWriteTextFormat*> const formatItalic,
|
||||
gsl::not_null<IDWriteFontFace1*> const font,
|
||||
gsl::not_null<IDWriteFontFace1*> const fontItalic,
|
||||
size_t const width,
|
||||
IBoxDrawingEffect* const boxEffect) :
|
||||
_factory{ factory.get() },
|
||||
_analyzer{ analyzer.get() },
|
||||
_format{ format.get() },
|
||||
_formatItalic{ formatItalic.get() },
|
||||
_formatInUse{ format.get() },
|
||||
_font{ font.get() },
|
||||
_fontItalic{ fontItalic.get() },
|
||||
_fontInUse{ font.get() },
|
||||
_boxDrawingEffect{ boxEffect },
|
||||
_localeName{},
|
||||
_numberSubstitution{},
|
||||
|
@ -113,6 +122,9 @@ CATCH_RETURN()
|
|||
RETURN_HR_IF_NULL(E_INVALIDARG, columns);
|
||||
*columns = 0;
|
||||
|
||||
_formatInUse = _format.Get();
|
||||
_fontInUse = _font.Get();
|
||||
|
||||
RETURN_IF_FAILED(_AnalyzeTextComplexity());
|
||||
RETURN_IF_FAILED(_AnalyzeRuns());
|
||||
RETURN_IF_FAILED(_ShapeGlyphRuns());
|
||||
|
@ -144,6 +156,10 @@ CATCH_RETURN()
|
|||
FLOAT originX,
|
||||
FLOAT originY) noexcept
|
||||
{
|
||||
const auto drawingContext = static_cast<const DrawingContext*>(clientDrawingContext);
|
||||
_formatInUse = drawingContext->useItalicFont ? _formatItalic.Get() : _format.Get();
|
||||
_fontInUse = drawingContext->useItalicFont ? _fontItalic.Get() : _font.Get();
|
||||
|
||||
RETURN_IF_FAILED(_AnalyzeTextComplexity());
|
||||
RETURN_IF_FAILED(_AnalyzeRuns());
|
||||
RETURN_IF_FAILED(_ShapeGlyphRuns());
|
||||
|
@ -183,7 +199,7 @@ CATCH_RETURN()
|
|||
const HRESULT hr = _analyzer->GetTextComplexity(
|
||||
_text.c_str(),
|
||||
textLength,
|
||||
_font.Get(),
|
||||
_fontInUse,
|
||||
&isTextSimple,
|
||||
&uiLengthRead,
|
||||
&_glyphIndices.at(glyphStart));
|
||||
|
@ -240,7 +256,7 @@ CATCH_RETURN()
|
|||
{
|
||||
if (!run.fontFace)
|
||||
{
|
||||
run.fontFace = _font;
|
||||
run.fontFace = _fontInUse;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -360,7 +376,7 @@ CATCH_RETURN()
|
|||
|
||||
USHORT designUnitsPerEm = metrics.designUnitsPerEm;
|
||||
|
||||
RETURN_IF_FAILED(_font->GetDesignGlyphAdvances(
|
||||
RETURN_IF_FAILED(_fontInUse->GetDesignGlyphAdvances(
|
||||
textLength,
|
||||
&_glyphIndices.at(glyphStart),
|
||||
&_glyphDesignUnitAdvances.at(glyphStart),
|
||||
|
@ -368,7 +384,7 @@ CATCH_RETURN()
|
|||
|
||||
for (size_t i = glyphStart; i < _glyphAdvances.size(); i++)
|
||||
{
|
||||
_glyphAdvances.at(i) = (float)_glyphDesignUnitAdvances.at(i) / designUnitsPerEm * _format->GetFontSize() * run.fontScale;
|
||||
_glyphAdvances.at(i) = (float)_glyphDesignUnitAdvances.at(i) / designUnitsPerEm * _formatInUse->GetFontSize() * run.fontScale;
|
||||
}
|
||||
|
||||
// Set all the clusters as sequential. In a simple run, we're going 1 to 1.
|
||||
|
@ -433,7 +449,7 @@ CATCH_RETURN()
|
|||
_glyphAdvances.resize(std::max(gsl::narrow_cast<size_t>(glyphStart) + gsl::narrow_cast<size_t>(actualGlyphCount), _glyphAdvances.size()));
|
||||
_glyphOffsets.resize(std::max(gsl::narrow_cast<size_t>(glyphStart) + gsl::narrow_cast<size_t>(actualGlyphCount), _glyphOffsets.size()));
|
||||
|
||||
const auto fontSizeFormat = _format->GetFontSize();
|
||||
const auto fontSizeFormat = _formatInUse->GetFontSize();
|
||||
const auto fontSize = fontSizeFormat * run.fontScale;
|
||||
|
||||
hr = _analyzer->GetGlyphPlacements(
|
||||
|
@ -913,7 +929,7 @@ CATCH_RETURN();
|
|||
// internal storage representation into something that matches DWrite's structures.
|
||||
DWRITE_GLYPH_RUN glyphRun;
|
||||
glyphRun.bidiLevel = run.bidiLevel;
|
||||
glyphRun.fontEmSize = _format->GetFontSize() * run.fontScale;
|
||||
glyphRun.fontEmSize = _formatInUse->GetFontSize() * run.fontScale;
|
||||
glyphRun.fontFace = run.fontFace.Get();
|
||||
glyphRun.glyphAdvances = &_glyphAdvances.at(run.glyphStart);
|
||||
glyphRun.glyphCount = run.glyphCount;
|
||||
|
@ -1226,7 +1242,7 @@ CATCH_RETURN();
|
|||
{
|
||||
// Get the font fallback first
|
||||
::Microsoft::WRL::ComPtr<IDWriteTextFormat1> format1;
|
||||
if (FAILED(_format.As(&format1)))
|
||||
if (FAILED(_formatInUse->QueryInterface(IID_PPV_ARGS(&format1))))
|
||||
{
|
||||
// If IDWriteTextFormat1 does not exist, return directly as this OS version doesn't have font fallback.
|
||||
return S_FALSE;
|
||||
|
@ -1318,7 +1334,7 @@ CATCH_RETURN();
|
|||
}
|
||||
else
|
||||
{
|
||||
run.fontFace = _font;
|
||||
run.fontFace = _fontInUse;
|
||||
}
|
||||
|
||||
// Store the font scale as well.
|
||||
|
@ -1458,7 +1474,7 @@ try
|
|||
else
|
||||
{
|
||||
::Microsoft::WRL::ComPtr<IBoxDrawingEffect> eff;
|
||||
RETURN_IF_FAILED(s_CalculateBoxEffect(_format.Get(), _width, run.fontFace.Get(), run.fontScale, &eff));
|
||||
RETURN_IF_FAILED(s_CalculateBoxEffect(_formatInUse, _width, run.fontFace.Get(), run.fontScale, &eff));
|
||||
|
||||
// store data in the run
|
||||
run.drawingEffect = std::move(eff);
|
||||
|
|
|
@ -22,8 +22,10 @@ namespace Microsoft::Console::Render
|
|||
|
||||
CustomTextLayout(gsl::not_null<IDWriteFactory1*> const factory,
|
||||
gsl::not_null<IDWriteTextAnalyzer1*> const analyzer,
|
||||
gsl::not_null<IDWriteTextFormat*> const format,
|
||||
gsl::not_null<IDWriteFontFace1*> const font,
|
||||
gsl::not_null<IDWriteTextFormat*> const normalFormat,
|
||||
gsl::not_null<IDWriteTextFormat*> const italicFormat,
|
||||
gsl::not_null<IDWriteFontFace1*> const normalFont,
|
||||
gsl::not_null<IDWriteFontFace1*> const italicFont,
|
||||
size_t const width,
|
||||
IBoxDrawingEffect* const boxEffect);
|
||||
|
||||
|
@ -160,11 +162,15 @@ namespace Microsoft::Console::Render
|
|||
// DirectWrite analyzer
|
||||
const ::Microsoft::WRL::ComPtr<IDWriteTextAnalyzer1> _analyzer;
|
||||
|
||||
// DirectWrite text format
|
||||
// DirectWrite text formats
|
||||
const ::Microsoft::WRL::ComPtr<IDWriteTextFormat> _format;
|
||||
const ::Microsoft::WRL::ComPtr<IDWriteTextFormat> _formatItalic;
|
||||
IDWriteTextFormat* _formatInUse;
|
||||
|
||||
// DirectWrite font face
|
||||
// DirectWrite font faces
|
||||
const ::Microsoft::WRL::ComPtr<IDWriteFontFace1> _font;
|
||||
const ::Microsoft::WRL::ComPtr<IDWriteFontFace1> _fontItalic;
|
||||
IDWriteFontFace1* _fontInUse;
|
||||
|
||||
// Box drawing effect
|
||||
const ::Microsoft::WRL::ComPtr<IBoxDrawingEffect> _boxDrawingEffect;
|
||||
|
|
|
@ -24,6 +24,7 @@ namespace Microsoft::Console::Render
|
|||
renderTarget(renderTarget),
|
||||
foregroundBrush(foregroundBrush),
|
||||
backgroundBrush(backgroundBrush),
|
||||
useItalicFont(false),
|
||||
forceGrayscaleAA(forceGrayscaleAA),
|
||||
dwriteFactory(dwriteFactory),
|
||||
spacing(spacing),
|
||||
|
@ -37,6 +38,7 @@ namespace Microsoft::Console::Render
|
|||
ID2D1RenderTarget* renderTarget;
|
||||
ID2D1SolidColorBrush* foregroundBrush;
|
||||
ID2D1SolidColorBrush* backgroundBrush;
|
||||
bool useItalicFont;
|
||||
bool forceGrayscaleAA;
|
||||
IDWriteFactory* dwriteFactory;
|
||||
DWRITE_LINE_SPACING spacing;
|
||||
|
|
|
@ -1909,6 +1909,7 @@ CATCH_RETURN()
|
|||
|
||||
// If we have a drawing context, it may be choosing its antialiasing based
|
||||
// on the colors. Update it if it exists.
|
||||
// Also record whether we need to render the text with an italic font.
|
||||
// We only need to do this here because this is called all the time on painting frames
|
||||
// and will update it in a timely fashion. Changing the AA mode or opacity do affect
|
||||
// it, but we will always hit updating the drawing brushes so we don't
|
||||
|
@ -1916,6 +1917,7 @@ CATCH_RETURN()
|
|||
if (_drawingContext)
|
||||
{
|
||||
_drawingContext->forceGrayscaleAA = _ShouldForceGrayscaleAA();
|
||||
_drawingContext->useItalicFont = textAttributes.IsItalic();
|
||||
}
|
||||
|
||||
if (textAttributes.IsHyperlink())
|
||||
|
@ -1943,8 +1945,10 @@ try
|
|||
fiFontInfo,
|
||||
_dpi,
|
||||
_dwriteTextFormat,
|
||||
_dwriteTextFormatItalic,
|
||||
_dwriteTextAnalyzer,
|
||||
_dwriteFontFace,
|
||||
_dwriteFontFaceItalic,
|
||||
_lineMetrics));
|
||||
|
||||
_glyphCell = fiFontInfo.GetSize();
|
||||
|
@ -1952,8 +1956,15 @@ try
|
|||
// Calculate and cache the box effect for the base font. Scale is 1.0f because the base font is exactly the scale we want already.
|
||||
RETURN_IF_FAILED(CustomTextLayout::s_CalculateBoxEffect(_dwriteTextFormat.Get(), _glyphCell.width(), _dwriteFontFace.Get(), 1.0f, &_boxDrawingEffect));
|
||||
|
||||
// Prepare the text layout
|
||||
_customLayout = WRL::Make<CustomTextLayout>(_dwriteFactory.Get(), _dwriteTextAnalyzer.Get(), _dwriteTextFormat.Get(), _dwriteFontFace.Get(), _glyphCell.width(), _boxDrawingEffect.Get());
|
||||
// Prepare the text layout.
|
||||
_customLayout = WRL::Make<CustomTextLayout>(_dwriteFactory.Get(),
|
||||
_dwriteTextAnalyzer.Get(),
|
||||
_dwriteTextFormat.Get(),
|
||||
_dwriteTextFormatItalic.Get(),
|
||||
_dwriteFontFace.Get(),
|
||||
_dwriteFontFaceItalic.Get(),
|
||||
_glyphCell.width(),
|
||||
_boxDrawingEffect.Get());
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
@ -2033,16 +2044,20 @@ float DxEngine::GetScaling() const noexcept
|
|||
int const iDpi) noexcept
|
||||
{
|
||||
Microsoft::WRL::ComPtr<IDWriteTextFormat> format;
|
||||
Microsoft::WRL::ComPtr<IDWriteTextFormat> formatItalic;
|
||||
Microsoft::WRL::ComPtr<IDWriteTextAnalyzer1> analyzer;
|
||||
Microsoft::WRL::ComPtr<IDWriteFontFace1> face;
|
||||
Microsoft::WRL::ComPtr<IDWriteFontFace1> faceItalic;
|
||||
LineMetrics lineMetrics;
|
||||
|
||||
return _GetProposedFont(pfiFontInfoDesired,
|
||||
pfiFontInfo,
|
||||
iDpi,
|
||||
format,
|
||||
formatItalic,
|
||||
analyzer,
|
||||
face,
|
||||
faceItalic,
|
||||
lineMetrics);
|
||||
}
|
||||
|
||||
|
@ -2311,8 +2326,10 @@ CATCH_RETURN();
|
|||
FontInfo& actual,
|
||||
const int dpi,
|
||||
Microsoft::WRL::ComPtr<IDWriteTextFormat>& textFormat,
|
||||
Microsoft::WRL::ComPtr<IDWriteTextFormat>& textFormatItalic,
|
||||
Microsoft::WRL::ComPtr<IDWriteTextAnalyzer1>& textAnalyzer,
|
||||
Microsoft::WRL::ComPtr<IDWriteFontFace1>& fontFace,
|
||||
Microsoft::WRL::ComPtr<IDWriteFontFace1>& fontFaceItalic,
|
||||
LineMetrics& lineMetrics) const noexcept
|
||||
{
|
||||
try
|
||||
|
@ -2447,11 +2464,33 @@ CATCH_RETURN();
|
|||
|
||||
THROW_IF_FAILED(format.As(&textFormat));
|
||||
|
||||
// We also need to create an italic variant of the font face and text
|
||||
// format, based on the same parameters, but using an italic style.
|
||||
std::wstring fontNameItalic = fontName;
|
||||
DWRITE_FONT_WEIGHT weightItalic = weight;
|
||||
DWRITE_FONT_STYLE styleItalic = DWRITE_FONT_STYLE_ITALIC;
|
||||
DWRITE_FONT_STRETCH stretchItalic = stretch;
|
||||
|
||||
const auto faceItalic = _ResolveFontFaceWithFallback(fontNameItalic, weightItalic, stretchItalic, styleItalic, fontLocaleName);
|
||||
|
||||
Microsoft::WRL::ComPtr<IDWriteTextFormat> formatItalic;
|
||||
THROW_IF_FAILED(_dwriteFactory->CreateTextFormat(fontNameItalic.data(),
|
||||
nullptr,
|
||||
weightItalic,
|
||||
styleItalic,
|
||||
stretchItalic,
|
||||
fontSize,
|
||||
localeName.data(),
|
||||
&formatItalic));
|
||||
|
||||
THROW_IF_FAILED(formatItalic.As(&textFormatItalic));
|
||||
|
||||
Microsoft::WRL::ComPtr<IDWriteTextAnalyzer> analyzer;
|
||||
THROW_IF_FAILED(_dwriteFactory->CreateTextAnalyzer(&analyzer));
|
||||
THROW_IF_FAILED(analyzer.As(&textAnalyzer));
|
||||
|
||||
fontFace = face;
|
||||
fontFaceItalic = faceItalic;
|
||||
|
||||
THROW_IF_FAILED(textFormat->SetLineSpacing(lineSpacing.method, lineSpacing.height, lineSpacing.baseline));
|
||||
THROW_IF_FAILED(textFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_NEAR));
|
||||
|
|
|
@ -196,7 +196,9 @@ namespace Microsoft::Console::Render
|
|||
|
||||
::Microsoft::WRL::ComPtr<IDWriteFactory1> _dwriteFactory;
|
||||
::Microsoft::WRL::ComPtr<IDWriteTextFormat> _dwriteTextFormat;
|
||||
::Microsoft::WRL::ComPtr<IDWriteTextFormat> _dwriteTextFormatItalic;
|
||||
::Microsoft::WRL::ComPtr<IDWriteFontFace1> _dwriteFontFace;
|
||||
::Microsoft::WRL::ComPtr<IDWriteFontFace1> _dwriteFontFaceItalic;
|
||||
::Microsoft::WRL::ComPtr<IDWriteTextAnalyzer1> _dwriteTextAnalyzer;
|
||||
::Microsoft::WRL::ComPtr<CustomTextLayout> _customLayout;
|
||||
::Microsoft::WRL::ComPtr<CustomTextRenderer> _customRenderer;
|
||||
|
@ -317,8 +319,10 @@ namespace Microsoft::Console::Render
|
|||
FontInfo& actual,
|
||||
const int dpi,
|
||||
::Microsoft::WRL::ComPtr<IDWriteTextFormat>& textFormat,
|
||||
::Microsoft::WRL::ComPtr<IDWriteTextFormat>& textFormatItalic,
|
||||
::Microsoft::WRL::ComPtr<IDWriteTextAnalyzer1>& textAnalyzer,
|
||||
::Microsoft::WRL::ComPtr<IDWriteFontFace1>& fontFace,
|
||||
::Microsoft::WRL::ComPtr<IDWriteFontFace1>& fontFaceItalic,
|
||||
LineMetrics& lineMetrics) const noexcept;
|
||||
|
||||
[[nodiscard]] til::size _GetClientSize() const;
|
||||
|
|
|
@ -87,6 +87,7 @@ namespace Microsoft::Console::Render
|
|||
bool _isTrueTypeFont;
|
||||
UINT _fontCodepage;
|
||||
HFONT _hfont;
|
||||
HFONT _hfontItalic;
|
||||
TEXTMETRICW _tmFontMetrics;
|
||||
|
||||
static const size_t s_cPolyTextCache = 80;
|
||||
|
@ -122,6 +123,7 @@ namespace Microsoft::Console::Render
|
|||
|
||||
COLORREF _lastFg;
|
||||
COLORREF _lastBg;
|
||||
bool _lastFontItalic;
|
||||
|
||||
[[nodiscard]] HRESULT _InvalidCombine(const RECT* const prc) noexcept;
|
||||
[[nodiscard]] HRESULT _InvalidOffset(const POINT* const ppt) noexcept;
|
||||
|
@ -152,7 +154,8 @@ namespace Microsoft::Console::Render
|
|||
[[nodiscard]] HRESULT _GetProposedFont(const FontInfoDesired& FontDesired,
|
||||
_Out_ FontInfo& Font,
|
||||
const int iDpi,
|
||||
_Inout_ wil::unique_hfont& hFont) noexcept;
|
||||
_Inout_ wil::unique_hfont& hFont,
|
||||
_Inout_ wil::unique_hfont& hFontItalic) noexcept;
|
||||
|
||||
COORD _GetFontSize() const;
|
||||
bool _IsMinimized() const;
|
||||
|
|
|
@ -29,8 +29,10 @@ GdiEngine::GdiEngine() :
|
|||
_fInvalidRectUsed(false),
|
||||
_lastFg(INVALID_COLOR),
|
||||
_lastBg(INVALID_COLOR),
|
||||
_lastFontItalic(false),
|
||||
_fPaintStarted(false),
|
||||
_hfont((HFONT)INVALID_HANDLE_VALUE)
|
||||
_hfont(nullptr),
|
||||
_hfontItalic(nullptr)
|
||||
{
|
||||
ZeroMemory(_pPolyText, sizeof(POLYTEXTW) * s_cPolyTextCache);
|
||||
_rcInvalid = { 0 };
|
||||
|
@ -88,6 +90,12 @@ GdiEngine::~GdiEngine()
|
|||
_hfont = nullptr;
|
||||
}
|
||||
|
||||
if (_hfontItalic != nullptr)
|
||||
{
|
||||
LOG_HR_IF(E_FAIL, !(DeleteObject(_hfontItalic)));
|
||||
_hfontItalic = nullptr;
|
||||
}
|
||||
|
||||
if (_hdcMemoryContext != nullptr)
|
||||
{
|
||||
LOG_HR_IF(E_FAIL, !(DeleteObject(_hdcMemoryContext)));
|
||||
|
@ -128,6 +136,9 @@ GdiEngine::~GdiEngine()
|
|||
LOG_HR_IF_NULL(E_FAIL, SelectFont(_hdcMemoryContext, _hfont));
|
||||
}
|
||||
|
||||
// Record the fact that the selected font is not italic.
|
||||
_lastFontItalic = false;
|
||||
|
||||
if (nullptr != hdcRealWindow)
|
||||
{
|
||||
LOG_HR_IF(E_FAIL, !(ReleaseDC(_hwndTargetWindow, hdcRealWindow)));
|
||||
|
@ -210,6 +221,14 @@ GdiEngine::~GdiEngine()
|
|||
RETURN_IF_FAILED(s_SetWindowLongWHelper(_hwndTargetWindow, GWL_CONSOLE_BKCOLOR, colorBackground));
|
||||
}
|
||||
|
||||
// If the italic attribute has changed, select an appropriate font variant.
|
||||
const auto fontItalic = textAttributes.IsItalic();
|
||||
if (fontItalic != _lastFontItalic)
|
||||
{
|
||||
SelectFont(_hdcMemoryContext, fontItalic ? _hfontItalic : _hfont);
|
||||
_lastFontItalic = fontItalic;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
@ -223,12 +242,15 @@ GdiEngine::~GdiEngine()
|
|||
// - S_OK if set successfully or relevant GDI error via HRESULT.
|
||||
[[nodiscard]] HRESULT GdiEngine::UpdateFont(const FontInfoDesired& FontDesired, _Out_ FontInfo& Font) noexcept
|
||||
{
|
||||
wil::unique_hfont hFont;
|
||||
RETURN_IF_FAILED(_GetProposedFont(FontDesired, Font, _iCurrentDpi, hFont));
|
||||
wil::unique_hfont hFont, hFontItalic;
|
||||
RETURN_IF_FAILED(_GetProposedFont(FontDesired, Font, _iCurrentDpi, hFont, hFontItalic));
|
||||
|
||||
// Select into DC
|
||||
RETURN_HR_IF_NULL(E_FAIL, SelectFont(_hdcMemoryContext, hFont.get()));
|
||||
|
||||
// Record the fact that the selected font is not italic.
|
||||
_lastFontItalic = false;
|
||||
|
||||
// Save off the font metrics for various other calculations
|
||||
RETURN_HR_IF(E_FAIL, !(GetTextMetricsW(_hdcMemoryContext, &_tmFontMetrics)));
|
||||
|
||||
|
@ -300,6 +322,16 @@ GdiEngine::~GdiEngine()
|
|||
// Save the font.
|
||||
_hfont = hFont.release();
|
||||
|
||||
// Persist italic font for cleanup (and free existing if necessary)
|
||||
if (_hfontItalic != nullptr)
|
||||
{
|
||||
LOG_HR_IF(E_FAIL, !(DeleteObject(_hfontItalic)));
|
||||
_hfontItalic = nullptr;
|
||||
}
|
||||
|
||||
// Save the italic font.
|
||||
_hfontItalic = hFontItalic.release();
|
||||
|
||||
// Save raster vs. TrueType and codepage data in case we need to convert.
|
||||
_isTrueTypeFont = Font.IsTrueTypeFont();
|
||||
_fontCodepage = Font.GetCodePage();
|
||||
|
@ -346,8 +378,8 @@ GdiEngine::~GdiEngine()
|
|||
// - S_OK if set successfully or relevant GDI error via HRESULT.
|
||||
[[nodiscard]] HRESULT GdiEngine::GetProposedFont(const FontInfoDesired& FontDesired, _Out_ FontInfo& Font, const int iDpi) noexcept
|
||||
{
|
||||
wil::unique_hfont hFont;
|
||||
return _GetProposedFont(FontDesired, Font, iDpi, hFont);
|
||||
wil::unique_hfont hFont, hFontItalic;
|
||||
return _GetProposedFont(FontDesired, Font, iDpi, hFont, hFontItalic);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -373,12 +405,14 @@ GdiEngine::~GdiEngine()
|
|||
// - Font - the actual font
|
||||
// - iDpi - The DPI we will have when rendering
|
||||
// - hFont - A smart pointer to receive a handle to a ready-to-use GDI font.
|
||||
// - hFontItalic - A smart pointer to receive a handle to an italic variant of the font.
|
||||
// Return Value:
|
||||
// - S_OK if set successfully or relevant GDI error via HRESULT.
|
||||
[[nodiscard]] HRESULT GdiEngine::_GetProposedFont(const FontInfoDesired& FontDesired,
|
||||
_Out_ FontInfo& Font,
|
||||
const int iDpi,
|
||||
_Inout_ wil::unique_hfont& hFont) noexcept
|
||||
_Inout_ wil::unique_hfont& hFont,
|
||||
_Inout_ wil::unique_hfont& hFontItalic) noexcept
|
||||
{
|
||||
wil::unique_hdc hdcTemp(CreateCompatibleDC(_hdcMemoryContext));
|
||||
RETURN_HR_IF_NULL(E_FAIL, hdcTemp.get());
|
||||
|
@ -395,6 +429,7 @@ GdiEngine::~GdiEngine()
|
|||
// it may very well decide to choose Courier New instead of the Terminal raster.
|
||||
#pragma prefast(suppress : 38037, "raster fonts get special handling, we need to get it this way")
|
||||
hFont.reset((HFONT)GetStockObject(OEM_FIXED_FONT));
|
||||
hFontItalic.reset((HFONT)GetStockObject(OEM_FIXED_FONT));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -454,6 +489,11 @@ GdiEngine::~GdiEngine()
|
|||
// Create font.
|
||||
hFont.reset(CreateFontIndirectW(&lf));
|
||||
RETURN_HR_IF_NULL(E_FAIL, hFont.get());
|
||||
|
||||
// Create italic variant of the font.
|
||||
lf.lfItalic = TRUE;
|
||||
hFontItalic.reset(CreateFontIndirectW(&lf));
|
||||
RETURN_HR_IF_NULL(E_FAIL, hFontItalic.get());
|
||||
}
|
||||
|
||||
// Select into DC
|
||||
|
|
|
@ -383,7 +383,10 @@ static constexpr bool _isActionableFromGround(const wchar_t wch) noexcept
|
|||
void StateMachine::_ActionExecute(const wchar_t wch)
|
||||
{
|
||||
_trace.TraceOnExecute(wch);
|
||||
_engine->ActionExecute(wch);
|
||||
const bool success = _engine->ActionExecute(wch);
|
||||
|
||||
// Trace the result.
|
||||
_trace.DispatchSequenceTrace(success);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
@ -397,7 +400,11 @@ void StateMachine::_ActionExecute(const wchar_t wch)
|
|||
void StateMachine::_ActionExecuteFromEscape(const wchar_t wch)
|
||||
{
|
||||
_trace.TraceOnExecuteFromEscape(wch);
|
||||
_engine->ActionExecuteFromEscape(wch);
|
||||
|
||||
const bool success = _engine->ActionExecuteFromEscape(wch);
|
||||
|
||||
// Trace the result.
|
||||
_trace.DispatchSequenceTrace(success);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
@ -409,7 +416,11 @@ void StateMachine::_ActionExecuteFromEscape(const wchar_t wch)
|
|||
void StateMachine::_ActionPrint(const wchar_t wch)
|
||||
{
|
||||
_trace.TraceOnAction(L"Print");
|
||||
_engine->ActionPrint(wch);
|
||||
|
||||
const bool success = _engine->ActionPrint(wch);
|
||||
|
||||
// Trace the result.
|
||||
_trace.DispatchSequenceTrace(success);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
|
|
@ -76,7 +76,11 @@ void ParserTracing::TraceCharInput(const wchar_t wch)
|
|||
|
||||
void ParserTracing::AddSequenceTrace(const wchar_t wch)
|
||||
{
|
||||
_sequenceTrace.push_back(wch);
|
||||
// Don't waste time storing this if no one is listening.
|
||||
if (TraceLoggingProviderEnabled(g_hConsoleVirtTermParserEventTraceProvider, WINEVENT_LEVEL_VERBOSE, 0))
|
||||
{
|
||||
_sequenceTrace.push_back(wch);
|
||||
}
|
||||
}
|
||||
|
||||
void ParserTracing::DispatchSequenceTrace(const bool fSuccess) noexcept
|
||||
|
|
|
@ -1016,7 +1016,7 @@ CEditSessionObject::Release()
|
|||
RETURN_HR_IF(E_FAIL, lStartResult > 0);
|
||||
|
||||
FullTextRange->CompareEnd(ec, InterimRange, TF_ANCHOR_END, &lEndResult);
|
||||
RETURN_HR_IF(E_FAIL, lEndResult != 1);
|
||||
RETURN_HR_IF(E_FAIL, lEndResult < 0);
|
||||
|
||||
if (lStartResult < 0)
|
||||
{
|
||||
|
|
|
@ -468,6 +468,13 @@ void Utils::InitializeCampbellColorTable(const gsl::span<COLORREF> table)
|
|||
std::copy(campbellColorTable.begin(), campbellColorTable.end(), table.begin());
|
||||
}
|
||||
|
||||
void Utils::InitializeCampbellColorTable(const gsl::span<til::color> table)
|
||||
{
|
||||
THROW_HR_IF(E_INVALIDARG, table.size() < 16);
|
||||
|
||||
std::copy(campbellColorTable.begin(), campbellColorTable.end(), table.begin());
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Fill the first 16 entries of a given color table with the Campbell color
|
||||
// scheme, in the Windows BGR order.
|
||||
|
|
|
@ -13,6 +13,7 @@ Abstract:
|
|||
namespace Microsoft::Console::Utils
|
||||
{
|
||||
void InitializeCampbellColorTable(const gsl::span<COLORREF> table);
|
||||
void InitializeCampbellColorTable(const gsl::span<til::color> table);
|
||||
void InitializeCampbellColorTableForConhost(const gsl::span<COLORREF> table);
|
||||
void SwapANSIColorOrderForConhost(const gsl::span<COLORREF> table);
|
||||
void Initialize256ColorTable(const gsl::span<COLORREF> table);
|
||||
|
|
Loading…
Reference in a new issue