mirror of
https://github.com/microsoft/terminal.git
synced 2026-04-07 14:50:55 +00:00
Compare commits
38 Commits
dev/miniks
...
dev/migrie
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
44dac039f7 | ||
|
|
d7053c3456 | ||
|
|
2ab46c7610 | ||
|
|
fbf4b8199c | ||
|
|
52654488e5 | ||
|
|
e1ff2a3e59 | ||
|
|
76de2aedc2 | ||
|
|
c390b61648 | ||
|
|
04f5ee7ebf | ||
|
|
d0ff5f6b5e | ||
|
|
3a91fc0ab4 | ||
|
|
0c3841a8b0 | ||
|
|
4351f32f5d | ||
|
|
ea2bd42ff4 | ||
|
|
1f8264d86b | ||
|
|
efb1fddb99 | ||
|
|
7bc5de613c | ||
|
|
bcbe246a93 | ||
|
|
03e25f12e9 | ||
|
|
7062a830b8 | ||
|
|
53df6c7f96 | ||
|
|
81b7e54659 | ||
|
|
3255177dd0 | ||
|
|
b62f5ea850 | ||
|
|
09471c3753 | ||
|
|
80da24ecf8 | ||
|
|
4715bf5525 | ||
|
|
e7df7d07a2 | ||
|
|
d9dba62f51 | ||
|
|
3cb631a191 | ||
|
|
7a46d36ac6 | ||
|
|
ea3e1b95a2 | ||
|
|
bcbb4aa163 | ||
|
|
500b7097f0 | ||
|
|
3948f7306d | ||
|
|
d898de6ddf | ||
|
|
1eadff93bb | ||
|
|
9327e35a76 |
@@ -8,7 +8,7 @@
|
||||
<!--<add key="Static Package Dependencies" value="dep\packages" />-->
|
||||
|
||||
<!-- Use our own NuGet Feed -->
|
||||
<add key="Windows Terminal NuGet Feed" value="https://terminalnuget.blob.core.windows.net/feed/index.json" />
|
||||
<add key="TerminalDependencies" value="https://pkgs.dev.azure.com/ms/terminal/_packaging/TerminalDependencies/nuget/v3/index.json" />
|
||||
|
||||
<!-- Internal NuGet feeds that may not be accessible outside Microsoft corporate network -->
|
||||
<!--<add key="TAEF - internal" value="https://microsoft.pkgs.visualstudio.com/DefaultCollection/_packaging/Taef/nuget/v3/index.json" />
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<XesUseOneStoreVersioning>true</XesUseOneStoreVersioning>
|
||||
<XesBaseYearForStoreVersion>2020</XesBaseYearForStoreVersion>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<VersionMinor>2</VersionMinor>
|
||||
<VersionMinor>3</VersionMinor>
|
||||
<VersionInfoProductName>Windows Terminal</VersionInfoProductName>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
"openTabColorPicker",
|
||||
"renameTab",
|
||||
"commandPalette",
|
||||
"wt",
|
||||
"unbound"
|
||||
],
|
||||
"type": "string"
|
||||
@@ -280,6 +281,23 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"WtAction": {
|
||||
"description": "Arguments corresponding to a wt Action",
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/ShortcutAction" },
|
||||
{
|
||||
"properties": {
|
||||
"action": { "type": "string", "pattern": "wt" },
|
||||
"commandline": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "a `wt` commandline to run in the current window"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"required": [ "commandline" ]
|
||||
},
|
||||
"Keybinding": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
@@ -296,6 +314,7 @@
|
||||
{ "$ref": "#/definitions/SplitPaneAction" },
|
||||
{ "$ref": "#/definitions/OpenSettingsAction" },
|
||||
{ "$ref": "#/definitions/SetTabColorAction" },
|
||||
{ "$ref": "#/definitions/WtAction" },
|
||||
{ "type": "null" }
|
||||
]
|
||||
},
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
---
|
||||
author: Mike Griese @zadjii-msft
|
||||
created on: 2020-5-13
|
||||
last updated: 2020-07-13
|
||||
issue id: 1571
|
||||
---
|
||||
|
||||
# New Tab Menu Customization
|
||||
|
||||
## Abstract
|
||||
|
||||
Many users have lots and _lots_ of profiles that they use. Some of these
|
||||
profiles the user might not use that frequently. When that happens, the new tab
|
||||
dropdown can become quite cluttered.
|
||||
|
||||
A common ask is for the ability to reorder and reorganize this dropdown. This
|
||||
spec provides a design for how the user might be able to specify the
|
||||
customization in their settings.
|
||||
|
||||
## Inspiration
|
||||
|
||||
Largely, this spec was inspired by discussion in
|
||||
[#1571](https://github.com/microsoft/terminal/issues/1571#issuecomment-519504048)
|
||||
and the _many_ linked threads.
|
||||
|
||||
## Solution Design
|
||||
|
||||
This design proposes adding a new setting `"newTabMenu"`. When unset, (the
|
||||
default), the new tab menu is populated with all the profiles, in the order they
|
||||
appear in the users settings file. When set, this enables the user to control
|
||||
the appearance of the new tab dropdown. Let's take a look at an example:
|
||||
|
||||
```json
|
||||
{
|
||||
"profiles":{ ... },
|
||||
"newTabMenu": [
|
||||
{ "type":"profile", "profile": "cmd" },
|
||||
{ "type":"profile", "profile": "Windows PowerShell" },
|
||||
{ "type":"separator" },
|
||||
{
|
||||
"type":"folder",
|
||||
"name": "ssh",
|
||||
"icon": "C:\\path\\to\\icon.png",
|
||||
"entries":[
|
||||
{ "type":"profile", "profile": "Host 1" },
|
||||
{ "type":"profile", "profile": "8.8.8.8" },
|
||||
{ "type":"profile", "profile": "Host 2" }
|
||||
]
|
||||
},
|
||||
{ "type":"separator" },
|
||||
{ "type":"profile", "profile": "Ubuntu-18.04" },
|
||||
{ "type":"profile", "profile": "Fedora" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
If a user were to use this as their new tab menu, that they would get is a menu
|
||||
that looks like this:
|
||||
|
||||

|
||||
|
||||
_fig 1_: A _very rough_ mockup of what this feature might look like
|
||||
|
||||
There are three `type`s of objects in this menu:
|
||||
* `"type":"profile"`: This is a profile. Clicking on this entry will open a new
|
||||
tab, with that profile. The profile is identified with the `"profile"`
|
||||
parameter, which accepts either a profile `name` or GUID.
|
||||
* `"type":"separator"`: This represents a XAML `MenuFlyoutSeparator`, enabling
|
||||
the user to visually space out entries.
|
||||
* `"type":"folder"`: This represents a nested menu of entries.
|
||||
- The `"name"` property provides a string of text to display for the group.
|
||||
- The `"icon"` property provides a path to a image to use as the icon. This
|
||||
property is optional.
|
||||
- The `"entries"` property specifies a list of menu entries that will appear
|
||||
nested under this entry. This can contain other `"type":"folder"` groups as
|
||||
well!
|
||||
* `"type":"action"`: This represents a menu entry that should execute a specific
|
||||
`ShortcutAction`.
|
||||
- The `"name"` property provides a string of text to display for the action.
|
||||
- The `"icon"` property provides a path to a image to use as the icon. This
|
||||
property is optional.
|
||||
- The `"name"` property specifies a shortcut action similar to the "command"
|
||||
property in keybindings. If this is a string, we'll treat it as a name of a
|
||||
`ShortcutAction`. If it's an object, we'll treat it like an `ActionAndArgs`
|
||||
|
||||
## UI/UX Design
|
||||
|
||||
See the above _figure 1_.
|
||||
|
||||
The profile's `icon` will also appear as the icon on `profile` entries. If
|
||||
there's a keybinding bound to open a new tab with that profile, then that will
|
||||
also be added to the `MenuFlyoutItem` as the accelerator text, similar to the
|
||||
text we have nowadays.
|
||||
|
||||
Beneath the list of profiles will _always_ be the same "Settings", "Feedback"
|
||||
and "About" entries, separated by a `MenuFlyoutSeparator`. This is consistent
|
||||
with the UI as it exists with no customization. These entries cannot be removed
|
||||
with this feature, only the list of profiles customized.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Accessibility
|
||||
|
||||
This menu will be added to the XAML tree in the same fashion as the current new
|
||||
tab flyout, so there should be no dramatic change here.
|
||||
|
||||
### Security
|
||||
|
||||
_(no change expected)_
|
||||
|
||||
### Reliability
|
||||
|
||||
_(no change expected)_
|
||||
|
||||
### Compatibility
|
||||
|
||||
_(no change expected)_
|
||||
|
||||
### Performance, Power, and Efficiency
|
||||
|
||||
## Potential Issues
|
||||
|
||||
Currently, the `openTab` and `splitPane` keybindings will accept a `index`
|
||||
parameter to say either:
|
||||
* "Create a new tab/pane with the N'th profile"
|
||||
* "Create a new tab/pane with the profile at index N in the new
|
||||
tab dropdown".
|
||||
|
||||
These two were previously synonymous, as the N'th profile was always the N'th in
|
||||
the dropdown. However, with this change, we'll be changing the meaning of that
|
||||
argument to mean explicitly the first option - "Open a tab/pane with the N'th
|
||||
profile".
|
||||
|
||||
A previous version of this spec considered changing the meaning of that
|
||||
parameter to mean "open the entry at index N", the second option. However, in
|
||||
[Command Palette, Addendum 1], we found that naming that command would become
|
||||
unnecessarily complex.
|
||||
|
||||
To cover that above scenario, we could consider adding an `index` parameter to
|
||||
the `openNewTabDropdown` action. If specified, that would open either the N'th
|
||||
action in the dropdown (ignoring seperators), or open the dropdown with the n'th
|
||||
item selected.
|
||||
|
||||
The N'th entry in the menu won't always be a profile: it might be a folder with
|
||||
more options, or it might be an action (that might not be opening a new tab/pane
|
||||
at all).
|
||||
|
||||
Given all the above scenarios, `openNewTabDropdown` with an `"index":N`
|
||||
parameter will behave in the following ways. If the Nth top-level entry in the
|
||||
new tab menu is a:
|
||||
* `"type":"profile"`: perform the `newTab` or `splitPane` action with that profile.
|
||||
* `"type":"folder"`: Focus the first element in the sub menu, so the user could
|
||||
navigate it with the keyboard.
|
||||
* `"type":"separator"`: Ignore these when counting top-level entries.
|
||||
* `"type":"action"`: Do nothing. During settings validation, display a warning
|
||||
to the user that the keybinding in question won't do anything.
|
||||
|
||||
So for example:
|
||||
|
||||
```
|
||||
New Tab Button ▽
|
||||
├─ Folder 1
|
||||
│ └─ Profile A
|
||||
│ └─ Action B
|
||||
├─ Separator
|
||||
├─ Folder 2
|
||||
│ └─ Profile C
|
||||
│ └─ Profile D
|
||||
├─ Action E
|
||||
└─ Profile F
|
||||
```
|
||||
|
||||
And assuming the user has bound:
|
||||
```json
|
||||
{
|
||||
"bindings":
|
||||
[
|
||||
{ "command": { "action": "openNewTabDropdown", "index": 0 }, "keys": "ctrl+shift+1" },
|
||||
{ "command": { "action": "openNewTabDropdown", "index": 1 }, "keys": "ctrl+shift+2" },
|
||||
{ "command": { "action": "openNewTabDropdown", "index": 2 }, "keys": "ctrl+shift+3" },
|
||||
{ "command": { "action": "openNewTabDropdown", "index": 3 }, "keys": "ctrl+shift+4" },
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
* <kbd>ctrl+shift+1</kbd> focuses "Profile A", but the user needs to press
|
||||
enter/space to creates a new tab/split
|
||||
* <kbd>ctrl+shift+2</kbd> focuses "Profile C", but the user needs to press
|
||||
enter/space to creates a new tab/split
|
||||
* <kbd>ctrl+shift+3</kbd> performs Action E
|
||||
* <kbd>ctrl+shift+4</kbd> Creates a new tab/split with Profile F
|
||||
|
||||
## Future considerations
|
||||
|
||||
* The user could set a `"name"`/`"text"`, or `"icon"` property to these menu
|
||||
items manually, to override the value from the profile
|
||||
- This would be especially useful for the `"folder"` or aforementioned
|
||||
`"action"` menu entry
|
||||
* [#2046] covers the addition of a "Command Palette", and commands that the user
|
||||
can run using that UI. Once that's added, we should make sure that we also
|
||||
support either:
|
||||
- A `ShortcutAction` for running a `command` from the command palette
|
||||
- A `{ "type": "command", "command": "<command name>" }` type of entry, that
|
||||
automatically uses the command's name and icon for the entry in the entry.
|
||||
* A similar structure could potemtially also be used for customizing the context
|
||||
menu within a control, or the context menu for the tab.
|
||||
- In both of those cases, it might be important to somehow refer to the
|
||||
context of the current tab or control in the json. Think for example about
|
||||
"Close tab" or "Close other tabs" - currently, those work by _knowing_ which
|
||||
tab the "action" is specifed for, not by actually using a `closeTab` action.
|
||||
* In the future, they might need to be implemented as something like
|
||||
- Close Tab: `{ "action": "closeTab", "index": "${selectedTab.index}" }`
|
||||
- Close Other Tabs: `{ "action": "closeTabs", "otherThan": "${selectedTab.index}" }`
|
||||
- Close Tabs to the Right: `{ "action": "closeTabs", "after":
|
||||
"${selectedTab.index}" }`
|
||||
* We could alternatively just imply that when those actions are specified in
|
||||
the tab context menu, they're implicitly defined as being for the current
|
||||
tab. That might be simpler.
|
||||
|
||||
|
||||
<!-- Footnotes -->
|
||||
[#2046]: https://github.com/microsoft/terminal/issues/2046
|
||||
[Command Palette, Addendum 1]: https://github.com/microsoft/terminal/blob/master/doc/specs/%232046%20-%20Unified%20keybindings%20and%20commands%2C%20and%20synthesized%20action%20names.md
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
228
doc/specs/#6899 - Action IDs/#6899 - Action IDs.md
Normal file
228
doc/specs/#6899 - Action IDs/#6899 - Action IDs.md
Normal file
@@ -0,0 +1,228 @@
|
||||
---
|
||||
author: Mike Griese @zadjii-msft
|
||||
created on: 2020-07-13
|
||||
last updated: 2020-07-22
|
||||
issue id: 6899
|
||||
---
|
||||
|
||||
# Action IDs
|
||||
|
||||
## Abstract
|
||||
|
||||
This document is intended to serve as an addition to the [Command Palette Spec],
|
||||
as well as the [New Tab Menu Customization Spec].
|
||||
|
||||
As we come to rely more on actions being a mechanism by which the user defines
|
||||
"do something in the Terminal", we'll want to make it even easier for users to
|
||||
re-use the actions that they've already defined, as to reduce duplicated json as
|
||||
much as possible. This spec proposes a mechanism by which actions could be
|
||||
uniquely identifiable, so that the user could refer to bindings in other
|
||||
contexts without needing to replicate an entire json blob.
|
||||
|
||||
## Solution Design
|
||||
|
||||
This spec was largely inspired by the following diagram from @DHowett:
|
||||
|
||||

|
||||
|
||||
The goal is to introduce an `id` parameter by which actions could be uniquely
|
||||
refered to. If we'd ever like to use an action outside the list of `actions`, we
|
||||
can simply refer to the action's ID, allowing the user to only define the action
|
||||
_once_.
|
||||
|
||||
We'll start by renaming `bindings` to `actions`. `bindings` was suggested as a
|
||||
rename for `keybindings` in [#6532], as a way to make the name more generic.
|
||||
Discussion with the team lead to the understanding that the name `actions` would
|
||||
be even better, as a way of making the meaning of the "list of actions" more
|
||||
obvious.
|
||||
|
||||
When we're parsing `actions`, we'll make three passes:
|
||||
* The first pass will scan the list for objects with an `id` property. We'll
|
||||
attempt to parse those entries into `ActionAndArgs` which we'll store in the
|
||||
global `id->ActionAndArgs` map. If any entry doesn't have an `id` set, we'll
|
||||
skip it in this phase. If an entry doesn't have a `command` set, we'll ignore
|
||||
it in this pass.
|
||||
* The second pass will scan for _keybindings_. Any entries with `keys` set will
|
||||
create a `KeyChord->ActionAndArgs` entry in the keybindings map. If the entry
|
||||
has an `id` set, then we'll simply re-use the action we've already parsed for
|
||||
the `id`, from the action map. If there isn't an `id`, then we'll parse the
|
||||
action manually at this time. Entries without a `keys` set will be ignored in
|
||||
this pass.
|
||||
* The final pass will be to generate _commands_. Similar to the keybindings
|
||||
pass, we'll attempt to lookup actions for entries with an `id` set. If there
|
||||
isn't an `id`, then we'll parse the action manually at this time. We'll then
|
||||
get the name for the entry, either from the `name` property if it's set, or
|
||||
the action's `GenerateName` method.
|
||||
|
||||
For a visual representation, let's assume the user has the following in their
|
||||
`actions`:
|
||||
|
||||

|
||||
|
||||
We'll first parse the `actions` to generate the mapping of `id`->`Actions`:
|
||||
|
||||

|
||||
|
||||
Then, we'll parse the `actions` to generate the mapping of keys to actions, with
|
||||
some actions already being defined in the map of `id`->`Actions`:
|
||||
|
||||

|
||||
|
||||
|
||||
When layering `actions`, if a later settings file contains an action with the
|
||||
same `id`, it will replace the current value. In this way, users can redefine
|
||||
actions, or remove default ones (with something like `{ "id":
|
||||
"Terminal.OpenTab", "command":null }`
|
||||
|
||||
We'd maintain a large list of default actions, each with unique `id`s set. These
|
||||
are all given `id`'s with a `Terminal.` prefix, to easily identify them as
|
||||
built-in, default actions. Not all of these actions will be given keys, but they
|
||||
will all be given `id`s.
|
||||
|
||||
> 👉 NOTE: The IDs for the default actions will need to be manually created, not
|
||||
> autogenerated. These `id`s are not strings displayed in the user interface, so
|
||||
> localization is not a concern.
|
||||
|
||||
As we add additional menus to the Terminal, like the customization for the new
|
||||
tab dropdown, or the tab context menu, or the `TermControl` context menu, they
|
||||
could all refer to these actions by `id`, rather than duplicating the same json.
|
||||
|
||||
|
||||
### Existing Scenarios
|
||||
|
||||
Keybindings will still be stored as a `keys->Action` mapping, so the user will
|
||||
still be able to override default keybindings exactly the same as before.
|
||||
|
||||
Similarly, commands in the Command Palette will continue using their existing
|
||||
`name->Action` mapping they're currently using. For a binding like
|
||||
|
||||
```json
|
||||
{ "keys": "ctrl+alt+x", "id": "Terminal.OpenDefaultSettings" },
|
||||
```
|
||||
* We'll bind whatever action is defined as `Terminal.OpenDefaultSettings` to
|
||||
<kbd>ctrl+alt+x</kbd>.
|
||||
* We'll use whatever action is defined as `Terminal.OpenDefaultSettings` to
|
||||
generate a name for the command palette.
|
||||
|
||||
### Future Context Menus
|
||||
|
||||
In [New Tab Menu Customization Spec], we discuss allowing the user to bind
|
||||
actions to the new tab menu. In that spec, they can do so with something like
|
||||
the following:
|
||||
|
||||
```json
|
||||
{
|
||||
"newTabMenu": [
|
||||
{ "type":"action", "command": { "action": "adjustFontSize", "delta": 1 }, }
|
||||
{ "type":"action", "command": { "action": "adjustFontSize", "delta": -1 }, }
|
||||
{ "type":"action", "command": "resetFontSize", }
|
||||
{ "type":"profile", "profile": "cmd" },
|
||||
{ "type":"profile", "profile": "Windows PowerShell" },
|
||||
{ "type":"separator" },
|
||||
{
|
||||
"type":"folder",
|
||||
"name": "Settings...",
|
||||
"icon": "C:\\path\\to\\icon.png",
|
||||
"entries":[
|
||||
{ "type":"action", "command": "openSettings" },
|
||||
{ "type":"action", "command": { "action": "openSettings", "target": "defaultsFile" } },
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
In this example, the user has also exposed the "Increase font size", "Decrease
|
||||
font size", and "Reset font size" actions, as well as the settings files in a
|
||||
submenu. With this proposal, the above could instead be re-written as:
|
||||
|
||||
```json
|
||||
{
|
||||
"newTabMenu": [
|
||||
{ "type":"action", "id": "Terminal.IncreaseFontSize" },
|
||||
{ "type":"action", "id": "Terminal.DecreaseFontSize" },
|
||||
{ "type":"action", "id": "Terminal.ResetFontSize" },
|
||||
{ "type":"profile", "profile": "cmd" },
|
||||
{ "type":"profile", "profile": "Windows PowerShell" },
|
||||
{ "type":"separator" },
|
||||
{
|
||||
"type":"folder",
|
||||
"name": "Settings...",
|
||||
"icon": "C:\\path\\to\\icon.png",
|
||||
"entries":[
|
||||
{ "type":"action", "id": "Terminal.OpenDefaultSettings" },
|
||||
{ "type":"action", "id": "Terminal.OpenSettings" },
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
In this example, the actions are looked up from the global map using the `id`
|
||||
provided, enabling the user to re-use their existing definitions. If the user
|
||||
re-defined the `Terminal.IncreaseFontSize` action to mean something else, then
|
||||
the action in the new tab menu will also be automatically updated.
|
||||
|
||||
Furthermore, when additional menus are added (such as the tab context menu, or
|
||||
the `TermControl` context menu), these could also leverage a similar syntax to
|
||||
the above to allow re-use of the `id` parameter.
|
||||
|
||||
Discussion with the team also suggested that users shouldn't be able to define
|
||||
actions in these menus _at all_. The actions should exclusively be defined in
|
||||
`actions`, and other menus should only be able to refer to these actions by
|
||||
`id`.
|
||||
|
||||
## UI/UX Design
|
||||
|
||||
There's not a whole lot of UI for this feature specifically. This is largely
|
||||
behind-the-scenes refactoring of how actions can be defined.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Accessibility
|
||||
|
||||
_(not applicable)_
|
||||
|
||||
### Security
|
||||
|
||||
_(no change expected)_
|
||||
|
||||
### Reliability
|
||||
|
||||
_(no change expected)_
|
||||
|
||||
### Compatibility
|
||||
|
||||
_(no change expected)_
|
||||
|
||||
### Performance, Power, and Efficiency
|
||||
|
||||
_(no change expected)_
|
||||
|
||||
## Potential Issues
|
||||
|
||||
This won't necessarily play well with iterable commands in the Command Palette,
|
||||
but that's okay. For iterable commands, users will still need to define the
|
||||
actions manually.
|
||||
|
||||
## Future considerations
|
||||
|
||||
* See the following issues for other places where this might be useful:
|
||||
- [#1912] - Context Menu for Tabs
|
||||
* See also [#5524], [#5025], [#5633]
|
||||
- [#3337] - Right-click menu inside TerminalControl (w/ Copy & Paste?)
|
||||
* See also [#5633] and [#5025], both those actions seem reasonable in either
|
||||
the tab context menu or the control context menu.
|
||||
|
||||
<!-- Footnotes -->
|
||||
[Command Palette Spec]: https://github.com/microsoft/terminal/blob/master/doc/specs/%232046%20-%20Command%20Palette.md
|
||||
[New Tab Menu Customization Spec]: https://github.com/microsoft/terminal/blob/master/doc/specs/%231571%20-%20New%20Tab%20Menu%20Customization.md
|
||||
|
||||
[#1571]: https://github.com/microsoft/terminal/issues/1571
|
||||
[#1912]: https://github.com/microsoft/terminal/issues/1912
|
||||
[#3337]: https://github.com/microsoft/terminal/issues/3337
|
||||
[#5025]: https://github.com/microsoft/terminal/issues/5025
|
||||
[#5524]: https://github.com/microsoft/terminal/issues/5524
|
||||
[#5633]: https://github.com/microsoft/terminal/issues/5633
|
||||
[#6532]: https://github.com/microsoft/terminal/issues/6532
|
||||
[#6899]: https://github.com/microsoft/terminal/issues/6899
|
||||
BIN
doc/specs/#6899 - Action IDs/data-mockup-002.png
Normal file
BIN
doc/specs/#6899 - Action IDs/data-mockup-002.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 104 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 130 KiB |
BIN
doc/specs/#6899 - Action IDs/data-mockup-actions-and-ids.png
Normal file
BIN
doc/specs/#6899 - Action IDs/data-mockup-actions-and-ids.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 112 KiB |
BIN
doc/specs/#6899 - Action IDs/data-mockup-actions.png
Normal file
BIN
doc/specs/#6899 - Action IDs/data-mockup-actions.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 55 KiB |
BIN
doc/specs/#6899 - Action IDs/data-mockup.png
Normal file
BIN
doc/specs/#6899 - Action IDs/data-mockup.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 63 KiB |
BIN
res/Cascadia.ttf
BIN
res/Cascadia.ttf
Binary file not shown.
Binary file not shown.
@@ -17,5 +17,5 @@ Please consult the [license](https://raw.githubusercontent.com/microsoft/cascadi
|
||||
|
||||
### Fonts Included
|
||||
|
||||
* Cascadia Code, Cascadia Mono (2007.01)
|
||||
* from microsoft/cascadia-code@311cc603f30635da704b6a7d13050e245e61667b
|
||||
* Cascadia Code, Cascadia Mono (2007.15)
|
||||
* from microsoft/cascadia-code@2a54363b2c867f7ae811b9a034c0024cef67de96
|
||||
|
||||
@@ -108,12 +108,7 @@ TextAttribute ATTR_ROW::GetAttrByColumn(const size_t column,
|
||||
{
|
||||
THROW_HR_IF(E_INVALIDARG, column >= _cchRowWidth);
|
||||
const auto runPos = FindAttrIndex(column, pApplies);
|
||||
return GetAttrByIndex(runPos);
|
||||
}
|
||||
|
||||
TextAttribute ATTR_ROW::GetAttrByIndex(const size_t index) const
|
||||
{
|
||||
return _list.at(index).GetAttributes();
|
||||
return _list.at(runPos).GetAttributes();
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -228,7 +223,7 @@ void ATTR_ROW::ReplaceAttrs(const TextAttribute& toBeReplacedAttr, const TextAtt
|
||||
// Return Value:
|
||||
// - STATUS_NO_MEMORY if there wasn't enough memory to insert the runs
|
||||
// otherwise STATUS_SUCCESS if we were successful.
|
||||
[[nodiscard]] HRESULT ATTR_ROW::InsertAttrRuns(const std::basic_string_view<TextAttributeRun> newAttrs,
|
||||
[[nodiscard]] HRESULT ATTR_ROW::InsertAttrRuns(const gsl::span<const TextAttributeRun> newAttrs,
|
||||
const size_t iStart,
|
||||
const size_t iEnd,
|
||||
const size_t cBufferWidth)
|
||||
@@ -255,7 +250,7 @@ void ATTR_ROW::ReplaceAttrs(const TextAttribute& toBeReplacedAttr, const TextAtt
|
||||
if (newAttrs.size() == 1)
|
||||
{
|
||||
// Get the new color attribute we're trying to apply
|
||||
const TextAttribute NewAttr = newAttrs.at(0).GetAttributes();
|
||||
const TextAttribute NewAttr = til::at(newAttrs, 0).GetAttributes();
|
||||
|
||||
// If the existing run was only 1 element...
|
||||
// ...and the new color is the same as the old, we don't have to do anything and can exit quick.
|
||||
@@ -377,7 +372,7 @@ void ATTR_ROW::ReplaceAttrs(const TextAttribute& toBeReplacedAttr, const TextAtt
|
||||
if (iStart == 0 && iEnd == iLastBufferCol)
|
||||
{
|
||||
// Just dump what we're given over what we have and call it a day.
|
||||
_list.assign(newAttrs.cbegin(), newAttrs.cend());
|
||||
_list.assign(newAttrs.begin(), newAttrs.end());
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
@@ -36,8 +36,6 @@ public:
|
||||
TextAttribute GetAttrByColumn(const size_t column,
|
||||
size_t* const pApplies) const;
|
||||
|
||||
TextAttribute GetAttrByIndex(const size_t index) const;
|
||||
|
||||
size_t GetNumberOfRuns() const noexcept;
|
||||
|
||||
size_t FindAttrIndex(const size_t index,
|
||||
@@ -48,7 +46,7 @@ public:
|
||||
|
||||
void Resize(const size_t newWidth);
|
||||
|
||||
[[nodiscard]] HRESULT InsertAttrRuns(const std::basic_string_view<TextAttributeRun> newAttrs,
|
||||
[[nodiscard]] HRESULT InsertAttrRuns(const gsl::span<const TextAttributeRun> newAttrs,
|
||||
const size_t iStart,
|
||||
const size_t iEnd,
|
||||
const size_t cBufferWidth);
|
||||
|
||||
@@ -112,9 +112,9 @@ OutputCellIterator::OutputCellIterator(const std::wstring_view utf16Text, const
|
||||
// - This is an iterator over legacy colors only. The text is not modified.
|
||||
// Arguments:
|
||||
// - legacyAttrs - One legacy color item per cell
|
||||
OutputCellIterator::OutputCellIterator(const std::basic_string_view<WORD> legacyAttrs) noexcept :
|
||||
OutputCellIterator::OutputCellIterator(const gsl::span<const WORD> legacyAttrs) noexcept :
|
||||
_mode(Mode::LegacyAttr),
|
||||
_currentView(s_GenerateViewLegacyAttr(legacyAttrs.at(0))),
|
||||
_currentView(s_GenerateViewLegacyAttr(til::at(legacyAttrs, 0))),
|
||||
_run(legacyAttrs),
|
||||
_attr(InvalidTextAttribute),
|
||||
_distance(0),
|
||||
@@ -127,9 +127,9 @@ OutputCellIterator::OutputCellIterator(const std::basic_string_view<WORD> legacy
|
||||
// - This is an iterator over legacy cell data. We will use the unicode text and the legacy color attribute.
|
||||
// Arguments:
|
||||
// - charInfos - Multiple cell with unicode text and legacy color data.
|
||||
OutputCellIterator::OutputCellIterator(const std::basic_string_view<CHAR_INFO> charInfos) noexcept :
|
||||
OutputCellIterator::OutputCellIterator(const gsl::span<const CHAR_INFO> charInfos) noexcept :
|
||||
_mode(Mode::CharInfo),
|
||||
_currentView(s_GenerateView(charInfos.at(0))),
|
||||
_currentView(s_GenerateView(til::at(charInfos, 0))),
|
||||
_run(charInfos),
|
||||
_attr(InvalidTextAttribute),
|
||||
_distance(0),
|
||||
@@ -142,9 +142,9 @@ OutputCellIterator::OutputCellIterator(const std::basic_string_view<CHAR_INFO> c
|
||||
// - This is an iterator over existing OutputCells with full text and color data.
|
||||
// Arguments:
|
||||
// - cells - Multiple cells in a run
|
||||
OutputCellIterator::OutputCellIterator(const std::basic_string_view<OutputCell> cells) :
|
||||
OutputCellIterator::OutputCellIterator(const gsl::span<const OutputCell> cells) :
|
||||
_mode(Mode::Cell),
|
||||
_currentView(s_GenerateView(cells.at(0))),
|
||||
_currentView(s_GenerateView(til::at(cells, 0))),
|
||||
_run(cells),
|
||||
_attr(InvalidTextAttribute),
|
||||
_distance(0),
|
||||
@@ -180,15 +180,15 @@ OutputCellIterator::operator bool() const noexcept
|
||||
}
|
||||
case Mode::Cell:
|
||||
{
|
||||
return _pos < std::get<std::basic_string_view<OutputCell>>(_run).length();
|
||||
return _pos < std::get<gsl::span<const OutputCell>>(_run).size();
|
||||
}
|
||||
case Mode::CharInfo:
|
||||
{
|
||||
return _pos < std::get<std::basic_string_view<CHAR_INFO>>(_run).length();
|
||||
return _pos < std::get<gsl::span<const CHAR_INFO>>(_run).size();
|
||||
}
|
||||
case Mode::LegacyAttr:
|
||||
{
|
||||
return _pos < std::get<std::basic_string_view<WORD>>(_run).length();
|
||||
return _pos < std::get<gsl::span<const WORD>>(_run).size();
|
||||
}
|
||||
default:
|
||||
FAIL_FAST_HR(E_NOTIMPL);
|
||||
@@ -265,7 +265,7 @@ OutputCellIterator& OutputCellIterator::operator++()
|
||||
_pos++;
|
||||
if (operator bool())
|
||||
{
|
||||
_currentView = s_GenerateView(std::get<std::basic_string_view<OutputCell>>(_run).at(_pos));
|
||||
_currentView = s_GenerateView(til::at(std::get<gsl::span<const OutputCell>>(_run), _pos));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -275,7 +275,7 @@ OutputCellIterator& OutputCellIterator::operator++()
|
||||
_pos++;
|
||||
if (operator bool())
|
||||
{
|
||||
_currentView = s_GenerateView(std::get<std::basic_string_view<CHAR_INFO>>(_run).at(_pos));
|
||||
_currentView = s_GenerateView(til::at(std::get<gsl::span<const CHAR_INFO>>(_run), _pos));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -285,7 +285,7 @@ OutputCellIterator& OutputCellIterator::operator++()
|
||||
_pos++;
|
||||
if (operator bool())
|
||||
{
|
||||
_currentView = s_GenerateViewLegacyAttr(std::get<std::basic_string_view<WORD>>(_run).at(_pos));
|
||||
_currentView = s_GenerateViewLegacyAttr(til::at(std::get<gsl::span<const WORD>>(_run), _pos));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -39,9 +39,9 @@ public:
|
||||
OutputCellIterator(const CHAR_INFO& charInfo, const size_t fillLimit = 0) noexcept;
|
||||
OutputCellIterator(const std::wstring_view utf16Text);
|
||||
OutputCellIterator(const std::wstring_view utf16Text, const TextAttribute attribute);
|
||||
OutputCellIterator(const std::basic_string_view<WORD> legacyAttributes) noexcept;
|
||||
OutputCellIterator(const std::basic_string_view<CHAR_INFO> charInfos) noexcept;
|
||||
OutputCellIterator(const std::basic_string_view<OutputCell> cells);
|
||||
OutputCellIterator(const gsl::span<const WORD> legacyAttributes) noexcept;
|
||||
OutputCellIterator(const gsl::span<const CHAR_INFO> charInfos) noexcept;
|
||||
OutputCellIterator(const gsl::span<const OutputCell> cells);
|
||||
~OutputCellIterator() = default;
|
||||
|
||||
OutputCellIterator& operator=(const OutputCellIterator& it) = default;
|
||||
@@ -86,13 +86,13 @@ private:
|
||||
};
|
||||
Mode _mode;
|
||||
|
||||
std::basic_string_view<WORD> _legacyAttrs;
|
||||
gsl::span<const WORD> _legacyAttrs;
|
||||
|
||||
std::variant<
|
||||
std::wstring_view,
|
||||
std::basic_string_view<WORD>,
|
||||
std::basic_string_view<CHAR_INFO>,
|
||||
std::basic_string_view<OutputCell>,
|
||||
gsl::span<const WORD>,
|
||||
gsl::span<const CHAR_INFO>,
|
||||
gsl::span<const OutputCell>,
|
||||
std::monostate>
|
||||
_run;
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ gsl::span<OutputCell> OutputCellRect::GetRow(const size_t row)
|
||||
// - Read-only iterator of OutputCells
|
||||
OutputCellIterator OutputCellRect::GetRowIter(const size_t row) const
|
||||
{
|
||||
const std::basic_string_view<OutputCell> view(_FindRowOffset(row), _cols);
|
||||
const gsl::span<const OutputCell> view(_FindRowOffset(row), _cols);
|
||||
|
||||
return OutputCellIterator(view);
|
||||
}
|
||||
|
||||
@@ -160,66 +160,98 @@ OutputCellIterator ROW::WriteCells(OutputCellIterator it, const size_t index, co
|
||||
// If we're given a right-side column limit, use it. Otherwise, the write limit is the final column index available in the char row.
|
||||
const auto finalColumnInRow = limitRight.value_or(_charRow.size() - 1);
|
||||
|
||||
while (it && currentIndex <= finalColumnInRow)
|
||||
if (it)
|
||||
{
|
||||
// Fill the color if the behavior isn't set to keeping the current color.
|
||||
if (it->TextAttrBehavior() != TextAttributeBehavior::Current)
|
||||
// Accumulate usages of the same color so we can spend less time in InsertAttrRuns rewriting it.
|
||||
auto currentColor = it->TextAttr();
|
||||
size_t colorUses = 0;
|
||||
size_t colorStarts = index;
|
||||
|
||||
while (it && currentIndex <= finalColumnInRow)
|
||||
{
|
||||
const TextAttributeRun attrRun{ 1, it->TextAttr() };
|
||||
LOG_IF_FAILED(_attrRow.InsertAttrRuns({ &attrRun, 1 },
|
||||
currentIndex,
|
||||
currentIndex,
|
||||
_charRow.size()));
|
||||
}
|
||||
|
||||
// Fill the text if the behavior isn't set to saying there's only a color stored in this iterator.
|
||||
if (it->TextAttrBehavior() != TextAttributeBehavior::StoredOnly)
|
||||
{
|
||||
const bool fillingLastColumn = currentIndex == finalColumnInRow;
|
||||
|
||||
// TODO: MSFT: 19452170 - We need to ensure when writing any trailing byte that the one to the left
|
||||
// is a matching leading byte. Likewise, if we're writing a leading byte, we need to make sure we still have space in this loop
|
||||
// for the trailing byte coming up before writing it.
|
||||
|
||||
// If we're trying to fill the first cell with a trailing byte, pad it out instead by clearing it.
|
||||
// Don't increment iterator. We'll advance the index and try again with this value on the next round through the loop.
|
||||
if (currentIndex == 0 && it->DbcsAttr().IsTrailing())
|
||||
// Fill the color if the behavior isn't set to keeping the current color.
|
||||
if (it->TextAttrBehavior() != TextAttributeBehavior::Current)
|
||||
{
|
||||
_charRow.ClearCell(currentIndex);
|
||||
// If the color of this cell is the same as the run we're currently on,
|
||||
// just increment the counter.
|
||||
if (currentColor == it->TextAttr())
|
||||
{
|
||||
++colorUses;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise, commit this color into the run and save off the new one.
|
||||
const TextAttributeRun run{ colorUses, currentColor };
|
||||
// Now commit the new color runs into the attr row.
|
||||
LOG_IF_FAILED(_attrRow.InsertAttrRuns({ &run, 1 },
|
||||
colorStarts,
|
||||
currentIndex - 1,
|
||||
_charRow.size()));
|
||||
currentColor = it->TextAttr();
|
||||
colorUses = 1;
|
||||
colorStarts = currentIndex;
|
||||
}
|
||||
}
|
||||
// If we're trying to fill the last cell with a leading byte, pad it out instead by clearing it.
|
||||
// Don't increment iterator. We'll exit because we couldn't write a lead at the end of a line.
|
||||
else if (fillingLastColumn && it->DbcsAttr().IsLeading())
|
||||
|
||||
// Fill the text if the behavior isn't set to saying there's only a color stored in this iterator.
|
||||
if (it->TextAttrBehavior() != TextAttributeBehavior::StoredOnly)
|
||||
{
|
||||
_charRow.ClearCell(currentIndex);
|
||||
_charRow.SetDoubleBytePadded(true);
|
||||
const bool fillingLastColumn = currentIndex == finalColumnInRow;
|
||||
|
||||
// TODO: MSFT: 19452170 - We need to ensure when writing any trailing byte that the one to the left
|
||||
// is a matching leading byte. Likewise, if we're writing a leading byte, we need to make sure we still have space in this loop
|
||||
// for the trailing byte coming up before writing it.
|
||||
|
||||
// If we're trying to fill the first cell with a trailing byte, pad it out instead by clearing it.
|
||||
// Don't increment iterator. We'll advance the index and try again with this value on the next round through the loop.
|
||||
if (currentIndex == 0 && it->DbcsAttr().IsTrailing())
|
||||
{
|
||||
_charRow.ClearCell(currentIndex);
|
||||
}
|
||||
// If we're trying to fill the last cell with a leading byte, pad it out instead by clearing it.
|
||||
// Don't increment iterator. We'll exit because we couldn't write a lead at the end of a line.
|
||||
else if (fillingLastColumn && it->DbcsAttr().IsLeading())
|
||||
{
|
||||
_charRow.ClearCell(currentIndex);
|
||||
_charRow.SetDoubleBytePadded(true);
|
||||
}
|
||||
// Otherwise, copy the data given and increment the iterator.
|
||||
else
|
||||
{
|
||||
_charRow.DbcsAttrAt(currentIndex) = it->DbcsAttr();
|
||||
_charRow.GlyphAt(currentIndex) = it->Chars();
|
||||
++it;
|
||||
}
|
||||
|
||||
// If we're asked to (un)set the wrap status and we just filled the last column with some text...
|
||||
// NOTE:
|
||||
// - wrap = std::nullopt --> don't change the wrap value
|
||||
// - wrap = true --> we're filling cells as a steam, consider this a wrap
|
||||
// - wrap = false --> we're filling cells as a block, unwrap
|
||||
if (wrap.has_value() && fillingLastColumn)
|
||||
{
|
||||
// set wrap status on the row to parameter's value.
|
||||
_charRow.SetWrapForced(wrap.value());
|
||||
}
|
||||
}
|
||||
// Otherwise, copy the data given and increment the iterator.
|
||||
else
|
||||
{
|
||||
_charRow.DbcsAttrAt(currentIndex) = it->DbcsAttr();
|
||||
_charRow.GlyphAt(currentIndex) = it->Chars();
|
||||
++it;
|
||||
}
|
||||
|
||||
// If we're asked to (un)set the wrap status and we just filled the last column with some text...
|
||||
// NOTE:
|
||||
// - wrap = std::nullopt --> don't change the wrap value
|
||||
// - wrap = true --> we're filling cells as a steam, consider this a wrap
|
||||
// - wrap = false --> we're filling cells as a block, unwrap
|
||||
if (wrap.has_value() && fillingLastColumn)
|
||||
{
|
||||
// set wrap status on the row to parameter's value.
|
||||
_charRow.SetWrapForced(wrap.value());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
// Move to the next cell for the next time through the loop.
|
||||
++currentIndex;
|
||||
}
|
||||
|
||||
// Move to the next cell for the next time through the loop.
|
||||
++currentIndex;
|
||||
// Now commit the final color into the attr row
|
||||
if (colorUses)
|
||||
{
|
||||
const TextAttributeRun run{ colorUses, currentColor };
|
||||
LOG_IF_FAILED(_attrRow.InsertAttrRuns({ &run, 1 },
|
||||
colorStarts,
|
||||
currentIndex - 1,
|
||||
_charRow.size()));
|
||||
}
|
||||
}
|
||||
|
||||
return it;
|
||||
|
||||
@@ -90,7 +90,7 @@ bool TextAttribute::IsLegacy() const noexcept
|
||||
// - reverseScreenMode: true if the screen mode is reversed.
|
||||
// Return Value:
|
||||
// - the foreground and background colors that should be displayed.
|
||||
std::pair<COLORREF, COLORREF> TextAttribute::CalculateRgbColors(const std::basic_string_view<COLORREF> colorTable,
|
||||
std::pair<COLORREF, COLORREF> TextAttribute::CalculateRgbColors(const gsl::span<const COLORREF> colorTable,
|
||||
const COLORREF defaultFgColor,
|
||||
const COLORREF defaultBgColor,
|
||||
const bool reverseScreenMode) const noexcept
|
||||
@@ -270,7 +270,7 @@ void TextAttribute::SetFaint(bool isFaint) noexcept
|
||||
WI_UpdateFlag(_extendedAttrs, ExtendedAttributes::Faint, isFaint);
|
||||
}
|
||||
|
||||
void TextAttribute::SetItalics(bool isItalic) noexcept
|
||||
void TextAttribute::SetItalic(bool isItalic) noexcept
|
||||
{
|
||||
WI_UpdateFlag(_extendedAttrs, ExtendedAttributes::Italics, isItalic);
|
||||
}
|
||||
@@ -290,13 +290,13 @@ void TextAttribute::SetCrossedOut(bool isCrossedOut) noexcept
|
||||
WI_UpdateFlag(_extendedAttrs, ExtendedAttributes::CrossedOut, isCrossedOut);
|
||||
}
|
||||
|
||||
void TextAttribute::SetUnderline(bool isUnderlined) noexcept
|
||||
void TextAttribute::SetUnderlined(bool isUnderlined) noexcept
|
||||
{
|
||||
// TODO:GH#2915 Treat underline separately from LVB_UNDERSCORE
|
||||
WI_UpdateFlag(_wAttrLegacy, COMMON_LVB_UNDERSCORE, isUnderlined);
|
||||
}
|
||||
|
||||
void TextAttribute::SetOverline(bool isOverlined) noexcept
|
||||
void TextAttribute::SetOverlined(bool isOverlined) noexcept
|
||||
{
|
||||
WI_UpdateFlag(_wAttrLegacy, COMMON_LVB_GRID_HORIZONTAL, isOverlined);
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ public:
|
||||
static TextAttribute StripErroneousVT16VersionsOfLegacyDefaults(const TextAttribute& attribute) noexcept;
|
||||
WORD GetLegacyAttributes() const noexcept;
|
||||
|
||||
std::pair<COLORREF, COLORREF> CalculateRgbColors(const std::basic_string_view<COLORREF> colorTable,
|
||||
std::pair<COLORREF, COLORREF> CalculateRgbColors(const gsl::span<const COLORREF> colorTable,
|
||||
const COLORREF defaultFgColor,
|
||||
const COLORREF defaultBgColor,
|
||||
const bool reverseScreenMode = false) const noexcept;
|
||||
@@ -100,12 +100,12 @@ public:
|
||||
|
||||
void SetBold(bool isBold) noexcept;
|
||||
void SetFaint(bool isFaint) noexcept;
|
||||
void SetItalics(bool isItalic) noexcept;
|
||||
void SetItalic(bool isItalic) noexcept;
|
||||
void SetBlinking(bool isBlinking) noexcept;
|
||||
void SetInvisible(bool isInvisible) noexcept;
|
||||
void SetCrossedOut(bool isCrossedOut) noexcept;
|
||||
void SetUnderline(bool isUnderlined) noexcept;
|
||||
void SetOverline(bool isOverlined) noexcept;
|
||||
void SetUnderlined(bool isUnderlined) noexcept;
|
||||
void SetOverlined(bool isOverlined) noexcept;
|
||||
void SetReverseVideo(bool isReversed) noexcept;
|
||||
|
||||
ExtendedAttributes GetExtendedAttributes() const noexcept;
|
||||
@@ -218,11 +218,12 @@ namespace WEX
|
||||
static WEX::Common::NoThrowString ToString(const TextAttribute& attr)
|
||||
{
|
||||
return WEX::Common::NoThrowString().Format(
|
||||
L"{FG:%s,BG:%s,bold:%d,wLegacy:(0x%04x)}",
|
||||
L"{FG:%s,BG:%s,bold:%d,wLegacy:(0x%04x),ext:(0x%02x)}",
|
||||
VerifyOutputTraits<TextColor>::ToString(attr._foreground).GetBuffer(),
|
||||
VerifyOutputTraits<TextColor>::ToString(attr._background).GetBuffer(),
|
||||
attr.IsBold(),
|
||||
attr._wAttrLegacy);
|
||||
attr._wAttrLegacy,
|
||||
static_cast<DWORD>(attr._extendedAttrs));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ void TextColor::SetDefault() noexcept
|
||||
// - brighten: if true, we'll brighten a dark color table index.
|
||||
// Return Value:
|
||||
// - a COLORREF containing the real value of this TextColor.
|
||||
COLORREF TextColor::GetColor(std::basic_string_view<COLORREF> colorTable,
|
||||
COLORREF TextColor::GetColor(gsl::span<const COLORREF> colorTable,
|
||||
const COLORREF defaultColor,
|
||||
bool brighten) const noexcept
|
||||
{
|
||||
@@ -158,9 +158,9 @@ COLORREF TextColor::GetColor(std::basic_string_view<COLORREF> colorTable,
|
||||
// If we find a match, return instead the bright version of this color
|
||||
for (size_t i = 0; i < 8; i++)
|
||||
{
|
||||
if (colorTable.at(i) == defaultColor)
|
||||
if (til::at(colorTable, i) == defaultColor)
|
||||
{
|
||||
return colorTable.at(i + 8);
|
||||
return til::at(colorTable, i + 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -173,11 +173,11 @@ COLORREF TextColor::GetColor(std::basic_string_view<COLORREF> colorTable,
|
||||
}
|
||||
else if (IsIndex16() && brighten)
|
||||
{
|
||||
return colorTable.at(_index | 8);
|
||||
return til::at(colorTable, _index | 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
return colorTable.at(_index);
|
||||
return til::at(colorTable, _index);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ public:
|
||||
void SetIndex(const BYTE index, const bool isIndex256) noexcept;
|
||||
void SetDefault() noexcept;
|
||||
|
||||
COLORREF GetColor(std::basic_string_view<COLORREF> colorTable,
|
||||
COLORREF GetColor(gsl::span<const COLORREF> colorTable,
|
||||
const COLORREF defaultColor,
|
||||
const bool brighten = false) const noexcept;
|
||||
|
||||
|
||||
@@ -787,7 +787,7 @@ const Cursor& TextBuffer::GetCursor() const noexcept
|
||||
return _currentAttributes;
|
||||
}
|
||||
|
||||
void TextBuffer::SetCurrentAttributes(const TextAttribute currentAttributes) noexcept
|
||||
void TextBuffer::SetCurrentAttributes(const TextAttribute& currentAttributes) noexcept
|
||||
{
|
||||
_currentAttributes = currentAttributes;
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ public:
|
||||
|
||||
[[nodiscard]] TextAttribute GetCurrentAttributes() const noexcept;
|
||||
|
||||
void SetCurrentAttributes(const TextAttribute currentAttributes) noexcept;
|
||||
void SetCurrentAttributes(const TextAttribute& currentAttributes) noexcept;
|
||||
|
||||
void Reset();
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ class TextAttributeTests
|
||||
COLORREF _colorTable[COLOR_TABLE_SIZE];
|
||||
COLORREF _defaultFg = RGB(1, 2, 3);
|
||||
COLORREF _defaultBg = RGB(4, 5, 6);
|
||||
std::basic_string_view<COLORREF> _GetTableView();
|
||||
gsl::span<const COLORREF> _GetTableView();
|
||||
};
|
||||
|
||||
bool TextAttributeTests::ClassSetup()
|
||||
@@ -51,9 +51,9 @@ bool TextAttributeTests::ClassSetup()
|
||||
return true;
|
||||
}
|
||||
|
||||
std::basic_string_view<COLORREF> TextAttributeTests::_GetTableView()
|
||||
gsl::span<const COLORREF> TextAttributeTests::_GetTableView()
|
||||
{
|
||||
return std::basic_string_view<COLORREF>(&_colorTable[0], COLOR_TABLE_SIZE);
|
||||
return gsl::span<const COLORREF>(&_colorTable[0], COLOR_TABLE_SIZE);
|
||||
}
|
||||
|
||||
void TextAttributeTests::TestRoundtripLegacy()
|
||||
|
||||
@@ -27,7 +27,7 @@ class TextColorTests
|
||||
COLORREF _colorTable[COLOR_TABLE_SIZE];
|
||||
COLORREF _defaultFg = RGB(1, 2, 3);
|
||||
COLORREF _defaultBg = RGB(4, 5, 6);
|
||||
std::basic_string_view<COLORREF> _GetTableView();
|
||||
gsl::span<const COLORREF> _GetTableView();
|
||||
};
|
||||
|
||||
bool TextColorTests::ClassSetup()
|
||||
@@ -51,9 +51,9 @@ bool TextColorTests::ClassSetup()
|
||||
return true;
|
||||
}
|
||||
|
||||
std::basic_string_view<COLORREF> TextColorTests::_GetTableView()
|
||||
gsl::span<const COLORREF> TextColorTests::_GetTableView()
|
||||
{
|
||||
return std::basic_string_view<COLORREF>(&_colorTable[0], COLOR_TABLE_SIZE);
|
||||
return gsl::span<const COLORREF>(&_colorTable[0], COLOR_TABLE_SIZE);
|
||||
}
|
||||
|
||||
void TextColorTests::TestDefaultColor()
|
||||
|
||||
@@ -147,10 +147,8 @@ namespace TerminalAppLocalTests
|
||||
{ "name": "command0", "command": { "action": "splitPane", "split": null } },
|
||||
{ "name": "command1", "command": { "action": "splitPane", "split": "vertical" } },
|
||||
{ "name": "command2", "command": { "action": "splitPane", "split": "horizontal" } },
|
||||
{ "name": "command3", "command": { "action": "splitPane", "split": "none" } },
|
||||
{ "name": "command4", "command": { "action": "splitPane" } },
|
||||
{ "name": "command5", "command": { "action": "splitPane", "split": "auto" } },
|
||||
{ "name": "command6", "command": { "action": "splitPane", "split": "foo" } }
|
||||
{ "name": "command5", "command": { "action": "splitPane", "split": "auto" } }
|
||||
])" };
|
||||
|
||||
const auto commands0Json = VerifyParseSucceeded(commands0String);
|
||||
@@ -159,7 +157,7 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_ARE_EQUAL(0u, commands.size());
|
||||
auto warnings = implementation::Command::LayerJson(commands, commands0Json);
|
||||
VERIFY_ARE_EQUAL(0u, warnings.size());
|
||||
VERIFY_ARE_EQUAL(7u, commands.size());
|
||||
VERIFY_ARE_EQUAL(5u, commands.size());
|
||||
|
||||
{
|
||||
auto command = commands.at(L"command0");
|
||||
@@ -191,16 +189,6 @@ namespace TerminalAppLocalTests
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::SplitState::Horizontal, realArgs.SplitStyle());
|
||||
}
|
||||
{
|
||||
auto command = commands.at(L"command3");
|
||||
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(winrt::TerminalApp::SplitState::Automatic, realArgs.SplitStyle());
|
||||
}
|
||||
{
|
||||
auto command = commands.at(L"command4");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
@@ -221,16 +209,6 @@ namespace TerminalAppLocalTests
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::SplitState::Automatic, realArgs.SplitStyle());
|
||||
}
|
||||
{
|
||||
auto command = commands.at(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(winrt::TerminalApp::SplitState::Automatic, realArgs.SplitStyle());
|
||||
}
|
||||
}
|
||||
void CommandTests::TestResourceKeyName()
|
||||
{
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
#include "pch.h"
|
||||
#include <WexTestClass.h>
|
||||
|
||||
#include "../TerminalApp/TerminalPage.h"
|
||||
#include "../TerminalApp/AppCommandlineArgs.h"
|
||||
#include "../TerminalApp/ActionArgs.h"
|
||||
|
||||
using namespace WEX::Logging;
|
||||
using namespace WEX::Common;
|
||||
@@ -52,6 +54,10 @@ namespace TerminalAppLocalTests
|
||||
|
||||
TEST_METHOD(CheckTypos);
|
||||
|
||||
TEST_METHOD(TestSimpleExecuteCommandlineAction);
|
||||
TEST_METHOD(TestMultipleCommandExecuteCommandlineAction);
|
||||
TEST_METHOD(TestInvalidExecuteCommandlineAction);
|
||||
|
||||
private:
|
||||
void _buildCommandlinesHelper(AppCommandlineArgs& appArgs,
|
||||
const size_t expectedSubcommands,
|
||||
@@ -1067,4 +1073,66 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_ARE_EQUAL(L"C:\\", myArgs.TerminalArgs().StartingDirectory());
|
||||
}
|
||||
}
|
||||
|
||||
void CommandlineTest::TestSimpleExecuteCommandlineAction()
|
||||
{
|
||||
auto args = winrt::make_self<implementation::ExecuteCommandlineArgs>();
|
||||
args->Commandline(L"new-tab");
|
||||
auto actions = implementation::TerminalPage::ConvertExecuteCommandlineToActions(*args);
|
||||
VERIFY_ARE_EQUAL(1u, actions.size());
|
||||
auto actionAndArgs = actions.at(0);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
|
||||
auto myArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(myArgs);
|
||||
VERIFY_IS_NOT_NULL(myArgs.TerminalArgs());
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().Commandline().empty());
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty());
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().ProfileIndex() == nullptr);
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty());
|
||||
}
|
||||
|
||||
void CommandlineTest::TestMultipleCommandExecuteCommandlineAction()
|
||||
{
|
||||
auto args = winrt::make_self<implementation::ExecuteCommandlineArgs>();
|
||||
args->Commandline(L"new-tab ; split-pane");
|
||||
auto actions = implementation::TerminalPage::ConvertExecuteCommandlineToActions(*args);
|
||||
VERIFY_ARE_EQUAL(2u, actions.size());
|
||||
{
|
||||
auto actionAndArgs = actions.at(0);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
|
||||
auto myArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(myArgs);
|
||||
VERIFY_IS_NOT_NULL(myArgs.TerminalArgs());
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().Commandline().empty());
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty());
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().ProfileIndex() == nullptr);
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty());
|
||||
}
|
||||
{
|
||||
auto actionAndArgs = actions.at(1);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
|
||||
auto myArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(myArgs);
|
||||
VERIFY_IS_NOT_NULL(myArgs.TerminalArgs());
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().Commandline().empty());
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty());
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().ProfileIndex() == nullptr);
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty());
|
||||
}
|
||||
}
|
||||
|
||||
void CommandlineTest::TestInvalidExecuteCommandlineAction()
|
||||
{
|
||||
auto args = winrt::make_self<implementation::ExecuteCommandlineArgs>();
|
||||
// -H and -V cannot be combined.
|
||||
args->Commandline(L"split-pane -H -V");
|
||||
auto actions = implementation::TerminalPage::ConvertExecuteCommandlineToActions(*args);
|
||||
VERIFY_ARE_EQUAL(0u, actions.size());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,10 +323,8 @@ namespace TerminalAppLocalTests
|
||||
{ "keys": ["ctrl+c"], "command": { "action": "splitPane", "split": null } },
|
||||
{ "keys": ["ctrl+d"], "command": { "action": "splitPane", "split": "vertical" } },
|
||||
{ "keys": ["ctrl+e"], "command": { "action": "splitPane", "split": "horizontal" } },
|
||||
{ "keys": ["ctrl+f"], "command": { "action": "splitPane", "split": "none" } },
|
||||
{ "keys": ["ctrl+g"], "command": { "action": "splitPane" } },
|
||||
{ "keys": ["ctrl+h"], "command": { "action": "splitPane", "split": "auto" } },
|
||||
{ "keys": ["ctrl+i"], "command": { "action": "splitPane", "split": "foo" } }
|
||||
{ "keys": ["ctrl+h"], "command": { "action": "splitPane", "split": "auto" } }
|
||||
])" };
|
||||
|
||||
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
|
||||
@@ -335,7 +333,7 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_IS_NOT_NULL(appKeyBindings);
|
||||
VERIFY_ARE_EQUAL(0u, appKeyBindings->_keyShortcuts.size());
|
||||
appKeyBindings->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(7u, appKeyBindings->_keyShortcuts.size());
|
||||
VERIFY_ARE_EQUAL(5u, appKeyBindings->_keyShortcuts.size());
|
||||
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('C') };
|
||||
@@ -364,15 +362,6 @@ namespace TerminalAppLocalTests
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::SplitState::Horizontal, realArgs.SplitStyle());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('F') };
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::SplitState::Automatic, realArgs.SplitStyle());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('G') };
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
|
||||
@@ -391,15 +380,6 @@ namespace TerminalAppLocalTests
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::SplitState::Automatic, realArgs.SplitStyle());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('I') };
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::SplitState::Automatic, realArgs.SplitStyle());
|
||||
}
|
||||
}
|
||||
|
||||
void KeyBindingsTests::TestSetTabColorArgs()
|
||||
@@ -407,7 +387,6 @@ namespace TerminalAppLocalTests
|
||||
const std::string bindings0String{ R"([
|
||||
{ "keys": ["ctrl+c"], "command": { "action": "setTabColor", "color": null } },
|
||||
{ "keys": ["ctrl+d"], "command": { "action": "setTabColor", "color": "#123456" } },
|
||||
{ "keys": ["ctrl+e"], "command": { "action": "setTabColor", "color": "thisStringObviouslyWontWork" } },
|
||||
{ "keys": ["ctrl+f"], "command": "setTabColor" },
|
||||
])" };
|
||||
|
||||
@@ -417,7 +396,7 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_IS_NOT_NULL(appKeyBindings);
|
||||
VERIFY_ARE_EQUAL(0u, appKeyBindings->_keyShortcuts.size());
|
||||
appKeyBindings->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(4u, appKeyBindings->_keyShortcuts.size());
|
||||
VERIFY_ARE_EQUAL(3u, appKeyBindings->_keyShortcuts.size());
|
||||
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('C') };
|
||||
@@ -439,15 +418,6 @@ namespace TerminalAppLocalTests
|
||||
// Remember that COLORREFs are actually BBGGRR order, while the string is in #RRGGBB order
|
||||
VERIFY_ARE_EQUAL(static_cast<uint32_t>(til::color(0x563412)), realArgs.TabColor().Value());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('E') };
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SetTabColor, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SetTabColorArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NULL(realArgs.TabColor());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('F') };
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
|
||||
|
||||
@@ -76,6 +76,8 @@ namespace TerminalAppLocalTests
|
||||
|
||||
TEST_METHOD(ValidateKeybindingsWarnings);
|
||||
|
||||
TEST_METHOD(ValidateExecuteCommandlineWarning);
|
||||
|
||||
TEST_METHOD(ValidateLegacyGlobalsWarning);
|
||||
|
||||
TEST_METHOD(TestTrailingCommas);
|
||||
@@ -1431,10 +1433,6 @@ namespace TerminalAppLocalTests
|
||||
{
|
||||
"name": "profile3",
|
||||
"closeOnExit": null
|
||||
},
|
||||
{
|
||||
"name": "profile4",
|
||||
"closeOnExit": { "clearly": "not a string" }
|
||||
}
|
||||
]
|
||||
})" };
|
||||
@@ -1449,7 +1447,6 @@ namespace TerminalAppLocalTests
|
||||
|
||||
// Unknown modes parse as "Graceful"
|
||||
VERIFY_ARE_EQUAL(CloseOnExitMode::Graceful, settings._profiles[3].GetCloseOnExitMode());
|
||||
VERIFY_ARE_EQUAL(CloseOnExitMode::Graceful, settings._profiles[4].GetCloseOnExitMode());
|
||||
}
|
||||
void SettingsTests::TestCloseOnExitCompatibilityShim()
|
||||
{
|
||||
@@ -2259,6 +2256,57 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter, settings->_warnings.at(3));
|
||||
}
|
||||
|
||||
void SettingsTests::ValidateExecuteCommandlineWarning()
|
||||
{
|
||||
Log::Comment(L"This test is affected by GH#6949, so we're just skipping it for now.");
|
||||
Log::Result(WEX::Logging::TestResults::Skipped);
|
||||
return;
|
||||
|
||||
// const std::string badSettings{ R"(
|
||||
// {
|
||||
// "defaultProfile": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
// "profiles": [
|
||||
// {
|
||||
// "name" : "profile0",
|
||||
// "guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}"
|
||||
// },
|
||||
// {
|
||||
// "name" : "profile1",
|
||||
// "guid": "{6239a42c-3333-49a3-80bd-e8fdd045185c}"
|
||||
// }
|
||||
// ],
|
||||
// "keybindings": [
|
||||
// { "name":null, "command": { "action": "wt" }, "keys": [ "ctrl+a" ] },
|
||||
// { "name":null, "command": { "action": "wt", "commandline":"" }, "keys": [ "ctrl+b" ] },
|
||||
// { "name":null, "command": { "action": "wt", "commandline":null }, "keys": [ "ctrl+c" ] }
|
||||
// ]
|
||||
// })" };
|
||||
|
||||
// const auto settingsObject = VerifyParseSucceeded(badSettings);
|
||||
|
||||
// auto settings = CascadiaSettings::FromJson(settingsObject);
|
||||
|
||||
// VERIFY_ARE_EQUAL(0u, settings->_globals._keybindings->_keyShortcuts.size());
|
||||
|
||||
// for (const auto& warning : settings->_globals._keybindingsWarnings)
|
||||
// {
|
||||
// Log::Comment(NoThrowString().Format(
|
||||
// L"warning:%d", warning));
|
||||
// }
|
||||
// VERIFY_ARE_EQUAL(3u, settings->_globals._keybindingsWarnings.size());
|
||||
// VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter, settings->_globals._keybindingsWarnings.at(0));
|
||||
// VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter, settings->_globals._keybindingsWarnings.at(1));
|
||||
// VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter, settings->_globals._keybindingsWarnings.at(2));
|
||||
|
||||
// settings->_ValidateKeybindings();
|
||||
|
||||
// VERIFY_ARE_EQUAL(4u, settings->_warnings.size());
|
||||
// VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::AtLeastOneKeybindingWarning, settings->_warnings.at(0));
|
||||
// VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter, settings->_warnings.at(1));
|
||||
// VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter, settings->_warnings.at(2));
|
||||
// VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter, settings->_warnings.at(3));
|
||||
}
|
||||
|
||||
void SettingsTests::ValidateLegacyGlobalsWarning()
|
||||
{
|
||||
const std::string badSettings{ R"(
|
||||
|
||||
@@ -24,6 +24,25 @@ static constexpr bool _IsMouseMessage(UINT uMsg)
|
||||
uMsg == WM_MOUSEMOVE || uMsg == WM_MOUSEWHEEL;
|
||||
}
|
||||
|
||||
// Helper static function to ensure that all ambiguous-width glyphs are reported as narrow.
|
||||
// See microsoft/terminal#2066 for more info.
|
||||
static bool _IsGlyphWideForceNarrowFallback(const std::wstring_view /* glyph */) noexcept
|
||||
{
|
||||
return false; // glyph is not wide.
|
||||
}
|
||||
|
||||
static bool _EnsureStaticInitialization()
|
||||
{
|
||||
// use C++11 magic statics to make sure we only do this once.
|
||||
static bool initialized = []() {
|
||||
// *** THIS IS A SINGLETON ***
|
||||
SetGlyphWidthFallback(_IsGlyphWideForceNarrowFallback);
|
||||
|
||||
return true;
|
||||
}();
|
||||
return initialized;
|
||||
}
|
||||
|
||||
LRESULT CALLBACK HwndTerminal::HwndTerminalWndProc(
|
||||
HWND hwnd,
|
||||
UINT uMsg,
|
||||
@@ -72,7 +91,7 @@ try
|
||||
{
|
||||
const auto bufferData = terminal->_terminal->RetrieveSelectedTextFromBuffer(false);
|
||||
LOG_IF_FAILED(terminal->_CopyTextToSystemClipboard(bufferData, true));
|
||||
terminal->_terminal->ClearSelection();
|
||||
TerminalClearSelection(terminal);
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
@@ -81,6 +100,11 @@ try
|
||||
terminal->_PasteTextFromClipboard();
|
||||
}
|
||||
return 0;
|
||||
case WM_DESTROY:
|
||||
// Release Terminal's hwnd so Teardown doesn't try to destroy it again
|
||||
terminal->_hwnd.release();
|
||||
terminal->Teardown();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
||||
@@ -114,14 +138,16 @@ static bool RegisterTermClass(HINSTANCE hInstance) noexcept
|
||||
}
|
||||
|
||||
HwndTerminal::HwndTerminal(HWND parentHwnd) :
|
||||
_desiredFont{ L"Consolas", 0, 10, { 0, 14 }, CP_UTF8 },
|
||||
_actualFont{ L"Consolas", 0, 10, { 0, 14 }, CP_UTF8, false },
|
||||
_desiredFont{ L"Consolas", 0, DEFAULT_FONT_WEIGHT, { 0, 14 }, CP_UTF8 },
|
||||
_actualFont{ L"Consolas", 0, DEFAULT_FONT_WEIGHT, { 0, 14 }, CP_UTF8, false },
|
||||
_uiaProvider{ nullptr },
|
||||
_uiaProviderInitialized{ false },
|
||||
_currentDpi{ USER_DEFAULT_SCREEN_DPI },
|
||||
_pfnWriteCallback{ nullptr },
|
||||
_multiClickTime{ 500 } // this will be overwritten by the windows system double-click time
|
||||
{
|
||||
_EnsureStaticInitialization();
|
||||
|
||||
HINSTANCE hInstance = wil::GetModuleInstanceHandle();
|
||||
|
||||
if (RegisterTermClass(hInstance))
|
||||
@@ -148,6 +174,11 @@ HwndTerminal::HwndTerminal(HWND parentHwnd) :
|
||||
}
|
||||
}
|
||||
|
||||
HwndTerminal::~HwndTerminal()
|
||||
{
|
||||
Teardown();
|
||||
}
|
||||
|
||||
HRESULT HwndTerminal::Initialize()
|
||||
{
|
||||
_terminal = std::make_unique<::Microsoft::Terminal::Core::Terminal>();
|
||||
@@ -162,9 +193,6 @@ HRESULT HwndTerminal::Initialize()
|
||||
RETURN_IF_FAILED(dxEngine->Enable());
|
||||
_renderer->AddRenderEngine(dxEngine.get());
|
||||
|
||||
const auto pfn = std::bind(&::Microsoft::Console::Render::Renderer::IsGlyphWideByFont, _renderer.get(), std::placeholders::_1);
|
||||
SetGlyphWidthFallback(pfn);
|
||||
|
||||
_UpdateFont(USER_DEFAULT_SCREEN_DPI);
|
||||
RECT windowRect;
|
||||
GetWindowRect(_hwnd.get(), &windowRect);
|
||||
@@ -181,8 +209,8 @@ HRESULT HwndTerminal::Initialize()
|
||||
_terminal->SetBackgroundCallback([](auto) {});
|
||||
|
||||
_terminal->Create(COORD{ 80, 25 }, 1000, *_renderer);
|
||||
_terminal->SetDefaultBackground(RGB(5, 27, 80));
|
||||
_terminal->SetDefaultForeground(RGB(255, 255, 255));
|
||||
_terminal->SetDefaultBackground(RGB(12, 12, 12));
|
||||
_terminal->SetDefaultForeground(RGB(204, 204, 204));
|
||||
_terminal->SetWriteInputCallback([=](std::wstring & input) noexcept { _WriteTextToConnection(input); });
|
||||
localPointerToThread->EnablePainting();
|
||||
|
||||
@@ -191,6 +219,33 @@ HRESULT HwndTerminal::Initialize()
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void HwndTerminal::Teardown() noexcept
|
||||
try
|
||||
{
|
||||
// As a rule, detach resources from the Terminal before shutting them down.
|
||||
// This ensures that teardown is reentrant.
|
||||
|
||||
// Shut down the renderer (and therefore the thread) before we implode
|
||||
if (auto localRenderEngine{ std::exchange(_renderEngine, nullptr) })
|
||||
{
|
||||
if (auto localRenderer{ std::exchange(_renderer, nullptr) })
|
||||
{
|
||||
localRenderer->TriggerTeardown();
|
||||
// renderer is destroyed
|
||||
}
|
||||
// renderEngine is destroyed
|
||||
}
|
||||
|
||||
if (auto localHwnd{ _hwnd.release() })
|
||||
{
|
||||
// If we're being called through WM_DESTROY, we won't get here (hwnd is already released)
|
||||
// If we're not, we may end up in Teardown _again_... but by the time we do, all other
|
||||
// resources have been released and will not be released again.
|
||||
DestroyWindow(localHwnd);
|
||||
}
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
void HwndTerminal::RegisterScrollCallback(std::function<void(int, int, int)> callback)
|
||||
{
|
||||
_terminal->SetScrollPositionChangedCallback(callback);
|
||||
@@ -467,11 +522,21 @@ try
|
||||
}
|
||||
CATCH_RETURN();
|
||||
|
||||
void HwndTerminal::_ClearSelection() noexcept
|
||||
try
|
||||
{
|
||||
auto lock{ _terminal->LockForWriting() };
|
||||
_terminal->ClearSelection();
|
||||
_renderer->TriggerSelection();
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
void _stdcall TerminalClearSelection(void* terminal)
|
||||
{
|
||||
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
|
||||
publicTerminal->_terminal->ClearSelection();
|
||||
auto publicTerminal = static_cast<HwndTerminal*>(terminal);
|
||||
publicTerminal->_ClearSelection();
|
||||
}
|
||||
|
||||
bool _stdcall TerminalIsSelectionActive(void* terminal)
|
||||
{
|
||||
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
|
||||
@@ -482,9 +547,10 @@ bool _stdcall TerminalIsSelectionActive(void* terminal)
|
||||
// Returns the selected text in the terminal.
|
||||
const wchar_t* _stdcall TerminalGetSelection(void* terminal)
|
||||
{
|
||||
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
|
||||
auto publicTerminal = static_cast<HwndTerminal*>(terminal);
|
||||
|
||||
const auto bufferData = publicTerminal->_terminal->RetrieveSelectedTextFromBuffer(false);
|
||||
publicTerminal->_ClearSelection();
|
||||
|
||||
// convert text: vector<string> --> string
|
||||
std::wstring selectedText;
|
||||
@@ -494,8 +560,6 @@ const wchar_t* _stdcall TerminalGetSelection(void* terminal)
|
||||
}
|
||||
|
||||
auto returnText = wil::make_cotaskmem_string_nothrow(selectedText.c_str());
|
||||
TerminalClearSelection(terminal);
|
||||
|
||||
return returnText.release();
|
||||
}
|
||||
|
||||
@@ -574,7 +638,7 @@ try
|
||||
{
|
||||
if (_terminal->IsSelectionActive())
|
||||
{
|
||||
_terminal->ClearSelection();
|
||||
_ClearSelection();
|
||||
if (ch == UNICODE_ESC)
|
||||
{
|
||||
// ESC should clear any selection before it triggers input.
|
||||
@@ -632,7 +696,7 @@ void _stdcall TerminalSetTheme(void* terminal, TerminalTheme theme, LPCWSTR font
|
||||
|
||||
publicTerminal->_terminal->SetCursorStyle(theme.CursorStyle);
|
||||
|
||||
publicTerminal->_desiredFont = { fontFamily, 0, 10, { 0, fontSize }, CP_UTF8 };
|
||||
publicTerminal->_desiredFont = { fontFamily, 0, DEFAULT_FONT_WEIGHT, { 0, fontSize }, CP_UTF8 };
|
||||
publicTerminal->_UpdateFont(newDpi);
|
||||
|
||||
// When the font changes the terminal dimensions need to be recalculated since the available row and column
|
||||
|
||||
@@ -51,9 +51,10 @@ public:
|
||||
HwndTerminal(HwndTerminal&&) = default;
|
||||
HwndTerminal& operator=(const HwndTerminal&) = default;
|
||||
HwndTerminal& operator=(HwndTerminal&&) = default;
|
||||
~HwndTerminal() = default;
|
||||
~HwndTerminal();
|
||||
|
||||
HRESULT Initialize();
|
||||
void Teardown() noexcept;
|
||||
void SendOutput(std::wstring_view data);
|
||||
HRESULT Refresh(const SIZE windowSize, _Out_ COORD* dimensions);
|
||||
void RegisterScrollCallback(std::function<void(int, int, int)> callback);
|
||||
@@ -112,6 +113,8 @@ private:
|
||||
HRESULT _MoveSelection(LPARAM lParam) noexcept;
|
||||
IRawElementProviderSimple* _GetUiaProvider() noexcept;
|
||||
|
||||
void _ClearSelection() noexcept;
|
||||
|
||||
bool _CanSendVTMouseInput() const noexcept;
|
||||
bool _SendMouseEvent(UINT uMsg, WPARAM wParam, LPARAM lParam) noexcept;
|
||||
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
#include "ActionArgs.h"
|
||||
#include "ActionAndArgs.h"
|
||||
#include "ActionAndArgs.g.cpp"
|
||||
|
||||
#include "JsonUtils.h"
|
||||
|
||||
#include <LibraryResources.h>
|
||||
|
||||
static constexpr std::string_view CopyTextKey{ "copy" };
|
||||
@@ -35,6 +38,7 @@ static constexpr std::string_view ToggleAlwaysOnTopKey{ "toggleAlwaysOnTop" };
|
||||
static constexpr std::string_view SetTabColorKey{ "setTabColor" };
|
||||
static constexpr std::string_view OpenTabColorPickerKey{ "openTabColorPicker" };
|
||||
static constexpr std::string_view RenameTabKey{ "renameTab" };
|
||||
static constexpr std::string_view ExecuteCommandlineKey{ "wt" };
|
||||
static constexpr std::string_view ToggleCommandPaletteKey{ "commandPalette" };
|
||||
|
||||
static constexpr std::string_view ActionKey{ "action" };
|
||||
@@ -44,6 +48,8 @@ static constexpr std::string_view UnboundKey{ "unbound" };
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
using namespace ::TerminalApp;
|
||||
|
||||
// Specifically use a map here over an unordered_map. We want to be able to
|
||||
// iterate over these entries in-order when we're serializing the keybindings.
|
||||
// HERE BE DRAGONS:
|
||||
@@ -84,6 +90,7 @@ namespace winrt::TerminalApp::implementation
|
||||
{ UnboundKey, ShortcutAction::Invalid },
|
||||
{ FindKey, ShortcutAction::Find },
|
||||
{ RenameTabKey, ShortcutAction::RenameTab },
|
||||
{ ExecuteCommandlineKey, ShortcutAction::ExecuteCommandline },
|
||||
{ ToggleCommandPaletteKey, ShortcutAction::ToggleCommandPalette },
|
||||
};
|
||||
|
||||
@@ -116,6 +123,8 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
{ ShortcutAction::RenameTab, winrt::TerminalApp::implementation::RenameTabArgs::FromJson },
|
||||
|
||||
{ ShortcutAction::ExecuteCommandline, winrt::TerminalApp::implementation::ExecuteCommandlineArgs::FromJson },
|
||||
|
||||
{ ShortcutAction::Invalid, nullptr },
|
||||
};
|
||||
|
||||
@@ -183,11 +192,9 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
else if (json.isObject())
|
||||
{
|
||||
const auto actionVal = json[JsonKey(ActionKey)];
|
||||
if (actionVal.isString())
|
||||
if (const auto actionString{ JsonUtils::GetValueForKey<std::optional<std::string>>(json, ActionKey) })
|
||||
{
|
||||
auto actionString = actionVal.asString();
|
||||
action = GetActionFromString(actionString);
|
||||
action = GetActionFromString(*actionString);
|
||||
argsVal = json;
|
||||
}
|
||||
}
|
||||
@@ -265,6 +272,7 @@ namespace winrt::TerminalApp::implementation
|
||||
{ ShortcutAction::SetTabColor, RS_(L"ResetTabColorCommandKey") },
|
||||
{ ShortcutAction::OpenTabColorPicker, RS_(L"OpenTabColorPickerCommandKey") },
|
||||
{ ShortcutAction::RenameTab, RS_(L"ResetTabNameCommandKey") },
|
||||
{ ShortcutAction::ExecuteCommandline, RS_(L"ExecuteCommandlineCommandKey") },
|
||||
{ ShortcutAction::ToggleCommandPalette, RS_(L"ToggleCommandPaletteCommandKey") },
|
||||
};
|
||||
}();
|
||||
@@ -281,5 +289,4 @@ namespace winrt::TerminalApp::implementation
|
||||
const auto found = GeneratedActionNames.find(_Action);
|
||||
return found != GeneratedActionNames.end() ? found->second : L"";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "OpenSettingsArgs.g.cpp"
|
||||
#include "SetTabColorArgs.g.cpp"
|
||||
#include "RenameTabArgs.g.cpp"
|
||||
#include "ExecuteCommandlineArgs.g.cpp"
|
||||
|
||||
#include <LibraryResources.h>
|
||||
|
||||
@@ -258,4 +259,17 @@ namespace winrt::TerminalApp::implementation
|
||||
return RS_(L"ResetTabNameCommandKey");
|
||||
}
|
||||
|
||||
winrt::hstring ExecuteCommandlineArgs::GenerateName() const
|
||||
{
|
||||
// "Run commandline "{_Commandline}" in this window"
|
||||
if (!_Commandline.empty())
|
||||
{
|
||||
return winrt::hstring{
|
||||
fmt::format(std::wstring_view(RS_(L"ExecuteCommandlineCommandKey")),
|
||||
_Commandline.c_str())
|
||||
};
|
||||
}
|
||||
return L"";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,12 +17,15 @@
|
||||
#include "OpenSettingsArgs.g.h"
|
||||
#include "SetTabColorArgs.g.h"
|
||||
#include "RenameTabArgs.g.h"
|
||||
#include "ExecuteCommandlineArgs.g.h"
|
||||
|
||||
#include "../../cascadia/inc/cppwinrt_utils.h"
|
||||
#include "Utils.h"
|
||||
#include "JsonUtils.h"
|
||||
#include "TerminalWarnings.h"
|
||||
|
||||
#include "TerminalSettingsSerializationHelpers.h"
|
||||
|
||||
// Notes on defining ActionArgs and ActionEventArgs:
|
||||
// * All properties specific to an action should be defined as an ActionArgs
|
||||
// class that implements IActionArgs
|
||||
@@ -31,6 +34,7 @@
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
using namespace ::TerminalApp;
|
||||
using FromJsonResult = std::tuple<winrt::TerminalApp::IActionArgs, std::vector<::TerminalApp::SettingsLoadWarnings>>;
|
||||
|
||||
struct ActionEventArgs : public ActionEventArgsT<ActionEventArgs>
|
||||
@@ -73,26 +77,11 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
// LOAD BEARING: Not using make_self here _will_ break you in the future!
|
||||
auto args = winrt::make_self<NewTerminalArgs>();
|
||||
if (auto commandline{ json[JsonKey(CommandlineKey)] })
|
||||
{
|
||||
args->_Commandline = winrt::to_hstring(commandline.asString());
|
||||
}
|
||||
if (auto startingDirectory{ json[JsonKey(StartingDirectoryKey)] })
|
||||
{
|
||||
args->_StartingDirectory = winrt::to_hstring(startingDirectory.asString());
|
||||
}
|
||||
if (auto tabTitle{ json[JsonKey(TabTitleKey)] })
|
||||
{
|
||||
args->_TabTitle = winrt::to_hstring(tabTitle.asString());
|
||||
}
|
||||
if (auto index{ json[JsonKey(ProfileIndexKey)] })
|
||||
{
|
||||
args->_ProfileIndex = index.asInt();
|
||||
}
|
||||
if (auto profile{ json[JsonKey(ProfileKey)] })
|
||||
{
|
||||
args->_Profile = winrt::to_hstring(profile.asString());
|
||||
}
|
||||
JsonUtils::GetValueForKey(json, CommandlineKey, args->_Commandline);
|
||||
JsonUtils::GetValueForKey(json, StartingDirectoryKey, args->_StartingDirectory);
|
||||
JsonUtils::GetValueForKey(json, TabTitleKey, args->_TabTitle);
|
||||
JsonUtils::GetValueForKey(json, ProfileIndexKey, args->_ProfileIndex);
|
||||
JsonUtils::GetValueForKey(json, ProfileKey, args->_Profile);
|
||||
return *args;
|
||||
}
|
||||
};
|
||||
@@ -120,10 +109,7 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
// LOAD BEARING: Not using make_self here _will_ break you in the future!
|
||||
auto args = winrt::make_self<CopyTextArgs>();
|
||||
if (auto singleLine{ json[JsonKey(SingleLineKey)] })
|
||||
{
|
||||
args->_SingleLine = singleLine.asBool();
|
||||
}
|
||||
JsonUtils::GetValueForKey(json, SingleLineKey, args->_SingleLine);
|
||||
return { *args, {} };
|
||||
}
|
||||
};
|
||||
@@ -177,49 +163,11 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
// LOAD BEARING: Not using make_self here _will_ break you in the future!
|
||||
auto args = winrt::make_self<SwitchToTabArgs>();
|
||||
if (auto tabIndex{ json[JsonKey(TabIndexKey)] })
|
||||
{
|
||||
args->_TabIndex = tabIndex.asUInt();
|
||||
}
|
||||
JsonUtils::GetValueForKey(json, TabIndexKey, args->_TabIndex);
|
||||
return { *args, {} };
|
||||
}
|
||||
};
|
||||
|
||||
// Possible Direction values
|
||||
// TODO:GH#2550/#3475 - move these to a centralized deserializing place
|
||||
static constexpr std::string_view LeftString{ "left" };
|
||||
static constexpr std::string_view RightString{ "right" };
|
||||
static constexpr std::string_view UpString{ "up" };
|
||||
static constexpr std::string_view DownString{ "down" };
|
||||
|
||||
// Function Description:
|
||||
// - Helper function for parsing a Direction from a string
|
||||
// Arguments:
|
||||
// - directionString: the string to attempt to parse
|
||||
// Return Value:
|
||||
// - The encoded Direction value, or Direction::None if it was an invalid string
|
||||
static TerminalApp::Direction ParseDirection(const std::string& directionString)
|
||||
{
|
||||
if (directionString == LeftString)
|
||||
{
|
||||
return TerminalApp::Direction::Left;
|
||||
}
|
||||
else if (directionString == RightString)
|
||||
{
|
||||
return TerminalApp::Direction::Right;
|
||||
}
|
||||
else if (directionString == UpString)
|
||||
{
|
||||
return TerminalApp::Direction::Up;
|
||||
}
|
||||
else if (directionString == DownString)
|
||||
{
|
||||
return TerminalApp::Direction::Down;
|
||||
}
|
||||
// default behavior for invalid data
|
||||
return TerminalApp::Direction::None;
|
||||
};
|
||||
|
||||
struct ResizePaneArgs : public ResizePaneArgsT<ResizePaneArgs>
|
||||
{
|
||||
ResizePaneArgs() = default;
|
||||
@@ -243,10 +191,7 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
// LOAD BEARING: Not using make_self here _will_ break you in the future!
|
||||
auto args = winrt::make_self<ResizePaneArgs>();
|
||||
if (auto directionString{ json[JsonKey(DirectionKey)] })
|
||||
{
|
||||
args->_Direction = ParseDirection(directionString.asString());
|
||||
}
|
||||
JsonUtils::GetValueForKey(json, DirectionKey, args->_Direction);
|
||||
if (args->_Direction == TerminalApp::Direction::None)
|
||||
{
|
||||
return { nullptr, { ::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter } };
|
||||
@@ -281,10 +226,7 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
// LOAD BEARING: Not using make_self here _will_ break you in the future!
|
||||
auto args = winrt::make_self<MoveFocusArgs>();
|
||||
if (auto directionString{ json[JsonKey(DirectionKey)] })
|
||||
{
|
||||
args->_Direction = ParseDirection(directionString.asString());
|
||||
}
|
||||
JsonUtils::GetValueForKey(json, DirectionKey, args->_Direction);
|
||||
if (args->_Direction == TerminalApp::Direction::None)
|
||||
{
|
||||
return { nullptr, { ::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter } };
|
||||
@@ -319,48 +261,11 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
// LOAD BEARING: Not using make_self here _will_ break you in the future!
|
||||
auto args = winrt::make_self<AdjustFontSizeArgs>();
|
||||
if (auto jsonDelta{ json[JsonKey(AdjustFontSizeDelta)] })
|
||||
{
|
||||
args->_Delta = jsonDelta.asInt();
|
||||
}
|
||||
JsonUtils::GetValueForKey(json, AdjustFontSizeDelta, args->_Delta);
|
||||
return { *args, {} };
|
||||
}
|
||||
};
|
||||
|
||||
// Possible SplitState values
|
||||
// TODO:GH#2550/#3475 - move these to a centralized deserializing place
|
||||
static constexpr std::string_view VerticalKey{ "vertical" };
|
||||
static constexpr std::string_view HorizontalKey{ "horizontal" };
|
||||
static constexpr std::string_view AutomaticKey{ "auto" };
|
||||
static TerminalApp::SplitState ParseSplitState(const std::string& stateString)
|
||||
{
|
||||
if (stateString == VerticalKey)
|
||||
{
|
||||
return TerminalApp::SplitState::Vertical;
|
||||
}
|
||||
else if (stateString == HorizontalKey)
|
||||
{
|
||||
return TerminalApp::SplitState::Horizontal;
|
||||
}
|
||||
else if (stateString == AutomaticKey)
|
||||
{
|
||||
return TerminalApp::SplitState::Automatic;
|
||||
}
|
||||
// default behavior for invalid data
|
||||
return TerminalApp::SplitState::Automatic;
|
||||
};
|
||||
|
||||
// Possible SplitType values
|
||||
static constexpr std::string_view DuplicateKey{ "duplicate" };
|
||||
static TerminalApp::SplitType ParseSplitModeState(const std::string& stateString)
|
||||
{
|
||||
if (stateString == DuplicateKey)
|
||||
{
|
||||
return TerminalApp::SplitType::Duplicate;
|
||||
}
|
||||
return TerminalApp::SplitType::Manual;
|
||||
}
|
||||
|
||||
struct SplitPaneArgs : public SplitPaneArgsT<SplitPaneArgs>
|
||||
{
|
||||
SplitPaneArgs() = default;
|
||||
@@ -391,48 +296,12 @@ namespace winrt::TerminalApp::implementation
|
||||
// LOAD BEARING: Not using make_self here _will_ break you in the future!
|
||||
auto args = winrt::make_self<SplitPaneArgs>();
|
||||
args->_TerminalArgs = NewTerminalArgs::FromJson(json);
|
||||
if (auto jsonStyle{ json[JsonKey(SplitKey)] })
|
||||
{
|
||||
args->_SplitStyle = ParseSplitState(jsonStyle.asString());
|
||||
}
|
||||
if (auto jsonStyle{ json[JsonKey(SplitModeKey)] })
|
||||
{
|
||||
args->_SplitMode = ParseSplitModeState(jsonStyle.asString());
|
||||
}
|
||||
JsonUtils::GetValueForKey(json, SplitKey, args->_SplitStyle);
|
||||
JsonUtils::GetValueForKey(json, SplitModeKey, args->_SplitMode);
|
||||
return { *args, {} };
|
||||
}
|
||||
};
|
||||
|
||||
// Possible SettingsTarget values
|
||||
// TODO:GH#2550/#3475 - move these to a centralized deserializing place
|
||||
static constexpr std::string_view SettingsFileString{ "settingsFile" };
|
||||
static constexpr std::string_view DefaultsFileString{ "defaultsFile" };
|
||||
static constexpr std::string_view AllFilesString{ "allFiles" };
|
||||
|
||||
// Function Description:
|
||||
// - Helper function for parsing a SettingsTarget from a string
|
||||
// Arguments:
|
||||
// - targetString: the string to attempt to parse
|
||||
// Return Value:
|
||||
// - The encoded SettingsTarget value, or SettingsTarget::SettingsFile if it was an invalid string
|
||||
static TerminalApp::SettingsTarget ParseSettingsTarget(const std::string& targetString)
|
||||
{
|
||||
if (targetString == SettingsFileString)
|
||||
{
|
||||
return TerminalApp::SettingsTarget::SettingsFile;
|
||||
}
|
||||
else if (targetString == DefaultsFileString)
|
||||
{
|
||||
return TerminalApp::SettingsTarget::DefaultsFile;
|
||||
}
|
||||
else if (targetString == AllFilesString)
|
||||
{
|
||||
return TerminalApp::SettingsTarget::AllFiles;
|
||||
}
|
||||
// default behavior for invalid data
|
||||
return TerminalApp::SettingsTarget::SettingsFile;
|
||||
};
|
||||
|
||||
struct OpenSettingsArgs : public OpenSettingsArgsT<OpenSettingsArgs>
|
||||
{
|
||||
OpenSettingsArgs() = default;
|
||||
@@ -456,10 +325,7 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
// LOAD BEARING: Not using make_self here _will_ break you in the future!
|
||||
auto args = winrt::make_self<OpenSettingsArgs>();
|
||||
if (auto targetString{ json[JsonKey(TargetKey)] })
|
||||
{
|
||||
args->_Target = ParseSettingsTarget(targetString.asString());
|
||||
}
|
||||
JsonUtils::GetValueForKey(json, TargetKey, args->_Target);
|
||||
return { *args, {} };
|
||||
}
|
||||
};
|
||||
@@ -487,16 +353,10 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
// LOAD BEARING: Not using make_self here _will_ break you in the future!
|
||||
auto args = winrt::make_self<SetTabColorArgs>();
|
||||
std::optional<til::color> temp;
|
||||
try
|
||||
if (const auto temp{ JsonUtils::GetValueForKey<std::optional<til::color>>(json, ColorKey) })
|
||||
{
|
||||
::TerminalApp::JsonUtils::GetOptionalColor(json, ColorKey, temp);
|
||||
if (temp.has_value())
|
||||
{
|
||||
args->_TabColor = static_cast<uint32_t>(temp.value());
|
||||
}
|
||||
args->_TabColor = static_cast<uint32_t>(*temp);
|
||||
}
|
||||
CATCH_LOG();
|
||||
return { *args, {} };
|
||||
}
|
||||
};
|
||||
@@ -524,13 +384,43 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
// LOAD BEARING: Not using make_self here _will_ break you in the future!
|
||||
auto args = winrt::make_self<RenameTabArgs>();
|
||||
if (auto title{ json[JsonKey(TitleKey)] })
|
||||
JsonUtils::GetValueForKey(json, TitleKey, args->_Title);
|
||||
return { *args, {} };
|
||||
}
|
||||
};
|
||||
|
||||
struct ExecuteCommandlineArgs : public ExecuteCommandlineArgsT<ExecuteCommandlineArgs>
|
||||
{
|
||||
ExecuteCommandlineArgs() = default;
|
||||
GETSET_PROPERTY(winrt::hstring, Commandline, L"");
|
||||
|
||||
static constexpr std::string_view CommandlineKey{ "commandline" };
|
||||
|
||||
public:
|
||||
hstring GenerateName() const;
|
||||
|
||||
bool Equals(const IActionArgs& other)
|
||||
{
|
||||
auto otherAsUs = other.try_as<ExecuteCommandlineArgs>();
|
||||
if (otherAsUs)
|
||||
{
|
||||
args->_Title = winrt::to_hstring(title.asString());
|
||||
return otherAsUs->_Commandline == _Commandline;
|
||||
}
|
||||
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<ExecuteCommandlineArgs>();
|
||||
JsonUtils::GetValueForKey(json, CommandlineKey, args->_Commandline);
|
||||
if (args->_Commandline.empty())
|
||||
{
|
||||
return { nullptr, { ::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter } };
|
||||
}
|
||||
return { *args, {} };
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace winrt::TerminalApp::factory_implementation
|
||||
|
||||
@@ -115,4 +115,9 @@ namespace TerminalApp
|
||||
{
|
||||
String Title { get; };
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass ExecuteCommandlineArgs : IActionArgs
|
||||
{
|
||||
String Commandline;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ using namespace winrt::Windows::ApplicationModel::DataTransfer;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace winrt::Windows::UI::Text;
|
||||
using namespace winrt::Windows::UI::Core;
|
||||
using namespace winrt::Windows::Foundation::Collections;
|
||||
using namespace winrt::Windows::System;
|
||||
using namespace winrt::Microsoft::Terminal;
|
||||
using namespace winrt::Microsoft::Terminal::Settings;
|
||||
@@ -334,4 +335,20 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleExecuteCommandline(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& actionArgs)
|
||||
{
|
||||
if (const auto& realArgs = actionArgs.ActionArgs().try_as<TerminalApp::ExecuteCommandlineArgs>())
|
||||
{
|
||||
auto actions = winrt::single_threaded_vector<winrt::TerminalApp::ActionAndArgs>(std::move(
|
||||
TerminalPage::ConvertExecuteCommandlineToActions(realArgs)));
|
||||
|
||||
if (_startupActions.Size() != 0)
|
||||
{
|
||||
actionArgs.Handled(true);
|
||||
_ProcessStartupActions(actions, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -599,7 +599,7 @@ void AppCommandlineArgs::_addCommandsForArg(std::vector<Commandline>& commands,
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - the deque of actions we've buffered as a result of parsing commands.
|
||||
std::deque<winrt::TerminalApp::ActionAndArgs>& AppCommandlineArgs::GetStartupActions()
|
||||
std::vector<winrt::TerminalApp::ActionAndArgs>& AppCommandlineArgs::GetStartupActions()
|
||||
{
|
||||
return _startupActions;
|
||||
}
|
||||
@@ -658,7 +658,8 @@ void AppCommandlineArgs::ValidateStartupCommands()
|
||||
auto newTerminalArgs = winrt::make_self<implementation::NewTerminalArgs>();
|
||||
args->TerminalArgs(*newTerminalArgs);
|
||||
newTabAction->Args(*args);
|
||||
_startupActions.push_front(*newTabAction);
|
||||
// push the arg onto the front
|
||||
_startupActions.insert(_startupActions.begin(), 1, *newTabAction);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -666,3 +667,52 @@ std::optional<winrt::TerminalApp::LaunchMode> AppCommandlineArgs::GetLaunchMode(
|
||||
{
|
||||
return _launchMode;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Attempts to parse an array of commandline args into a list of
|
||||
// commands to execute, and then parses these commands. As commands are
|
||||
// successfully parsed, they will generate ShortcutActions for us to be
|
||||
// able to execute. If we fail to parse any commands, we'll return the
|
||||
// error code from the failure to parse that command, and stop processing
|
||||
// additional commands.
|
||||
// - The first arg in args should be the program name "wt" (or some variant). It
|
||||
// will be ignored during parsing.
|
||||
// Arguments:
|
||||
// - args: an array of strings to process as a commandline. These args can contain spaces
|
||||
// Return Value:
|
||||
// - 0 if the commandline was successfully parsed
|
||||
int AppCommandlineArgs::ParseArgs(winrt::array_view<const winrt::hstring>& args)
|
||||
{
|
||||
auto commands = ::TerminalApp::AppCommandlineArgs::BuildCommands(args);
|
||||
|
||||
for (auto& cmdBlob : commands)
|
||||
{
|
||||
// On one hand, it seems like we should be able to have one
|
||||
// AppCommandlineArgs for parsing all of them, and collect the
|
||||
// results one at a time.
|
||||
//
|
||||
// On the other hand, re-using a CLI::App seems to leave state from
|
||||
// previous parsings around, so we could get mysterious behavior
|
||||
// where one command affects the values of the next.
|
||||
//
|
||||
// From https://cliutils.github.io/CLI11/book/chapters/options.html:
|
||||
// > If that option is not given, CLI11 will not touch the initial
|
||||
// > value. This allows you to set up defaults by simply setting
|
||||
// > your value beforehand.
|
||||
//
|
||||
// So we pretty much need the to either manually reset the state
|
||||
// each command, or build new ones.
|
||||
const auto result = ParseCommand(cmdBlob);
|
||||
|
||||
// If this succeeded, result will be 0. Otherwise, the caller should
|
||||
// exit(result), to exit the program.
|
||||
if (result != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// If all the args were successfully parsed, we'll have some commands
|
||||
// built in _appArgs, which we'll use when the application starts up.
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -28,13 +28,15 @@ public:
|
||||
|
||||
AppCommandlineArgs();
|
||||
~AppCommandlineArgs() = default;
|
||||
|
||||
int ParseCommand(const Commandline& command);
|
||||
int ParseArgs(winrt::array_view<const winrt::hstring>& args);
|
||||
|
||||
static std::vector<Commandline> BuildCommands(const std::vector<const wchar_t*>& args);
|
||||
static std::vector<Commandline> BuildCommands(winrt::array_view<const winrt::hstring>& args);
|
||||
|
||||
void ValidateStartupCommands();
|
||||
std::deque<winrt::TerminalApp::ActionAndArgs>& GetStartupActions();
|
||||
std::vector<winrt::TerminalApp::ActionAndArgs>& GetStartupActions();
|
||||
const std::string& GetExitMessage();
|
||||
bool ShouldExitEarly() const noexcept;
|
||||
|
||||
@@ -90,7 +92,7 @@ private:
|
||||
std::optional<winrt::TerminalApp::LaunchMode> _launchMode{ std::nullopt };
|
||||
// Are you adding more args here? Make sure to reset them in _resetStateToDefault
|
||||
|
||||
std::deque<winrt::TerminalApp::ActionAndArgs> _startupActions;
|
||||
std::vector<winrt::TerminalApp::ActionAndArgs> _startupActions;
|
||||
std::string _exitMessage;
|
||||
bool _shouldExitEarly{ false };
|
||||
|
||||
|
||||
@@ -474,7 +474,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - a point containing the requested dimensions in pixels.
|
||||
winrt::Windows::Foundation::Point AppLogic::GetLaunchDimensions(uint32_t dpi)
|
||||
winrt::Windows::Foundation::Size AppLogic::GetLaunchDimensions(uint32_t dpi)
|
||||
{
|
||||
if (!_loadedInitialSettings)
|
||||
{
|
||||
@@ -504,7 +504,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// of the height calculation here.
|
||||
auto titlebar = TitlebarControl{ static_cast<uint64_t>(0) };
|
||||
titlebar.Measure({ SHRT_MAX, SHRT_MAX });
|
||||
proposedSize.Y += (titlebar.DesiredSize().Height) * scale;
|
||||
proposedSize.Height += (titlebar.DesiredSize().Height) * scale;
|
||||
}
|
||||
else if (_settings->GlobalSettings().AlwaysShowTabs())
|
||||
{
|
||||
@@ -519,7 +519,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// For whatever reason, there's about 6px of unaccounted-for space
|
||||
// in the application. I couldn't tell you where these 6px are
|
||||
// coming from, but they need to be included in this math.
|
||||
proposedSize.Y += (tabControl.DesiredSize().Height + 6) * scale;
|
||||
proposedSize.Width += (tabControl.DesiredSize().Height + 6) * scale;
|
||||
}
|
||||
|
||||
return proposedSize;
|
||||
@@ -974,7 +974,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// or 0. (see AppLogic::_ParseArgs)
|
||||
int32_t AppLogic::SetStartupCommandline(array_view<const winrt::hstring> args)
|
||||
{
|
||||
const auto result = _ParseArgs(args);
|
||||
const auto result = _appArgs.ParseArgs(args);
|
||||
if (result == 0)
|
||||
{
|
||||
_appArgs.ValidateStartupCommands();
|
||||
@@ -984,53 +984,6 @@ namespace winrt::TerminalApp::implementation
|
||||
return result;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Attempts to parse an array of commandline args into a list of
|
||||
// commands to execute, and then parses these commands. As commands are
|
||||
// successfully parsed, they will generate ShortcutActions for us to be
|
||||
// able to execute. If we fail to parse any commands, we'll return the
|
||||
// error code from the failure to parse that command, and stop processing
|
||||
// additional commands.
|
||||
// Arguments:
|
||||
// - args: an array of strings to process as a commandline. These args can contain spaces
|
||||
// Return Value:
|
||||
// - 0 if the commandline was successfully parsed
|
||||
int AppLogic::_ParseArgs(winrt::array_view<const hstring>& args)
|
||||
{
|
||||
auto commands = ::TerminalApp::AppCommandlineArgs::BuildCommands(args);
|
||||
|
||||
for (auto& cmdBlob : commands)
|
||||
{
|
||||
// On one hand, it seems like we should be able to have one
|
||||
// AppCommandlineArgs for parsing all of them, and collect the
|
||||
// results one at a time.
|
||||
//
|
||||
// On the other hand, re-using a CLI::App seems to leave state from
|
||||
// previous parsings around, so we could get mysterious behavior
|
||||
// where one command affects the values of the next.
|
||||
//
|
||||
// From https://cliutils.github.io/CLI11/book/chapters/options.html:
|
||||
// > If that option is not given, CLI11 will not touch the initial
|
||||
// > value. This allows you to set up defaults by simply setting
|
||||
// > your value beforehand.
|
||||
//
|
||||
// So we pretty much need the to either manually reset the state
|
||||
// each command, or build new ones.
|
||||
const auto result = _appArgs.ParseCommand(cmdBlob);
|
||||
|
||||
// If this succeeded, result will be 0. Otherwise, the caller should
|
||||
// exit(result), to exit the program.
|
||||
if (result != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// If all the args were successfully parsed, we'll have some commands
|
||||
// built in _appArgs, which we'll use when the application starts up.
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - If there were any errors parsing the commandline that was used to
|
||||
// initialize the terminal, this will return a string containing that
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace winrt::TerminalApp::implementation
|
||||
bool Fullscreen() const;
|
||||
bool AlwaysOnTop() const;
|
||||
|
||||
Windows::Foundation::Point GetLaunchDimensions(uint32_t dpi);
|
||||
Windows::Foundation::Size GetLaunchDimensions(uint32_t dpi);
|
||||
winrt::Windows::Foundation::Point GetLaunchInitialPositions(int32_t defaultInitialX, int32_t defaultInitialY);
|
||||
winrt::Windows::UI::Xaml::ElementTheme GetRequestedTheme();
|
||||
LaunchMode GetLaunchMode();
|
||||
|
||||
@@ -45,7 +45,8 @@ namespace TerminalApp
|
||||
Boolean Fullscreen { get; };
|
||||
Boolean AlwaysOnTop { get; };
|
||||
|
||||
Windows.Foundation.Point GetLaunchDimensions(UInt32 dpi);
|
||||
Windows.Foundation.Size GetLaunchDimensions(UInt32 dpi);
|
||||
|
||||
Windows.Foundation.Point GetLaunchInitialPositions(Int32 defaultInitialX, Int32 defaultInitialY);
|
||||
Windows.UI.Xaml.ElementTheme GetRequestedTheme();
|
||||
LaunchMode GetLaunchMode();
|
||||
|
||||
@@ -93,7 +93,7 @@ const Profile* CascadiaSettings::FindProfile(GUID profileGuid) const noexcept
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - an iterable collection of all of our Profiles.
|
||||
std::basic_string_view<Profile> CascadiaSettings::GetProfiles() const noexcept
|
||||
gsl::span<const Profile> CascadiaSettings::GetProfiles() const noexcept
|
||||
{
|
||||
return { &_profiles[0], _profiles.size() };
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ public:
|
||||
|
||||
GlobalAppSettings& GlobalSettings();
|
||||
|
||||
std::basic_string_view<Profile> GetProfiles() const noexcept;
|
||||
gsl::span<const Profile> GetProfiles() const noexcept;
|
||||
|
||||
winrt::TerminalApp::AppKeyBindings GetKeybindings() const noexcept;
|
||||
|
||||
|
||||
@@ -249,9 +249,9 @@ void CascadiaSettings::_LoadDynamicProfiles()
|
||||
const auto disabledProfileSources = CascadiaSettings::_GetDisabledProfileSourcesJsonObject(_userSettings);
|
||||
if (disabledProfileSources.isArray())
|
||||
{
|
||||
for (const auto& ns : disabledProfileSources)
|
||||
for (const auto& json : disabledProfileSources)
|
||||
{
|
||||
ignoredNamespaces.emplace(GetWstringFromJson(ns));
|
||||
ignoredNamespaces.emplace(JsonUtils::GetValue<std::wstring>(json));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -105,9 +105,9 @@ ColorScheme ColorScheme::FromJson(const Json::Value& json)
|
||||
// - true iff the json object has the same `name` as we do.
|
||||
bool ColorScheme::ShouldBeLayered(const Json::Value& json) const
|
||||
{
|
||||
if (const auto name{ json[JsonKey(NameKey)] })
|
||||
std::wstring nameFromJson{};
|
||||
if (JsonUtils::GetValueForKey(json, NameKey, nameFromJson))
|
||||
{
|
||||
const auto nameFromJson = GetWstringFromJson(name);
|
||||
return nameFromJson == _schemeName;
|
||||
}
|
||||
return false;
|
||||
@@ -125,39 +125,16 @@ bool ColorScheme::ShouldBeLayered(const Json::Value& json) const
|
||||
// <none>
|
||||
void ColorScheme::LayerJson(const Json::Value& json)
|
||||
{
|
||||
if (auto name{ json[JsonKey(NameKey)] })
|
||||
{
|
||||
_schemeName = winrt::to_hstring(name.asString());
|
||||
}
|
||||
if (auto fgString{ json[JsonKey(ForegroundKey)] })
|
||||
{
|
||||
const auto color = Utils::ColorFromHexString(fgString.asString());
|
||||
_defaultForeground = color;
|
||||
}
|
||||
if (auto bgString{ json[JsonKey(BackgroundKey)] })
|
||||
{
|
||||
const auto color = Utils::ColorFromHexString(bgString.asString());
|
||||
_defaultBackground = color;
|
||||
}
|
||||
if (auto sbString{ json[JsonKey(SelectionBackgroundKey)] })
|
||||
{
|
||||
const auto color = Utils::ColorFromHexString(sbString.asString());
|
||||
_selectionBackground = color;
|
||||
}
|
||||
if (auto sbString{ json[JsonKey(CursorColorKey)] })
|
||||
{
|
||||
const auto color = Utils::ColorFromHexString(sbString.asString());
|
||||
_cursorColor = color;
|
||||
}
|
||||
JsonUtils::GetValueForKey(json, NameKey, _schemeName);
|
||||
JsonUtils::GetValueForKey(json, ForegroundKey, _defaultForeground);
|
||||
JsonUtils::GetValueForKey(json, BackgroundKey, _defaultBackground);
|
||||
JsonUtils::GetValueForKey(json, SelectionBackgroundKey, _selectionBackground);
|
||||
JsonUtils::GetValueForKey(json, CursorColorKey, _cursorColor);
|
||||
|
||||
int i = 0;
|
||||
for (const auto& current : TableColors)
|
||||
{
|
||||
if (auto str{ json[JsonKey(current)] })
|
||||
{
|
||||
const auto color = Utils::ColorFromHexString(str.asString());
|
||||
_table.at(i) = color;
|
||||
}
|
||||
JsonUtils::GetValueForKey(json, current, _table.at(i));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
@@ -200,11 +177,7 @@ til::color ColorScheme::GetCursorColor() const noexcept
|
||||
// - the name of the color scheme represented by `json` as a std::wstring optional
|
||||
// i.e. the value of the `name` property.
|
||||
// - returns std::nullopt if `json` doesn't have the `name` property
|
||||
std::optional<std::wstring> TerminalApp::ColorScheme::GetNameFromJson(const Json::Value& json)
|
||||
std::optional<std::wstring> ColorScheme::GetNameFromJson(const Json::Value& json)
|
||||
{
|
||||
if (const auto name{ json[JsonKey(NameKey)] })
|
||||
{
|
||||
return GetWstringFromJson(name);
|
||||
}
|
||||
return std::nullopt;
|
||||
return JsonUtils::GetValueForKey<std::optional<std::wstring>>(json, NameKey);
|
||||
}
|
||||
|
||||
@@ -7,10 +7,12 @@
|
||||
|
||||
#include "Utils.h"
|
||||
#include "ActionAndArgs.h"
|
||||
#include "JsonUtils.h"
|
||||
#include <LibraryResources.h>
|
||||
|
||||
using namespace winrt::Microsoft::Terminal::Settings;
|
||||
using namespace winrt::TerminalApp;
|
||||
using namespace ::TerminalApp;
|
||||
|
||||
static constexpr std::string_view NameKey{ "name" };
|
||||
static constexpr std::string_view IconPathKey{ "iconPath" };
|
||||
@@ -35,25 +37,17 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
if (name.isObject())
|
||||
{
|
||||
try
|
||||
if (const auto resourceKey{ JsonUtils::GetValueForKey<std::optional<std::wstring>>(name, "key") })
|
||||
{
|
||||
if (const auto keyJson{ name[JsonKey("key")] })
|
||||
if (HasLibraryResourceWithName(*resourceKey))
|
||||
{
|
||||
// Make sure the key is present before we try
|
||||
// loading it. Otherwise we'll crash
|
||||
const auto resourceKey = GetWstringFromJson(keyJson);
|
||||
if (HasLibraryResourceWithName(resourceKey))
|
||||
{
|
||||
return GetLibraryResourceString(resourceKey);
|
||||
}
|
||||
return GetLibraryResourceString(*resourceKey);
|
||||
}
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
else if (name.isString())
|
||||
{
|
||||
auto nameStr = name.asString();
|
||||
return winrt::to_hstring(nameStr);
|
||||
return JsonUtils::GetValue<winrt::hstring>(name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -271,16 +271,17 @@ namespace winrt::TerminalApp::implementation
|
||||
};
|
||||
|
||||
// Method Description:
|
||||
// - Update our list of filtered actions to reflect the current contents of
|
||||
// - Produce a list of filtered actions to reflect the current contents of
|
||||
// the input box. For more details on which commands will be displayed,
|
||||
// see `_getWeight`.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// - A collection that will receive the filtered actions
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void CommandPalette::_updateFilteredActions()
|
||||
std::vector<winrt::TerminalApp::Command> CommandPalette::_collectFilteredActions()
|
||||
{
|
||||
_filteredActions.Clear();
|
||||
std::vector<winrt::TerminalApp::Command> actions;
|
||||
|
||||
auto searchText = _searchBox().Text();
|
||||
const bool addAll = searchText.empty();
|
||||
|
||||
@@ -303,10 +304,10 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
for (auto action : sortedCommands)
|
||||
{
|
||||
_filteredActions.Append(action);
|
||||
actions.push_back(action);
|
||||
}
|
||||
|
||||
return;
|
||||
return actions;
|
||||
}
|
||||
|
||||
// Here, there was some filter text.
|
||||
@@ -343,7 +344,56 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
auto top = heap.top();
|
||||
heap.pop();
|
||||
_filteredActions.Append(top.command);
|
||||
actions.push_back(top.command);
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Update our list of filtered actions to reflect the current contents of
|
||||
// the input box. For more details on which commands will be displayed,
|
||||
// see `_getWeight`.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void CommandPalette::_updateFilteredActions()
|
||||
{
|
||||
auto actions = _collectFilteredActions();
|
||||
|
||||
// Make _filteredActions look identical to actions, using only Insert and Remove.
|
||||
// This allows WinUI to nicely animate the ListView as it changes.
|
||||
for (uint32_t i = 0; i < _filteredActions.Size() && i < actions.size(); i++)
|
||||
{
|
||||
for (uint32_t j = i; j < _filteredActions.Size(); j++)
|
||||
{
|
||||
if (_filteredActions.GetAt(j) == actions[i])
|
||||
{
|
||||
for (uint32_t k = i; k < j; k++)
|
||||
{
|
||||
_filteredActions.RemoveAt(i);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (_filteredActions.GetAt(i) != actions[i])
|
||||
{
|
||||
_filteredActions.InsertAt(i, actions[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove any extra trailing items from the destination
|
||||
while (_filteredActions.Size() > actions.size())
|
||||
{
|
||||
_filteredActions.RemoveAtEnd();
|
||||
}
|
||||
|
||||
// Add any extra trailing items from the source
|
||||
while (_filteredActions.Size() < actions.size())
|
||||
{
|
||||
_filteredActions.Append(actions[_filteredActions.Size()]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ namespace winrt::TerminalApp::implementation
|
||||
void _selectNextItem(const bool moveDown);
|
||||
|
||||
void _updateFilteredActions();
|
||||
std::vector<winrt::TerminalApp::Command> _collectFilteredActions();
|
||||
static int _getWeight(const winrt::hstring& searchText, const winrt::hstring& name);
|
||||
void _close();
|
||||
|
||||
|
||||
@@ -195,9 +195,10 @@ the MIT License. See LICENSE in the project root for license information. -->
|
||||
<Grid HorizontalAlignment="Stretch" >
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="16"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="16"/> <!-- icon -->
|
||||
<ColumnDefinition Width="Auto"/> <!-- command label -->
|
||||
<ColumnDefinition Width="*"/> <!-- key chord -->
|
||||
<ColumnDefinition Width="16"/> <!-- gutter for scrollbar -->
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- TODO GH#6644: Add Icon to command palette entries, in column 0 -->
|
||||
|
||||
@@ -7,11 +7,11 @@
|
||||
#include "../../inc/DefaultSettings.h"
|
||||
#include "Utils.h"
|
||||
#include "JsonUtils.h"
|
||||
#include "TerminalSettingsSerializationHelpers.h"
|
||||
|
||||
using namespace TerminalApp;
|
||||
using namespace winrt::Microsoft::Terminal::Settings;
|
||||
using namespace winrt::TerminalApp;
|
||||
using namespace winrt::Windows::Data::Json;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace ::Microsoft::Console;
|
||||
using namespace winrt::Microsoft::UI::Xaml::Controls;
|
||||
@@ -44,21 +44,6 @@ static constexpr std::string_view ForceFullRepaintRenderingKey{ "experimental.re
|
||||
static constexpr std::string_view SoftwareRenderingKey{ "experimental.rendering.software" };
|
||||
static constexpr std::string_view ForceVTInputKey{ "experimental.input.forceVT" };
|
||||
|
||||
// Launch mode values
|
||||
static constexpr std::wstring_view DefaultLaunchModeValue{ L"default" };
|
||||
static constexpr std::wstring_view MaximizedLaunchModeValue{ L"maximized" };
|
||||
static constexpr std::wstring_view FullscreenLaunchModeValue{ L"fullscreen" };
|
||||
|
||||
// Tab Width Mode values
|
||||
static constexpr std::wstring_view EqualTabWidthModeValue{ L"equal" };
|
||||
static constexpr std::wstring_view TitleLengthTabWidthModeValue{ L"titleLength" };
|
||||
static constexpr std::wstring_view TitleLengthCompactModeValue{ L"compact" };
|
||||
|
||||
// Theme values
|
||||
static constexpr std::wstring_view LightThemeValue{ L"light" };
|
||||
static constexpr std::wstring_view DarkThemeValue{ L"dark" };
|
||||
static constexpr std::wstring_view SystemThemeValue{ L"system" };
|
||||
|
||||
#ifdef _DEBUG
|
||||
static constexpr bool debugFeaturesDefault{ true };
|
||||
#else
|
||||
@@ -149,66 +134,51 @@ GlobalAppSettings GlobalAppSettings::FromJson(const Json::Value& json)
|
||||
|
||||
void GlobalAppSettings::LayerJson(const Json::Value& json)
|
||||
{
|
||||
if (auto defaultProfile{ json[JsonKey(DefaultProfileKey)] })
|
||||
{
|
||||
_unparsedDefaultProfile.emplace(GetWstringFromJson(defaultProfile));
|
||||
}
|
||||
JsonUtils::GetValueForKey(json, DefaultProfileKey, _unparsedDefaultProfile);
|
||||
|
||||
JsonUtils::GetBool(json, AlwaysShowTabsKey, _AlwaysShowTabs);
|
||||
JsonUtils::GetValueForKey(json, AlwaysShowTabsKey, _AlwaysShowTabs);
|
||||
|
||||
JsonUtils::GetBool(json, ConfirmCloseAllKey, _ConfirmCloseAllTabs);
|
||||
JsonUtils::GetValueForKey(json, ConfirmCloseAllKey, _ConfirmCloseAllTabs);
|
||||
|
||||
JsonUtils::GetInt(json, InitialRowsKey, _InitialRows);
|
||||
JsonUtils::GetValueForKey(json, InitialRowsKey, _InitialRows);
|
||||
|
||||
JsonUtils::GetInt(json, InitialColsKey, _InitialCols);
|
||||
JsonUtils::GetValueForKey(json, InitialColsKey, _InitialCols);
|
||||
|
||||
if (auto initialPosition{ json[JsonKey(InitialPositionKey)] })
|
||||
{
|
||||
_ParseInitialPosition(initialPosition.asString(), _InitialPosition);
|
||||
}
|
||||
JsonUtils::GetValueForKey(json, InitialPositionKey, _InitialPosition);
|
||||
|
||||
JsonUtils::GetBool(json, ShowTitleInTitlebarKey, _ShowTitleInTitlebar);
|
||||
JsonUtils::GetValueForKey(json, ShowTitleInTitlebarKey, _ShowTitleInTitlebar);
|
||||
|
||||
JsonUtils::GetBool(json, ShowTabsInTitlebarKey, _ShowTabsInTitlebar);
|
||||
JsonUtils::GetValueForKey(json, ShowTabsInTitlebarKey, _ShowTabsInTitlebar);
|
||||
|
||||
JsonUtils::GetWstring(json, WordDelimitersKey, _WordDelimiters);
|
||||
JsonUtils::GetValueForKey(json, WordDelimitersKey, _WordDelimiters);
|
||||
|
||||
JsonUtils::GetBool(json, CopyOnSelectKey, _CopyOnSelect);
|
||||
JsonUtils::GetValueForKey(json, CopyOnSelectKey, _CopyOnSelect);
|
||||
|
||||
JsonUtils::GetBool(json, CopyFormattingKey, _CopyFormatting);
|
||||
JsonUtils::GetValueForKey(json, CopyFormattingKey, _CopyFormatting);
|
||||
|
||||
JsonUtils::GetBool(json, WarnAboutLargePasteKey, _WarnAboutLargePaste);
|
||||
JsonUtils::GetValueForKey(json, WarnAboutLargePasteKey, _WarnAboutLargePaste);
|
||||
|
||||
JsonUtils::GetBool(json, WarnAboutMultiLinePasteKey, _WarnAboutMultiLinePaste);
|
||||
JsonUtils::GetValueForKey(json, WarnAboutMultiLinePasteKey, _WarnAboutMultiLinePaste);
|
||||
|
||||
if (auto launchMode{ json[JsonKey(LaunchModeKey)] })
|
||||
{
|
||||
_LaunchMode = _ParseLaunchMode(GetWstringFromJson(launchMode));
|
||||
}
|
||||
JsonUtils::GetValueForKey(json, LaunchModeKey, _LaunchMode);
|
||||
|
||||
if (auto theme{ json[JsonKey(ThemeKey)] })
|
||||
{
|
||||
_Theme = _ParseTheme(GetWstringFromJson(theme));
|
||||
}
|
||||
JsonUtils::GetValueForKey(json, ThemeKey, _Theme);
|
||||
|
||||
if (auto tabWidthMode{ json[JsonKey(TabWidthModeKey)] })
|
||||
{
|
||||
_TabWidthMode = _ParseTabWidthMode(GetWstringFromJson(tabWidthMode));
|
||||
}
|
||||
JsonUtils::GetValueForKey(json, TabWidthModeKey, _TabWidthMode);
|
||||
|
||||
JsonUtils::GetBool(json, SnapToGridOnResizeKey, _SnapToGridOnResize);
|
||||
JsonUtils::GetValueForKey(json, SnapToGridOnResizeKey, _SnapToGridOnResize);
|
||||
|
||||
JsonUtils::GetBool(json, ForceFullRepaintRenderingKey, _ForceFullRepaintRendering);
|
||||
// GetValueForKey will only override the current value if the key exists
|
||||
JsonUtils::GetValueForKey(json, DebugFeaturesKey, _DebugFeaturesEnabled);
|
||||
|
||||
JsonUtils::GetBool(json, SoftwareRenderingKey, _SoftwareRendering);
|
||||
JsonUtils::GetBool(json, ForceVTInputKey, _ForceVTInput);
|
||||
JsonUtils::GetValueForKey(json, ForceFullRepaintRenderingKey, _ForceFullRepaintRendering);
|
||||
|
||||
// GetBool will only override the current value if the key exists
|
||||
JsonUtils::GetBool(json, DebugFeaturesKey, _DebugFeaturesEnabled);
|
||||
JsonUtils::GetValueForKey(json, SoftwareRenderingKey, _SoftwareRendering);
|
||||
JsonUtils::GetValueForKey(json, ForceVTInputKey, _ForceVTInput);
|
||||
|
||||
JsonUtils::GetBool(json, EnableStartupTaskKey, _StartOnUserLogin);
|
||||
JsonUtils::GetValueForKey(json, EnableStartupTaskKey, _StartOnUserLogin);
|
||||
|
||||
JsonUtils::GetBool(json, AlwaysOnTopKey, _AlwaysOnTop);
|
||||
JsonUtils::GetValueForKey(json, AlwaysOnTopKey, _AlwaysOnTop);
|
||||
|
||||
// This is a helper lambda to get the keybindings and commands out of both
|
||||
// and array of objects. We'll use this twice, once on the legacy
|
||||
@@ -229,123 +199,12 @@ void GlobalAppSettings::LayerJson(const Json::Value& json)
|
||||
warnings = winrt::TerminalApp::implementation::Command::LayerJson(_commands, bindings);
|
||||
// It's possible that the user provided commands have some warnings
|
||||
// in them, similar to the keybindings.
|
||||
_keybindingsWarnings.insert(_keybindingsWarnings.end(), warnings.begin(), warnings.end());
|
||||
}
|
||||
};
|
||||
parseBindings(LegacyKeybindingsKey);
|
||||
parseBindings(BindingsKey);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper function for converting a user-specified cursor style corresponding
|
||||
// CursorStyle enum value
|
||||
// Arguments:
|
||||
// - themeString: The string value from the settings file to parse
|
||||
// Return Value:
|
||||
// - The corresponding enum value which maps to the string provided by the user
|
||||
ElementTheme GlobalAppSettings::_ParseTheme(const std::wstring& themeString) noexcept
|
||||
{
|
||||
if (themeString == LightThemeValue)
|
||||
{
|
||||
return ElementTheme::Light;
|
||||
}
|
||||
else if (themeString == DarkThemeValue)
|
||||
{
|
||||
return ElementTheme::Dark;
|
||||
}
|
||||
// default behavior for invalid data or SystemThemeValue
|
||||
return ElementTheme::Default;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper function for converting the initial position string into
|
||||
// 2 coordinate values. We allow users to only provide one coordinate,
|
||||
// thus, we use comma as the separator:
|
||||
// (100, 100): standard input string
|
||||
// (, 100), (100, ): if a value is missing, we set this value as a default
|
||||
// (,): both x and y are set to default
|
||||
// (abc, 100): if a value is not valid, we treat it as default
|
||||
// (100, 100, 100): we only read the first two values, this is equivalent to (100, 100)
|
||||
// Arguments:
|
||||
// - initialPosition: the initial position string from json
|
||||
// ret: reference to a struct whose optionals will be populated
|
||||
// Return Value:
|
||||
// - None
|
||||
void GlobalAppSettings::_ParseInitialPosition(const std::string& initialPosition,
|
||||
LaunchPosition& ret) noexcept
|
||||
{
|
||||
static constexpr char singleCharDelim = ',';
|
||||
std::stringstream tokenStream(initialPosition);
|
||||
std::string token;
|
||||
uint8_t initialPosIndex = 0;
|
||||
|
||||
// Get initial position values till we run out of delimiter separated values in the stream
|
||||
// or we hit max number of allowable values (= 2)
|
||||
// Non-numeral values or empty string will be caught as exception and we do not assign them
|
||||
for (; std::getline(tokenStream, token, singleCharDelim) && (initialPosIndex < 2); initialPosIndex++)
|
||||
{
|
||||
try
|
||||
{
|
||||
int32_t position = std::stoi(token);
|
||||
if (initialPosIndex == 0)
|
||||
{
|
||||
ret.x.emplace(position);
|
||||
}
|
||||
|
||||
if (initialPosIndex == 1)
|
||||
{
|
||||
ret.y.emplace(position);
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper function for converting the user-specified launch mode
|
||||
// to a LaunchMode enum value
|
||||
// Arguments:
|
||||
// - launchModeString: The string value from the settings file to parse
|
||||
// Return Value:
|
||||
// - The corresponding enum value which maps to the string provided by the user
|
||||
LaunchMode GlobalAppSettings::_ParseLaunchMode(const std::wstring& launchModeString) noexcept
|
||||
{
|
||||
if (launchModeString == MaximizedLaunchModeValue)
|
||||
{
|
||||
return LaunchMode::MaximizedMode;
|
||||
}
|
||||
else if (launchModeString == FullscreenLaunchModeValue)
|
||||
{
|
||||
return LaunchMode::FullscreenMode;
|
||||
}
|
||||
|
||||
return LaunchMode::DefaultMode;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper function for converting the user-specified tab width
|
||||
// to a TabViewWidthMode enum value
|
||||
// Arguments:
|
||||
// - tabWidthModeString: The string value from the settings file to parse
|
||||
// Return Value:
|
||||
// - The corresponding enum value which maps to the string provided by the user
|
||||
TabViewWidthMode GlobalAppSettings::_ParseTabWidthMode(const std::wstring& tabWidthModeString) noexcept
|
||||
{
|
||||
if (tabWidthModeString == TitleLengthTabWidthModeValue)
|
||||
{
|
||||
return TabViewWidthMode::SizeToContent;
|
||||
}
|
||||
else if (tabWidthModeString == TitleLengthCompactModeValue)
|
||||
{
|
||||
return TabViewWidthMode::Compact;
|
||||
}
|
||||
// default behavior for invalid data or EqualTabWidthValue
|
||||
return TabViewWidthMode::Equal;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Adds the given colorscheme to our map of schemes, using its name as the key.
|
||||
// Arguments:
|
||||
|
||||
@@ -17,6 +17,7 @@ Author(s):
|
||||
#include "AppKeyBindings.h"
|
||||
#include "ColorScheme.h"
|
||||
#include "Command.h"
|
||||
#include "SettingsTypes.h"
|
||||
|
||||
// fwdecl unittest classes
|
||||
namespace TerminalAppLocalTests
|
||||
@@ -28,12 +29,6 @@ namespace TerminalAppLocalTests
|
||||
namespace TerminalApp
|
||||
{
|
||||
class GlobalAppSettings;
|
||||
|
||||
struct LaunchPosition
|
||||
{
|
||||
std::optional<int> x;
|
||||
std::optional<int> y;
|
||||
};
|
||||
};
|
||||
|
||||
class TerminalApp::GlobalAppSettings final
|
||||
@@ -96,15 +91,6 @@ private:
|
||||
std::unordered_map<std::wstring, ColorScheme> _colorSchemes;
|
||||
std::unordered_map<winrt::hstring, winrt::TerminalApp::Command> _commands;
|
||||
|
||||
static winrt::Windows::UI::Xaml::ElementTheme _ParseTheme(const std::wstring& themeString) noexcept;
|
||||
|
||||
static winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode _ParseTabWidthMode(const std::wstring& tabWidthModeString) noexcept;
|
||||
|
||||
static void _ParseInitialPosition(const std::string& initialPosition,
|
||||
LaunchPosition& ret) noexcept;
|
||||
|
||||
static winrt::TerminalApp::LaunchMode _ParseLaunchMode(const std::wstring& launchModeString) noexcept;
|
||||
|
||||
friend class TerminalAppLocalTests::SettingsTests;
|
||||
friend class TerminalAppLocalTests::ColorSchemeTests;
|
||||
};
|
||||
|
||||
@@ -1,125 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "Utils.h"
|
||||
#include "JsonUtils.h"
|
||||
#include "../../types/inc/Utils.hpp"
|
||||
|
||||
void TerminalApp::JsonUtils::GetOptionalColor(const Json::Value& json,
|
||||
std::string_view key,
|
||||
std::optional<til::color>& target)
|
||||
{
|
||||
const auto conversionFn = [](const Json::Value& value) -> til::color {
|
||||
return ::Microsoft::Console::Utils::ColorFromHexString(value.asString());
|
||||
};
|
||||
GetOptionalValue(json,
|
||||
key,
|
||||
target,
|
||||
conversionFn);
|
||||
}
|
||||
|
||||
void TerminalApp::JsonUtils::GetOptionalString(const Json::Value& json,
|
||||
std::string_view key,
|
||||
std::optional<std::wstring>& target)
|
||||
{
|
||||
const auto conversionFn = [](const Json::Value& value) -> std::wstring {
|
||||
return GetWstringFromJson(value);
|
||||
};
|
||||
GetOptionalValue(json,
|
||||
key,
|
||||
target,
|
||||
conversionFn);
|
||||
}
|
||||
|
||||
void TerminalApp::JsonUtils::GetOptionalGuid(const Json::Value& json,
|
||||
std::string_view key,
|
||||
std::optional<GUID>& target)
|
||||
{
|
||||
const auto conversionFn = [](const Json::Value& value) -> GUID {
|
||||
return ::Microsoft::Console::Utils::GuidFromString(GetWstringFromJson(value));
|
||||
};
|
||||
GetOptionalValue(json,
|
||||
key,
|
||||
target,
|
||||
conversionFn);
|
||||
}
|
||||
|
||||
void TerminalApp::JsonUtils::GetOptionalDouble(const Json::Value& json,
|
||||
std::string_view key,
|
||||
std::optional<double>& target)
|
||||
{
|
||||
const auto conversionFn = [](const Json::Value& value) -> double {
|
||||
return value.asFloat();
|
||||
};
|
||||
const auto validationFn = [](const Json::Value& value) -> bool {
|
||||
return value.isNumeric();
|
||||
};
|
||||
GetOptionalValue(json,
|
||||
key,
|
||||
target,
|
||||
conversionFn,
|
||||
validationFn);
|
||||
}
|
||||
|
||||
void TerminalApp::JsonUtils::GetInt(const Json::Value& json,
|
||||
std::string_view key,
|
||||
int& target)
|
||||
{
|
||||
const auto conversionFn = [](const Json::Value& value) -> int {
|
||||
return value.asInt();
|
||||
};
|
||||
const auto validationFn = [](const Json::Value& value) -> bool {
|
||||
return value.isInt();
|
||||
};
|
||||
GetValue(json, key, target, conversionFn, validationFn);
|
||||
}
|
||||
|
||||
void TerminalApp::JsonUtils::GetUInt(const Json::Value& json,
|
||||
std::string_view key,
|
||||
uint32_t& target)
|
||||
{
|
||||
const auto conversionFn = [](const Json::Value& value) -> uint32_t {
|
||||
return value.asUInt();
|
||||
};
|
||||
const auto validationFn = [](const Json::Value& value) -> bool {
|
||||
return value.isUInt();
|
||||
};
|
||||
GetValue(json, key, target, conversionFn, validationFn);
|
||||
}
|
||||
|
||||
void TerminalApp::JsonUtils::GetDouble(const Json::Value& json,
|
||||
std::string_view key,
|
||||
double& target)
|
||||
{
|
||||
const auto conversionFn = [](const Json::Value& value) -> double {
|
||||
return value.asFloat();
|
||||
};
|
||||
const auto validationFn = [](const Json::Value& value) -> bool {
|
||||
return value.isNumeric();
|
||||
};
|
||||
GetValue(json, key, target, conversionFn, validationFn);
|
||||
}
|
||||
|
||||
void TerminalApp::JsonUtils::GetBool(const Json::Value& json,
|
||||
std::string_view key,
|
||||
bool& target)
|
||||
{
|
||||
const auto conversionFn = [](const Json::Value& value) -> bool {
|
||||
return value.asBool();
|
||||
};
|
||||
const auto validationFn = [](const Json::Value& value) -> bool {
|
||||
return value.isBool();
|
||||
};
|
||||
GetValue(json, key, target, conversionFn, validationFn);
|
||||
}
|
||||
|
||||
void TerminalApp::JsonUtils::GetWstring(const Json::Value& json,
|
||||
std::string_view key,
|
||||
std::wstring& target)
|
||||
{
|
||||
const auto conversionFn = [](const Json::Value& value) -> std::wstring {
|
||||
return GetWstringFromJson(value);
|
||||
};
|
||||
GetValue(json, key, target, conversionFn, nullptr);
|
||||
}
|
||||
@@ -9,136 +9,483 @@ Abstract:
|
||||
- Helpers for the TerminalApp project
|
||||
Author(s):
|
||||
- Mike Griese - August 2019
|
||||
|
||||
- Dustin Howett - January 2020
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <json.h>
|
||||
|
||||
#include "../types/inc/utils.hpp"
|
||||
|
||||
namespace winrt
|
||||
{
|
||||
// If we don't use winrt, nobody will include the ConversionTraits for winrt stuff.
|
||||
// If nobody includes it, these forward declarations will suffice.
|
||||
struct guid;
|
||||
struct hstring;
|
||||
namespace Windows::Foundation
|
||||
{
|
||||
template<typename T>
|
||||
struct IReference;
|
||||
}
|
||||
}
|
||||
|
||||
namespace TerminalApp::JsonUtils
|
||||
{
|
||||
void GetOptionalColor(const Json::Value& json,
|
||||
std::string_view key,
|
||||
std::optional<til::color>& target);
|
||||
|
||||
void GetOptionalString(const Json::Value& json,
|
||||
std::string_view key,
|
||||
std::optional<std::wstring>& target);
|
||||
|
||||
void GetOptionalGuid(const Json::Value& json,
|
||||
std::string_view key,
|
||||
std::optional<GUID>& target);
|
||||
|
||||
void GetOptionalDouble(const Json::Value& json,
|
||||
std::string_view key,
|
||||
std::optional<double>& target);
|
||||
|
||||
// Method Description:
|
||||
// - Helper that can be used for retrieving an optional value from a json
|
||||
// object, and parsing it's value to layer on a given target object.
|
||||
// - If the key we're looking for _doesn't_ exist in the json object,
|
||||
// we'll leave the target object unmodified.
|
||||
// - If the key exists in the json object, but is set to `null`, then
|
||||
// we'll instead set the target back to nullopt.
|
||||
// - Each caller should provide a conversion function that takes a
|
||||
// Json::Value and returns an object of the same type as target.
|
||||
// Arguments:
|
||||
// - json: The json object to search for the given key
|
||||
// - key: The key to look for in the json object
|
||||
// - target: the optional object to receive the value from json
|
||||
// - conversion: a std::function<T(const Json::Value&)> which can be used to
|
||||
// convert the Json::Value to the appropriate type.
|
||||
// - validation: optional, if provided, will be called first to ensure that
|
||||
// the json::value is of the correct type before attempting to call
|
||||
// `conversion`.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
template<typename T, typename F>
|
||||
void GetOptionalValue(const Json::Value& json,
|
||||
std::string_view key,
|
||||
std::optional<T>& target,
|
||||
F&& conversion,
|
||||
const std::function<bool(const Json::Value&)>& validation = nullptr)
|
||||
namespace Detail
|
||||
{
|
||||
if (json.isMember(JsonKey(key)))
|
||||
// Function Description:
|
||||
// - Returns a string_view to a Json::Value's internal string storage,
|
||||
// hopefully without copying it.
|
||||
__declspec(noinline) inline const std::string_view GetStringView(const Json::Value& json)
|
||||
{
|
||||
if (auto jsonVal{ json[JsonKey(key)] })
|
||||
{
|
||||
if (validation == nullptr || validation(jsonVal))
|
||||
{
|
||||
target = conversion(jsonVal);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This branch is hit when the json object contained the key,
|
||||
// but the key was set to `null`. In this case, explicitly clear
|
||||
// the target.
|
||||
target = std::nullopt;
|
||||
}
|
||||
const char* begin{ nullptr };
|
||||
const char* end{ nullptr };
|
||||
json.getString(&begin, &end);
|
||||
const std::string_view zeroCopyString{ begin, gsl::narrow_cast<size_t>(end - begin) };
|
||||
return zeroCopyString;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct DeduceOptional
|
||||
{
|
||||
using Type = typename std::decay<T>::type;
|
||||
static constexpr bool IsOptional = false;
|
||||
};
|
||||
|
||||
template<typename TOpt>
|
||||
struct DeduceOptional<std::optional<TOpt>>
|
||||
{
|
||||
using Type = typename std::decay<TOpt>::type;
|
||||
static constexpr bool IsOptional = true;
|
||||
};
|
||||
|
||||
template<typename TOpt>
|
||||
struct DeduceOptional<::winrt::Windows::Foundation::IReference<TOpt>>
|
||||
{
|
||||
using Type = typename std::decay<TOpt>::type;
|
||||
static constexpr bool IsOptional = true;
|
||||
};
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper that can be used for retrieving a value from a json
|
||||
// object, and parsing it's value to set on a given target object.
|
||||
// - If the key we're looking for _doesn't_ exist in the json object,
|
||||
// we'll leave the target object unmodified.
|
||||
// - If the key exists in the json object, we'll use the provided
|
||||
// `validation` function to ensure that the json value is of the
|
||||
// correct type.
|
||||
// - If we successfully validate the json value type (or no validation
|
||||
// function was provided), then we'll use `conversion` to parse the
|
||||
// value and place the result into `target`
|
||||
// - Each caller should provide a conversion function that takes a
|
||||
// Json::Value and returns an object of the same type as target.
|
||||
// - Unlike GetOptionalValue, if the key exists but is set to `null`, we'll
|
||||
// just ignore it.
|
||||
// Arguments:
|
||||
// - json: The json object to search for the given key
|
||||
// - key: The key to look for in the json object
|
||||
// - target: the optional object to receive the value from json
|
||||
// - conversion: a std::function<T(const Json::Value&)> which can be used to
|
||||
// convert the Json::Value to the appropriate type.
|
||||
// - validation: optional, if provided, will be called first to ensure that
|
||||
// the json::value is of the correct type before attempting to call
|
||||
// `conversion`.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
template<typename T, typename F>
|
||||
void GetValue(const Json::Value& json,
|
||||
std::string_view key,
|
||||
T& target,
|
||||
F&& conversion,
|
||||
const std::function<bool(const Json::Value&)>& validation = nullptr)
|
||||
// These exceptions cannot use localized messages, as we do not have
|
||||
// guaranteed access to the resource loader.
|
||||
class TypeMismatchException : public std::runtime_error
|
||||
{
|
||||
if (json.isMember(JsonKey(key)))
|
||||
public:
|
||||
TypeMismatchException() :
|
||||
runtime_error("unexpected data type") {}
|
||||
};
|
||||
|
||||
class KeyedException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
KeyedException(const std::string_view key, std::exception_ptr exception) :
|
||||
runtime_error(fmt::format("error parsing \"{0}\"", key).c_str()),
|
||||
_key{ key },
|
||||
_innerException{ std::move(exception) } {}
|
||||
|
||||
std::string GetKey() const
|
||||
{
|
||||
if (auto jsonVal{ json[JsonKey(key)] })
|
||||
return _key;
|
||||
}
|
||||
|
||||
[[noreturn]] void RethrowInner() const
|
||||
{
|
||||
std::rethrow_exception(_innerException);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string _key;
|
||||
std::exception_ptr _innerException;
|
||||
};
|
||||
|
||||
class UnexpectedValueException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
UnexpectedValueException(const std::string_view value) :
|
||||
runtime_error(fmt::format("unexpected value \"{0}\"", value).c_str()),
|
||||
_value{ value } {}
|
||||
|
||||
std::string GetValue() const
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string _value;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct ConversionTrait
|
||||
{
|
||||
// Forward-declare these so the linker can pick up specializations from elsewhere!
|
||||
T FromJson(const Json::Value&);
|
||||
bool CanConvert(const Json::Value& json);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ConversionTrait<std::string>
|
||||
{
|
||||
std::string FromJson(const Json::Value& json)
|
||||
{
|
||||
return json.asString();
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return json.isString();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ConversionTrait<std::wstring>
|
||||
{
|
||||
std::wstring FromJson(const Json::Value& json)
|
||||
{
|
||||
return til::u8u16(Detail::GetStringView(json));
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return json.isString();
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef WINRT_BASE_H
|
||||
template<>
|
||||
struct ConversionTrait<winrt::hstring> : public ConversionTrait<std::wstring>
|
||||
{
|
||||
// Leverage the wstring converter's validation
|
||||
winrt::hstring FromJson(const Json::Value& json)
|
||||
{
|
||||
return winrt::hstring{ til::u8u16(Detail::GetStringView(json)) };
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
template<>
|
||||
struct ConversionTrait<bool>
|
||||
{
|
||||
bool FromJson(const Json::Value& json)
|
||||
{
|
||||
return json.asBool();
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return json.isBool();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ConversionTrait<int>
|
||||
{
|
||||
int FromJson(const Json::Value& json)
|
||||
{
|
||||
return json.asInt();
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return json.isInt();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ConversionTrait<unsigned int>
|
||||
{
|
||||
unsigned int FromJson(const Json::Value& json)
|
||||
{
|
||||
return json.asUInt();
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return json.isUInt();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ConversionTrait<float>
|
||||
{
|
||||
float FromJson(const Json::Value& json)
|
||||
{
|
||||
return json.asFloat();
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return json.isNumeric();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ConversionTrait<double>
|
||||
{
|
||||
double FromJson(const Json::Value& json)
|
||||
{
|
||||
return json.asDouble();
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return json.isNumeric();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ConversionTrait<GUID>
|
||||
{
|
||||
GUID FromJson(const Json::Value& json)
|
||||
{
|
||||
return ::Microsoft::Console::Utils::GuidFromString(til::u8u16(Detail::GetStringView(json)));
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
if (!json.isString())
|
||||
{
|
||||
if (validation == nullptr || validation(jsonVal))
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto string{ Detail::GetStringView(json) };
|
||||
return string.length() == 38 && string.front() == '{' && string.back() == '}';
|
||||
}
|
||||
};
|
||||
|
||||
// (GUID and winrt::guid are mutually convertible!)
|
||||
template<>
|
||||
struct ConversionTrait<winrt::guid> : public ConversionTrait<GUID>
|
||||
{
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ConversionTrait<til::color>
|
||||
{
|
||||
til::color FromJson(const Json::Value& json)
|
||||
{
|
||||
return ::Microsoft::Console::Utils::ColorFromHexString(Detail::GetStringView(json));
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
if (!json.isString())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto string{ Detail::GetStringView(json) };
|
||||
return (string.length() == 7 || string.length() == 4) && string.front() == '#';
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename TBase>
|
||||
struct EnumMapper
|
||||
{
|
||||
using BaseEnumMapper = EnumMapper<T, TBase>;
|
||||
using ValueType = T;
|
||||
using pair_type = std::pair<std::string_view, T>;
|
||||
T FromJson(const Json::Value& json)
|
||||
{
|
||||
const auto name{ Detail::GetStringView(json) };
|
||||
for (const auto& pair : TBase::mappings)
|
||||
{
|
||||
if (pair.first == name)
|
||||
{
|
||||
target = conversion(jsonVal);
|
||||
return pair.second;
|
||||
}
|
||||
}
|
||||
|
||||
throw UnexpectedValueException{ name };
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return json.isString();
|
||||
}
|
||||
};
|
||||
|
||||
// FlagMapper is EnumMapper, but it works for bitfields.
|
||||
// It supports a string (single flag) or an array of strings.
|
||||
// Does an O(n*m) search; meant for small search spaces!
|
||||
//
|
||||
// Cleverly leverage EnumMapper to do the heavy lifting.
|
||||
template<typename T, typename TBase>
|
||||
struct FlagMapper : public EnumMapper<T, TBase>
|
||||
{
|
||||
private:
|
||||
// Hide BaseEnumMapper so FlagMapper's consumers cannot see
|
||||
// it.
|
||||
using BaseEnumMapper = EnumMapper<T, TBase>::BaseEnumMapper;
|
||||
|
||||
public:
|
||||
using BaseFlagMapper = FlagMapper<T, TBase>;
|
||||
static constexpr T AllSet{ static_cast<T>(~0u) };
|
||||
static constexpr T AllClear{ static_cast<T>(0u) };
|
||||
|
||||
T FromJson(const Json::Value& json)
|
||||
{
|
||||
if (json.isString())
|
||||
{
|
||||
return BaseEnumMapper::FromJson(json);
|
||||
}
|
||||
else if (json.isArray())
|
||||
{
|
||||
unsigned int seen{ 0 };
|
||||
T value{};
|
||||
for (const auto& element : json)
|
||||
{
|
||||
const auto newFlag{ BaseEnumMapper::FromJson(element) };
|
||||
if (++seen > 1 &&
|
||||
((newFlag == AllClear && value != AllClear) ||
|
||||
(value == AllClear && newFlag != AllClear)))
|
||||
{
|
||||
// attempt to combine AllClear (explicitly) with anything else
|
||||
throw UnexpectedValueException{ element.asString() };
|
||||
}
|
||||
value |= newFlag;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
// We'll only get here if CanConvert has failed us.
|
||||
return AllClear;
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return BaseEnumMapper::CanConvert(json) || json.isArray();
|
||||
}
|
||||
};
|
||||
|
||||
// Method Description:
|
||||
// - Helper that will populate a reference with a value converted from a json object.
|
||||
// Arguments:
|
||||
// - json: the json object to convert
|
||||
// - target: the value to populate with the converted result
|
||||
// Return Value:
|
||||
// - a boolean indicating whether the value existed (in this case, was non-null)
|
||||
//
|
||||
// GetValue, type-deduced, manual converter
|
||||
template<typename T, typename Converter>
|
||||
bool GetValue(const Json::Value& json, T& target, Converter&& conv)
|
||||
{
|
||||
if constexpr (Detail::DeduceOptional<T>::IsOptional)
|
||||
{
|
||||
// FOR OPTION TYPES
|
||||
// - If the json object is set to `null`, then
|
||||
// we'll instead set the target back to the empty optional.
|
||||
if (json.isNull())
|
||||
{
|
||||
target = T{}; // zero-construct an empty optional
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (json)
|
||||
{
|
||||
if (!conv.CanConvert(json))
|
||||
{
|
||||
throw TypeMismatchException{};
|
||||
}
|
||||
|
||||
target = conv.FromJson(json);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void GetInt(const Json::Value& json,
|
||||
std::string_view key,
|
||||
int& target);
|
||||
// GetValue, forced return type, manual converter
|
||||
template<typename T, typename Converter>
|
||||
std::decay_t<T> GetValue(const Json::Value& json, Converter&& conv)
|
||||
{
|
||||
std::decay_t<T> local{};
|
||||
GetValue(json, local, std::forward<Converter>(conv));
|
||||
return local; // returns zero-initialized or value
|
||||
}
|
||||
|
||||
void GetUInt(const Json::Value& json,
|
||||
std::string_view key,
|
||||
uint32_t& target);
|
||||
// GetValueForKey, type-deduced, manual converter
|
||||
template<typename T, typename Converter>
|
||||
bool GetValueForKey(const Json::Value& json, std::string_view key, T& target, Converter&& conv)
|
||||
{
|
||||
if (auto found{ json.find(&*key.cbegin(), (&*key.cbegin()) + key.size()) })
|
||||
{
|
||||
try
|
||||
{
|
||||
return GetValue(*found, target, std::forward<Converter>(conv));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Wrap any caught exceptions in one that preserves context.
|
||||
throw KeyedException(key, std::current_exception());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void GetDouble(const Json::Value& json,
|
||||
std::string_view key,
|
||||
double& target);
|
||||
// GetValueForKey, forced return type, manual converter
|
||||
template<typename T, typename Converter>
|
||||
std::decay_t<T> GetValueForKey(const Json::Value& json, std::string_view key, Converter&& conv)
|
||||
{
|
||||
std::decay_t<T> local{};
|
||||
GetValueForKey(json, key, local, std::forward<Converter>(conv));
|
||||
return local; // returns zero-initialized?
|
||||
}
|
||||
|
||||
void GetBool(const Json::Value& json,
|
||||
std::string_view key,
|
||||
bool& target);
|
||||
// GetValue, type-deduced, with automatic converter
|
||||
template<typename T>
|
||||
bool GetValue(const Json::Value& json, T& target)
|
||||
{
|
||||
return GetValue(json, target, ConversionTrait<typename Detail::DeduceOptional<T>::Type>{});
|
||||
}
|
||||
|
||||
void GetWstring(const Json::Value& json,
|
||||
std::string_view key,
|
||||
std::wstring& target);
|
||||
// GetValue, forced return type, with automatic converter
|
||||
template<typename T>
|
||||
std::decay_t<T> GetValue(const Json::Value& json)
|
||||
{
|
||||
std::decay_t<T> local{};
|
||||
GetValue(json, local, ConversionTrait<typename Detail::DeduceOptional<T>::Type>{});
|
||||
return local; // returns zero-initialized or value
|
||||
}
|
||||
|
||||
// GetValueForKey, type-deduced, with automatic converter
|
||||
template<typename T>
|
||||
bool GetValueForKey(const Json::Value& json, std::string_view key, T& target)
|
||||
{
|
||||
return GetValueForKey(json, key, target, ConversionTrait<typename Detail::DeduceOptional<T>::Type>{});
|
||||
}
|
||||
|
||||
// GetValueForKey, forced return type, with automatic converter
|
||||
template<typename T>
|
||||
std::decay_t<T> GetValueForKey(const Json::Value& json, std::string_view key)
|
||||
{
|
||||
return GetValueForKey<T>(json, key, ConversionTrait<typename Detail::DeduceOptional<T>::Type>{});
|
||||
}
|
||||
|
||||
// Get multiple values for keys (json, k, &v, k, &v, k, &v, ...).
|
||||
// Uses the default converter for each v.
|
||||
// Careful: this can cause a template explosion.
|
||||
constexpr void GetValuesForKeys(const Json::Value& /*json*/) {}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
void GetValuesForKeys(const Json::Value& json, std::string_view key1, T&& val1, Args&&... args)
|
||||
{
|
||||
GetValueForKey(json, key1, val1);
|
||||
GetValuesForKeys(json, std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
#define JSON_ENUM_MAPPER(...) \
|
||||
template<> \
|
||||
struct ::TerminalApp::JsonUtils::ConversionTrait<__VA_ARGS__> : \
|
||||
public ::TerminalApp::JsonUtils::EnumMapper<__VA_ARGS__, ::TerminalApp::JsonUtils::ConversionTrait<__VA_ARGS__>>
|
||||
|
||||
#define JSON_FLAG_MAPPER(...) \
|
||||
template<> \
|
||||
struct ::TerminalApp::JsonUtils::ConversionTrait<__VA_ARGS__> : \
|
||||
public ::TerminalApp::JsonUtils::FlagMapper<__VA_ARGS__, ::TerminalApp::JsonUtils::ConversionTrait<__VA_ARGS__>>
|
||||
|
||||
#define JSON_MAPPINGS(Count) \
|
||||
static constexpr std::array<pair_type, Count> mappings
|
||||
|
||||
@@ -1,490 +0,0 @@
|
||||
/*++
|
||||
Copyright (c) Microsoft Corporation
|
||||
Licensed under the MIT license.
|
||||
|
||||
Module Name:
|
||||
- JsonUtils.h
|
||||
|
||||
Abstract:
|
||||
- Helpers for the TerminalApp project
|
||||
Author(s):
|
||||
- Mike Griese - August 2019
|
||||
- Dustin Howett - January 2020
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <json.h>
|
||||
|
||||
#include "../types/inc/utils.hpp"
|
||||
|
||||
namespace winrt
|
||||
{
|
||||
// If we don't use winrt, nobody will include the ConversionTraits for winrt stuff.
|
||||
// If nobody includes it, these forward declarations will suffice.
|
||||
struct guid;
|
||||
struct hstring;
|
||||
namespace Windows::Foundation
|
||||
{
|
||||
template<typename T>
|
||||
struct IReference;
|
||||
}
|
||||
}
|
||||
|
||||
namespace TerminalApp::JsonUtils
|
||||
{
|
||||
namespace Detail
|
||||
{
|
||||
// Function Description:
|
||||
// - Returns a string_view to a Json::Value's internal string storage,
|
||||
// hopefully without copying it.
|
||||
__declspec(noinline) inline const std::string_view GetStringView(const Json::Value& json)
|
||||
{
|
||||
const char* begin{ nullptr };
|
||||
const char* end{ nullptr };
|
||||
json.getString(&begin, &end);
|
||||
const std::string_view zeroCopyString{ begin, gsl::narrow_cast<size_t>(end - begin) };
|
||||
return zeroCopyString;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct DeduceOptional
|
||||
{
|
||||
using Type = typename std::decay<T>::type;
|
||||
static constexpr bool IsOptional = false;
|
||||
};
|
||||
|
||||
template<typename TOpt>
|
||||
struct DeduceOptional<std::optional<TOpt>>
|
||||
{
|
||||
using Type = typename std::decay<TOpt>::type;
|
||||
static constexpr bool IsOptional = true;
|
||||
};
|
||||
|
||||
template<typename TOpt>
|
||||
struct DeduceOptional<::winrt::Windows::Foundation::IReference<TOpt>>
|
||||
{
|
||||
using Type = typename std::decay<TOpt>::type;
|
||||
static constexpr bool IsOptional = true;
|
||||
};
|
||||
}
|
||||
|
||||
// These exceptions cannot use localized messages, as we do not have
|
||||
// guaranteed access to the resource loader.
|
||||
class TypeMismatchException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
TypeMismatchException() :
|
||||
runtime_error("unexpected data type") {}
|
||||
};
|
||||
|
||||
class KeyedException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
KeyedException(const std::string_view key, std::exception_ptr exception) :
|
||||
runtime_error(fmt::format("error parsing \"{0}\"", key).c_str()),
|
||||
_key{ key },
|
||||
_innerException{ std::move(exception) } {}
|
||||
|
||||
std::string GetKey() const
|
||||
{
|
||||
return _key;
|
||||
}
|
||||
|
||||
[[noreturn]] void RethrowInner() const
|
||||
{
|
||||
std::rethrow_exception(_innerException);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string _key;
|
||||
std::exception_ptr _innerException;
|
||||
};
|
||||
|
||||
class UnexpectedValueException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
UnexpectedValueException(const std::string_view value) :
|
||||
runtime_error(fmt::format("unexpected value \"{0}\"", value).c_str()),
|
||||
_value{ value } {}
|
||||
|
||||
std::string GetValue() const
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string _value;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct ConversionTrait
|
||||
{
|
||||
// Forward-declare these so the linker can pick up specializations from elsewhere!
|
||||
T FromJson(const Json::Value&);
|
||||
bool CanConvert(const Json::Value& json);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ConversionTrait<std::string>
|
||||
{
|
||||
std::string FromJson(const Json::Value& json)
|
||||
{
|
||||
return json.asString();
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return json.isString();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ConversionTrait<std::wstring>
|
||||
{
|
||||
std::wstring FromJson(const Json::Value& json)
|
||||
{
|
||||
return til::u8u16(Detail::GetStringView(json));
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return json.isString();
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef WINRT_BASE_H
|
||||
template<>
|
||||
struct ConversionTrait<winrt::hstring> : public ConversionTrait<std::wstring>
|
||||
{
|
||||
// Leverage the wstring converter's validation
|
||||
winrt::hstring FromJson(const Json::Value& json)
|
||||
{
|
||||
return winrt::hstring{ til::u8u16(Detail::GetStringView(json)) };
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
template<>
|
||||
struct ConversionTrait<bool>
|
||||
{
|
||||
bool FromJson(const Json::Value& json)
|
||||
{
|
||||
return json.asBool();
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return json.isBool();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ConversionTrait<int>
|
||||
{
|
||||
int FromJson(const Json::Value& json)
|
||||
{
|
||||
return json.asInt();
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return json.isInt();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ConversionTrait<unsigned int>
|
||||
{
|
||||
unsigned int FromJson(const Json::Value& json)
|
||||
{
|
||||
return json.asUInt();
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return json.isUInt();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ConversionTrait<float>
|
||||
{
|
||||
float FromJson(const Json::Value& json)
|
||||
{
|
||||
return json.asFloat();
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return json.isNumeric();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ConversionTrait<double>
|
||||
{
|
||||
double FromJson(const Json::Value& json)
|
||||
{
|
||||
return json.asDouble();
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return json.isNumeric();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ConversionTrait<GUID>
|
||||
{
|
||||
GUID FromJson(const Json::Value& json)
|
||||
{
|
||||
return ::Microsoft::Console::Utils::GuidFromString(til::u8u16(Detail::GetStringView(json)));
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
if (!json.isString())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto string{ Detail::GetStringView(json) };
|
||||
return string.length() == 38 && string.front() == '{' && string.back() == '}';
|
||||
}
|
||||
};
|
||||
|
||||
// (GUID and winrt::guid are mutually convertible!)
|
||||
template<>
|
||||
struct ConversionTrait<winrt::guid> : public ConversionTrait<GUID>
|
||||
{
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ConversionTrait<til::color>
|
||||
{
|
||||
til::color FromJson(const Json::Value& json)
|
||||
{
|
||||
return ::Microsoft::Console::Utils::ColorFromHexString(Detail::GetStringView(json));
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
if (!json.isString())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto string{ Detail::GetStringView(json) };
|
||||
return (string.length() == 7 || string.length() == 4) && string.front() == '#';
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename TBase>
|
||||
struct EnumMapper
|
||||
{
|
||||
using BaseEnumMapper = EnumMapper<T, TBase>;
|
||||
using pair_type = std::pair<std::string_view, T>;
|
||||
T FromJson(const Json::Value& json)
|
||||
{
|
||||
const auto name{ Detail::GetStringView(json) };
|
||||
for (const auto& pair : TBase::mappings)
|
||||
{
|
||||
if (pair.first == name)
|
||||
{
|
||||
return pair.second;
|
||||
}
|
||||
}
|
||||
|
||||
throw UnexpectedValueException{ name };
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return json.isString();
|
||||
}
|
||||
};
|
||||
|
||||
// FlagMapper is EnumMapper, but it works for bitfields.
|
||||
// It supports a string (single flag) or an array of strings.
|
||||
// Does an O(n*m) search; meant for small search spaces!
|
||||
//
|
||||
// Cleverly leverage EnumMapper to do the heavy lifting.
|
||||
template<typename T, typename TBase>
|
||||
struct FlagMapper : public EnumMapper<T, TBase>
|
||||
{
|
||||
private:
|
||||
// Hide BaseEnumMapper so FlagMapper's consumers cannot see
|
||||
// it.
|
||||
using BaseEnumMapper = EnumMapper<T, TBase>::BaseEnumMapper;
|
||||
|
||||
public:
|
||||
using BaseFlagMapper = FlagMapper<T, TBase>;
|
||||
static constexpr T AllSet{ static_cast<T>(~0u) };
|
||||
static constexpr T AllClear{ static_cast<T>(0u) };
|
||||
|
||||
T FromJson(const Json::Value& json)
|
||||
{
|
||||
if (json.isString())
|
||||
{
|
||||
return BaseEnumMapper::FromJson(json);
|
||||
}
|
||||
else if (json.isArray())
|
||||
{
|
||||
unsigned int seen{ 0 };
|
||||
T value{};
|
||||
for (const auto& element : json)
|
||||
{
|
||||
const auto newFlag{ BaseEnumMapper::FromJson(element) };
|
||||
if (++seen > 1 &&
|
||||
((newFlag == AllClear && value != AllClear) ||
|
||||
(value == AllClear && newFlag != AllClear)))
|
||||
{
|
||||
// attempt to combine AllClear (explicitly) with anything else
|
||||
throw UnexpectedValueException{ element.asString() };
|
||||
}
|
||||
value |= newFlag;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
// We'll only get here if CanConvert has failed us.
|
||||
return AllClear;
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return BaseEnumMapper::CanConvert(json) || json.isArray();
|
||||
}
|
||||
};
|
||||
|
||||
// Method Description:
|
||||
// - Helper that will populate a reference with a value converted from a json object.
|
||||
// Arguments:
|
||||
// - json: the json object to convert
|
||||
// - target: the value to populate with the converted result
|
||||
// Return Value:
|
||||
// - a boolean indicating whether the value existed (in this case, was non-null)
|
||||
//
|
||||
// GetValue, type-deduced, manual converter
|
||||
template<typename T, typename Converter>
|
||||
bool GetValue(const Json::Value& json, T& target, Converter&& conv)
|
||||
{
|
||||
if constexpr (Detail::DeduceOptional<T>::IsOptional)
|
||||
{
|
||||
// FOR OPTION TYPES
|
||||
// - If the json object is set to `null`, then
|
||||
// we'll instead set the target back to the empty optional.
|
||||
if (json.isNull())
|
||||
{
|
||||
target = T{}; // zero-construct an empty optional
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (json)
|
||||
{
|
||||
if (!conv.CanConvert(json))
|
||||
{
|
||||
throw TypeMismatchException{};
|
||||
}
|
||||
|
||||
target = conv.FromJson(json);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// GetValue, forced return type, manual converter
|
||||
template<typename T, typename Converter>
|
||||
std::decay_t<T> GetValue(const Json::Value& json, Converter&& conv)
|
||||
{
|
||||
std::decay_t<T> local{};
|
||||
GetValue(json, local, std::forward<Converter>(conv));
|
||||
return local; // returns zero-initialized or value
|
||||
}
|
||||
|
||||
// GetValueForKey, type-deduced, manual converter
|
||||
template<typename T, typename Converter>
|
||||
bool GetValueForKey(const Json::Value& json, std::string_view key, T& target, Converter&& conv)
|
||||
{
|
||||
if (auto found{ json.find(&*key.cbegin(), (&*key.cbegin()) + key.size()) })
|
||||
{
|
||||
try
|
||||
{
|
||||
return GetValue(*found, target, std::forward<Converter>(conv));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Wrap any caught exceptions in one that preserves context.
|
||||
throw KeyedException(key, std::current_exception());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// GetValueForKey, forced return type, manual converter
|
||||
template<typename T, typename Converter>
|
||||
std::decay_t<T> GetValueForKey(const Json::Value& json, std::string_view key, Converter&& conv)
|
||||
{
|
||||
std::decay_t<T> local{};
|
||||
GetValueForKey(json, key, local, std::forward<Converter>(conv));
|
||||
return local; // returns zero-initialized?
|
||||
}
|
||||
|
||||
// GetValue, type-deduced, with automatic converter
|
||||
template<typename T>
|
||||
bool GetValue(const Json::Value& json, T& target)
|
||||
{
|
||||
return GetValue(json, target, ConversionTrait<typename Detail::DeduceOptional<T>::Type>{});
|
||||
}
|
||||
|
||||
// GetValue, forced return type, with automatic converter
|
||||
template<typename T>
|
||||
std::decay_t<T> GetValue(const Json::Value& json)
|
||||
{
|
||||
std::decay_t<T> local{};
|
||||
GetValue(json, local, ConversionTrait<typename Detail::DeduceOptional<T>::Type>{});
|
||||
return local; // returns zero-initialized or value
|
||||
}
|
||||
|
||||
// GetValueForKey, type-deduced, with automatic converter
|
||||
template<typename T>
|
||||
bool GetValueForKey(const Json::Value& json, std::string_view key, T& target)
|
||||
{
|
||||
return GetValueForKey(json, key, target, ConversionTrait<typename Detail::DeduceOptional<T>::Type>{});
|
||||
}
|
||||
|
||||
// GetValueForKey, forced return type, with automatic converter
|
||||
template<typename T>
|
||||
std::decay_t<T> GetValueForKey(const Json::Value& json, std::string_view key)
|
||||
{
|
||||
return GetValueForKey<T>(json, key, ConversionTrait<typename Detail::DeduceOptional<T>::Type>{});
|
||||
}
|
||||
|
||||
// Get multiple values for keys (json, k, &v, k, &v, k, &v, ...).
|
||||
// Uses the default converter for each v.
|
||||
// Careful: this can cause a template explosion.
|
||||
constexpr void GetValuesForKeys(const Json::Value& /*json*/) {}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
void GetValuesForKeys(const Json::Value& json, std::string_view key1, T&& val1, Args&&... args)
|
||||
{
|
||||
GetValueForKey(json, key1, val1);
|
||||
GetValuesForKeys(json, std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
#define JSON_ENUM_MAPPER(...) \
|
||||
template<> \
|
||||
struct ::TerminalApp::JsonUtils::ConversionTrait<__VA_ARGS__> : \
|
||||
public ::TerminalApp::JsonUtils::EnumMapper<__VA_ARGS__, ::TerminalApp::JsonUtils::ConversionTrait<__VA_ARGS__>>
|
||||
|
||||
#define JSON_FLAG_MAPPER(...) \
|
||||
template<> \
|
||||
struct ::TerminalApp::JsonUtils::ConversionTrait<__VA_ARGS__> : \
|
||||
public ::TerminalApp::JsonUtils::FlagMapper<__VA_ARGS__, ::TerminalApp::JsonUtils::ConversionTrait<__VA_ARGS__>>
|
||||
|
||||
#define JSON_MAPPINGS(Count) \
|
||||
static constexpr std::array<pair_type, Count> mappings
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "CascadiaSettings.h"
|
||||
|
||||
using namespace winrt::Windows::Foundation;
|
||||
using namespace winrt::Windows::Graphics::Display;
|
||||
using namespace winrt::Windows::UI;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace winrt::Windows::UI::Core;
|
||||
@@ -921,6 +922,102 @@ bool Pane::CanSplit(SplitState 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
|
||||
// processing of many "split-pane" commands, which could happen _before_ we've
|
||||
// laid out a Pane for the first time. When this happens, the Pane's don't
|
||||
// have an actual size yet. However, we'd still like to figure out if the pane
|
||||
// could be split, once they're all laid out.
|
||||
// - This method assumes that the Pane we're attempting to split is `target`,
|
||||
// and this method should be called on the root of a tree of Panes.
|
||||
// - We'll walk down the tree attempting to find `target`. As we traverse the
|
||||
// tree, we'll reduce the size passed to each subsequent recursive call. The
|
||||
// size passed to this method represents how much space this Pane _will_ have
|
||||
// to use.
|
||||
// * 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.
|
||||
// * If this pane is _any other leaf_, then just return nullopt, to indicate
|
||||
// that the `target` Pane is not down this branch.
|
||||
// * If this pane is a parent, calculate how much space our children will be
|
||||
// able to use, and recurse into them.
|
||||
// Arguments:
|
||||
// - target: The Pane we're attempting to split.
|
||||
// - splitType: The direction we're attempting to split in.
|
||||
// - availableSpace: The theoretical space that's available for this pane to be able to split.
|
||||
// Return Value:
|
||||
// - nullopt if `target` is not this pane or a child of this pane, otherwise
|
||||
// true iff we could split this pane, given `availableSpace`
|
||||
// Note:
|
||||
// - This method is highly similar to Pane::PreCalculateAutoSplit
|
||||
std::optional<bool> Pane::PreCalculateCanSplit(const std::shared_ptr<Pane> target,
|
||||
SplitState splitType,
|
||||
const winrt::Windows::Foundation::Size availableSpace) const
|
||||
{
|
||||
if (_IsLeaf())
|
||||
{
|
||||
if (target.get() == this)
|
||||
{
|
||||
// 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();
|
||||
|
||||
if (splitType == SplitState::None)
|
||||
{
|
||||
return { false };
|
||||
}
|
||||
|
||||
else if (splitType == SplitState::Vertical)
|
||||
{
|
||||
const auto widthMinusSeparator = availableSpace.Width - CombinedPaneBorderSize;
|
||||
const auto newWidth = widthMinusSeparator * Half;
|
||||
|
||||
return { newWidth > minSize.Width };
|
||||
}
|
||||
|
||||
else if (splitType == SplitState::Horizontal)
|
||||
{
|
||||
const auto heightMinusSeparator = availableSpace.Height - CombinedPaneBorderSize;
|
||||
const auto newHeight = heightMinusSeparator * Half;
|
||||
|
||||
return { newHeight > minSize.Height };
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If this pane is _any other leaf_, then just return nullopt, to
|
||||
// indicate that the `target` Pane is not down this branch.
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If this pane is a parent, calculate how much space our children will
|
||||
// be able to use, and recurse into them.
|
||||
|
||||
const bool isVerticalSplit = _splitState == SplitState::Vertical;
|
||||
const float firstWidth = isVerticalSplit ?
|
||||
(availableSpace.Width * _desiredSplitPosition) - PaneBorderSize :
|
||||
availableSpace.Width;
|
||||
const float secondWidth = isVerticalSplit ?
|
||||
(availableSpace.Width - firstWidth) - PaneBorderSize :
|
||||
availableSpace.Width;
|
||||
const float firstHeight = !isVerticalSplit ?
|
||||
(availableSpace.Height * _desiredSplitPosition) - PaneBorderSize :
|
||||
availableSpace.Height;
|
||||
const float secondHeight = !isVerticalSplit ?
|
||||
(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 });
|
||||
}
|
||||
|
||||
// We should not possibly be getting here - both the above branches should
|
||||
// return a value.
|
||||
FAIL_FAST();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Split the focused pane in our tree of panes, and place the given
|
||||
// TermControl into the newly created pane. If we're the focused pane, then
|
||||
|
||||
@@ -64,7 +64,9 @@ public:
|
||||
const winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
|
||||
float CalcSnappedDimension(const bool widthOrHeight, const float dimension) const;
|
||||
std::optional<winrt::TerminalApp::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::TerminalApp::SplitState splitType,
|
||||
const winrt::Windows::Foundation::Size availableSpace) const;
|
||||
void Shutdown();
|
||||
void Close();
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <DefaultSettings.h>
|
||||
|
||||
#include "LegacyProfileGeneratorNamespaces.h"
|
||||
#include "TerminalSettingsSerializationHelpers.h"
|
||||
|
||||
using namespace TerminalApp;
|
||||
using namespace winrt::Microsoft::Terminal::Settings;
|
||||
@@ -52,57 +53,6 @@ static constexpr std::string_view BackgroundImageAlignmentKey{ "backgroundImageA
|
||||
static constexpr std::string_view RetroTerminalEffectKey{ "experimental.retroTerminalEffect" };
|
||||
static constexpr std::string_view AntialiasingModeKey{ "antialiasingMode" };
|
||||
|
||||
// Possible values for closeOnExit
|
||||
static constexpr std::string_view CloseOnExitAlways{ "always" };
|
||||
static constexpr std::string_view CloseOnExitGraceful{ "graceful" };
|
||||
static constexpr std::string_view CloseOnExitNever{ "never" };
|
||||
|
||||
// Possible values for Scrollbar state
|
||||
static constexpr std::wstring_view AlwaysVisible{ L"visible" };
|
||||
static constexpr std::wstring_view AlwaysHide{ L"hidden" };
|
||||
|
||||
// Possible values for Cursor Shape
|
||||
static constexpr std::wstring_view CursorShapeVintage{ L"vintage" };
|
||||
static constexpr std::wstring_view CursorShapeBar{ L"bar" };
|
||||
static constexpr std::wstring_view CursorShapeUnderscore{ L"underscore" };
|
||||
static constexpr std::wstring_view CursorShapeFilledbox{ L"filledBox" };
|
||||
static constexpr std::wstring_view CursorShapeEmptybox{ L"emptyBox" };
|
||||
|
||||
// Possible values for Font Weight
|
||||
static constexpr std::string_view FontWeightThin{ "thin" };
|
||||
static constexpr std::string_view FontWeightExtraLight{ "extra-light" };
|
||||
static constexpr std::string_view FontWeightLight{ "light" };
|
||||
static constexpr std::string_view FontWeightSemiLight{ "semi-light" };
|
||||
static constexpr std::string_view FontWeightNormal{ "normal" };
|
||||
static constexpr std::string_view FontWeightMedium{ "medium" };
|
||||
static constexpr std::string_view FontWeightSemiBold{ "semi-bold" };
|
||||
static constexpr std::string_view FontWeightBold{ "bold" };
|
||||
static constexpr std::string_view FontWeightExtraBold{ "extra-bold" };
|
||||
static constexpr std::string_view FontWeightBlack{ "black" };
|
||||
static constexpr std::string_view FontWeightExtraBlack{ "extra-black" };
|
||||
|
||||
// Possible values for Image Stretch Mode
|
||||
static constexpr std::string_view ImageStretchModeNone{ "none" };
|
||||
static constexpr std::string_view ImageStretchModeFill{ "fill" };
|
||||
static constexpr std::string_view ImageStretchModeUniform{ "uniform" };
|
||||
static constexpr std::string_view ImageStretchModeUniformTofill{ "uniformToFill" };
|
||||
|
||||
// Possible values for Image Alignment
|
||||
static constexpr std::string_view ImageAlignmentCenter{ "center" };
|
||||
static constexpr std::string_view ImageAlignmentLeft{ "left" };
|
||||
static constexpr std::string_view ImageAlignmentTop{ "top" };
|
||||
static constexpr std::string_view ImageAlignmentRight{ "right" };
|
||||
static constexpr std::string_view ImageAlignmentBottom{ "bottom" };
|
||||
static constexpr std::string_view ImageAlignmentTopLeft{ "topLeft" };
|
||||
static constexpr std::string_view ImageAlignmentTopRight{ "topRight" };
|
||||
static constexpr std::string_view ImageAlignmentBottomLeft{ "bottomLeft" };
|
||||
static constexpr std::string_view ImageAlignmentBottomRight{ "bottomRight" };
|
||||
|
||||
// Possible values for TextAntialiasingMode
|
||||
static constexpr std::wstring_view AntialiasingModeGrayscale{ L"grayscale" };
|
||||
static constexpr std::wstring_view AntialiasingModeCleartype{ L"cleartype" };
|
||||
static constexpr std::wstring_view AntialiasingModeAliased{ L"aliased" };
|
||||
|
||||
Profile::Profile() :
|
||||
Profile(std::nullopt)
|
||||
{
|
||||
@@ -248,8 +198,7 @@ TerminalSettings Profile::CreateTerminalSettings(const std::unordered_map<std::w
|
||||
|
||||
if (_scrollbarState)
|
||||
{
|
||||
ScrollbarState result = ParseScrollbarState(_scrollbarState.value());
|
||||
terminalSettings.ScrollState(result);
|
||||
terminalSettings.ScrollState(_scrollbarState.value());
|
||||
}
|
||||
|
||||
if (HasBackgroundImage())
|
||||
@@ -350,11 +299,9 @@ bool Profile::ShouldBeLayered(const Json::Value& json) const
|
||||
|
||||
// First, check that GUIDs match. This is easy. If they don't match, they
|
||||
// should _definitely_ not layer.
|
||||
if (json.isMember(JsonKey(GuidKey)))
|
||||
if (const auto otherGuid{ JsonUtils::GetValueForKey<std::optional<GUID>>(json, GuidKey) })
|
||||
{
|
||||
const auto guid{ json[JsonKey(GuidKey)] };
|
||||
const auto otherGuid = Utils::GuidFromString(GetWstringFromJson(guid));
|
||||
if (_guid.value() != otherGuid)
|
||||
if (otherGuid != _guid) // optional compare takes care of this
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -368,16 +315,17 @@ bool Profile::ShouldBeLayered(const Json::Value& json) const
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& otherSource = json.isMember(JsonKey(SourceKey)) ? json[JsonKey(SourceKey)] : Json::Value::null;
|
||||
std::optional<std::wstring> otherSource;
|
||||
bool otherHadSource = JsonUtils::GetValueForKey(json, SourceKey, otherSource);
|
||||
|
||||
// For profiles with a `source`, also check the `source` property.
|
||||
bool sourceMatches = false;
|
||||
if (_source.has_value())
|
||||
{
|
||||
if (json.isMember(JsonKey(SourceKey)))
|
||||
if (otherHadSource)
|
||||
{
|
||||
const auto otherSourceString = GetWstringFromJson(otherSource);
|
||||
sourceMatches = otherSourceString == _source.value();
|
||||
// If we have a source and the other has a source, compare them!
|
||||
sourceMatches = otherSource == _source;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -395,52 +343,13 @@ bool Profile::ShouldBeLayered(const Json::Value& json) const
|
||||
}
|
||||
else
|
||||
{
|
||||
// We do not have a source. The only way we match is if source is set to null or "".
|
||||
if (otherSource.isNull() || (otherSource.isString() && otherSource == ""))
|
||||
{
|
||||
sourceMatches = true;
|
||||
}
|
||||
// We do not have a source. The only way we match is if source is unset or set to "".
|
||||
sourceMatches = (!otherSource.has_value() || otherSource.value() == L"");
|
||||
}
|
||||
|
||||
return sourceMatches;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper function to convert a json value into a value of the Stretch enum.
|
||||
// Calls into ParseImageStretchMode. Used with JsonUtils::GetOptionalValue.
|
||||
// Arguments:
|
||||
// - json: the Json::Value object to parse.
|
||||
// Return Value:
|
||||
// - An appropriate value from Windows.UI.Xaml.Media.Stretch
|
||||
Media::Stretch Profile::_ConvertJsonToStretchMode(const Json::Value& json)
|
||||
{
|
||||
return Profile::ParseImageStretchMode(json.asString());
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper function to convert a json value into a value of the Stretch enum.
|
||||
// Calls into ParseImageAlignment. Used with JsonUtils::GetOptionalValue.
|
||||
// Arguments:
|
||||
// - json: the Json::Value object to parse.
|
||||
// Return Value:
|
||||
// - A pair of HorizontalAlignment and VerticalAlignment
|
||||
std::tuple<HorizontalAlignment, VerticalAlignment> Profile::_ConvertJsonToAlignment(const Json::Value& json)
|
||||
{
|
||||
return Profile::ParseImageAlignment(json.asString());
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper function to convert a json value into a bool.
|
||||
// Used with JsonUtils::GetOptionalValue.
|
||||
// Arguments:
|
||||
// - json: the Json::Value object to parse.
|
||||
// Return Value:
|
||||
// - A bool
|
||||
bool Profile::_ConvertJsonToBool(const Json::Value& json)
|
||||
{
|
||||
return json.asBool();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Layer values from the given json object on top of the existing properties
|
||||
// of this object. For any keys we're expecting to be able to parse in the
|
||||
@@ -456,89 +365,45 @@ bool Profile::_ConvertJsonToBool(const Json::Value& json)
|
||||
void Profile::LayerJson(const Json::Value& json)
|
||||
{
|
||||
// Profile-specific Settings
|
||||
JsonUtils::GetWstring(json, NameKey, _name);
|
||||
|
||||
JsonUtils::GetOptionalGuid(json, GuidKey, _guid);
|
||||
|
||||
JsonUtils::GetBool(json, HiddenKey, _hidden);
|
||||
JsonUtils::GetValueForKey(json, NameKey, _name);
|
||||
JsonUtils::GetValueForKey(json, GuidKey, _guid);
|
||||
JsonUtils::GetValueForKey(json, HiddenKey, _hidden);
|
||||
|
||||
// Core Settings
|
||||
JsonUtils::GetOptionalColor(json, ForegroundKey, _defaultForeground);
|
||||
|
||||
JsonUtils::GetOptionalColor(json, BackgroundKey, _defaultBackground);
|
||||
|
||||
JsonUtils::GetOptionalColor(json, SelectionBackgroundKey, _selectionBackground);
|
||||
|
||||
JsonUtils::GetOptionalColor(json, CursorColorKey, _cursorColor);
|
||||
|
||||
JsonUtils::GetOptionalString(json, ColorSchemeKey, _schemeName);
|
||||
JsonUtils::GetValueForKey(json, ForegroundKey, _defaultForeground);
|
||||
JsonUtils::GetValueForKey(json, BackgroundKey, _defaultBackground);
|
||||
JsonUtils::GetValueForKey(json, SelectionBackgroundKey, _selectionBackground);
|
||||
JsonUtils::GetValueForKey(json, CursorColorKey, _cursorColor);
|
||||
JsonUtils::GetValueForKey(json, ColorSchemeKey, _schemeName);
|
||||
|
||||
// TODO:MSFT:20642297 - Use a sentinel value (-1) for "Infinite scrollback"
|
||||
JsonUtils::GetInt(json, HistorySizeKey, _historySize);
|
||||
|
||||
JsonUtils::GetBool(json, SnapOnInputKey, _snapOnInput);
|
||||
|
||||
JsonUtils::GetBool(json, AltGrAliasingKey, _altGrAliasing);
|
||||
|
||||
JsonUtils::GetUInt(json, CursorHeightKey, _cursorHeight);
|
||||
|
||||
if (json.isMember(JsonKey(CursorShapeKey)))
|
||||
{
|
||||
auto cursorShape{ json[JsonKey(CursorShapeKey)] };
|
||||
_cursorShape = _ParseCursorShape(GetWstringFromJson(cursorShape));
|
||||
}
|
||||
JsonUtils::GetOptionalString(json, TabTitleKey, _tabTitle);
|
||||
JsonUtils::GetValueForKey(json, HistorySizeKey, _historySize);
|
||||
JsonUtils::GetValueForKey(json, SnapOnInputKey, _snapOnInput);
|
||||
JsonUtils::GetValueForKey(json, AltGrAliasingKey, _altGrAliasing);
|
||||
JsonUtils::GetValueForKey(json, CursorHeightKey, _cursorHeight);
|
||||
JsonUtils::GetValueForKey(json, CursorShapeKey, _cursorShape);
|
||||
JsonUtils::GetValueForKey(json, TabTitleKey, _tabTitle);
|
||||
|
||||
// Control Settings
|
||||
JsonUtils::GetOptionalGuid(json, ConnectionTypeKey, _connectionType);
|
||||
|
||||
JsonUtils::GetWstring(json, CommandlineKey, _commandline);
|
||||
|
||||
JsonUtils::GetWstring(json, FontFaceKey, _fontFace);
|
||||
|
||||
JsonUtils::GetInt(json, FontSizeKey, _fontSize);
|
||||
|
||||
if (json.isMember(JsonKey(FontWeightKey)))
|
||||
{
|
||||
auto fontWeight{ json[JsonKey(FontWeightKey)] };
|
||||
_fontWeight = _ParseFontWeight(fontWeight);
|
||||
}
|
||||
|
||||
JsonUtils::GetDouble(json, AcrylicTransparencyKey, _acrylicTransparency);
|
||||
|
||||
JsonUtils::GetBool(json, UseAcrylicKey, _useAcrylic);
|
||||
|
||||
JsonUtils::GetBool(json, SuppressApplicationTitleKey, _suppressApplicationTitle);
|
||||
|
||||
if (json.isMember(JsonKey(CloseOnExitKey)))
|
||||
{
|
||||
auto closeOnExit{ json[JsonKey(CloseOnExitKey)] };
|
||||
_closeOnExitMode = ParseCloseOnExitMode(closeOnExit);
|
||||
}
|
||||
|
||||
JsonUtils::GetWstring(json, PaddingKey, _padding);
|
||||
|
||||
JsonUtils::GetOptionalString(json, ScrollbarStateKey, _scrollbarState);
|
||||
|
||||
JsonUtils::GetOptionalString(json, StartingDirectoryKey, _startingDirectory);
|
||||
|
||||
JsonUtils::GetOptionalString(json, IconKey, _icon);
|
||||
|
||||
JsonUtils::GetOptionalString(json, BackgroundImageKey, _backgroundImage);
|
||||
|
||||
JsonUtils::GetOptionalDouble(json, BackgroundImageOpacityKey, _backgroundImageOpacity);
|
||||
|
||||
JsonUtils::GetOptionalValue(json, BackgroundImageStretchModeKey, _backgroundImageStretchMode, &Profile::_ConvertJsonToStretchMode);
|
||||
|
||||
JsonUtils::GetOptionalValue(json, BackgroundImageAlignmentKey, _backgroundImageAlignment, &Profile::_ConvertJsonToAlignment);
|
||||
|
||||
JsonUtils::GetOptionalValue(json, RetroTerminalEffectKey, _retroTerminalEffect, Profile::_ConvertJsonToBool);
|
||||
|
||||
if (json.isMember(JsonKey(AntialiasingModeKey)))
|
||||
{
|
||||
auto antialiasingMode{ json[JsonKey(AntialiasingModeKey)] };
|
||||
_antialiasingMode = ParseTextAntialiasingMode(GetWstringFromJson(antialiasingMode));
|
||||
}
|
||||
JsonUtils::GetValueForKey(json, FontWeightKey, _fontWeight);
|
||||
JsonUtils::GetValueForKey(json, ConnectionTypeKey, _connectionType);
|
||||
JsonUtils::GetValueForKey(json, CommandlineKey, _commandline);
|
||||
JsonUtils::GetValueForKey(json, FontFaceKey, _fontFace);
|
||||
JsonUtils::GetValueForKey(json, FontSizeKey, _fontSize);
|
||||
JsonUtils::GetValueForKey(json, AcrylicTransparencyKey, _acrylicTransparency);
|
||||
JsonUtils::GetValueForKey(json, UseAcrylicKey, _useAcrylic);
|
||||
JsonUtils::GetValueForKey(json, SuppressApplicationTitleKey, _suppressApplicationTitle);
|
||||
JsonUtils::GetValueForKey(json, CloseOnExitKey, _closeOnExitMode);
|
||||
JsonUtils::GetValueForKey(json, PaddingKey, _padding);
|
||||
JsonUtils::GetValueForKey(json, ScrollbarStateKey, _scrollbarState);
|
||||
JsonUtils::GetValueForKey(json, StartingDirectoryKey, _startingDirectory);
|
||||
JsonUtils::GetValueForKey(json, IconKey, _icon);
|
||||
JsonUtils::GetValueForKey(json, BackgroundImageKey, _backgroundImage);
|
||||
JsonUtils::GetValueForKey(json, BackgroundImageOpacityKey, _backgroundImageOpacity);
|
||||
JsonUtils::GetValueForKey(json, BackgroundImageStretchModeKey, _backgroundImageStretchMode);
|
||||
JsonUtils::GetValueForKey(json, BackgroundImageAlignmentKey, _backgroundImageAlignment);
|
||||
JsonUtils::GetValueForKey(json, RetroTerminalEffectKey, _retroTerminalEffect);
|
||||
JsonUtils::GetValueForKey(json, AntialiasingModeKey, _antialiasingMode);
|
||||
}
|
||||
|
||||
void Profile::SetFontFace(std::wstring fontFace) noexcept
|
||||
@@ -770,249 +635,6 @@ std::wstring Profile::EvaluateStartingDirectory(const std::wstring& directory)
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper function for converting a user-specified font weight value to its corresponding enum
|
||||
// Arguments:
|
||||
// - The value from the settings.json file
|
||||
// Return Value:
|
||||
// - The corresponding value which maps to the string provided by the user
|
||||
winrt::Windows::UI::Text::FontWeight Profile::_ParseFontWeight(const Json::Value& json)
|
||||
{
|
||||
if (json.isUInt())
|
||||
{
|
||||
winrt::Windows::UI::Text::FontWeight weight;
|
||||
weight.Weight = static_cast<uint16_t>(json.asUInt());
|
||||
|
||||
// We're only accepting variable values between 100 and 990 so we don't go too crazy.
|
||||
if (weight.Weight >= 100 && weight.Weight <= 990)
|
||||
{
|
||||
return weight;
|
||||
}
|
||||
}
|
||||
|
||||
if (json.isString())
|
||||
{
|
||||
auto fontWeight = json.asString();
|
||||
if (fontWeight == FontWeightThin)
|
||||
{
|
||||
return winrt::Windows::UI::Text::FontWeights::Thin();
|
||||
}
|
||||
else if (fontWeight == FontWeightExtraLight)
|
||||
{
|
||||
return winrt::Windows::UI::Text::FontWeights::ExtraLight();
|
||||
}
|
||||
else if (fontWeight == FontWeightLight)
|
||||
{
|
||||
return winrt::Windows::UI::Text::FontWeights::Light();
|
||||
}
|
||||
else if (fontWeight == FontWeightSemiLight)
|
||||
{
|
||||
return winrt::Windows::UI::Text::FontWeights::SemiLight();
|
||||
}
|
||||
else if (fontWeight == FontWeightNormal)
|
||||
{
|
||||
return winrt::Windows::UI::Text::FontWeights::Normal();
|
||||
}
|
||||
else if (fontWeight == FontWeightMedium)
|
||||
{
|
||||
return winrt::Windows::UI::Text::FontWeights::Medium();
|
||||
}
|
||||
else if (fontWeight == FontWeightSemiBold)
|
||||
{
|
||||
return winrt::Windows::UI::Text::FontWeights::SemiBold();
|
||||
}
|
||||
else if (fontWeight == FontWeightBold)
|
||||
{
|
||||
return winrt::Windows::UI::Text::FontWeights::Bold();
|
||||
}
|
||||
else if (fontWeight == FontWeightExtraBold)
|
||||
{
|
||||
return winrt::Windows::UI::Text::FontWeights::ExtraBold();
|
||||
}
|
||||
else if (fontWeight == FontWeightBlack)
|
||||
{
|
||||
return winrt::Windows::UI::Text::FontWeights::Black();
|
||||
}
|
||||
else if (fontWeight == FontWeightExtraBlack)
|
||||
{
|
||||
return winrt::Windows::UI::Text::FontWeights::ExtraBlack();
|
||||
}
|
||||
}
|
||||
|
||||
return winrt::Windows::UI::Text::FontWeights::Normal();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper function for converting a user-specified closeOnExit value to its corresponding enum
|
||||
// Arguments:
|
||||
// - The value from the settings.json file
|
||||
// Return Value:
|
||||
// - The corresponding enum value which maps to the string provided by the user
|
||||
CloseOnExitMode Profile::ParseCloseOnExitMode(const Json::Value& json)
|
||||
{
|
||||
if (json.isBool())
|
||||
{
|
||||
return json.asBool() ? CloseOnExitMode::Graceful : CloseOnExitMode::Never;
|
||||
}
|
||||
|
||||
if (json.isString())
|
||||
{
|
||||
auto closeOnExit = json.asString();
|
||||
if (closeOnExit == CloseOnExitAlways)
|
||||
{
|
||||
return CloseOnExitMode::Always;
|
||||
}
|
||||
else if (closeOnExit == CloseOnExitGraceful)
|
||||
{
|
||||
return CloseOnExitMode::Graceful;
|
||||
}
|
||||
else if (closeOnExit == CloseOnExitNever)
|
||||
{
|
||||
return CloseOnExitMode::Never;
|
||||
}
|
||||
}
|
||||
|
||||
return CloseOnExitMode::Graceful;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper function for converting a user-specified scrollbar state to its corresponding enum
|
||||
// Arguments:
|
||||
// - The value from the settings.json file
|
||||
// Return Value:
|
||||
// - The corresponding enum value which maps to the string provided by the user
|
||||
ScrollbarState Profile::ParseScrollbarState(const std::wstring& scrollbarState)
|
||||
{
|
||||
if (scrollbarState == AlwaysVisible)
|
||||
{
|
||||
return ScrollbarState::Visible;
|
||||
}
|
||||
else if (scrollbarState == AlwaysHide)
|
||||
{
|
||||
return ScrollbarState::Hidden;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ScrollbarState::Visible;
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper function for converting a user-specified image stretch mode
|
||||
// to the appropriate enum value
|
||||
// Arguments:
|
||||
// - The value from the settings.json file
|
||||
// Return Value:
|
||||
// - The corresponding enum value which maps to the string provided by the user
|
||||
Media::Stretch Profile::ParseImageStretchMode(const std::string_view imageStretchMode)
|
||||
{
|
||||
if (imageStretchMode == ImageStretchModeNone)
|
||||
{
|
||||
return Media::Stretch::None;
|
||||
}
|
||||
else if (imageStretchMode == ImageStretchModeFill)
|
||||
{
|
||||
return Media::Stretch::Fill;
|
||||
}
|
||||
else if (imageStretchMode == ImageStretchModeUniform)
|
||||
{
|
||||
return Media::Stretch::Uniform;
|
||||
}
|
||||
else // Fall through to default behavior
|
||||
{
|
||||
return Media::Stretch::UniformToFill;
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper function for converting a user-specified image horizontal and vertical
|
||||
// alignment to the appropriate enum values tuple
|
||||
// Arguments:
|
||||
// - The value from the settings.json file
|
||||
// Return Value:
|
||||
// - The corresponding enum values tuple which maps to the string provided by the user
|
||||
std::tuple<HorizontalAlignment, VerticalAlignment> Profile::ParseImageAlignment(const std::string_view imageAlignment)
|
||||
{
|
||||
if (imageAlignment == ImageAlignmentTopLeft)
|
||||
{
|
||||
return std::make_tuple(HorizontalAlignment::Left,
|
||||
VerticalAlignment::Top);
|
||||
}
|
||||
else if (imageAlignment == ImageAlignmentBottomLeft)
|
||||
{
|
||||
return std::make_tuple(HorizontalAlignment::Left,
|
||||
VerticalAlignment::Bottom);
|
||||
}
|
||||
else if (imageAlignment == ImageAlignmentLeft)
|
||||
{
|
||||
return std::make_tuple(HorizontalAlignment::Left,
|
||||
VerticalAlignment::Center);
|
||||
}
|
||||
else if (imageAlignment == ImageAlignmentTopRight)
|
||||
{
|
||||
return std::make_tuple(HorizontalAlignment::Right,
|
||||
VerticalAlignment::Top);
|
||||
}
|
||||
else if (imageAlignment == ImageAlignmentBottomRight)
|
||||
{
|
||||
return std::make_tuple(HorizontalAlignment::Right,
|
||||
VerticalAlignment::Bottom);
|
||||
}
|
||||
else if (imageAlignment == ImageAlignmentRight)
|
||||
{
|
||||
return std::make_tuple(HorizontalAlignment::Right,
|
||||
VerticalAlignment::Center);
|
||||
}
|
||||
else if (imageAlignment == ImageAlignmentTop)
|
||||
{
|
||||
return std::make_tuple(HorizontalAlignment::Center,
|
||||
VerticalAlignment::Top);
|
||||
}
|
||||
else if (imageAlignment == ImageAlignmentBottom)
|
||||
{
|
||||
return std::make_tuple(HorizontalAlignment::Center,
|
||||
VerticalAlignment::Bottom);
|
||||
}
|
||||
else // Fall through to default alignment
|
||||
{
|
||||
return std::make_tuple(HorizontalAlignment::Center,
|
||||
VerticalAlignment::Center);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper function for converting a user-specified cursor style corresponding
|
||||
// CursorStyle enum value
|
||||
// Arguments:
|
||||
// - cursorShapeString: The string value from the settings file to parse
|
||||
// Return Value:
|
||||
// - The corresponding enum value which maps to the string provided by the user
|
||||
CursorStyle Profile::_ParseCursorShape(const std::wstring& cursorShapeString)
|
||||
{
|
||||
if (cursorShapeString == CursorShapeVintage)
|
||||
{
|
||||
return CursorStyle::Vintage;
|
||||
}
|
||||
else if (cursorShapeString == CursorShapeBar)
|
||||
{
|
||||
return CursorStyle::Bar;
|
||||
}
|
||||
else if (cursorShapeString == CursorShapeUnderscore)
|
||||
{
|
||||
return CursorStyle::Underscore;
|
||||
}
|
||||
else if (cursorShapeString == CursorShapeFilledbox)
|
||||
{
|
||||
return CursorStyle::FilledBox;
|
||||
}
|
||||
else if (cursorShapeString == CursorShapeEmptybox)
|
||||
{
|
||||
return CursorStyle::EmptyBox;
|
||||
}
|
||||
// default behavior for invalid data
|
||||
return CursorStyle::Bar;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - If this profile never had a GUID set for it, generate a runtime GUID for
|
||||
// the profile. If a profile had their guid manually set to {0}, this method
|
||||
@@ -1078,17 +700,13 @@ GUID Profile::_GenerateGuidForProfile(const std::wstring& name, const std::optio
|
||||
// - The json's `guid`, or a guid synthesized for it.
|
||||
GUID Profile::GetGuidOrGenerateForJson(const Json::Value& json) noexcept
|
||||
{
|
||||
std::optional<GUID> guid{ std::nullopt };
|
||||
|
||||
JsonUtils::GetOptionalGuid(json, GuidKey, guid);
|
||||
if (guid)
|
||||
if (const auto guid{ JsonUtils::GetValueForKey<std::optional<GUID>>(json, GuidKey) })
|
||||
{
|
||||
return guid.value();
|
||||
}
|
||||
|
||||
const auto name = GetWstringFromJson(json[JsonKey(NameKey)]);
|
||||
std::optional<std::wstring> source{ std::nullopt };
|
||||
JsonUtils::GetOptionalString(json, SourceKey, source);
|
||||
const auto name{ JsonUtils::GetValueForKey<std::wstring>(json, NameKey) };
|
||||
const auto source{ JsonUtils::GetValueForKey<std::optional<std::wstring>>(json, SourceKey) };
|
||||
|
||||
return Profile::_GenerateGuidForProfile(name, source);
|
||||
}
|
||||
@@ -1097,28 +715,3 @@ void Profile::SetRetroTerminalEffect(bool value) noexcept
|
||||
{
|
||||
_retroTerminalEffect = value;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper function for converting a user-specified antialiasing mode
|
||||
// corresponding TextAntialiasingMode enum value
|
||||
// Arguments:
|
||||
// - antialiasingMode: The string value from the settings file to parse
|
||||
// Return Value:
|
||||
// - The corresponding enum value which maps to the string provided by the user
|
||||
TextAntialiasingMode Profile::ParseTextAntialiasingMode(const std::wstring& antialiasingMode)
|
||||
{
|
||||
if (antialiasingMode == AntialiasingModeCleartype)
|
||||
{
|
||||
return TextAntialiasingMode::Cleartype;
|
||||
}
|
||||
else if (antialiasingMode == AntialiasingModeAliased)
|
||||
{
|
||||
return TextAntialiasingMode::Aliased;
|
||||
}
|
||||
else if (antialiasingMode == AntialiasingModeGrayscale)
|
||||
{
|
||||
return TextAntialiasingMode::Grayscale;
|
||||
}
|
||||
// default behavior for invalid data
|
||||
return TextAntialiasingMode::Grayscale;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ Author(s):
|
||||
--*/
|
||||
#pragma once
|
||||
#include "ColorScheme.h"
|
||||
#include "SettingsTypes.h"
|
||||
|
||||
// fwdecl unittest classes
|
||||
namespace TerminalAppLocalTests
|
||||
@@ -35,14 +36,7 @@ constexpr GUID RUNTIME_GENERATED_PROFILE_NAMESPACE_GUID = { 0xf65ddb7e, 0x706b,
|
||||
namespace TerminalApp
|
||||
{
|
||||
class Profile;
|
||||
|
||||
enum class CloseOnExitMode
|
||||
{
|
||||
Never = 0,
|
||||
Graceful,
|
||||
Always
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
class TerminalApp::Profile final
|
||||
{
|
||||
@@ -107,24 +101,8 @@ public:
|
||||
private:
|
||||
static std::wstring EvaluateStartingDirectory(const std::wstring& directory);
|
||||
|
||||
static winrt::Microsoft::Terminal::Settings::ScrollbarState ParseScrollbarState(const std::wstring& scrollbarState);
|
||||
static winrt::Windows::UI::Xaml::Media::Stretch ParseImageStretchMode(const std::string_view imageStretchMode);
|
||||
static winrt::Windows::UI::Xaml::Media::Stretch _ConvertJsonToStretchMode(const Json::Value& json);
|
||||
static std::tuple<winrt::Windows::UI::Xaml::HorizontalAlignment, winrt::Windows::UI::Xaml::VerticalAlignment> ParseImageAlignment(const std::string_view imageAlignment);
|
||||
static std::tuple<winrt::Windows::UI::Xaml::HorizontalAlignment, winrt::Windows::UI::Xaml::VerticalAlignment> _ConvertJsonToAlignment(const Json::Value& json);
|
||||
|
||||
static winrt::Windows::UI::Text::FontWeight _ParseFontWeight(const Json::Value& json);
|
||||
|
||||
static CloseOnExitMode ParseCloseOnExitMode(const Json::Value& json);
|
||||
|
||||
static winrt::Microsoft::Terminal::Settings::CursorStyle _ParseCursorShape(const std::wstring& cursorShapeString);
|
||||
|
||||
static winrt::Microsoft::Terminal::Settings::TextAntialiasingMode ParseTextAntialiasingMode(const std::wstring& antialiasingMode);
|
||||
|
||||
static GUID _GenerateGuidForProfile(const std::wstring& name, const std::optional<std::wstring>& source) noexcept;
|
||||
|
||||
static bool _ConvertJsonToBool(const Json::Value& json);
|
||||
|
||||
std::optional<GUID> _guid{ std::nullopt };
|
||||
std::optional<std::wstring> _source{ std::nullopt };
|
||||
std::wstring _name;
|
||||
@@ -159,7 +137,7 @@ private:
|
||||
std::optional<winrt::Windows::UI::Xaml::Media::Stretch> _backgroundImageStretchMode;
|
||||
std::optional<std::tuple<winrt::Windows::UI::Xaml::HorizontalAlignment, winrt::Windows::UI::Xaml::VerticalAlignment>> _backgroundImageAlignment;
|
||||
|
||||
std::optional<std::wstring> _scrollbarState;
|
||||
std::optional<::winrt::Microsoft::Terminal::Settings::ScrollbarState> _scrollbarState;
|
||||
CloseOnExitMode _closeOnExitMode;
|
||||
std::wstring _padding;
|
||||
|
||||
|
||||
@@ -543,6 +543,10 @@
|
||||
<data name="ResetTabNameCommandKey" xml:space="preserve">
|
||||
<value>Reset tab title</value>
|
||||
</data>
|
||||
<data name="ExecuteCommandlineCommandKey" xml:space="preserve">
|
||||
<value>Run commandline "{0}" in this window</value>
|
||||
<comment>{0} will be replaced with user-defined commandline</comment>
|
||||
</data>
|
||||
<data name="CrimsonColorButton.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
|
||||
<value>Crimson</value>
|
||||
</data>
|
||||
|
||||
28
src/cascadia/TerminalApp/SettingsTypes.h
Normal file
28
src/cascadia/TerminalApp/SettingsTypes.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/*++
|
||||
Copyright (c) Microsoft Corporation
|
||||
Licensed under the MIT license.
|
||||
|
||||
Module Name:
|
||||
- SettingsTypes.h
|
||||
|
||||
Abstract:
|
||||
- Types used in the settings model (non-exported)
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace TerminalApp
|
||||
{
|
||||
enum class CloseOnExitMode
|
||||
{
|
||||
Never = 0,
|
||||
Graceful,
|
||||
Always
|
||||
};
|
||||
|
||||
struct LaunchPosition
|
||||
{
|
||||
std::optional<int> x;
|
||||
std::optional<int> y;
|
||||
};
|
||||
};
|
||||
@@ -194,6 +194,10 @@ namespace winrt::TerminalApp::implementation
|
||||
_RenameTabHandlers(*this, *eventArgs);
|
||||
break;
|
||||
}
|
||||
case ShortcutAction::ExecuteCommandline:
|
||||
{
|
||||
_ExecuteCommandlineHandlers(*this, *eventArgs);
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ namespace winrt::TerminalApp::implementation
|
||||
TYPED_EVENT(SetTabColor, TerminalApp::ShortcutActionDispatch, TerminalApp::ActionEventArgs);
|
||||
TYPED_EVENT(OpenTabColorPicker, TerminalApp::ShortcutActionDispatch, TerminalApp::ActionEventArgs);
|
||||
TYPED_EVENT(RenameTab, TerminalApp::ShortcutActionDispatch, TerminalApp::ActionEventArgs);
|
||||
TYPED_EVENT(ExecuteCommandline, TerminalApp::ShortcutActionDispatch, TerminalApp::ActionEventArgs);
|
||||
// clang-format on
|
||||
|
||||
private:
|
||||
|
||||
@@ -35,10 +35,11 @@ namespace TerminalApp
|
||||
ToggleFocusMode,
|
||||
ToggleFullscreen,
|
||||
ToggleAlwaysOnTop,
|
||||
OpenSettings,
|
||||
SetTabColor,
|
||||
OpenTabColorPicker,
|
||||
OpenSettings,
|
||||
RenameTab,
|
||||
ExecuteCommandline,
|
||||
ToggleCommandPalette
|
||||
};
|
||||
|
||||
@@ -84,5 +85,6 @@ namespace TerminalApp
|
||||
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, ActionEventArgs> SetTabColor;
|
||||
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, ActionEventArgs> OpenTabColorPicker;
|
||||
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, ActionEventArgs> RenameTab;
|
||||
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, ActionEventArgs> ExecuteCommandline;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -875,6 +875,10 @@ namespace winrt::TerminalApp::implementation
|
||||
return _rootPane->PreCalculateAutoSplit(_activePane, availableSpace).value_or(SplitState::Vertical);
|
||||
}
|
||||
|
||||
bool Tab::PreCalculateCanSplit(SplitState splitType, winrt::Windows::Foundation::Size availableSpace) const
|
||||
{
|
||||
return _rootPane->PreCalculateCanSplit(_activePane, splitType, availableSpace).value_or(false);
|
||||
}
|
||||
DEFINE_EVENT(Tab, ActivePaneChanged, _ActivePaneChangedHandlers, winrt::delegate<>);
|
||||
DEFINE_EVENT(Tab, ColorSelected, _colorSelected, winrt::delegate<winrt::Windows::UI::Color>);
|
||||
DEFINE_EVENT(Tab, ColorCleared, _colorCleared, winrt::delegate<>);
|
||||
|
||||
@@ -40,6 +40,7 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
float CalcSnappedDimension(const bool widthOrHeight, const float dimension) const;
|
||||
SplitState PreCalculateAutoSplit(winrt::Windows::Foundation::Size rootSize) const;
|
||||
bool PreCalculateCanSplit(SplitState splitType, winrt::Windows::Foundation::Size availableSpace) const;
|
||||
|
||||
void ResizeContent(const winrt::Windows::Foundation::Size& newSize);
|
||||
void ResizePane(const winrt::TerminalApp::Direction& direction);
|
||||
|
||||
@@ -46,7 +46,8 @@ namespace winrt
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
TerminalPage::TerminalPage() :
|
||||
_tabs{ winrt::single_threaded_observable_vector<TerminalApp::Tab>() }
|
||||
_tabs{ winrt::single_threaded_observable_vector<TerminalApp::Tab>() },
|
||||
_startupActions{ winrt::single_threaded_vector<winrt::TerminalApp::ActionAndArgs>() }
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
@@ -223,7 +224,7 @@ namespace winrt::TerminalApp::implementation
|
||||
if (_startupState == StartupState::NotInitialized)
|
||||
{
|
||||
_startupState = StartupState::InStartup;
|
||||
if (_startupActions.empty())
|
||||
if (_startupActions.Size() == 0)
|
||||
{
|
||||
_OpenNewTab(nullptr);
|
||||
|
||||
@@ -231,22 +232,27 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
else
|
||||
{
|
||||
_ProcessStartupActions();
|
||||
_ProcessStartupActions(_startupActions, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Process all the startup actions in our list of startup actions. We'll
|
||||
// do this all at once here.
|
||||
// - Process all the startup actions in the provided list of startup
|
||||
// actions. We'll do this all at once here.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// - actions: a winrt vector of actions to process. Note that this must NOT
|
||||
// be an IVector&, because we need the collection to be accessible on the
|
||||
// other side of the co_await.
|
||||
// - initial: if true, we're parsing these args during startup, and we
|
||||
// should fire an Initialized event.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
winrt::fire_and_forget TerminalPage::_ProcessStartupActions()
|
||||
winrt::fire_and_forget TerminalPage::_ProcessStartupActions(Windows::Foundation::Collections::IVector<winrt::TerminalApp::ActionAndArgs> actions,
|
||||
const bool initial)
|
||||
{
|
||||
// If there are no actions left, do nothing.
|
||||
if (_startupActions.empty())
|
||||
if (actions.Size() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -256,11 +262,20 @@ namespace winrt::TerminalApp::implementation
|
||||
co_await winrt::resume_foreground(Dispatcher(), CoreDispatcherPriority::Normal);
|
||||
if (auto page{ weakThis.get() })
|
||||
{
|
||||
for (const auto& action : _startupActions)
|
||||
for (const auto& action : actions)
|
||||
{
|
||||
_actionDispatch->DoAction(action);
|
||||
if (auto page{ weakThis.get() })
|
||||
{
|
||||
_actionDispatch->DoAction(action);
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (initial)
|
||||
{
|
||||
_CompleteInitialization();
|
||||
}
|
||||
}
|
||||
@@ -857,6 +872,7 @@ namespace winrt::TerminalApp::implementation
|
||||
_actionDispatch->SetTabColor({ this, &TerminalPage::_HandleSetTabColor });
|
||||
_actionDispatch->OpenTabColorPicker({ this, &TerminalPage::_HandleOpenTabColorPicker });
|
||||
_actionDispatch->RenameTab({ this, &TerminalPage::_HandleRenameTab });
|
||||
_actionDispatch->ExecuteCommandline({ this, &TerminalPage::_HandleExecuteCommandline });
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -1364,19 +1380,20 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
const auto controlConnection = _CreateConnectionFromSettings(realGuid, controlSettings);
|
||||
|
||||
const auto canSplit = focusedTab->CanSplitPane(splitType);
|
||||
|
||||
if (!canSplit && _startupState == StartupState::Initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const float contentWidth = ::base::saturated_cast<float>(_tabContent.ActualWidth());
|
||||
const float contentHeight = ::base::saturated_cast<float>(_tabContent.ActualHeight());
|
||||
const winrt::Windows::Foundation::Size availableSpace{ contentWidth, contentHeight };
|
||||
|
||||
auto realSplitType = splitType;
|
||||
if (realSplitType == SplitState::Automatic && _startupState < StartupState::Initialized)
|
||||
if (realSplitType == SplitState::Automatic)
|
||||
{
|
||||
float contentWidth = gsl::narrow_cast<float>(_tabContent.ActualWidth());
|
||||
float contentHeight = gsl::narrow_cast<float>(_tabContent.ActualHeight());
|
||||
realSplitType = focusedTab->PreCalculateAutoSplit({ contentWidth, contentHeight });
|
||||
realSplitType = focusedTab->PreCalculateAutoSplit(availableSpace);
|
||||
}
|
||||
|
||||
const auto canSplit = focusedTab->PreCalculateCanSplit(realSplitType, availableSpace);
|
||||
if (!canSplit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TermControl newControl{ controlSettings, controlConnection };
|
||||
@@ -1930,9 +1947,13 @@ namespace winrt::TerminalApp::implementation
|
||||
// - actions: a list of Actions to process on startup.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalPage::SetStartupActions(std::deque<winrt::TerminalApp::ActionAndArgs>& actions)
|
||||
void TerminalPage::SetStartupActions(std::vector<winrt::TerminalApp::ActionAndArgs>& actions)
|
||||
{
|
||||
_startupActions = actions;
|
||||
// The fastest way to copy all the actions out of the std::vector and
|
||||
// put them into a winrt::IVector is by making a copy, then moving the
|
||||
// copy into the winrt vector ctor.
|
||||
auto listCopy = actions;
|
||||
_startupActions = winrt::single_threaded_vector<winrt::TerminalApp::ActionAndArgs>(std::move(listCopy));
|
||||
}
|
||||
|
||||
winrt::TerminalApp::IDialogPresenter TerminalPage::DialogPresenter() const
|
||||
@@ -2192,6 +2213,49 @@ namespace winrt::TerminalApp::implementation
|
||||
// TODO GH#3327: Look at what to do with the NC area when we have XAML theming
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - This is a helper method to get the commandline out of a
|
||||
// ExecuteCommandline action, break it into subcommands, and attempt to
|
||||
// parse it into actions. This is used by _HandleExecuteCommandline for
|
||||
// processing commandlines in the current WT window.
|
||||
// Arguments:
|
||||
// - args: the ExecuteCommandlineArgs to synthesize a list of startup actions for.
|
||||
// Return Value:
|
||||
// - an empty list if we failed to parse, otherwise a list of actions to execute.
|
||||
std::vector<winrt::TerminalApp::ActionAndArgs> TerminalPage::ConvertExecuteCommandlineToActions(const TerminalApp::ExecuteCommandlineArgs& args)
|
||||
{
|
||||
if (!args || args.Commandline().empty())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
// Convert the commandline into an array of args with
|
||||
// CommandLineToArgvW, similar to how the app typically does when
|
||||
// called from the commandline.
|
||||
int argc = 0;
|
||||
wil::unique_any<LPWSTR*, decltype(&::LocalFree), ::LocalFree> argv{ CommandLineToArgvW(args.Commandline().c_str(), &argc) };
|
||||
if (argv)
|
||||
{
|
||||
std::vector<winrt::hstring> args;
|
||||
|
||||
// Make sure the first argument is wt.exe, because ParseArgs will
|
||||
// always skip the program name. The particular value of this first
|
||||
// string doesn't terribly matter.
|
||||
args.emplace_back(L"wt.exe");
|
||||
for (auto& elem : wil::make_range(argv.get(), argc))
|
||||
{
|
||||
args.emplace_back(elem);
|
||||
}
|
||||
winrt::array_view<const winrt::hstring> argsView{ args };
|
||||
|
||||
::TerminalApp::AppCommandlineArgs appArgs;
|
||||
if (appArgs.ParseArgs(argsView) == 0)
|
||||
{
|
||||
return appArgs.GetStartupActions();
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void TerminalPage::_CommandPaletteClosed(const IInspectable& /*sender*/,
|
||||
const RoutedEventArgs& /*eventArgs*/)
|
||||
{
|
||||
|
||||
@@ -56,7 +56,8 @@ namespace winrt::TerminalApp::implementation
|
||||
bool Fullscreen() const;
|
||||
bool AlwaysOnTop() const;
|
||||
|
||||
void SetStartupActions(std::deque<winrt::TerminalApp::ActionAndArgs>& actions);
|
||||
void SetStartupActions(std::vector<winrt::TerminalApp::ActionAndArgs>& actions);
|
||||
static std::vector<winrt::TerminalApp::ActionAndArgs> ConvertExecuteCommandlineToActions(const TerminalApp::ExecuteCommandlineArgs& args);
|
||||
|
||||
winrt::TerminalApp::IDialogPresenter DialogPresenter() const;
|
||||
void DialogPresenter(winrt::TerminalApp::IDialogPresenter dialogPresenter);
|
||||
@@ -106,8 +107,8 @@ namespace winrt::TerminalApp::implementation
|
||||
winrt::Windows::UI::Xaml::Controls::Grid::LayoutUpdated_revoker _layoutUpdatedRevoker;
|
||||
StartupState _startupState{ StartupState::NotInitialized };
|
||||
|
||||
std::deque<winrt::TerminalApp::ActionAndArgs> _startupActions;
|
||||
winrt::fire_and_forget _ProcessStartupActions();
|
||||
Windows::Foundation::Collections::IVector<winrt::TerminalApp::ActionAndArgs> _startupActions;
|
||||
winrt::fire_and_forget _ProcessStartupActions(Windows::Foundation::Collections::IVector<winrt::TerminalApp::ActionAndArgs> actions, const bool initial);
|
||||
|
||||
void _ShowAboutDialog();
|
||||
void _ShowCloseWarningDialog();
|
||||
@@ -221,6 +222,7 @@ namespace winrt::TerminalApp::implementation
|
||||
void _HandleSetTabColor(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandleOpenTabColorPicker(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandleRenameTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandleExecuteCommandline(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandleToggleCommandPalette(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
// Make sure to hook new actions up in _RegisterActionCallbacks!
|
||||
#pragma endregion
|
||||
|
||||
272
src/cascadia/TerminalApp/TerminalSettingsSerializationHelpers.h
Normal file
272
src/cascadia/TerminalApp/TerminalSettingsSerializationHelpers.h
Normal file
@@ -0,0 +1,272 @@
|
||||
/*++
|
||||
Copyright (c) Microsoft Corporation
|
||||
Licensed under the MIT license.
|
||||
|
||||
Module Name:
|
||||
- TerminalSettingsSerializationHelpers.h
|
||||
|
||||
Abstract:
|
||||
- Specializations of the JsonUtils helpers for things that might end up in a
|
||||
settings document.
|
||||
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#include "JsonUtils.h"
|
||||
#include "SettingsTypes.h"
|
||||
|
||||
#include <winrt/Microsoft.Terminal.Settings.h>
|
||||
#include <winrt/TerminalApp.h>
|
||||
|
||||
JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::CursorStyle)
|
||||
{
|
||||
static constexpr std::array<pair_type, 5> mappings = {
|
||||
pair_type{ "bar", ValueType::Bar },
|
||||
pair_type{ "vintage", ValueType::Vintage },
|
||||
pair_type{ "underscore", ValueType::Underscore },
|
||||
pair_type{ "filledBox", ValueType::FilledBox },
|
||||
pair_type{ "emptyBox", ValueType::EmptyBox }
|
||||
};
|
||||
};
|
||||
|
||||
JSON_ENUM_MAPPER(::winrt::Windows::UI::Xaml::Media::Stretch)
|
||||
{
|
||||
static constexpr std::array<pair_type, 4> mappings = {
|
||||
pair_type{ "uniformToFill", ValueType::UniformToFill },
|
||||
pair_type{ "none", ValueType::None },
|
||||
pair_type{ "fill", ValueType::Fill },
|
||||
pair_type{ "uniform", ValueType::Uniform }
|
||||
};
|
||||
};
|
||||
|
||||
JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::ScrollbarState)
|
||||
{
|
||||
static constexpr std::array<pair_type, 2> mappings = {
|
||||
pair_type{ "visible", ValueType::Visible },
|
||||
pair_type{ "hidden", ValueType::Hidden }
|
||||
};
|
||||
};
|
||||
|
||||
JSON_ENUM_MAPPER(std::tuple<::winrt::Windows::UI::Xaml::HorizontalAlignment, ::winrt::Windows::UI::Xaml::VerticalAlignment>)
|
||||
{
|
||||
// reduce repetition
|
||||
using HA = ::winrt::Windows::UI::Xaml::HorizontalAlignment;
|
||||
using VA = ::winrt::Windows::UI::Xaml::VerticalAlignment;
|
||||
static constexpr std::array<pair_type, 9> mappings = {
|
||||
pair_type{ "center", std::make_tuple(HA::Center, VA::Center) },
|
||||
pair_type{ "topLeft", std::make_tuple(HA::Left, VA::Top) },
|
||||
pair_type{ "bottomLeft", std::make_tuple(HA::Left, VA::Bottom) },
|
||||
pair_type{ "left", std::make_tuple(HA::Left, VA::Center) },
|
||||
pair_type{ "topRight", std::make_tuple(HA::Right, VA::Top) },
|
||||
pair_type{ "bottomRight", std::make_tuple(HA::Right, VA::Bottom) },
|
||||
pair_type{ "right", std::make_tuple(HA::Right, VA::Center) },
|
||||
pair_type{ "top", std::make_tuple(HA::Center, VA::Top) },
|
||||
pair_type{ "bottom", std::make_tuple(HA::Center, VA::Bottom) }
|
||||
};
|
||||
};
|
||||
|
||||
JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::TextAntialiasingMode)
|
||||
{
|
||||
static constexpr std::array<pair_type, 3> mappings = {
|
||||
pair_type{ "grayscale", ValueType::Grayscale },
|
||||
pair_type{ "cleartype", ValueType::Cleartype },
|
||||
pair_type{ "aliased", ValueType::Aliased }
|
||||
};
|
||||
};
|
||||
|
||||
// Type Description:
|
||||
// - Helper for converting a user-specified closeOnExit value to its corresponding enum
|
||||
JSON_ENUM_MAPPER(::TerminalApp::CloseOnExitMode)
|
||||
{
|
||||
JSON_MAPPINGS(3) = {
|
||||
pair_type{ "always", ValueType::Always },
|
||||
pair_type{ "graceful", ValueType::Graceful },
|
||||
pair_type{ "never", ValueType::Never },
|
||||
};
|
||||
|
||||
// Override mapping parser to add boolean parsing
|
||||
CloseOnExitMode FromJson(const Json::Value& json)
|
||||
{
|
||||
if (json.isBool())
|
||||
{
|
||||
return json.asBool() ? ValueType::Graceful : ValueType::Never;
|
||||
}
|
||||
return EnumMapper::FromJson(json);
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return EnumMapper::CanConvert(json) || json.isBool();
|
||||
}
|
||||
};
|
||||
|
||||
// This specialization isn't using JSON_ENUM_MAPPER because we need to have a different
|
||||
// value type (unsinged int) and return type (FontWeight struct). JSON_ENUM_MAPPER
|
||||
// expects that the value type _is_ the return type.
|
||||
template<>
|
||||
struct ::TerminalApp::JsonUtils::ConversionTrait<::winrt::Windows::UI::Text::FontWeight> :
|
||||
public ::TerminalApp::JsonUtils::EnumMapper<
|
||||
unsigned int,
|
||||
::TerminalApp::JsonUtils::ConversionTrait<::winrt::Windows::UI::Text::FontWeight>>
|
||||
{
|
||||
// The original parser used the font weight getters Bold(), Normal(), etc.
|
||||
// They were both cumbersome and *not constant expressions*
|
||||
JSON_MAPPINGS(11) = {
|
||||
pair_type{ "thin", 100u },
|
||||
pair_type{ "extra-light", 200u },
|
||||
pair_type{ "light", 300u },
|
||||
pair_type{ "semi-light", 350u },
|
||||
pair_type{ "normal", 400u },
|
||||
pair_type{ "medium", 500u },
|
||||
pair_type{ "semi-bold", 600u },
|
||||
pair_type{ "bold", 700u },
|
||||
pair_type{ "extra-bold", 800u },
|
||||
pair_type{ "black", 900u },
|
||||
pair_type{ "extra-black", 950u },
|
||||
};
|
||||
|
||||
// Override mapping parser to add boolean parsing
|
||||
auto FromJson(const Json::Value& json)
|
||||
{
|
||||
unsigned int value{ 400 };
|
||||
if (json.isUInt())
|
||||
{
|
||||
value = json.asUInt();
|
||||
}
|
||||
else
|
||||
{
|
||||
value = BaseEnumMapper::FromJson(json);
|
||||
}
|
||||
|
||||
::winrt::Windows::UI::Text::FontWeight weight{
|
||||
static_cast<uint16_t>(std::clamp(value, 100u, 990u))
|
||||
};
|
||||
return weight;
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return BaseEnumMapper::CanConvert(json) || json.isUInt();
|
||||
}
|
||||
};
|
||||
|
||||
JSON_ENUM_MAPPER(::winrt::Windows::UI::Xaml::ElementTheme)
|
||||
{
|
||||
JSON_MAPPINGS(3) = {
|
||||
pair_type{ "system", ValueType::Default },
|
||||
pair_type{ "light", ValueType::Light },
|
||||
pair_type{ "dark", ValueType::Dark },
|
||||
};
|
||||
};
|
||||
|
||||
JSON_ENUM_MAPPER(::winrt::TerminalApp::LaunchMode)
|
||||
{
|
||||
JSON_MAPPINGS(3) = {
|
||||
pair_type{ "default", ValueType::DefaultMode },
|
||||
pair_type{ "maximized", ValueType::MaximizedMode },
|
||||
pair_type{ "fullscreen", ValueType::FullscreenMode },
|
||||
};
|
||||
};
|
||||
|
||||
JSON_ENUM_MAPPER(::winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode)
|
||||
{
|
||||
JSON_MAPPINGS(3) = {
|
||||
pair_type{ "equal", ValueType::Equal },
|
||||
pair_type{ "titleLength", ValueType::SizeToContent },
|
||||
pair_type{ "compact", ValueType::Compact },
|
||||
};
|
||||
};
|
||||
|
||||
// Type Description:
|
||||
// - Helper for converting the initial position string into
|
||||
// 2 coordinate values. We allow users to only provide one coordinate,
|
||||
// thus, we use comma as the separator:
|
||||
// (100, 100): standard input string
|
||||
// (, 100), (100, ): if a value is missing, we set this value as a default
|
||||
// (,): both x and y are set to default
|
||||
// (abc, 100): if a value is not valid, we treat it as default
|
||||
// (100, 100, 100): we only read the first two values, this is equivalent to (100, 100)
|
||||
template<>
|
||||
struct ::TerminalApp::JsonUtils::ConversionTrait<::TerminalApp::LaunchPosition>
|
||||
{
|
||||
::TerminalApp::LaunchPosition FromJson(const Json::Value& json)
|
||||
{
|
||||
::TerminalApp::LaunchPosition ret;
|
||||
std::string initialPosition{ json.asString() };
|
||||
static constexpr char singleCharDelim = ',';
|
||||
std::stringstream tokenStream(initialPosition);
|
||||
std::string token;
|
||||
uint8_t initialPosIndex = 0;
|
||||
|
||||
// Get initial position values till we run out of delimiter separated values in the stream
|
||||
// or we hit max number of allowable values (= 2)
|
||||
// Non-numeral values or empty string will be caught as exception and we do not assign them
|
||||
for (; std::getline(tokenStream, token, singleCharDelim) && (initialPosIndex < 2); initialPosIndex++)
|
||||
{
|
||||
try
|
||||
{
|
||||
int32_t position = std::stoi(token);
|
||||
if (initialPosIndex == 0)
|
||||
{
|
||||
ret.x.emplace(position);
|
||||
}
|
||||
|
||||
if (initialPosIndex == 1)
|
||||
{
|
||||
ret.y.emplace(position);
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return json.isString();
|
||||
}
|
||||
};
|
||||
|
||||
// Possible Direction values
|
||||
JSON_ENUM_MAPPER(::winrt::TerminalApp::Direction)
|
||||
{
|
||||
JSON_MAPPINGS(4) = {
|
||||
pair_type{ "left", ValueType::Left },
|
||||
pair_type{ "right", ValueType::Right },
|
||||
pair_type{ "up", ValueType::Up },
|
||||
pair_type{ "down", ValueType::Down },
|
||||
};
|
||||
};
|
||||
|
||||
// Possible SplitState values
|
||||
JSON_ENUM_MAPPER(::winrt::TerminalApp::SplitState)
|
||||
{
|
||||
JSON_MAPPINGS(3) = {
|
||||
pair_type{ "vertical", ValueType::Vertical },
|
||||
pair_type{ "horizontal", ValueType::Horizontal },
|
||||
pair_type{ "auto", ValueType::Automatic },
|
||||
};
|
||||
};
|
||||
|
||||
// Possible SplitType values
|
||||
JSON_ENUM_MAPPER(::winrt::TerminalApp::SplitType)
|
||||
{
|
||||
JSON_MAPPINGS(1) = {
|
||||
pair_type{ "duplicate", ValueType::Duplicate },
|
||||
};
|
||||
};
|
||||
|
||||
JSON_ENUM_MAPPER(::winrt::TerminalApp::SettingsTarget)
|
||||
{
|
||||
JSON_MAPPINGS(3) = {
|
||||
pair_type{ "settingsFile", ValueType::SettingsFile },
|
||||
pair_type{ "defaultsFile", ValueType::DefaultsFile },
|
||||
pair_type{ "allFiles", ValueType::AllFiles },
|
||||
};
|
||||
};
|
||||
@@ -1,18 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "Utils.h"
|
||||
|
||||
// Method Description:
|
||||
// - Constructs a wstring from a given Json::Value object. Reads the object as
|
||||
// a std::string using asString, then builds an hstring from that std::string,
|
||||
// then converts that hstring into a std::wstring.
|
||||
// Arguments:
|
||||
// - json: the Json::Value to parse as a string
|
||||
// Return Value:
|
||||
// - the wstring equivalent of the value in json
|
||||
std::wstring GetWstringFromJson(const Json::Value& json)
|
||||
{
|
||||
return winrt::to_hstring(json.asString()).c_str();
|
||||
}
|
||||
@@ -13,8 +13,6 @@ Author(s):
|
||||
--*/
|
||||
#pragma once
|
||||
|
||||
std::wstring GetWstringFromJson(const Json::Value& json);
|
||||
|
||||
// Method Description:
|
||||
// - Create a std::string from a string_view. We do this because we can't look
|
||||
// up a key in a Json::Value with a string_view directly, so instead we'll use
|
||||
|
||||
@@ -187,7 +187,7 @@
|
||||
"foreground": "#839496",
|
||||
"background": "#002B36",
|
||||
"cursorColor": "#FFFFFF",
|
||||
"black": "#073642",
|
||||
"black": "#002B36",
|
||||
"red": "#DC322F",
|
||||
"green": "#859900",
|
||||
"yellow": "#B58900",
|
||||
@@ -195,7 +195,7 @@
|
||||
"purple": "#D33682",
|
||||
"cyan": "#2AA198",
|
||||
"white": "#EEE8D5",
|
||||
"brightBlack": "#002B36",
|
||||
"brightBlack": "#073642",
|
||||
"brightRed": "#CB4B16",
|
||||
"brightGreen": "#586E75",
|
||||
"brightYellow": "#657B83",
|
||||
@@ -209,7 +209,7 @@
|
||||
"foreground": "#657B83",
|
||||
"background": "#FDF6E3",
|
||||
"cursorColor": "#002B36",
|
||||
"black": "#073642",
|
||||
"black": "#002B36",
|
||||
"red": "#DC322F",
|
||||
"green": "#859900",
|
||||
"yellow": "#B58900",
|
||||
@@ -217,7 +217,7 @@
|
||||
"purple": "#D33682",
|
||||
"cyan": "#2AA198",
|
||||
"white": "#EEE8D5",
|
||||
"brightBlack": "#002B36",
|
||||
"brightBlack": "#073642",
|
||||
"brightRed": "#CB4B16",
|
||||
"brightGreen": "#586E75",
|
||||
"brightYellow": "#657B83",
|
||||
|
||||
@@ -109,6 +109,7 @@
|
||||
<ClInclude Include="../JsonUtils.h" />
|
||||
<ClInclude Include="../Utils.h" />
|
||||
<ClInclude Include="../DefaultProfileUtils.h" />
|
||||
<ClInclude Include="../TerminalSettingsSerializationHelpers.h" />
|
||||
<ClInclude Include="../TerminalWarnings.h" />
|
||||
<ClInclude Include="../IDynamicProfileGenerator.h" />
|
||||
<ClInclude Include="../PowershellCoreProfileGenerator.h" />
|
||||
@@ -178,8 +179,6 @@
|
||||
<ClCompile Include="../CascadiaSettingsSerialization.cpp" />
|
||||
<ClCompile Include="../AppKeyBindingsSerialization.cpp" />
|
||||
<ClCompile Include="../KeyChordSerialization.cpp" />
|
||||
<ClCompile Include="../JsonUtils.cpp" />
|
||||
<ClCompile Include="../Utils.cpp" />
|
||||
<ClCompile Include="../DefaultProfileUtils.cpp" />
|
||||
<ClCompile Include="../PowershellCoreProfileGenerator.cpp" />
|
||||
<ClCompile Include="../WslDistroGenerator.cpp" />
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="../init.cpp" />
|
||||
<ClCompile Include="../Utils.cpp" />
|
||||
<ClCompile Include="pch.cpp" />
|
||||
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
|
||||
<ClCompile Include="../AzureCloudShellGenerator.cpp">
|
||||
@@ -50,9 +49,6 @@
|
||||
<ClCompile Include="$(OpenConsoleDir)\dep\jsoncpp\jsoncpp.cpp">
|
||||
<Filter>json</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="../JsonUtils.cpp">
|
||||
<Filter>json</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="../Tab.cpp">
|
||||
<Filter>tab</Filter>
|
||||
</ClCompile>
|
||||
@@ -92,6 +88,9 @@
|
||||
<ClInclude Include="../GlobalAppSettings.h">
|
||||
<Filter>settings</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="../TerminalSettingsSerializationHelpers.h">
|
||||
<Filter>settings</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="../KeyChordSerialization.h">
|
||||
<Filter>settings</Filter>
|
||||
</ClInclude>
|
||||
@@ -199,4 +198,4 @@
|
||||
<Filter>app</Filter>
|
||||
</ApplicationDefinition>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
#include <winrt/Windows.Foundation.Collections.h>
|
||||
#include <winrt/Windows.Foundation.Metadata.h>
|
||||
#include <winrt/Windows.Graphics.Display.h>
|
||||
#include <winrt/windows.ui.core.h>
|
||||
#include <winrt/Windows.ui.input.h>
|
||||
#include <winrt/Windows.UI.Text.h>
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
using namespace ::Microsoft::Console::Types;
|
||||
using namespace ::Microsoft::Terminal::Core;
|
||||
using namespace winrt::Windows::Graphics::Display;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace winrt::Windows::UI::Xaml::Input;
|
||||
using namespace winrt::Windows::UI::Xaml::Automation::Peers;
|
||||
@@ -628,7 +629,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
// Then, using the font, get the number of characters that can fit.
|
||||
// Resize our terminal connection to match that size, and initialize the terminal with that size.
|
||||
const auto viewInPixels = Viewport::FromDimensions({ 0, 0 }, windowSize);
|
||||
THROW_IF_FAILED(dxEngine->SetWindowSize({ viewInPixels.Width(), viewInPixels.Height() }));
|
||||
LOG_IF_FAILED(dxEngine->SetWindowSize({ viewInPixels.Width(), viewInPixels.Height() }));
|
||||
|
||||
// Update DxEngine's SelectionBackground
|
||||
dxEngine->SetSelectionBackground(_settings.SelectionBackground());
|
||||
@@ -2274,28 +2275,65 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
// as font size, scrollbar and other control scaling, etc. Make sure the
|
||||
// caller knows what monitor the control is about to appear on.
|
||||
// Return Value:
|
||||
// - a point containing the requested dimensions in pixels.
|
||||
winrt::Windows::Foundation::Point TermControl::GetProposedDimensions(IControlSettings const& settings, const uint32_t dpi)
|
||||
// - a size containing the requested dimensions in pixels.
|
||||
winrt::Windows::Foundation::Size TermControl::GetProposedDimensions(IControlSettings const& settings, const uint32_t dpi)
|
||||
{
|
||||
// If the settings have negative or zero row or column counts, ignore those counts.
|
||||
// (The lower TerminalCore layer also has upper bounds as well, but at this layer
|
||||
// we may eventually impose different ones depending on how many pixels we can address.)
|
||||
const auto cols = ::base::saturated_cast<float>(std::max(settings.InitialCols(), 1));
|
||||
const auto rows = ::base::saturated_cast<float>(std::max(settings.InitialRows(), 1));
|
||||
|
||||
const winrt::Windows::Foundation::Size initialSize{ cols, rows };
|
||||
|
||||
return GetProposedDimensions(initialSize,
|
||||
settings.FontSize(),
|
||||
settings.FontWeight(),
|
||||
settings.FontFace(),
|
||||
settings.ScrollState(),
|
||||
settings.Padding(),
|
||||
dpi);
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Determines how much space (in pixels) an app would need to reserve to
|
||||
// create a control with the settings stored in the settings param. This
|
||||
// accounts for things like the font size and face, the initialRows and
|
||||
// initialCols, and scrollbar visibility. The returned sized is based upon
|
||||
// the provided DPI value
|
||||
// Arguments:
|
||||
// - initialSizeInChars: The size to get the proposed dimensions for.
|
||||
// - fontHeight: The font height to use to calculate the proposed size for.
|
||||
// - fontWeight: The font weight to use to calculate the proposed size for.
|
||||
// - fontFace: The font name to use to calculate the proposed size for.
|
||||
// - scrollState: The ScrollbarState to use to calculate the proposed size for.
|
||||
// - padding: The padding to use to calculate the proposed size for.
|
||||
// - dpi: The DPI we should create the terminal at. This affects things such
|
||||
// as font size, scrollbar and other control scaling, etc. Make sure the
|
||||
// caller knows what monitor the control is about to appear on.
|
||||
// Return Value:
|
||||
// - a size containing the requested dimensions in pixels.
|
||||
winrt::Windows::Foundation::Size TermControl::GetProposedDimensions(const winrt::Windows::Foundation::Size& initialSizeInChars,
|
||||
const int32_t& fontHeight,
|
||||
const winrt::Windows::UI::Text::FontWeight& fontWeight,
|
||||
const winrt::hstring& fontFace,
|
||||
const Microsoft::Terminal::Settings::ScrollbarState& scrollState,
|
||||
const winrt::hstring& padding,
|
||||
const uint32_t dpi)
|
||||
{
|
||||
const auto cols = ::base::saturated_cast<int>(initialSizeInChars.Width);
|
||||
const auto rows = ::base::saturated_cast<int>(initialSizeInChars.Height);
|
||||
|
||||
// Initialize our font information.
|
||||
const auto fontFace = settings.FontFace();
|
||||
const short fontHeight = gsl::narrow_cast<short>(settings.FontSize());
|
||||
const auto fontWeight = settings.FontWeight();
|
||||
// The font width doesn't terribly matter, we'll only be using the
|
||||
// height to look it up
|
||||
// The other params here also largely don't matter.
|
||||
// The family is only used to determine if the font is truetype or
|
||||
// not, but DX doesn't use that info at all.
|
||||
// The Codepage is additionally not actually used by the DX engine at all.
|
||||
FontInfo actualFont = { fontFace, 0, fontWeight.Weight, { 0, fontHeight }, CP_UTF8, false };
|
||||
FontInfo actualFont = { fontFace, 0, fontWeight.Weight, { 0, gsl::narrow_cast<short>(fontHeight) }, CP_UTF8, false };
|
||||
FontInfoDesired desiredFont = { actualFont };
|
||||
|
||||
// If the settings have negative or zero row or column counts, ignore those counts.
|
||||
// (The lower TerminalCore layer also has upper bounds as well, but at this layer
|
||||
// we may eventually impose different ones depending on how many pixels we can address.)
|
||||
const auto cols = std::max(settings.InitialCols(), 1);
|
||||
const auto rows = std::max(settings.InitialRows(), 1);
|
||||
|
||||
// Create a DX engine and initialize it with our font and DPI. We'll
|
||||
// then use it to measure how much space the requested rows and columns
|
||||
// will take up.
|
||||
@@ -2315,13 +2353,13 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
double width = cols * fontSize.X;
|
||||
|
||||
// Reserve additional space if scrollbar is intended to be visible
|
||||
if (settings.ScrollState() == ScrollbarState::Visible)
|
||||
if (scrollState == ScrollbarState::Visible)
|
||||
{
|
||||
width += scrollbarSize;
|
||||
}
|
||||
|
||||
double height = rows * fontSize.Y;
|
||||
auto thickness = _ParseThicknessFromPadding(settings.Padding());
|
||||
auto thickness = _ParseThicknessFromPadding(padding);
|
||||
// GH#2061 - make sure to account for the size the padding _will be_ scaled to
|
||||
width += scale * (thickness.Left + thickness.Right);
|
||||
height += scale * (thickness.Top + thickness.Bottom);
|
||||
@@ -2354,21 +2392,41 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
// have a visible character.
|
||||
winrt::Windows::Foundation::Size TermControl::MinimumSize()
|
||||
{
|
||||
const auto fontSize = _actualFont.GetSize();
|
||||
double width = fontSize.X;
|
||||
double height = fontSize.Y;
|
||||
// Reserve additional space if scrollbar is intended to be visible
|
||||
if (_settings.ScrollState() == ScrollbarState::Visible)
|
||||
if (_initializedTerminal)
|
||||
{
|
||||
width += ScrollBar().ActualWidth();
|
||||
const auto fontSize = _actualFont.GetSize();
|
||||
double width = fontSize.X;
|
||||
double height = fontSize.Y;
|
||||
// Reserve additional space if scrollbar is intended to be visible
|
||||
if (_settings.ScrollState() == ScrollbarState::Visible)
|
||||
{
|
||||
width += ScrollBar().ActualWidth();
|
||||
}
|
||||
|
||||
// Account for the size of any padding
|
||||
const auto padding = GetPadding();
|
||||
width += padding.Left + padding.Right;
|
||||
height += padding.Top + padding.Bottom;
|
||||
|
||||
return { gsl::narrow_cast<float>(width), gsl::narrow_cast<float>(height) };
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the terminal hasn't been initialized yet, then the font size will
|
||||
// have dimensions {1, fontSize.Y}, which can mess with consumers of
|
||||
// this method. In that case, we'll need to pre-calculate the font
|
||||
// width, before we actually have a renderer or swapchain.
|
||||
const winrt::Windows::Foundation::Size minSize{ 1, 1 };
|
||||
const double scaleFactor = DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel();
|
||||
const auto dpi = ::base::saturated_cast<uint32_t>(USER_DEFAULT_SCREEN_DPI * scaleFactor);
|
||||
return GetProposedDimensions(minSize,
|
||||
_settings.FontSize(),
|
||||
_settings.FontWeight(),
|
||||
_settings.FontFace(),
|
||||
_settings.ScrollState(),
|
||||
_settings.Padding(),
|
||||
dpi);
|
||||
}
|
||||
|
||||
// Account for the size of any padding
|
||||
const auto padding = GetPadding();
|
||||
width += padding.Left + padding.Right;
|
||||
height += padding.Top + padding.Bottom;
|
||||
|
||||
return { gsl::narrow_cast<float>(width), gsl::narrow_cast<float>(height) };
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
||||
@@ -100,7 +100,14 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
|
||||
TerminalConnection::ConnectionState ConnectionState() const;
|
||||
|
||||
static Windows::Foundation::Point GetProposedDimensions(Microsoft::Terminal::Settings::IControlSettings const& settings, const uint32_t dpi);
|
||||
static Windows::Foundation::Size GetProposedDimensions(Microsoft::Terminal::Settings::IControlSettings const& settings, const uint32_t dpi);
|
||||
static Windows::Foundation::Size GetProposedDimensions(const winrt::Windows::Foundation::Size& initialSizeInChars,
|
||||
const int32_t& fontSize,
|
||||
const winrt::Windows::UI::Text::FontWeight& fontWeight,
|
||||
const winrt::hstring& fontFace,
|
||||
const Microsoft::Terminal::Settings::ScrollbarState& scrollState,
|
||||
const winrt::hstring& padding,
|
||||
const uint32_t dpi);
|
||||
|
||||
// clang-format off
|
||||
// -------------------------------- WinRT Events ---------------------------------
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace Microsoft.Terminal.TerminalControl
|
||||
TermControl();
|
||||
TermControl(Microsoft.Terminal.Settings.IControlSettings settings, Microsoft.Terminal.TerminalConnection.ITerminalConnection connection);
|
||||
|
||||
static Windows.Foundation.Point GetProposedDimensions(Microsoft.Terminal.Settings.IControlSettings settings, UInt32 dpi);
|
||||
static Windows.Foundation.Size GetProposedDimensions(Microsoft.Terminal.Settings.IControlSettings settings, UInt32 dpi);
|
||||
|
||||
void UpdateSettings(Microsoft.Terminal.Settings.IControlSettings newSettings);
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ ThrottledFunc<>::ThrottledFunc(ThrottledFunc::Func func, TimeSpan delay, CoreDis
|
||||
// - <none>
|
||||
void ThrottledFunc<>::Run()
|
||||
{
|
||||
if (_isRunPending.test_and_set())
|
||||
if (_isRunPending.test_and_set(std::memory_order_acquire))
|
||||
{
|
||||
// already pending
|
||||
return;
|
||||
@@ -44,7 +44,7 @@ void ThrottledFunc<>::Run()
|
||||
if (auto self{ weakThis.lock() })
|
||||
{
|
||||
timer.Stop();
|
||||
self->_isRunPending.clear();
|
||||
self->_isRunPending.clear(std::memory_order_release);
|
||||
self->_func();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -354,12 +354,12 @@ bool TerminalDispatch::EnableAlternateScroll(const bool enabled) noexcept
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TerminalDispatch::SetPrivateModes(const std::basic_string_view<DispatchTypes::PrivateModeParams> params) noexcept
|
||||
bool TerminalDispatch::SetPrivateModes(const gsl::span<const DispatchTypes::PrivateModeParams> params) noexcept
|
||||
{
|
||||
return _SetResetPrivateModes(params, true);
|
||||
}
|
||||
|
||||
bool TerminalDispatch::ResetPrivateModes(const std::basic_string_view<DispatchTypes::PrivateModeParams> params) noexcept
|
||||
bool TerminalDispatch::ResetPrivateModes(const gsl::span<const DispatchTypes::PrivateModeParams> params) noexcept
|
||||
{
|
||||
return _SetResetPrivateModes(params, false);
|
||||
}
|
||||
@@ -374,7 +374,7 @@ bool TerminalDispatch::ResetPrivateModes(const std::basic_string_view<DispatchTy
|
||||
// - enable - True for set, false for unset.
|
||||
// Return Value:
|
||||
// - True if ALL params were handled successfully. False otherwise.
|
||||
bool TerminalDispatch::_SetResetPrivateModes(const std::basic_string_view<DispatchTypes::PrivateModeParams> params, const bool enable) noexcept
|
||||
bool TerminalDispatch::_SetResetPrivateModes(const gsl::span<const DispatchTypes::PrivateModeParams> params, const bool enable) noexcept
|
||||
{
|
||||
// because the user might chain together params we don't support with params we DO support, execute all
|
||||
// params in the sequence, and only return failure if we failed at least one of them
|
||||
|
||||
@@ -13,7 +13,7 @@ public:
|
||||
void Print(const wchar_t wchPrintable) noexcept override;
|
||||
void PrintString(const std::wstring_view string) noexcept override;
|
||||
|
||||
bool SetGraphicsRendition(const std::basic_string_view<::Microsoft::Console::VirtualTerminal::DispatchTypes::GraphicsOptions> options) noexcept override;
|
||||
bool SetGraphicsRendition(const gsl::span<const ::Microsoft::Console::VirtualTerminal::DispatchTypes::GraphicsOptions> options) noexcept override;
|
||||
|
||||
bool CursorPosition(const size_t line,
|
||||
const size_t column) noexcept override; // CUP
|
||||
@@ -59,16 +59,16 @@ public:
|
||||
bool EnableAnyEventMouseMode(const bool enabled) noexcept override; // ?1003
|
||||
bool EnableAlternateScroll(const bool enabled) noexcept override; // ?1007
|
||||
|
||||
bool SetPrivateModes(const std::basic_string_view<::Microsoft::Console::VirtualTerminal::DispatchTypes::PrivateModeParams> /*params*/) noexcept override; // DECSET
|
||||
bool ResetPrivateModes(const std::basic_string_view<::Microsoft::Console::VirtualTerminal::DispatchTypes::PrivateModeParams> /*params*/) noexcept override; // DECRST
|
||||
bool SetPrivateModes(const gsl::span<const ::Microsoft::Console::VirtualTerminal::DispatchTypes::PrivateModeParams> /*params*/) noexcept override; // DECSET
|
||||
bool ResetPrivateModes(const gsl::span<const ::Microsoft::Console::VirtualTerminal::DispatchTypes::PrivateModeParams> /*params*/) noexcept override; // DECRST
|
||||
|
||||
private:
|
||||
::Microsoft::Terminal::Core::ITerminalApi& _terminalApi;
|
||||
|
||||
size_t _SetRgbColorsHelper(const std::basic_string_view<::Microsoft::Console::VirtualTerminal::DispatchTypes::GraphicsOptions> options,
|
||||
size_t _SetRgbColorsHelper(const gsl::span<const ::Microsoft::Console::VirtualTerminal::DispatchTypes::GraphicsOptions> options,
|
||||
TextAttribute& attr,
|
||||
const bool isForeground) noexcept;
|
||||
|
||||
bool _SetResetPrivateModes(const std::basic_string_view<::Microsoft::Console::VirtualTerminal::DispatchTypes::PrivateModeParams> params, const bool enable) noexcept;
|
||||
bool _SetResetPrivateModes(const gsl::span<const ::Microsoft::Console::VirtualTerminal::DispatchTypes::PrivateModeParams> params, const bool enable) noexcept;
|
||||
bool _PrivateModeParamsHelper(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::PrivateModeParams param, const bool enable) noexcept;
|
||||
};
|
||||
|
||||
@@ -41,7 +41,7 @@ const BYTE BRIGHT_WHITE = BRIGHT_ATTR | RED_ATTR | GREEN_ATTR | BLUE_ATTR;
|
||||
// - isForeground - Whether or not the parsed color is for the foreground.
|
||||
// Return Value:
|
||||
// - The number of options consumed, not including the initial 38/48.
|
||||
size_t TerminalDispatch::_SetRgbColorsHelper(const std::basic_string_view<DispatchTypes::GraphicsOptions> options,
|
||||
size_t TerminalDispatch::_SetRgbColorsHelper(const gsl::span<const DispatchTypes::GraphicsOptions> options,
|
||||
TextAttribute& attr,
|
||||
const bool isForeground) noexcept
|
||||
{
|
||||
@@ -94,7 +94,7 @@ size_t TerminalDispatch::_SetRgbColorsHelper(const std::basic_string_view<Dispat
|
||||
// one at a time by setting or removing flags in the font style properties.
|
||||
// Return Value:
|
||||
// - True if handled successfully. False otherwise.
|
||||
bool TerminalDispatch::SetGraphicsRendition(const std::basic_string_view<DispatchTypes::GraphicsOptions> options) noexcept
|
||||
bool TerminalDispatch::SetGraphicsRendition(const gsl::span<const DispatchTypes::GraphicsOptions> options) noexcept
|
||||
{
|
||||
TextAttribute attr = _terminalApi.GetTextAttributes();
|
||||
|
||||
@@ -126,10 +126,10 @@ bool TerminalDispatch::SetGraphicsRendition(const std::basic_string_view<Dispatc
|
||||
attr.SetFaint(false);
|
||||
break;
|
||||
case Italics:
|
||||
attr.SetItalics(true);
|
||||
attr.SetItalic(true);
|
||||
break;
|
||||
case NotItalics:
|
||||
attr.SetItalics(false);
|
||||
attr.SetItalic(false);
|
||||
break;
|
||||
case BlinkOrXterm256Index:
|
||||
attr.SetBlinking(true);
|
||||
@@ -156,16 +156,16 @@ bool TerminalDispatch::SetGraphicsRendition(const std::basic_string_view<Dispatc
|
||||
attr.SetReverseVideo(false);
|
||||
break;
|
||||
case Underline:
|
||||
attr.SetUnderline(true);
|
||||
attr.SetUnderlined(true);
|
||||
break;
|
||||
case NoUnderline:
|
||||
attr.SetUnderline(false);
|
||||
attr.SetUnderlined(false);
|
||||
break;
|
||||
case Overline:
|
||||
attr.SetOverline(true);
|
||||
attr.SetOverlined(true);
|
||||
break;
|
||||
case NoOverline:
|
||||
attr.SetOverline(false);
|
||||
attr.SetOverlined(false);
|
||||
break;
|
||||
case ForegroundBlack:
|
||||
attr.SetIndexedForeground(DARK_BLACK);
|
||||
@@ -264,10 +264,10 @@ bool TerminalDispatch::SetGraphicsRendition(const std::basic_string_view<Dispatc
|
||||
attr.SetIndexedBackground(BRIGHT_WHITE);
|
||||
break;
|
||||
case ForegroundExtended:
|
||||
i += _SetRgbColorsHelper(options.substr(i + 1), attr, true);
|
||||
i += _SetRgbColorsHelper(options.subspan(i + 1), attr, true);
|
||||
break;
|
||||
case BackgroundExtended:
|
||||
i += _SetRgbColorsHelper(options.substr(i + 1), attr, false);
|
||||
i += _SetRgbColorsHelper(options.subspan(i + 1), attr, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,9 +286,9 @@ void AppHost::_HandleCreateWindow(const HWND hwnd, RECT proposedRect, winrt::Ter
|
||||
auto initialSize = _logic.GetLaunchDimensions(dpix);
|
||||
|
||||
const short islandWidth = Utils::ClampToShortMax(
|
||||
static_cast<long>(ceil(initialSize.X)), 1);
|
||||
static_cast<long>(ceil(initialSize.Width)), 1);
|
||||
const short islandHeight = Utils::ClampToShortMax(
|
||||
static_cast<long>(ceil(initialSize.Y)), 1);
|
||||
static_cast<long>(ceil(initialSize.Height)), 1);
|
||||
|
||||
// Get the size of a window we'd need to host that client rect. This will
|
||||
// add the titlebar space.
|
||||
|
||||
@@ -62,6 +62,16 @@ namespace Microsoft.Terminal.Wpf
|
||||
/// </summary>
|
||||
WM_CHAR = 0x0102,
|
||||
|
||||
/// <summary>
|
||||
/// The WM_SYSKEYDOWN message is posted to the window with the keyboard focus when a system key is pressed. A system key is F10 or Alt+Something.
|
||||
/// </summary>
|
||||
WM_SYSKEYDOWN = 0x0104,
|
||||
|
||||
/// <summary>
|
||||
/// The WM_SYSKEYDOWN message is posted to the window with the keyboard focus when a system key is released. A system key is F10 or Alt+Something.
|
||||
/// </summary>
|
||||
WM_SYSKEYUP = 0x0105,
|
||||
|
||||
/// <summary>
|
||||
/// The WM_MOUSEMOVE message is posted to a window when the cursor moves. If the mouse is not captured, the message is posted to the window that contains the cursor. Otherwise, the message is posted to the window that has captured the mouse.
|
||||
/// </summary>
|
||||
|
||||
@@ -235,6 +235,7 @@ namespace Microsoft.Terminal.Wpf
|
||||
this.Focus();
|
||||
NativeMethods.SetFocus(this.hwnd);
|
||||
break;
|
||||
case NativeMethods.WindowMessage.WM_SYSKEYDOWN: // fallthrough
|
||||
case NativeMethods.WindowMessage.WM_KEYDOWN:
|
||||
{
|
||||
// WM_KEYDOWN lParam layout documentation: https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-keydown
|
||||
@@ -246,6 +247,7 @@ namespace Microsoft.Terminal.Wpf
|
||||
break;
|
||||
}
|
||||
|
||||
case NativeMethods.WindowMessage.WM_SYSKEYUP: // fallthrough
|
||||
case NativeMethods.WindowMessage.WM_KEYUP:
|
||||
{
|
||||
// WM_KEYUP lParam layout documentation: https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-keyup
|
||||
@@ -255,9 +257,13 @@ namespace Microsoft.Terminal.Wpf
|
||||
}
|
||||
|
||||
case NativeMethods.WindowMessage.WM_CHAR:
|
||||
// WM_CHAR lParam layout documentation: https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-char
|
||||
NativeMethods.TerminalSendCharEvent(this.terminal, (char)wParam, (ushort)((uint)lParam >> 16));
|
||||
break;
|
||||
{
|
||||
// WM_CHAR lParam layout documentation: https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-char
|
||||
ulong scanCode = (((ulong)lParam) & 0x00FF0000) >> 16;
|
||||
NativeMethods.TerminalSendCharEvent(this.terminal, (char)wParam, (ushort)scanCode);
|
||||
break;
|
||||
}
|
||||
|
||||
case NativeMethods.WindowMessage.WM_WINDOWPOSCHANGED:
|
||||
var windowpos = (NativeMethods.WINDOWPOS)Marshal.PtrToStructure(lParam, typeof(NativeMethods.WINDOWPOS));
|
||||
if (((NativeMethods.SetWindowPosFlags)windowpos.flags).HasFlag(NativeMethods.SetWindowPosFlags.SWP_NOSIZE))
|
||||
|
||||
@@ -28,8 +28,6 @@ namespace TerminalAppUnitTests
|
||||
TEST_METHOD(ParseSimpleColorScheme);
|
||||
TEST_METHOD(ProfileGeneratesGuid);
|
||||
|
||||
TEST_METHOD(TestWrongValueType);
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
{
|
||||
InitializeJsonReader();
|
||||
@@ -169,58 +167,4 @@ namespace TerminalAppUnitTests
|
||||
VERIFY_ARE_EQUAL(profile3.GetGuid(), nullGuid);
|
||||
VERIFY_ARE_EQUAL(profile4.GetGuid(), cmdGuid);
|
||||
}
|
||||
|
||||
void JsonTests::TestWrongValueType()
|
||||
{
|
||||
// This json blob has a whole bunch of settings with the wrong value
|
||||
// types - strings for int values, ints for strings, floats for ints,
|
||||
// etc. When we encounter data that's the wrong data type, we should
|
||||
// gracefully ignore it, as opposed to throwing an exception, causing us
|
||||
// to fail to load the settings at all.
|
||||
|
||||
const std::string settings0String{ R"(
|
||||
{
|
||||
"defaultProfile" : "{00000000-1111-0000-0000-000000000000}",
|
||||
"profiles": [
|
||||
{
|
||||
"guid" : "{00000000-1111-0000-0000-000000000000}",
|
||||
"acrylicOpacity" : "0.5",
|
||||
"closeOnExit" : "true",
|
||||
"fontSize" : "10",
|
||||
"historySize" : 1234.5678,
|
||||
"padding" : 20,
|
||||
"snapOnInput" : "false",
|
||||
"icon" : 4,
|
||||
"backgroundImageOpacity": false,
|
||||
"useAcrylic" : 14
|
||||
}
|
||||
]
|
||||
})" };
|
||||
|
||||
const auto settings0Json = VerifyParseSucceeded(settings0String);
|
||||
|
||||
CascadiaSettings settings;
|
||||
|
||||
settings._ParseJsonString(settings0String, false);
|
||||
// We should not throw an exception trying to parse the settings here.
|
||||
settings.LayerJson(settings._userSettings);
|
||||
|
||||
VERIFY_ARE_EQUAL(1u, settings._profiles.size());
|
||||
auto& profile = settings._profiles.at(0);
|
||||
Profile defaults{};
|
||||
|
||||
VERIFY_ARE_EQUAL(defaults._acrylicTransparency, profile._acrylicTransparency);
|
||||
VERIFY_ARE_EQUAL(defaults._closeOnExitMode, profile._closeOnExitMode);
|
||||
VERIFY_ARE_EQUAL(defaults._fontSize, profile._fontSize);
|
||||
VERIFY_ARE_EQUAL(defaults._historySize, profile._historySize);
|
||||
// A 20 as an int can still be treated as a json string
|
||||
VERIFY_ARE_EQUAL(L"20", profile._padding);
|
||||
VERIFY_ARE_EQUAL(defaults._snapOnInput, profile._snapOnInput);
|
||||
// 4 is a valid string value
|
||||
VERIFY_ARE_EQUAL(L"4", profile._icon);
|
||||
// false is not a valid optional<double>
|
||||
VERIFY_IS_FALSE(profile._backgroundImageOpacity.has_value());
|
||||
VERIFY_ARE_EQUAL(defaults._useAcrylic, profile._useAcrylic);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#include "precomp.h"
|
||||
|
||||
#include "../TerminalApp/JsonUtilsNew.h"
|
||||
#include "../TerminalApp/JsonUtils.h"
|
||||
|
||||
using namespace Microsoft::Console;
|
||||
using namespace WEX::Logging;
|
||||
|
||||
@@ -208,12 +208,12 @@ public:
|
||||
size_t& written) noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT WriteConsoleInputAImpl(InputBuffer& context,
|
||||
const std::basic_string_view<INPUT_RECORD> buffer,
|
||||
const gsl::span<const INPUT_RECORD> buffer,
|
||||
size_t& written,
|
||||
const bool append) noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT WriteConsoleInputWImpl(InputBuffer& context,
|
||||
const std::basic_string_view<INPUT_RECORD> buffer,
|
||||
const gsl::span<const INPUT_RECORD> buffer,
|
||||
size_t& written,
|
||||
const bool append) noexcept override;
|
||||
|
||||
@@ -228,7 +228,7 @@ public:
|
||||
Microsoft::Console::Types::Viewport& writtenRectangle) noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT WriteConsoleOutputAttributeImpl(IConsoleOutputObject& OutContext,
|
||||
const std::basic_string_view<WORD> attrs,
|
||||
const gsl::span<const WORD> attrs,
|
||||
const COORD target,
|
||||
size_t& used) noexcept override;
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ void WriteToScreen(SCREEN_INFORMATION& screenInfo, const Viewport& region)
|
||||
// Return Value:
|
||||
// - S_OK, E_INVALIDARG or similar HRESULT error.
|
||||
[[nodiscard]] HRESULT ApiRoutines::WriteConsoleOutputAttributeImpl(IConsoleOutputObject& OutContext,
|
||||
const std::basic_string_view<WORD> attrs,
|
||||
const gsl::span<const WORD> attrs,
|
||||
const COORD target,
|
||||
size_t& used) noexcept
|
||||
{
|
||||
|
||||
@@ -149,7 +149,7 @@ std::unordered_map<std::wstring,
|
||||
|
||||
if (target.has_value() && target->size() > 0)
|
||||
{
|
||||
gsl::at(*target, 0) = UNICODE_NULL;
|
||||
til::at(*target, 0) = UNICODE_NULL;
|
||||
}
|
||||
|
||||
std::wstring exeNameString(exeName);
|
||||
@@ -211,7 +211,7 @@ std::unordered_map<std::wstring,
|
||||
{
|
||||
if (target.size() > 0)
|
||||
{
|
||||
gsl::at(target, 0) = ANSI_NULL;
|
||||
til::at(target, 0) = ANSI_NULL;
|
||||
}
|
||||
|
||||
LockConsole();
|
||||
@@ -451,7 +451,7 @@ void Alias::s_ClearCmdExeAliases()
|
||||
|
||||
if (aliasBuffer.has_value() && aliasBuffer->size() > 0)
|
||||
{
|
||||
gsl::at(*aliasBuffer, 0) = UNICODE_NULL;
|
||||
til::at(*aliasBuffer, 0) = UNICODE_NULL;
|
||||
}
|
||||
|
||||
std::wstring exeNameString(exeName);
|
||||
@@ -543,7 +543,7 @@ void Alias::s_ClearCmdExeAliases()
|
||||
{
|
||||
if (alias.size() > 0)
|
||||
{
|
||||
gsl::at(alias, 0) = '\0';
|
||||
til::at(alias, 0) = '\0';
|
||||
}
|
||||
|
||||
LockConsole();
|
||||
@@ -698,7 +698,7 @@ void Alias::s_ClearCmdExeAliases()
|
||||
writtenOrNeeded = 0;
|
||||
if (aliasExesBuffer.has_value() && aliasExesBuffer->size() > 0)
|
||||
{
|
||||
gsl::at(*aliasExesBuffer, 0) = UNICODE_NULL;
|
||||
til::at(*aliasExesBuffer, 0) = UNICODE_NULL;
|
||||
}
|
||||
|
||||
LPWSTR AliasExesBufferPtrW = aliasExesBuffer.has_value() ? aliasExesBuffer->data() : nullptr;
|
||||
@@ -761,7 +761,7 @@ void Alias::s_ClearCmdExeAliases()
|
||||
{
|
||||
if (aliasExes.size() > 0)
|
||||
{
|
||||
gsl::at(aliasExes, 0) = '\0';
|
||||
til::at(aliasExes, 0) = '\0';
|
||||
}
|
||||
|
||||
LockConsole();
|
||||
|
||||
@@ -112,7 +112,7 @@ void ConversionAreaInfo::SetAttributes(const TextAttribute& attr)
|
||||
void ConversionAreaInfo::WriteText(const std::vector<OutputCell>& text,
|
||||
const SHORT column)
|
||||
{
|
||||
std::basic_string_view<OutputCell> view(text.data(), text.size());
|
||||
gsl::span<const OutputCell> view(text.data(), text.size());
|
||||
_screenBuffer->Write(view, { column, 0 });
|
||||
}
|
||||
|
||||
|
||||
@@ -57,15 +57,15 @@ void ConsoleImeInfo::RedrawCompMessage()
|
||||
// - attributes - Encoded attributes including the cursor position and the color index (to the array)
|
||||
// - colorArray - An array of colors to use for the text
|
||||
void ConsoleImeInfo::WriteCompMessage(const std::wstring_view text,
|
||||
const std::basic_string_view<BYTE> attributes,
|
||||
const std::basic_string_view<WORD> colorArray)
|
||||
const gsl::span<const BYTE> attributes,
|
||||
const gsl::span<const WORD> colorArray)
|
||||
{
|
||||
ClearAllAreas();
|
||||
|
||||
// Save copies of the composition message in case we need to redraw it as things scroll/resize
|
||||
_text = text;
|
||||
_attributes = attributes;
|
||||
_colorArray = colorArray;
|
||||
_attributes.assign(attributes.begin(), attributes.end());
|
||||
_colorArray.assign(colorArray.begin(), colorArray.end());
|
||||
|
||||
_WriteUndeterminedChars(text, attributes, colorArray);
|
||||
}
|
||||
@@ -177,8 +177,8 @@ void ConsoleImeInfo::ClearAllAreas()
|
||||
// Return Value:
|
||||
// - TextAttribute object with color and cursor and line drawing data.
|
||||
TextAttribute ConsoleImeInfo::s_RetrieveAttributeAt(const size_t pos,
|
||||
const std::basic_string_view<BYTE> attributes,
|
||||
const std::basic_string_view<WORD> colorArray)
|
||||
const gsl::span<const BYTE> attributes,
|
||||
const gsl::span<const WORD> colorArray)
|
||||
{
|
||||
// Encoded attribute is the shorthand information passed from the IME
|
||||
// that contains a cursor position packed in along with which color in the
|
||||
@@ -214,8 +214,8 @@ TextAttribute ConsoleImeInfo::s_RetrieveAttributeAt(const size_t pos,
|
||||
// Return Value:
|
||||
// - Vector of OutputCells where each one represents one cell of the output buffer.
|
||||
std::vector<OutputCell> ConsoleImeInfo::s_ConvertToCells(const std::wstring_view text,
|
||||
const std::basic_string_view<BYTE> attributes,
|
||||
const std::basic_string_view<WORD> colorArray)
|
||||
const gsl::span<const BYTE> attributes,
|
||||
const gsl::span<const WORD> colorArray)
|
||||
{
|
||||
std::vector<OutputCell> cells;
|
||||
|
||||
@@ -389,8 +389,8 @@ std::vector<OutputCell>::const_iterator ConsoleImeInfo::_WriteConversionArea(con
|
||||
// each text character. This view must be the same size as the text view.
|
||||
// - colorArray - 8 colors to be used to format the text for display
|
||||
void ConsoleImeInfo::_WriteUndeterminedChars(const std::wstring_view text,
|
||||
const std::basic_string_view<BYTE> attributes,
|
||||
const std::basic_string_view<WORD> colorArray)
|
||||
const gsl::span<const BYTE> attributes,
|
||||
const gsl::span<const WORD> colorArray)
|
||||
{
|
||||
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
||||
SCREEN_INFORMATION& screenInfo = gci.GetActiveOutputBuffer();
|
||||
|
||||
@@ -48,8 +48,8 @@ public:
|
||||
[[nodiscard]] HRESULT ResizeAllAreas(const COORD newSize);
|
||||
|
||||
void WriteCompMessage(const std::wstring_view text,
|
||||
const std::basic_string_view<BYTE> attributes,
|
||||
const std::basic_string_view<WORD> colorArray);
|
||||
const gsl::span<const BYTE> attributes,
|
||||
const gsl::span<const WORD> colorArray);
|
||||
|
||||
void WriteResultMessage(const std::wstring_view text);
|
||||
|
||||
@@ -64,18 +64,18 @@ private:
|
||||
void _ClearComposition();
|
||||
|
||||
void _WriteUndeterminedChars(const std::wstring_view text,
|
||||
const std::basic_string_view<BYTE> attributes,
|
||||
const std::basic_string_view<WORD> colorArray);
|
||||
const gsl::span<const BYTE> attributes,
|
||||
const gsl::span<const WORD> colorArray);
|
||||
|
||||
void _InsertConvertedString(const std::wstring_view text);
|
||||
|
||||
static TextAttribute s_RetrieveAttributeAt(const size_t pos,
|
||||
const std::basic_string_view<BYTE> attributes,
|
||||
const std::basic_string_view<WORD> colorArray);
|
||||
const gsl::span<const BYTE> attributes,
|
||||
const gsl::span<const WORD> colorArray);
|
||||
|
||||
static std::vector<OutputCell> s_ConvertToCells(const std::wstring_view text,
|
||||
const std::basic_string_view<BYTE> attributes,
|
||||
const std::basic_string_view<WORD> colorArray);
|
||||
const gsl::span<const BYTE> attributes,
|
||||
const gsl::span<const WORD> colorArray);
|
||||
|
||||
std::vector<OutputCell>::const_iterator _WriteConversionArea(const std::vector<OutputCell>::const_iterator begin,
|
||||
const std::vector<OutputCell>::const_iterator end,
|
||||
@@ -86,6 +86,6 @@ private:
|
||||
bool _isSavedCursorVisible;
|
||||
|
||||
std::wstring _text;
|
||||
std::basic_string<BYTE> _attributes;
|
||||
std::basic_string<WORD> _colorArray;
|
||||
std::vector<BYTE> _attributes;
|
||||
std::vector<WORD> _colorArray;
|
||||
};
|
||||
|
||||
@@ -125,8 +125,8 @@ void WriteConvRegionToScreen(const SCREEN_INFORMATION& ScreenInfo,
|
||||
}
|
||||
|
||||
[[nodiscard]] HRESULT ImeComposeData(std::wstring_view text,
|
||||
std::basic_string_view<BYTE> attributes,
|
||||
std::basic_string_view<WORD> colorArray)
|
||||
gsl::span<const BYTE> attributes,
|
||||
gsl::span<const WORD> colorArray)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@@ -67,10 +67,10 @@ DWORD UnicodeRasterFontCellMungeOnRead(const gsl::span<CHAR_INFO> buffer)
|
||||
for (DWORD iSrc = 0; iSrc < buffer.size(); iSrc++)
|
||||
{
|
||||
// If it's not a trailing byte, copy it straight over, stripping out the Leading/Trailing flags from the attributes field.
|
||||
auto& src{ gsl::at(buffer, iSrc) };
|
||||
auto& src{ til::at(buffer, iSrc) };
|
||||
if (!WI_IsFlagSet(src.Attributes, COMMON_LVB_TRAILING_BYTE))
|
||||
{
|
||||
auto& dst{ gsl::at(buffer, iDst) };
|
||||
auto& dst{ til::at(buffer, iDst) };
|
||||
dst = src;
|
||||
WI_ClearAllFlags(dst.Attributes, COMMON_LVB_SBCSDBCS);
|
||||
iDst++;
|
||||
|
||||
@@ -472,7 +472,7 @@ void EventsToUnicode(_Inout_ std::deque<std::unique_ptr<IInputEvent>>& inEvents,
|
||||
// Return Value:
|
||||
// - HRESULT indicating success or failure
|
||||
[[nodiscard]] HRESULT ApiRoutines::WriteConsoleInputAImpl(InputBuffer& context,
|
||||
const std::basic_string_view<INPUT_RECORD> buffer,
|
||||
const gsl::span<const INPUT_RECORD> buffer,
|
||||
size_t& written,
|
||||
const bool append) noexcept
|
||||
{
|
||||
@@ -516,7 +516,7 @@ void EventsToUnicode(_Inout_ std::deque<std::unique_ptr<IInputEvent>>& inEvents,
|
||||
// Return Value:
|
||||
// - HRESULT indicating success or failure
|
||||
[[nodiscard]] HRESULT ApiRoutines::WriteConsoleInputWImpl(InputBuffer& context,
|
||||
const std::basic_string_view<INPUT_RECORD> buffer,
|
||||
const gsl::span<const INPUT_RECORD> buffer,
|
||||
size_t& written,
|
||||
const bool append) noexcept
|
||||
{
|
||||
@@ -1042,7 +1042,7 @@ void EventsToUnicode(_Inout_ std::deque<std::unique_ptr<IInputEvent>>& inEvents,
|
||||
const auto subspan = buffer.subspan(totalOffset, writeRectangle.Width());
|
||||
|
||||
// Convert to a CHAR_INFO view to fit into the iterator
|
||||
const auto charInfos = std::basic_string_view<CHAR_INFO>(subspan.data(), subspan.size());
|
||||
const auto charInfos = gsl::span<const CHAR_INFO>(subspan.data(), subspan.size());
|
||||
|
||||
// Make the iterator and write to the target position.
|
||||
OutputCellIterator it(charInfos);
|
||||
|
||||
@@ -1604,7 +1604,7 @@ void DoSrvPrivateRefreshWindow(_In_ const SCREEN_INFORMATION& screenInfo)
|
||||
|
||||
if (title.has_value() && title->size() > 0)
|
||||
{
|
||||
gsl::at(*title, 0) = ANSI_NULL;
|
||||
til::at(*title, 0) = ANSI_NULL;
|
||||
}
|
||||
|
||||
// Get the appropriate title and length depending on the mode.
|
||||
@@ -1667,7 +1667,7 @@ void DoSrvPrivateRefreshWindow(_In_ const SCREEN_INFORMATION& screenInfo)
|
||||
|
||||
if (title.size() > 0)
|
||||
{
|
||||
gsl::at(title, 0) = ANSI_NULL;
|
||||
til::at(title, 0) = ANSI_NULL;
|
||||
}
|
||||
|
||||
// Figure out how big our temporary Unicode buffer must be to get the title.
|
||||
@@ -1722,7 +1722,7 @@ void DoSrvPrivateRefreshWindow(_In_ const SCREEN_INFORMATION& screenInfo)
|
||||
// If we didn't copy anything back and there is space, null terminate the given buffer and return.
|
||||
if (title.size() > 0)
|
||||
{
|
||||
gsl::at(title, 0) = ANSI_NULL;
|
||||
til::at(title, 0) = ANSI_NULL;
|
||||
written = 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -782,7 +782,7 @@ HRESULT GetConsoleCommandHistoryWImplHelper(const std::wstring_view exeName,
|
||||
writtenOrNeeded = 0;
|
||||
if (historyBuffer.size() > 0)
|
||||
{
|
||||
gsl::at(historyBuffer, 0) = UNICODE_NULL;
|
||||
til::at(historyBuffer, 0) = UNICODE_NULL;
|
||||
}
|
||||
|
||||
CommandHistory* const CommandHistory = CommandHistory::s_FindByExe(exeName);
|
||||
@@ -859,7 +859,7 @@ HRESULT ApiRoutines::GetConsoleCommandHistoryAImpl(const std::string_view exeNam
|
||||
{
|
||||
if (commandHistory.size() > 0)
|
||||
{
|
||||
gsl::at(commandHistory, 0) = ANSI_NULL;
|
||||
til::at(commandHistory, 0) = ANSI_NULL;
|
||||
}
|
||||
|
||||
LockConsole();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user