Compare commits

..

51 Commits

Author SHA1 Message Date
Dustin L. Howett
a7cd201a6f Migrate spelling-0.0.21 changes from main 2020-08-07 11:58:38 -05:00
Dustin L. Howett
3c319b6ecd Migrate spelling-0.0.19 changes from main 2020-08-07 11:58:38 -05:00
Mike Griese
ffa27cda7e Force everything to be getters only. Doesn't work yet, but it's close 2020-08-07 11:58:38 -05:00
Carlos Zamora
1c6aa4d109 Move ICore/ControlSettings to TerminalControl project (#7167)
## Summary of the Pull Request
Move `ICoreSettings` and `IControlSettings` from the TerminalSettings project to the TerminalCore and TerminalControl projects respectively. Also entirely removes the TerminalSettings project.

The purpose of these interfaces is unchanged. `ICoreSettings` is used to instantiate a terminal. `IControlSettings` (which requires an `ICoreSettings`) is used to instantiate a UWP terminal control.

## References
Closes #7140 
Related Epic: #885 
Related Spec: #6904 

## PR Checklist
* [X] Closes #7140 
* [X] CLA signed
* [X] Tests ~added~/passed (no additional tests necessary)
* [X] ~Documentation updated~
* [X] ~Schema updated~

## Detailed Description of the Pull Request / Additional comments
A lot of the work here was having to deal with winmd files across all of these projects. The TerminalCore project now outputs a Microsoft.Terminal.TerminalControl.winmd. Some magic happens in TerminalControl.vcxproj to get this to work properly.

## Validation Steps Performed
Deployed Windows Terminal and opened a few new tabs.
2020-08-07 14:46:52 +00:00
Dustin L. Howett
858905f492 Disable MinimalCoreWin when OpenConsoleUniversalApp is false (#7203)
This fixes the build the rest of the way in VS 16.7. Something about the
way we were using the Store/Container flags caused some of our projects
to end up linking kernel32.lib only (MinimalCoreWin==KernelOnly).
The best way to solve it once and for all is to make sure MinimalCoreWin
is always set.

References 313568d0e5.
2020-08-06 23:55:07 +00:00
Mike Griese
0a30b856a9 Add closeOtherTabs, closeTabsAfter actions (#7176)
## Summary of the Pull Request

Adds support for two actions, `closeOtherTabs` and `closeTabsAfter`. Both these actions accept an `index` parameter.

* `closeOtherTabs`: Close tabs other than `index`
* `closeTabsAfter`: Close tabs after `index` (This is also "Close tabs to the right")

## References
* This PR is being made to unblock @RahulRavishankar in #1912
## PR Checklist
* [x] I work here
* [ ] Tests added/passed
* [x] Requires documentation to be updated
* [ ] We should file an issue for "add an `index` param to `closeTab`" to add similar support to the close tab action
* [ ] We should file an issue for "make the `index` param to `closeOtherTabs`, `closeTabsAfter` optional" to make them both work on the _active_ tab when there's no `index` provided

## Validation Steps Performed
* _Verified that_ closing all tabs when I have the `index`'th tab selected _works as expected_
* _Verified that_ closing all tabs when I have a tab other than the `index`'th tab selected _works as expected_
* _Verified that_ closing tabs to the right when I have the `index`'th tab selected _works as expected_
* _Verified that_ closing tabs to the right when I have a tab other than the `index`'th tab selected _works as expected_
    - This one has one caveat: for whatever reason, if you run this action when the tab that's currently focused is _before_ the `index` param, then the tabs will expand to fill the entire width of the tab row, until you mouse over them. Probably has something to do with tabs not resizing down until there's a mouse exit event.
2020-08-06 21:47:50 +00:00
Mike Griese
f215b56ca3 Add a "shell vs terminal" section to Niksa.md (#7202)
I've typed this up way too many times now. I'm sticking this comment in Niksa.md, and if it's ever an insufficient explanation of the differences, we can elaborate.
2020-08-06 16:23:54 -05:00
Leonard Hecker
b617c434a1 Fix #7064: Ignore key events without scan code (#7145)
Up until #4999 we deferred all key events to the character event handler
for which `ToUnicodeEx` returned a valid character and alternatively
those who aren't a special key combination as listed in
`TerminalInput`'s implementation.

Since #4999 we started acknowledging/handling all key events no matter
whether they're actually a known key combination. Given non-ASCII inputs
the Win32 `SendInput()` method generates certain sequences that aren't
recognizable combinations though and if they're handled by the key event
handler no follow up character event is sent containing the unicode
character.

This PR adds another condition and defers all key events without scan
code (i.e. those not representable by the current keyboard layout) to
the character event handler.

I'm absolutely not certain that this PR doesn't have a negative effect
on other kinds of inputs.

Is it common for key events to not contain a scan code? I personally
haven't seen it happen before AutoHotKey/SendInput.

Before this PR is merged it'd be nice to have a good testing plan in
place in order to ensure nothing breaks.

## Validation Steps Performed

Remapped `AltGr+8` to `»` using AutoHotKey using `<^>!8::SendInput {Raw}»`.
Ensured `»` is printed if `AltGr+8` is pressed.

Closes #7064
Closes #7120
2020-08-06 01:03:58 +00:00
Dustin Howett
b759bdb711 Merge OS changes from the inbox branch 2020-08-05 11:38:20 -07:00
Dustin Howett
a3c8b2d8aa Merged PR 5011080: Migrate OSS up to cd7235661
Carlos Zamora (1)
* UIA: use full buffer comparison in rects and endpoint setter (GH-6447)

Dan Thompson (2)
* Tweaks: normalize TextAttribute method names (adjective form) (GH-6951)
* Fix 'bcz exclusive' typo (GH-6938)

Dustin L. Howett (4)
* Fix VT mouse capture issues in Terminal and conhost (GH-7166)
* version: bump to 1.3 on master
* Update Cascadia Code to 2007.15 (GH-6958)
* Move to the TerminalDependencies NuGet feed (GH-6954)

James Holderness (3)
* Render the SGR "underlined" attribute in the style of the font (CC-7148)
* Add support for the "crossed-out" graphic rendition attribute (CC-7143)
* Refactor grid line renderers with support for more line types (CC-7107)

Leonard Hecker (1)
* Added til::spsc, a lock-free, single-producer/-consumer FIFO queue (CC-6751)

Michael Niksa (6)
* Update TAEF to 10.57.200731005-develop (GH-7164)
* Skip DX invalidation if we've already scrolled an entire screen worth of height (GH-6922)
* Commit attr runs less frequently by accumulating length of color run (GH-6919)
* Set memory order on slow atomics (GH-6920)
* Cache the viewport to make invalidation faster (GH-6918)
* Correct comment in this SPSC test as a quick follow up to merge.

Related work items: MSFT-28208358
2020-08-05 18:06:09 +00:00
Dustin L. Howett
cd7235661e wpf: fixup mouse wheel events from screen coords (#7168)
I found this while crawling through conhost's WindowIo. Mouse wheel
events come in in screen coordinates, unlike literally every other mouse
event.

The WPF control was doing it wrong.
2020-08-04 17:38:34 +00:00
Dustin L. Howett
d29be591a8 Fix VT mouse capture issues in Terminal and conhost (#7166)
This pull request fixes capture and event generation in VT mouse mode
for both conhost and terminal.

Fixes #6401.

[1/3] Terminal: clamp mouse events to the viewport, don't throw them away

 gnome-terminal (at least) sends mouse events whose x/y are at the
 extreme ends of the buffer when a drag starts inside the terminal and
 then exits it.

 We would previously discard any mouse events that exited the borders of
 the viewport. Now we will keep emitting events where X/Y=0/w/h.

[2/3] conhost: clamp VT mouse to viewport, capture pointer

 This is the same as (1), but for conhost. conhost wasn't already
 capturing the pointer when VT mouse mode was in use. By capturing, we
 ensure that events that happen outside the screen still result in events
 sent to an application (like a release after a drag)

[3/3] wpf: capture the pointer when VT mouse is enabled

 This is the same as (2), but for the WPF control. Clamping is handled
 in TerminalCore in (1), so we didn't need to do it in WPF.
2020-08-04 01:43:17 +00:00
Carlos Zamora
eb8bb09e5b Move TerminalSettings object to TermApp Project (#7163)
Move TerminalSettings object from TerminalSettings project
(Microsoft.Terminal.Settings) to TerminalApp project. `TerminalSettings`
specifically operates as a bridge that exposes any necessary information
to a TerminalControl.

Closes #7139 
Related Epic: #885
Related Spec: #6904

## PR Checklist
* [X] Closes #7139 
* [X] CLA signed
* [X] Tests ~added~/passed (no additional tests necessary)
* [X] ~Documentation updated~
* [X] ~Schema updated~

## Validation Steps Performed
Deployed Windows Terminal and opened a few new tabs.
2020-08-03 22:54:22 +00:00
Mike Griese
7bf9225c15 Research how many characters users are typing before dismissing the cmdpal (#7165)
Add some user research to determine what the average number of characters a user types before executing a cmdpal action.

This might need to be modified when it merges with #6732
2020-08-03 21:13:30 +00:00
Michael Niksa
8bad88cf9c Update TAEF to 10.57.200731005-develop (#7164)
Updates TAEF to 10.57.200731005-develop

## PR Checklist
* [x] Helps #6992 by bringing `wttlog.dll` along with the rest of TAEF.
* [x] I work here.
* [x] Automated tests in CI
* [x] No doc/schema update necessary (checked for docs in this repo)
* [x] Am core contributor.
2020-08-03 20:47:02 +00:00
Mike Griese
46f7772261 Research how many characters users are typing before dismissing the cmdpal 2020-08-03 15:32:53 -05:00
Mike Griese
14c94f5963 Add a spec for per-profile tab colors (#7134)
Co-authored-by: Mike Griese <zadjii@gmail.com>

## Summary of the Pull Request

This spec is a subset of #5772, but specific to per-profile tab colors. We've had enough requests for that in the last few days that I want to pull that feature out into it's own spec, so we can get that approved and implemented in a future-proof way.

> This spec describes a way to specify tab colors in a profile in a way that will
> be forward compatible with theming the Terminal. This spec will be largely
> dedicated to the design of a single setting, but within the context of theming.
> 

## PR Checklist
* [x] Specs: #1337
* [x] References: #5772 
* [x] I work here

## Detailed Description of the Pull Request / Additional comments
_\*<sup>\*</sup><sub>\*</sub> read the spec  <sub>\*</sub><sup>\*</sup>\*_
2020-08-03 15:01:36 -05:00
James Holderness
158a1708a6 Render the SGR "underlined" attribute in the style of the font (#7148)
This PR updates the rendering of the _underlined_ graphic rendition
attribute, using the style specified in the active font, instead of just
reusing the grid line at the bottom of the character cell.

* Support for drawing the correct underline effect in the grid line
  renderer was added in #7107.

There was already an `ExtendedAttributes` flag defined for the
underlined state, but I needed to update the `SetUnderlined` and
`IsUnderlined` methods in the `TextAttribute`  class to use that flag
now in place of the legacy `LVB_UNDERSCORE` attribute. This enables
underlines set via a VT sequence to be tracked separately from
`LVB_UNDERSCORE` grid lines set via the console API.

I then needed to update the `Renderer::s_GetGridlines` method to
activate the `GridLines::Underline` style when the `Underlined`
attribute was set. The `GridLines::Bottom` style is still triggered by
the `LVB_UNDERSCORE` attribute to produce the bottom grid line effect.

Validation
----------

Because this is a change from the existing behaviour, certain unit tests
that were expecting the `LVB_UNDERSCORE` to be toggled by `SGR 4` and
`SGR 24` have now had to be updated to check the `Underlined` flag
instead.

There were also some UI Automation tests that were checking for `SGR 4`
mapping to `LVB_UNDERSCORE` attribute, which I've now substituted with a
test of the `SGR 53` overline attribute mapping to
`LVB_GRID_HORIZONTAL`. These tests only work with legacy attributes, so
they can't access the extended underline state, and I thought a
replacement test that covered similar ground would be better than
dropping the tests altogether.

As far as the visual rendering is concerned, I've manually confirmed
that the VT underline sequences now draw the underline in the correct
position and style, while grid lines output via the console API are
still displayed in their original form.

Closes #2915
2020-08-03 12:49:25 +00:00
James Holderness
ef4aed944a Add support for the "crossed-out" graphic rendition attribute (#7143)
This PR adds support for the ANSI _crossed-out_ graphic rendition
attribute, which is enabled by the `SGR 9` escape sequence.

* Support for the escape sequences and storage of the attribute was
  originally added in #2917.
* Support for drawing the strikethrough effect in the grid line renderer
  was added in #7107.

Since the majority of the code required for this attribute had already
been implemented, it was just a matter of activating the
`GridLines::Strikethrough` style in the `Renderer::s_GetGridlines`
method when the `CrossedOut` attribute was set.

VALIDATION

There were already some unit tests in place in `VtRendererTest` and the
`ScreenBufferTests`, but I've also now extended the SGR tests in
`AdapterTest` to cover this attribute.

I've manually confirmed the first test case from #6205 now works as
expected in both conhost and Terminal.

Closes #6205
2020-08-01 08:09:37 +00:00
Dustin L. Howett
dd0f7b701a Send ENHANCED_KEY in Win32 input mode in the wpf/uwp controls (#7106)
When we added support for win32 input mode, we neglected to pass
`ENHANCED_KEY` through the two surfaces that would generate events. This
broke arrow keys in much the same way was #2397, but in a different
layer.

While I was working on the WPF control, I took a moment to refactor the
message cracking out into a helper. It's a lot easier on the eyes than
four lines of bit shifting repeated three times.

Fixes #7074
2020-07-31 17:16:27 +00:00
PankajBhojwani
8b669b5484 Implement split pane with new tab button (#7117)
Allows splitting pane (with default settings) by holding down ALT and pressing the new tab button ('+')

## PR Checklist
* [X] Closes #6757 
* [X] Works here.
* [X] Manual test (below)
* [X] Is core contributor. 

## More detailed description

Pretty much exactly the code added in #5928 (all credit to @carlos-zamora), but put at the new tab button event binding

## Validation steps

Seems to work - holding ALT while pressing '+' opens a pane instead of a tab. Holding ALT while starting up terminal for the first time does not seem to affect the behaviour.
2020-07-31 01:07:47 +00:00
Dustin Howett
f49ae2451d Merged PR 4988918: Reflect OS changes for LKG10
Reverts "Merged PR 4670666: Workaround for LKG10 internal linker error"

Related work items: MSFT:25439646, MSFT:26598503
2020-07-30 22:55:57 +00:00
Dustin Howett
5c5c437ab8 Merged PR 4963673: OS-side build fixes for 09471c375 (gsl-3.1.0 update)
Retrieved from https://microsoft.visualstudio.com os.2020 OS official/rs_onecore_dep_uxp 29b1a1d663d0047dc0d2125dd05f75959bca27ef

Related work items: MSFT:27866336
2020-07-30 22:48:48 +00:00
James Holderness
6ee8099a2c Refactor grid line renderers with support for more line types (#7107)
This is a refactoring of the grid line renderers, adjusting the line
widths to scale with the font size, and optimising the implementation to
cut down on the number of draw calls. It also extends the supported grid
line types to include true underlines and strike-through lines in the
style of the active font.

The main gist of the optimisation was to render the horizontal lines
with a single draw call, instead of a loop with lots of little strokes
joined together. In the case of the vertical lines, which still needed
to be handled in a loop, I've tried to move the majority of static
calculations outside the loop, so there is bit of optimisation there
too.

At the same time this code was updated to support a variable stroke
width for the lines, instead of having them hardcoded to 1 pixel. The
width is now calculated as a fraction of the font size (0.025 "em"),
which is still going to be 1 pixel wide in most typical usage, but will
scale up appropriately if you zoom in far enough.

And in preparation for supporting the SGR strike-through attribute, and
true underlines, I've extended the grid line renders with options for
handling those line types as well. The offset and thickness of the lines
is obtained from the font metrics (rounded to a pixel width, with a
minimum of one pixel), so they match the style of the font.

VALIDATION

For now we're still only rendering grid lines, and only the top and
bottom lines in the case of the DirectX renderer in Windows Terminal. So
to test, I hacked in some code to force the renderer to use all the
different options, confirming that they were working in both the GDI and
DirectX renderers.

I've tested the output with a number of different fonts, comparing it
with the same text rendered in WordPad. For the most part they match
exactly, but there can be slight differences when we adjust the font
size for grid alignment. And in the case of the GDI renderer, where
we're working with pixel heights rather than points, it's difficult to
match the sizes exactly.

This is a first step towards supporting the strike-through attribute
(#6205) and true underlines (#2915).

Closes #6911
2020-07-30 22:43:37 +00:00
PankajBhojwani
2f5ba9471d Implement SetCursorColor in Terminal (#7123)
This was never hooked up to the TerminalCore implementation.

Closes #7102
2020-07-30 22:24:59 +00:00
PankajBhojwani
bf90869f30 Fixed window title not updating with tab rename (#7119)
* Fixed window title not updating with tab rename

* pass the correct title to event handler instead
2020-07-30 14:58:28 -04:00
Javier
f486a6504c Expose text selection through terminal WPF control API (#7121)
We've been trying to improve the copy/paste experience with the terminal
in Visual Studio. Once of our problematic scenarios is when the terminal
is connected to a remote environment and the user attempts to
copy/paste. This gets forwarded to the remote shell that copy/paste into
the remote clipboard instead of the local one. 

So we opted to add ctrl+shift+c/v to the terminal and need access to the
selected text via the terminal control

VALIDATION
Tested with Visual Studio integrated terminal
2020-07-30 16:55:07 +00:00
Dustin Howett
52d0e3cd52 Merged PR 4955623: Migrate OSS up to 09471c375 (gsl-3.1.0 update)
- Move to GSL 3.1.0 (GH-6908)
- Replace the color table init code with two const arrays (GH-6913)
- Replace basic_string_view<T> with span<const T> (GH-6921)
- Replace gsl::at with a new til::at(span) for pre-checked bounds (GH-6925)

Related work items: MSFT:27866336
2020-07-23 00:27:05 +00:00
Dustin Howett
878ed57db6 Merged PR 4947060: Migrate OS delayload changes
[Git2Git] Git Train: Merge of building/rs_onecore_dep_uxp/200720-1445 into official/rs_onecore_dep_uxp
Retrieved from https://microsoft.visualstudio.com os.2020 OS official/rs_onecore_dep_uxp 5e1509a00f1acdefe1e6392468ffc9ae44dc89cb

Related work items: MSFT:25731998
2020-07-22 23:29:15 +00:00
Dustin L. Howett
76de2aedc2 wpf: fix a handful of issues with the wpf control (#6983)
* send alt/F10 through the control
  We were not listening for WM_SYSKEY{UP,DOWN}
* extract the actual scancode during WM_CHAR, not the bitfield
  We were accidentally sending some of the additional keypress data in with
  the character event in Win32 Input Mode
* set default fg/bg to campbell
  The WPF control starts up in PowerShell blue even though it's not typically used
  in PowerShell blue.
* don't rely on the font to determine wideness
  This is a cross-port of #2928 to the WPF control
* deterministic shutdown
  In testing, I saw a handful of crashes on teardown because we were not shutting
  down the render thread properly.
* don't pass 10 for the font weight ...
  When Cascadia Code is set, it just looks silly.
* trigger render when selection is cleared, do it under lock

Fixes #6966.
2020-07-20 23:13:20 +00:00
Carlos Zamora
c390b61648 UIA: use full buffer comparison in rects and endpoint setter (#6447)
In UiaTextRange, `_getBufferSize` returns an optimized version of the
size of the buffer to be the origin and the last character in the
buffer. This is to improve performance on search or checking if you are
currently on the last word/line.

When setting the endpoint and drawing the bounding rectangles, we should
be retrieving the true buffer size. This is because it is still possible
to create UiaTextRanges that are outside of this optimized size. The
main source of this is `ExpandToEnclosingUnit()` when the unit is
`Document`. The end _should_ be the last visible character, but it isn't
because that would break our tests.

This is an incomplete solution. #6986 is a follow up to completely test
and implement the solution.

The crash in #6402 was caused by getting the document range (a range of
the full text buffer),  then moving the end by one character. When we
get the document range, we get the optimized size of the buffer (the
position of the last character). Moving by one character is valid
because the buffer still has more to explore. We then crash from
checking if the new position is valid on the **optimized size**, not the
**real size**.

REFERENCES

#6986 - follow up to properly handle/test this "end of buffer" problem

Closes #6402
2020-07-20 23:10:55 +00:00
Dustin L. Howett
04f5ee7ebf Swap brightBlack/black in the Solarized color schemes (#6985)
Original notes from @M-Pixel:

> Console applications assume that backgrounds are black, and that
> `lightBlack`/`DarkGrey` are lighter than `black`/`Black`.  This
> assumption is accounted for by all color schemes in `defaults.json`,
> except for the Solarized themes.
> 
> The Solarized Dark theme, in particular, makes `-Parameters` invisible
> against the background in PowerShell, which is obviously an unacceptable
> usability flaw.
> 
> This change makes `black` and `background` to the same (which is common
> throughout the color schemes), and makes `brightBlack` (`DarkGray` in
> .NET) lighter than black (which is obviously more correct given the
> meanings of those words).

Out of the box, we ship a pretty bad behavior.

If I look at all of the existing shipped color schemes--and that
includes things like Tango and One Half--we are universally following a
`background` == `black` rule.

If I consult gnome-terminal or xterm, they do the same thing; Xterm by
default, gnome-terminal for solarized. The background generally matches
color index `0` across all their **dark** schemes. Konsole and
lxterminal disagree and map background to `0 intense` for Solarized.

I want to put our Solarized schemes on a deprecation path, but
unfortunately we still need to ship _something_ for users who we're
going to strand on them.

I'm going to have to swallow my bitter and say that yes, we should
probably just change the index mapping and go with something that works
right out of the box while we figure out how to do perceptual color
nudging and eventually remove bad defaults (like Solarized).

From #6618.

Fixes #4047.
Closes #6618.
2020-07-20 13:50:50 -07:00
Mike Griese
d0ff5f6b5e Add support for running a wt commandline in the curent window WITH A KEYBINDING (#6537)
## Summary of the Pull Request

Adds a execute commandline action (`wt`), which lets a user bind a key to a specific `wt` commandline. This commandline will get parsed and run _in the current window_. 

## References

* Related to #4472 
* Related to #5400 - I need this for the commandline mode of the Command Palette
* Related to #5970

## PR Checklist
* [x] Closes oh, there's not actually an issue for this.
* [x] I work here
* [x] Tests added/passed
* [ ] Requires documentation to be updated - yes it does

## Detailed Description of the Pull Request / Additional comments

One important part of this change concerns how panes are initialized at runtime. We've had some persistent trouble with initializing multiple panes, because they rely on knowing how big they'll actually be, to be able to determine if they can split again. 

We previously worked around this by ignoring the size check when we were in "startup", processing an initial commandline. This PR however requires us to be able to know the initial size of a pane at runtime, but before the parents have necessarily been added to the tree, or had their renderer's set up.

This led to the development of `Pane::PreCalculateCanSplit`, which is very highly similar to `Pane::PreCalculateAutoSplit`. This method attempts to figure out how big a pane _will_ take, before the parent has necessarily laid out. 

This also involves a small change to `TermControl`, because if its renderer hasn't been set up yet, it'll always think the font is `{0, fontHeight}`, which will let the Terminal keep splitting in the x direction. This change also makes the TermControl set up a renderer to get the real font size when it hasn't yet been initialized.

## Validation Steps Performed

This was what the json blob I was using for testing evolved into

```json
        {
            "command": {
                "action":"wt",
                "commandline": "new-tab cmd.exe /k #work 15 ; split-pane cmd.exe /k #work 15 ; split-pane cmd.exe /k media-commandline ; new-tab powershell dev\\symbols.ps1 ; new-tab -p \"Ubuntu\" ; new-tab -p \"haunter.gif\" ; focus-tab -t 0",

            },
            "keys": ["ctrl+shift+n"]
        }
```

I also added some tests.

# TODO
* [x] Creating a `{ "command": "wt" }` action without a commandline will spawn a new `wt.exe` process?
  - Probably should just do nothing for the empty string
2020-07-17 21:05:29 +00:00
Michael Niksa
3a91fc0ab4 Skip DX invalidation if we've already scrolled an entire screen worth of height (#6922)
We spend a lot of time invalidating in the DX Renderer. This is a
creative trick to not bother invalidating any further if we can tell
that the bitmap is already completely invalidated. That is, if we've
scrolled at least an entire screen in height... then the entire bitmap
had to have been marked as invalid as the new areas were "uncovered" by
the `InvalidateScroll` command. So further setting invalid bits on top
of a fully invalid map is pointless. 

Note: I didn't use `bitmap::all()` here because it is significantly
slower to check all the bits than it is to just reason out that the
bitmap was already fully marked.

## Validation Steps Performed
- Run `time cat big.txt`. Checked average time before/after, WPR traces
  before/after.
2020-07-17 19:32:36 +00:00
jtippet
0c3841a8b0 Add gutter to Command Palette to avoid overlapping with scrollbar (#6965)
The command palette has some content that can overlap with its
scrollbar.  This PR adds a 16px gutter for the scrollbar, as recommended
[here](https://docs.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/scroll-controls#:~:text=leave%2016px%20of%20padding%20on%20the%20edge%20of%20the%20viewport).

## Detailed Description of the Pull Request / Additional comments
You can repro the overlap in the default configuration by grabbing the
scrollbar with the mouse pointer.  But there's an accessibility option
that makes this more obvious: Settings > Display > Automatically hide
scroll bars.  With that option enabled, the text is _always_
overlapping.

The gutter does look slightly larger than it needs to be when the
scrollbar is thin or completely hidden.  Dynamic reflow may help, but
unfortunately, I don't know enough XAML to wire that up.  MUX has a
promising visual state named `ScrollBarsSeparatorExpanded`, so the
scientists suggest we _could_, while the designers are still pondering
whether we _should_.

## Validation Steps Performed

Old appearance:
![image](https://user-images.githubusercontent.com/10259764/87817879-94d85100-c81e-11ea-956c-ca0e23576fef.png)

New appearance with fat scrollbars:
![image](https://user-images.githubusercontent.com/10259764/87817914-a4579a00-c81e-11ea-9e9d-195969e6da95.png)

New appearance with thin scrollbars:
![image](https://user-images.githubusercontent.com/10259764/87818061-dff26400-c81e-11ea-866e-088f72276343.png)

New appearance with no scrollbar:
![image](https://user-images.githubusercontent.com/10259764/87819674-7758b680-c821-11ea-98b7-dddd1573c242.png)
2020-07-17 19:27:32 +00:00
Michael Niksa
4351f32f5d Commit attr runs less frequently by accumulating length of color run (#6919)
The act of calling `InsertAttrRuns` is relatively slow. Instead of
calling it a bunch of times to meddle with colors one cell at a time,
we'll accumulate a length of color and call it to make it act all at
once. This is great for when one color full line is getting replaced
with another color full line OR when a line is being replaced with the
same color all at once. There's significantly fewer checks to be made
inside `InsertAttrRuns` if we can help it out by accumulating the length
of each color before asking it to stitch it into the storage.

Validation
----------

- Run `time cat big.txt` and `time cat ls.txt` under VS Performance
  Profiler.
2020-07-17 17:53:01 +00:00
Michael Niksa
ea2bd42ff4 Set memory order on slow atomics (#6920)
By default, the memory order on atomics is `seq_cst`. This is a relatively expensive ordering and it shows in situations where we're rapidly signaling a consumer to pick up something from a producer. I've instead attempted to switch these to `release` (producer) and `acquire` (consumer) to improve the performance of these signals. 

## Validation Steps Performed
- Run `time cat big.txt` and `time cat ls.txt` under VS Performance Profiler. 

## PR Checklist
* [x] Closes perf itch
* [x] I work here
* [x] Manual test
* [x] Documentation irrelevant.
* [x] Schema irrelevant.
* [x] Am core contributor.
2020-07-17 17:11:45 +00:00
Dan Thompson
1f8264d86b Tweaks: normalize TextAttribute method names (adjective form) (#6951)
## Summary of the Pull Request

Text can have various attributes, such as "bold", "italic", "underlined", etc.  The TextAttribute class embodies this. It has methods to set/query these attributes.

This change tweaks a few of the method names to make them match. I.e. for an imaginary text property "Foo", we should have methods along the lines of:

```
IsFoo
SetFoo(bool isFoo)
```

And variations should match: we should have "Foo" and "OverFoo", not "Fooey" and "OverFoo".

I chose to standardize on the adjective form, since that's what we are closest to already. The attributes I attacked here are:

SetItalic**s** --> SetItalic
SetUnderline --> SetUnderline**d**
SetOverline --> SetOverline**d**

("italic" is an adjective; "italics" is a plural noun, representing letters or words in an italic typeface)

And I also added methods for "DoublyUnderlined" for good measure.

I stopped short of renaming the GraphicsOptions enum values to match, too; but I'd be willing to do that in a follow-up change if people wanted it.

## Validation Steps Performed
It builds, and tests still pass.
2020-07-17 15:50:23 +00:00
Dustin L. Howett
efb1fddb99 Convert most of our JSON deserializers to use type-based conversion (#6590)
This pull request converts the following JSON deserializers to use the
new JSON deserializer pattern:

* Profile
* Command
* ColorScheme
* Action/Args
* GlobalSettings
* CascadiaSettingsSerialization

This is the completion of a long-term JSON refactoring that makes our
parser and deserializer more type-safe and robust. We're finally able to
get rid of all our manual enum conversion code and unify JSON conversion
around _types_ instead of around _keys_.

I've introduced another file filled with template specializations,
TerminalSettingsSerializationHelpers.h, which comprises a single unit
that holds all of the JSON deserializers (and eventually serializers)
for every type that comes from TerminalApp or TerminalSettings.

I've also moved some types out of Profile and GlobalAppSettings into a
new SettingsTypes.h to improve settings locality.

This does to some extent constitute a breaking change for already-broken
settings. Instead of parsing "successfully" (where invalid values are
null or 0 or unknown or unset), deserialization will now fail when
there's a type mismatch. Because of that, some tests had to be removed.

While I was on a refactoring spree, I removed a number of helpless
helpers, like GetWstringFromJson (which converted a u8 string to an
hstring to make a wstring out of its data pointer :|) and
_ConvertJsonToBool.

In the future, we can make the error types more robust and give them
position and type information such that a conformant application can
display rich error information ("line 3 column 3, I expected a string,
you gave me an integer").

Closes #2550.
2020-07-17 01:31:09 +00:00
Dustin L. Howett
7bc5de613c version: bump to 1.3 on master 2020-07-16 18:18:33 -07:00
Dustin L. Howett
bcbe246a93 Update Cascadia Code to 2007.15 (#6958) 2020-07-16 18:14:57 -07:00
Dustin L. Howett
03e25f12e9 Move to the TerminalDependencies NuGet feed (#6954)
After we stood up our own NuGet feed in Azure blob storage, Azure DevOps
came out with a public artifact feed feature. I've migrated all our
packages over, and the only thing left to do is switch our project's
NuGet config to use it.

Fixes #6952
2020-07-16 17:33:23 -07:00
jtippet
7062a830b8 Smooth animation of command palette filtering (#6939)
The command palette is a ListView of commands.  As you type into the
search box, commands are added or removed from the ListView.  Currently,
each update is done by completely clearing the backing list, then adding
back any items that should be displayed.  However, this defeats the
ListView's built-in animations: upon every keystroke, ListView displays
its list-clearing animation, then animates the insertion of every item
that wasn't deleted.  This results in noticeable flickering.

This PR changes the update logic so that it updates the list using
(roughly) the minimum number of Insert and Remove calls, so the ListView
makes smoother transitions as you type.

I implemented it by keeping the existing code that builds the filtered
list, but I changed it to build into a scratch list.  Then I grafted on
a generic delta algorithm to make the real list look like the scratch
list.

To verify the delta algorithm, I tested all 360,000 permutations of
pairs of up to 5 element lists in a toy C# app.

## Validation
I'm not sure if my screen capture tool really caught all the flickering
here, but the screencasts below should give a rough idea of the
difference.  (All the flickering was becoming a nuisance while I was
testing out the HC changes.)

See the images in #6939 for more info.

Co-authored-by: Jeffrey Tippet <jtippet@microsoft.com>
2020-07-16 17:33:03 -07:00
Dan Thompson
53df6c7f96 Fix 'bcz exclusive' typo (#6938)
Without this fix, you'd end up with an empty target variable, and so
msbuild would complain and tell you to give it a target.
2020-07-16 16:58:48 -07:00
Michael Niksa
81b7e54659 Cache the viewport to make invalidation faster (#6918)
In `Renderer::TriggerRedraw`, the act of fetching the viewport from the
`pData` over and over is wasted time. We already have a cached variable
of the viewport that is updated on every scroll check (on
`TriggerScroll` and on `PaintFrame`.) Scrolling wouldn't be working
correctly if the clients weren't already notifying us that the viewport
has changed for scroll purposes, so we can just keep using that cached
value for the invalidation restriction to speed things up over fetching
it again.

## Validation Steps Performed
- Run `time cat big.txt`. Checked average time before/after, WPR traces
  before/after.

## PR Checklist
* [x] Closes perf itch
* [x] I work here
* [x] Manual test
* [x] Documentation irrelevant.
* [x] Schema irrelevant.
* [x] Am core contributor.
2020-07-16 21:46:10 +00:00
Michael Niksa
3255177dd0 Correct comment in this SPSC test as a quick follow up to merge. 2020-07-16 13:53:56 -07:00
Leonard Hecker
b62f5ea850 Added til::spsc, a lock-free, single-producer/-consumer FIFO queue (#6751)
## Summary of the Pull Request

This PR adds the `til::spsc` namespace, which implements a lock-free, single-producer, single-consumer FIFO queue ("channel"). The queue efficiently blocks the caller using Futexes if no data can be written to / read from the queue (e.g. using `WaitOnAddress` on Windows). Furthermore it allows batching of data and contains logic to signal the caller if the other side has been dropped/destructed.

## PR Checklist
* [ ] Closes #xxx
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [x] Tests added/passed
* [ ] Documentation updated. If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx
* [ ] Schema updated.
* [x] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #xxx

## Detailed Description of the Pull Request / Additional comments

`til::spsc::details::arc<T>` contains most of the queue's logic and as such has the relevant documentation for its design.

## Validation Steps Performed

The queue was tested on Windows, Linux and macOS using MSVC, gcc and llvm and each of their available runtime introspection utilities in order to ensure no race conditions or memory leaks occur.
2020-07-16 20:49:06 +00:00
Dustin L. Howett
09471c3753 Replace gsl::at with a new til::at(span) for pre-checked bounds (#6925)
The recent changes to use gsl::span everywhere added a few bounds checks
along codepaths where we were already checking bounds. Some of them may
be non-obvious to the optimizer, so we can now use til::at to help them
along.

To accomplish this, I've added a new overload of til::at that takes a
span and directly accesses its backing buffer.
2020-07-15 10:29:36 -07:00
Dustin L. Howett
80da24ecf8 Replace basic_string_view<T> with span<const T> (#6921)
We were using std::basic_string_view as a stand-in for std::span so that
we could change over all at once when C++20 dropped with full span
support. That day's not here yet, but as of 54a7fce3e we're using GSL 3,
whose span is C++20-compliant.

This commit replaces every instance of basic_string_view that was not
referring to an actual string with a span of the appropriate type.

I moved the `const` qualifier into span's `T` because while
`basic_string_view.at()` returns `const T&`, `span.at()` returns `T&`
(without the const). I wanted to maintain the invariant that members of
the span were immutable.

* Mechanical Changes
   * `sv.at(x)` -> `gsl::at(sp, x)`
   * `sv.c{begin,end}` -> `sp.{begin,end}` (span's iterators are const)

I had to replace a `std::basic_string<>` with a `std::vector<>` in
ConImeInfo, and I chose to replace a manual array walk in
ScreenInfoUiaProviderBase with a ranged-for. Please review those
specifically.

This will almost certainly cause a code size regression in Windows
because I'm blowing out all the PGO counts. Whoops.

Related: #3956, #975.
2020-07-15 16:40:42 +00:00
Dustin L. Howett
4715bf5525 Replace the color table init code with two const arrays (#6913)
This results in smaller code and faster copying. I chose til::color even
though it results in slightly worse codegen (byteswapping in a tight
loop) than COLORREF (SSE-enlightened block copy) because eventually the
internal representations of the color tables will also be til::color and
_then_ it will become a block copy.
2020-07-15 15:53:38 +00:00
Dustin Howett
ebfd852970 Merged PR 4921418: Merge OSS up to ff27fdfed 2020-07-15 00:13:22 +00:00
227 changed files with 4720 additions and 3360 deletions

View File

@@ -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" />

View File

@@ -160,15 +160,11 @@ EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalConnection", "src\cascadia\TerminalConnection\TerminalConnection.vcxproj", "{CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalCore", "src\cascadia\TerminalCore\lib\TerminalCore-lib.vcxproj", "{CA5CAD1A-ABCD-429C-B551-8562EC954746}"
ProjectSection(ProjectDependencies) = postProject
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalControl", "src\cascadia\TerminalControl\TerminalControl.vcxproj", "{CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}"
ProjectSection(ProjectDependencies) = postProject
{CA5CAD1A-C46D-4588-B1C0-40F31AE9100B} = {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}
{CA5CAD1A-ABCD-429C-B551-8562EC954746} = {CA5CAD1A-ABCD-429C-B551-8562EC954746}
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}
{1CF55140-EF6A-4736-A403-957E4F7430BB} = {1CF55140-EF6A-4736-A403-957E4F7430BB}
{48D21369-3D7B-4431-9967-24E81292CF63} = {48D21369-3D7B-4431-9967-24E81292CF63}
EndProjectSection
@@ -178,7 +174,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WindowsTerminal", "src\casc
{CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}
{CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12} = {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}
{CA5CAD1A-ABCD-429C-B551-8562EC954746} = {CA5CAD1A-ABCD-429C-B551-8562EC954746}
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}
{9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B} = {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}
EndProjectSection
EndProject
@@ -187,11 +182,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalApp", "src\cascadia
{CA5CAD1A-9A12-429C-B551-8562EC954746} = {CA5CAD1A-9A12-429C-B551-8562EC954746}
{CA5CAD1A-C46D-4588-B1C0-40F31AE9100B} = {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}
{CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalSettings", "src\cascadia\TerminalSettings\TerminalSettings.vcxproj", "{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WindowsTerminalShellExt", "src\cascadia\ShellExtension\WindowsTerminalShellExt.vcxproj", "{F2ED628A-DB22-446F-A081-4CC845B51A2B}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnitTests_TerminalCore", "src\cascadia\UnitTests_TerminalCore\UnitTests.vcxproj", "{2C2BEEF4-9333-4D05-B12A-1905CBF112F9}"
@@ -238,7 +230,6 @@ EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalAppLib", "src\cascadia\TerminalApp\lib\TerminalAppLib.vcxproj", "{CA5CAD1A-9A12-429C-B551-8562EC954746}"
ProjectSection(ProjectDependencies) = postProject
{CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LocalTests_TerminalApp", "src\cascadia\LocalTests_TerminalApp\TerminalApp.LocalTests.vcxproj", "{CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}"
@@ -1389,35 +1380,6 @@ Global
{CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}.Release|x64.Build.0 = Release|x64
{CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}.Release|x86.ActiveCfg = Release|Win32
{CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}.Release|x86.Build.0 = Release|Win32
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.AuditMode|Any CPU.ActiveCfg = Debug|Win32
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.AuditMode|ARM64.ActiveCfg = Release|ARM64
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.AuditMode|DotNet_x64Test.ActiveCfg = Debug|Win32
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.AuditMode|DotNet_x86Test.ActiveCfg = Debug|Win32
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.AuditMode|x64.ActiveCfg = AuditMode|x64
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.AuditMode|x64.Build.0 = AuditMode|x64
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.AuditMode|x86.ActiveCfg = Release|Win32
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Debug|Any CPU.ActiveCfg = Debug|Win32
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Debug|ARM64.ActiveCfg = Debug|ARM64
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Debug|ARM64.Build.0 = Debug|ARM64
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Debug|DotNet_x64Test.ActiveCfg = Debug|x64
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Debug|DotNet_x64Test.Build.0 = Debug|x64
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Debug|DotNet_x86Test.Build.0 = Debug|Win32
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Debug|x64.ActiveCfg = Debug|x64
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Debug|x64.Build.0 = Debug|x64
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Debug|x86.ActiveCfg = Debug|Win32
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Debug|x86.Build.0 = Debug|Win32
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Release|Any CPU.ActiveCfg = Release|Win32
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Release|ARM64.ActiveCfg = Release|ARM64
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Release|ARM64.Build.0 = Release|ARM64
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Release|DotNet_x64Test.ActiveCfg = Release|x64
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Release|DotNet_x64Test.Build.0 = Release|x64
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Release|DotNet_x86Test.ActiveCfg = Release|Win32
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Release|DotNet_x86Test.Build.0 = Release|Win32
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Release|x64.ActiveCfg = Release|x64
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Release|x64.Build.0 = Release|x64
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Release|x86.ActiveCfg = Release|Win32
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Release|x86.Build.0 = Release|Win32
{F2ED628A-DB22-446F-A081-4CC845B51A2B}.AuditMode|Any CPU.ActiveCfg = Release|Win32
{F2ED628A-DB22-446F-A081-4CC845B51A2B}.AuditMode|ARM64.ActiveCfg = Release|ARM64
{F2ED628A-DB22-446F-A081-4CC845B51A2B}.AuditMode|DotNet_x64Test.ActiveCfg = AuditMode|Win32
@@ -2073,7 +2035,6 @@ Global
{CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {59840756-302F-44DF-AA47-441A9D673202}
{CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B} = {59840756-302F-44DF-AA47-441A9D673202}
{CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12} = {59840756-302F-44DF-AA47-441A9D673202}
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {59840756-302F-44DF-AA47-441A9D673202}
{F2ED628A-DB22-446F-A081-4CC845B51A2B} = {59840756-302F-44DF-AA47-441A9D673202}
{2C2BEEF4-9333-4D05-B12A-1905CBF112F9} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E}
{EF3E32A7-5FF6-42B4-B6E2-96CD7D033F00} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB}

View File

@@ -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>

View File

@@ -9,6 +9,8 @@ This document serves as a storage point for those posts.
- [Output Processing between "Far East" and "Western"](#fesb)
- [Why do we not backport things?](#backport)
- [Why can't we have mixed elevated and non-elevated tabs in the Terminal?](#elevation)
- [What's the difference between a shell and a terminal?](#shell-vs-terminal)
## <a name="cmd"></a>Why do we avoid changing CMD.exe?
`setlocal` doesn't behave the same way as an environment variable. It's a thing that would have to be put in at the top of the batch script that is `somefile.cmd` as one of its first commands to adjust the way that one specific batch file is processed by the `cmd.exe` engine. That's probably not suitable for your needs, but that's the way we have to go.
@@ -179,3 +181,20 @@ Other platforms have accepted that risk in preference for user convenience. They
Original Source: https://github.com/microsoft/terminal/issues/632#issuecomment-519375707
## <a name="shell-vs-terminal"></a>What's the difference between a shell and a terminal?
_guest speaker @zadjii-msft_
I think there might be a bit of a misunderstanding here - there are two different kinds of applications we're talking about here:
* shell applications, like `cmd.exe`, `powershell`, `zsh`, etc. These are text-only applications that emit streams of characters. They don't care at all about how they're eventually rendered to the user. These are also sometimes referred to as "commandline client" applications.
* terminal applications, like the Windows Terminal, gnome-terminal, xterm, iterm2, hyper. These are graphical applications that can be used to render the output of commandline clients.
On Windows, if you just run `cmd.exe` directly, the OS will create an instance of `conhost.exe` as the _terminal_ for `cmd.exe`. The same thing happens for `powershell.exe`, the system will creates a new conhost window for any client that's not already connected to a terminal of some sort. This has lead to an enormous amount of confusion for people thinking that a conhost window is actually a "`cmd` window". `cmd` can't have a window, it's just a commandline application. Its window is always some other terminal.
Any terminal can run any commandline client application. So you can use the Windows Terminal to run whatever shell you want. I use mine for both `cmd` and `powershell`, and also WSL:
![image](https://user-images.githubusercontent.com/18356694/89556758-79d27e80-d7d7-11ea-84e2-10710e09ef4a.png)
It's not the Terminal's responsibility to remember the commands executed by a commandline client. That's the responsibility of the _shell_. How would the terminal remember commands executed by something like `emacs` or `vim`? Those are both applications where the user is typing input and hitting enter, like they would at a cmd prompt, but without something that resembles a command history.
Original Source: https://github.com/microsoft/terminal/issues/6500#issuecomment-670035468

View File

@@ -63,6 +63,9 @@
"openTabColorPicker",
"renameTab",
"commandPalette",
"wt",
"closeOtherTabs",
"closeTabsAfter",
"unbound"
],
"type": "string"
@@ -280,6 +283,57 @@
}
]
},
"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" ]
},
"CloseOtherTabsAction": {
"description": "Arguments for a closeOtherTabs action",
"allOf": [
{ "$ref": "#/definitions/ShortcutAction" },
{
"properties": {
"action": { "type": "string", "pattern": "closeOtherTabs" },
"index": {
"type": "integer",
"default": "",
"description": "close the tabs following the tab at this index"
}
}
}
],
"required": [ "index" ]
},
"CloseTabsAfterAction": {
"description": "Arguments for a closeTabsAfter action",
"allOf": [
{ "$ref": "#/definitions/ShortcutAction" },
{
"properties": {
"action": { "type": "string", "pattern": "closeTabsAfter" },
"index": {
"type": "integer",
"default": "",
"description": "close the tabs other than the one at this index"
}
}
}
],
"required": [ "index" ]
},
"Keybinding": {
"additionalProperties": false,
"properties": {
@@ -296,6 +350,9 @@
{ "$ref": "#/definitions/SplitPaneAction" },
{ "$ref": "#/definitions/OpenSettingsAction" },
{ "$ref": "#/definitions/SetTabColorAction" },
{ "$ref": "#/definitions/WtAction" },
{ "$ref": "#/definitions/CloseOtherTabsAction" },
{ "$ref": "#/definitions/CloseTabsAfterAction" },
{ "type": "null" }
]
},

View File

@@ -0,0 +1,146 @@
---
author: Mike Griese @zadjii-msft
created on: 2020-07-31
last updated: 2020-08-03
issue id: #1337
---
# Per-Profile Tab Colors
## Abstract
This spec describes a way to specify tab colors in a profile in a way that will
be forward compatible with theming the Terminal. This spec will be largely
dedicated to the design of a single setting, but within the context of theming.
## Inspiration
Following the addition of the Tab Color Picker in [#3789], we've had numerous
requests for the ability to set the color of a tab directly within a profile.
While largely we're tracking theming in [#3327], including the specification of
a tab color, the theming spec ([#5772] )is very large and will take a while to
revise and approve. This spec is intended to pull a single point out from that
spec to make it more easily reviewable, and implement it in a way that will
continue working when we add support for themes in the future.
## Solution Design
To enable per-profile tab colors, we'll add a single setting: `tabColor`. For
now<sup>[[1](#user-content-footnote-1)]</sup>, this setting will accept any
`#rrggbb` color string.
Since each profile creates a `Pane` with a `TermControl`, we'll need to store
this color not in the `Tab`, but somewhere below `Pane`, so that when you switch
between Panes in a tab with different `tabColor`s, the color will update
automatically. When a new `TermControl` is created, we'll store this color in the
`TermControl`'s `Terminal` core. This is to support the future possibility of
setting the tab color via VT sequences.
A Tab's color will be the result of layering a variety of sources, from the bottom up:
Color | | Set by
-- | -- | --
Runtime Color | _optional_ |Color Picker / `setTabColor` action
Control Tab Color | _optional_ | Profile's `tabColor`, or a color set by VT
Theme Tab Background | _optional_ | `tab.backgroundColor` in the theme
Tab Default Color | **default** | TabView in XAML
Some examples:
* **Scenario 1**: The user has set `"tabColor": "#ff0000"` in their profile.
When they create tabs with that profile, instead of appearing in the default
color for the TabView, they'll be `#ff0000`.
* **Scenario 2**: The user has set `"tabColor": "#ff0000"` in their profile.
When they try to set the color for that tab (with the color picker) to
`#0000ff`, the tab's color is updated to reflect this new blue color. When
they clear the runtime color (with the color picker), the tab will return to
`#ff0000`.
* **Scenario 3**: The user has two profiles with colors set, one to `"tabColor":
"#ff0000"`, and the other with `"tabColor": "#00ff00"`. If they open these
profiles in two panes side-by side, the tab's color will update to reflect the
color from the currently-focused control.
* **Scenario 4**: The user has two profiles with colors set, one to `"tabColor":
"#ff0000"`, and the other with `"tabColor": "#00ff00"`. If they open these
profiles in two panes side-by side, and try to set the color for that tab
(with the color picker) to `#0000ff`, the tab's color is updated to reflect
this new blue color. Regardless of which pane is focused, the tab will be
blue.
* **Scenario 5**: The user has set `"tabColor": "#ff0000"` in their profile
("Profile A"), and `"tab.backgroundColor": "#00ff00"`in their theme. When they
create tabs with "Profile A", the tabs will appear red. Other tabs (for
profiles without `tabColor` set) will appear green, using the color from the
theme.
## UI/UX Design
In general, this is going to look exactly like the colored tabs look now.
![preview](profile-tabColor-000.gif)
## Capabilities
<table>
<tr>
<td><strong>Accessibility</strong></td>
<td>
N/A
</td>
</tr>
<tr>
<td><strong>Security</strong></td>
<td>
N/A
</td>
</tr>
<tr>
<td><strong>Reliability</strong></td>
<td>
No expected change
</td>
</tr>
<tr>
<td><strong>Compatibility</strong></td>
<td>
This entire spec outlines how this feature is designed with a emphasis on future
compatibility. As such, there are no expected regressions in the future when we
do add support for themes.
</td>
</tr>
<tr>
<td><strong>Performance, Power, and Efficiency</strong></td>
<td>
No expected change
</td>
</tr>
</table>
## Potential Issues
None expected.
## Footnotes
<a id="footnote-1"><a>[1]: When full theming support is added, themes will
provide support for setting colors as one of a variety of values:
* An `#rrggbb` string
* The system accent color
* The current background color of the Terminal
* A value from a given resource key from XAML
When support for these other types of "smart" colors is added, then the profile
`tabColor` setting will also gracefully accept these values.
## Future considerations
* It's not out of the realm of possibility that someone might want to color each
_pane_'s color at runtime. In that case, the runtime color would be stored in
the `Pane`, not the `Tab`.
<!-- Footnotes -->
[#1337]: https://github.com/microsoft/terminal/issues/1337
[#3789]: https://github.com/microsoft/terminal/issues/3789
[#3327]: https://github.com/microsoft/terminal/issues/3327
[#5772]: https://github.com/microsoft/terminal/pull/5772

Binary file not shown.

After

Width:  |  Height:  |  Size: 816 KiB

Binary file not shown.

Binary file not shown.

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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
@@ -246,8 +246,7 @@ bool TextAttribute::IsCrossedOut() const noexcept
bool TextAttribute::IsUnderlined() const noexcept
{
// TODO:GH#2915 Treat underline separately from LVB_UNDERSCORE
return WI_IsFlagSet(_wAttrLegacy, COMMON_LVB_UNDERSCORE);
return WI_IsFlagSet(_extendedAttrs, ExtendedAttributes::Underlined);
}
bool TextAttribute::IsOverlined() const noexcept
@@ -270,7 +269,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 +289,12 @@ 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);
WI_UpdateFlag(_extendedAttrs, ExtendedAttributes::Underlined, isUnderlined);
}
void TextAttribute::SetOverline(bool isOverlined) noexcept
void TextAttribute::SetOverlined(bool isOverlined) noexcept
{
WI_UpdateFlag(_wAttrLegacy, COMMON_LVB_GRID_HORIZONTAL, isOverlined);
}

View File

@@ -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));
}
};
}

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -118,7 +118,7 @@ public:
[[nodiscard]] TextAttribute GetCurrentAttributes() const noexcept;
void SetCurrentAttributes(const TextAttribute currentAttributes) noexcept;
void SetCurrentAttributes(const TextAttribute& currentAttributes) noexcept;
void Reset();

View File

@@ -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()

View File

@@ -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()

View File

@@ -10,7 +10,7 @@
using namespace Microsoft::Console;
using namespace TerminalApp;
using namespace winrt::TerminalApp;
using namespace winrt::Microsoft::Terminal::Settings;
using namespace winrt::Microsoft::Terminal::TerminalControl;
using namespace WEX::Logging;
using namespace WEX::TestExecution;
using namespace WEX::Common;
@@ -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()
{

View File

@@ -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());
}
}

View File

@@ -11,7 +11,7 @@
using namespace Microsoft::Console;
using namespace TerminalApp;
using namespace winrt::TerminalApp;
using namespace winrt::Microsoft::Terminal::Settings;
using namespace winrt::Microsoft::Terminal::TerminalControl;
using namespace WEX::Logging;
using namespace WEX::TestExecution;
using namespace WEX::Common;
@@ -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);

View File

@@ -16,7 +16,7 @@ using namespace WEX::Logging;
using namespace WEX::TestExecution;
using namespace WEX::Common;
using namespace winrt::TerminalApp;
using namespace winrt::Microsoft::Terminal::Settings;
using namespace winrt::Microsoft::Terminal::TerminalControl;
namespace TerminalAppLocalTests
{
@@ -76,6 +76,8 @@ namespace TerminalAppLocalTests
TEST_METHOD(ValidateKeybindingsWarnings);
TEST_METHOD(ValidateExecuteCommandlineWarning);
TEST_METHOD(ValidateLegacyGlobalsWarning);
TEST_METHOD(TestTrailingCommas);
@@ -91,7 +93,7 @@ namespace TerminalAppLocalTests
void SettingsTests::TryCreateWinRTType()
{
winrt::Microsoft::Terminal::Settings::TerminalSettings settings;
TerminalSettings settings;
VERIFY_IS_NOT_NULL(settings);
auto oldFontSize = settings.FontSize();
settings.FontSize(oldFontSize + 5);
@@ -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"(

View File

@@ -84,7 +84,7 @@ namespace TerminalAppLocalTests
{
// Verify we can create a WinRT type we authored
// Just creating it is enough to know that everything is working.
winrt::Microsoft::Terminal::Settings::TerminalSettings settings;
TerminalSettings settings;
VERIFY_IS_NOT_NULL(settings);
auto oldFontSize = settings.FontSize();
settings.FontSize(oldFontSize + 5);
@@ -140,7 +140,7 @@ namespace TerminalAppLocalTests
// 4. one of our types that uses MUX/Xaml in this dll (Tab).
// Just creating all of them is enough to know that everything is working.
const auto profileGuid{ Utils::CreateGuid() };
winrt::Microsoft::Terminal::Settings::TerminalSettings settings{};
TerminalSettings settings{};
VERIFY_IS_NOT_NULL(settings);
winrt::Microsoft::Terminal::TerminalConnection::EchoConnection conn{};
VERIFY_IS_NOT_NULL(conn);

View File

@@ -79,7 +79,6 @@
<!-- If you don't reference these projects here, the
_ConsoleGenerateAdditionalWinmdManifests step won't gather the winmd's -->
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalSettings\TerminalSettings.vcxproj" />
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalControl\TerminalControl.vcxproj" />
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalConnection\TerminalConnection.vcxproj" />
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalApp\TerminalApp.vcxproj" />

View File

@@ -45,11 +45,11 @@
"taef.png" is actually in the package. taef.png will get copied to the
OutputPath when taef is run, but if this isn't set to false, then the CI
will try and make sure taef.png is in the OutputPath at build time.-->
<PropertyGroup Label="UserMacros">
<GenerateAppxPackageOnBuild>false</GenerateAppxPackageOnBuild>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<DisableSpecificWarnings>4453;%(DisableSpecificWarnings)</DisableSpecificWarnings>
@@ -94,7 +94,6 @@
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalConnection\TerminalConnection.vcxproj">
<Project>{CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}</Project>
</ProjectReference>
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalSettings\TerminalSettings.vcxproj" />
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalControl\TerminalControl.vcxproj" />
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalApp\TerminalApp.vcxproj">
@@ -114,7 +113,7 @@
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestExecutor.WinRTCore">
<HintPath>$(OpenConsoleDir)\packages\Taef.Redist.Wlk.10.51.200127004\lib\Microsoft.VisualStudio.TestPlatform.TestExecutor.WinRTCore.winmd</HintPath>
<HintPath>$(OpenConsoleDir)\packages\Taef.Redist.Wlk.10.57.200731005-develop\lib\Microsoft.VisualStudio.TestPlatform.TestExecutor.WinRTCore.winmd</HintPath>
<IsWinMDFile>true</IsWinMDFile>
<!-- This path is _relative to the .winmd_ -->

View File

@@ -24,18 +24,18 @@ public:
// Return Value:
// - The ActionAndArgs bound to the given key, or nullptr if nothing is bound to it.
static const winrt::TerminalApp::ActionAndArgs GetActionAndArgs(const winrt::TerminalApp::implementation::AppKeyBindings& bindings,
const winrt::Microsoft::Terminal::Settings::KeyChord& kc)
const winrt::Microsoft::Terminal::TerminalControl::KeyChord& kc)
{
std::wstring buffer{ L"" };
if (WI_IsFlagSet(kc.Modifiers(), winrt::Microsoft::Terminal::Settings::KeyModifiers::Ctrl))
if (WI_IsFlagSet(kc.Modifiers(), winrt::Microsoft::Terminal::TerminalControl::KeyModifiers::Ctrl))
{
buffer += L"Ctrl+";
}
if (WI_IsFlagSet(kc.Modifiers(), winrt::Microsoft::Terminal::Settings::KeyModifiers::Shift))
if (WI_IsFlagSet(kc.Modifiers(), winrt::Microsoft::Terminal::TerminalControl::KeyModifiers::Shift))
{
buffer += L"Shift+";
}
if (WI_IsFlagSet(kc.Modifiers(), winrt::Microsoft::Terminal::Settings::KeyModifiers::Alt))
if (WI_IsFlagSet(kc.Modifiers(), winrt::Microsoft::Terminal::TerminalControl::KeyModifiers::Alt))
{
buffer += L"Alt+";
}

View File

@@ -21,7 +21,26 @@ static constexpr bool _IsMouseMessage(UINT uMsg)
return uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONUP || uMsg == WM_LBUTTONDBLCLK ||
uMsg == WM_MBUTTONDOWN || uMsg == WM_MBUTTONUP || uMsg == WM_MBUTTONDBLCLK ||
uMsg == WM_RBUTTONDOWN || uMsg == WM_RBUTTONUP || uMsg == WM_RBUTTONDBLCLK ||
uMsg == WM_MOUSEMOVE || uMsg == WM_MOUSEWHEEL;
uMsg == WM_MOUSEMOVE || uMsg == WM_MOUSEWHEEL || uMsg == WM_MOUSEHWHEEL;
}
// 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(
@@ -36,10 +55,29 @@ try
if (terminal)
{
if (_IsMouseMessage(uMsg) && terminal->_CanSendVTMouseInput())
if (_IsMouseMessage(uMsg))
{
if (terminal->_SendMouseEvent(uMsg, wParam, lParam))
if (terminal->_CanSendVTMouseInput() && terminal->_SendMouseEvent(uMsg, wParam, lParam))
{
// GH#6401: Capturing the mouse ensures that we get drag/release events
// even if the user moves outside the window.
// _SendMouseEvent returns false if the terminal's not in VT mode, so we'll
// fall through to release the capture.
switch (uMsg)
{
case WM_LBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_RBUTTONDOWN:
SetCapture(hwnd);
break;
case WM_LBUTTONUP:
case WM_MBUTTONUP:
case WM_RBUTTONUP:
ReleaseCapture();
break;
}
// Suppress all mouse events that made it into the terminal.
return 0;
}
}
@@ -57,6 +95,10 @@ try
return 0;
case WM_LBUTTONUP:
terminal->_singleClickTouchdownPos = std::nullopt;
[[fallthrough]];
case WM_MBUTTONUP:
case WM_RBUTTONUP:
ReleaseCapture();
break;
case WM_MOUSEMOVE:
if (WI_IsFlagSet(wParam, MK_LBUTTON))
@@ -72,7 +114,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 +123,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 +161,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 +197,11 @@ HwndTerminal::HwndTerminal(HWND parentHwnd) :
}
}
HwndTerminal::~HwndTerminal()
{
Teardown();
}
HRESULT HwndTerminal::Initialize()
{
_terminal = std::make_unique<::Microsoft::Terminal::Core::Terminal>();
@@ -162,9 +216,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 +232,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 +242,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 +545,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 +570,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 +583,6 @@ const wchar_t* _stdcall TerminalGetSelection(void* terminal)
}
auto returnText = wil::make_cotaskmem_string_nothrow(selectedText.c_str());
TerminalClearSelection(terminal);
return returnText.release();
}
@@ -541,16 +628,21 @@ bool HwndTerminal::_CanSendVTMouseInput() const noexcept
bool HwndTerminal::_SendMouseEvent(UINT uMsg, WPARAM wParam, LPARAM lParam) noexcept
try
{
const til::point cursorPosition{
til::point cursorPosition{
GET_X_LPARAM(lParam),
GET_Y_LPARAM(lParam),
};
const til::size fontSize{ this->_actualFont.GetSize() };
short wheelDelta{ 0 };
if (uMsg == WM_MOUSEWHEEL)
if (uMsg == WM_MOUSEWHEEL || uMsg == WM_MOUSEHWHEEL)
{
wheelDelta = HIWORD(wParam);
// If it's a *WHEEL event, it's in screen coordinates, not window (?!)
POINT coordsToTransform = cursorPosition;
ScreenToClient(_hwnd.get(), &coordsToTransform);
cursorPosition = coordsToTransform;
}
return _terminal->SendMouseEvent(cursorPosition / fontSize, uMsg, getControlKeyState(), wheelDelta);
@@ -561,20 +653,24 @@ catch (...)
return false;
}
void HwndTerminal::_SendKeyEvent(WORD vkey, WORD scanCode, bool keyDown) noexcept
void HwndTerminal::_SendKeyEvent(WORD vkey, WORD scanCode, WORD flags, bool keyDown) noexcept
try
{
const auto flags = getControlKeyState();
_terminal->SendKeyEvent(vkey, scanCode, flags, keyDown);
auto modifiers = getControlKeyState();
if (WI_IsFlagSet(flags, ENHANCED_KEY))
{
modifiers |= ControlKeyStates::EnhancedKey;
}
_terminal->SendKeyEvent(vkey, scanCode, modifiers, keyDown);
}
CATCH_LOG();
void HwndTerminal::_SendCharEvent(wchar_t ch, WORD scanCode) noexcept
void HwndTerminal::_SendCharEvent(wchar_t ch, WORD scanCode, WORD flags) noexcept
try
{
if (_terminal->IsSelectionActive())
{
_terminal->ClearSelection();
_ClearSelection();
if (ch == UNICODE_ESC)
{
// ESC should clear any selection before it triggers input.
@@ -589,21 +685,25 @@ try
return;
}
const auto flags = getControlKeyState();
_terminal->SendCharEvent(ch, scanCode, flags);
auto modifiers = getControlKeyState();
if (WI_IsFlagSet(flags, ENHANCED_KEY))
{
modifiers |= ControlKeyStates::EnhancedKey;
}
_terminal->SendCharEvent(ch, scanCode, modifiers);
}
CATCH_LOG();
void _stdcall TerminalSendKeyEvent(void* terminal, WORD vkey, WORD scanCode, bool keyDown)
void _stdcall TerminalSendKeyEvent(void* terminal, WORD vkey, WORD scanCode, WORD flags, bool keyDown)
{
const auto publicTerminal = static_cast<HwndTerminal*>(terminal);
publicTerminal->_SendKeyEvent(vkey, scanCode, keyDown);
publicTerminal->_SendKeyEvent(vkey, scanCode, flags, keyDown);
}
void _stdcall TerminalSendCharEvent(void* terminal, wchar_t ch, WORD scanCode)
void _stdcall TerminalSendCharEvent(void* terminal, wchar_t ch, WORD scanCode, WORD flags)
{
const auto publicTerminal = static_cast<HwndTerminal*>(terminal);
publicTerminal->_SendCharEvent(ch, scanCode);
publicTerminal->_SendCharEvent(ch, scanCode, flags);
}
void _stdcall DestroyTerminal(void* terminal)
@@ -632,7 +732,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

View File

@@ -34,8 +34,8 @@ __declspec(dllexport) bool _stdcall TerminalIsSelectionActive(void* terminal);
__declspec(dllexport) void _stdcall DestroyTerminal(void* terminal);
__declspec(dllexport) void _stdcall TerminalSetTheme(void* terminal, TerminalTheme theme, LPCWSTR fontFamily, short fontSize, int newDpi);
__declspec(dllexport) void _stdcall TerminalRegisterWriteCallback(void* terminal, const void __stdcall callback(wchar_t*));
__declspec(dllexport) void _stdcall TerminalSendKeyEvent(void* terminal, WORD vkey, WORD scanCode, bool keyDown);
__declspec(dllexport) void _stdcall TerminalSendCharEvent(void* terminal, wchar_t ch, WORD scanCode);
__declspec(dllexport) void _stdcall TerminalSendKeyEvent(void* terminal, WORD vkey, WORD scanCode, WORD flags, bool keyDown);
__declspec(dllexport) void _stdcall TerminalSendCharEvent(void* terminal, wchar_t ch, WORD flags, WORD scanCode);
__declspec(dllexport) void _stdcall TerminalBlinkCursor(void* terminal);
__declspec(dllexport) void _stdcall TerminalSetCursorVisible(void* terminal, const bool visible);
__declspec(dllexport) void _stdcall TerminalSetFocus(void* terminal);
@@ -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);
@@ -92,8 +93,8 @@ private:
friend void _stdcall TerminalClearSelection(void* terminal);
friend const wchar_t* _stdcall TerminalGetSelection(void* terminal);
friend bool _stdcall TerminalIsSelectionActive(void* terminal);
friend void _stdcall TerminalSendKeyEvent(void* terminal, WORD vkey, WORD scanCode, bool keyDown);
friend void _stdcall TerminalSendCharEvent(void* terminal, wchar_t ch, WORD scanCode);
friend void _stdcall TerminalSendKeyEvent(void* terminal, WORD vkey, WORD scanCode, WORD flags, bool keyDown);
friend void _stdcall TerminalSendCharEvent(void* terminal, wchar_t ch, WORD scanCode, WORD flags);
friend void _stdcall TerminalSetTheme(void* terminal, TerminalTheme theme, LPCWSTR fontFamily, short fontSize, int newDpi);
friend void _stdcall TerminalBlinkCursor(void* terminal);
friend void _stdcall TerminalSetCursorVisible(void* terminal, const bool visible);
@@ -112,11 +113,13 @@ 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;
void _SendKeyEvent(WORD vkey, WORD scanCode, bool keyDown) noexcept;
void _SendCharEvent(wchar_t ch, WORD scanCode) noexcept;
void _SendKeyEvent(WORD vkey, WORD scanCode, WORD flags, bool keyDown) noexcept;
void _SendCharEvent(wchar_t ch, WORD scanCode, WORD flags) noexcept;
// Inherited via IControlAccessibilityInfo
COORD GetFontSize() const override;

View File

@@ -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,7 +38,10 @@ 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 CloseOtherTabsKey{ "closeOtherTabs" };
static constexpr std::string_view CloseTabsAfterKey{ "closeTabsAfter" };
static constexpr std::string_view ActionKey{ "action" };
@@ -44,6 +50,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,7 +92,10 @@ namespace winrt::TerminalApp::implementation
{ UnboundKey, ShortcutAction::Invalid },
{ FindKey, ShortcutAction::Find },
{ RenameTabKey, ShortcutAction::RenameTab },
{ ExecuteCommandlineKey, ShortcutAction::ExecuteCommandline },
{ ToggleCommandPaletteKey, ShortcutAction::ToggleCommandPalette },
{ CloseOtherTabsKey, ShortcutAction::CloseOtherTabs },
{ CloseTabsAfterKey, ShortcutAction::CloseTabsAfter },
};
using ParseResult = std::tuple<IActionArgs, std::vector<::TerminalApp::SettingsLoadWarnings>>;
@@ -116,6 +127,12 @@ namespace winrt::TerminalApp::implementation
{ ShortcutAction::RenameTab, winrt::TerminalApp::implementation::RenameTabArgs::FromJson },
{ ShortcutAction::ExecuteCommandline, winrt::TerminalApp::implementation::ExecuteCommandlineArgs::FromJson },
{ ShortcutAction::CloseOtherTabs, winrt::TerminalApp::implementation::CloseOtherTabsArgs::FromJson },
{ ShortcutAction::CloseTabsAfter, winrt::TerminalApp::implementation::CloseTabsAfterArgs::FromJson },
{ ShortcutAction::Invalid, nullptr },
};
@@ -183,11 +200,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,7 +280,10 @@ 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") },
{ ShortcutAction::CloseOtherTabs, L"" }, // Intentionally omitted, must be generated by GenerateName
{ ShortcutAction::CloseTabsAfter, L"" }, // Intentionally omitted, must be generated by GenerateName
};
}();
@@ -281,5 +299,4 @@ namespace winrt::TerminalApp::implementation
const auto found = GeneratedActionNames.find(_Action);
return found != GeneratedActionNames.end() ? found->second : L"";
}
}

View File

@@ -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,34 @@ 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"";
}
winrt::hstring CloseOtherTabsArgs::GenerateName() const
{
// "Close tabs other than index {0}"
return winrt::hstring{
fmt::format(std::wstring_view(RS_(L"CloseOtherTabsCommandKey")),
_Index)
};
}
winrt::hstring CloseTabsAfterArgs::GenerateName() const
{
// "Close tabs after index {0}"
return winrt::hstring{
fmt::format(std::wstring_view(RS_(L"CloseTabsAfterCommandKey")),
_Index)
};
}
}

View File

@@ -17,12 +17,17 @@
#include "OpenSettingsArgs.g.h"
#include "SetTabColorArgs.g.h"
#include "RenameTabArgs.g.h"
#include "ExecuteCommandlineArgs.g.h"
#include "CloseOtherTabsArgs.g.h"
#include "CloseTabsAfterArgs.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 +36,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 +79,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 +111,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 +165,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 +193,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 +228,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 +263,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 +298,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 +327,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 +355,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 +386,99 @@ 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, {} };
}
};
struct CloseOtherTabsArgs : public CloseOtherTabsArgsT<CloseOtherTabsArgs>
{
CloseOtherTabsArgs() = default;
GETSET_PROPERTY(uint32_t, Index, 0);
static constexpr std::string_view IndexKey{ "index" };
public:
hstring GenerateName() const;
bool Equals(const IActionArgs& other)
{
auto otherAsUs = other.try_as<CloseOtherTabsArgs>();
if (otherAsUs)
{
return otherAsUs->_Index == _Index;
}
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<CloseOtherTabsArgs>();
JsonUtils::GetValueForKey(json, IndexKey, args->_Index);
return { *args, {} };
}
};
struct CloseTabsAfterArgs : public CloseTabsAfterArgsT<CloseTabsAfterArgs>
{
CloseTabsAfterArgs() = default;
GETSET_PROPERTY(uint32_t, Index, 0);
static constexpr std::string_view IndexKey{ "index" };
public:
hstring GenerateName() const;
bool Equals(const IActionArgs& other)
{
auto otherAsUs = other.try_as<CloseTabsAfterArgs>();
if (otherAsUs)
{
return otherAsUs->_Index == _Index;
}
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<CloseTabsAfterArgs>();
JsonUtils::GetValueForKey(json, IndexKey, args->_Index);
return { *args, {} };
}
};
}
namespace winrt::TerminalApp::factory_implementation

View File

@@ -115,4 +115,19 @@ namespace TerminalApp
{
String Title { get; };
};
[default_interface] runtimeclass ExecuteCommandlineArgs : IActionArgs
{
String Commandline;
};
[default_interface] runtimeclass CloseOtherTabsArgs : IActionArgs
{
UInt32 Index { get; };
};
[default_interface] runtimeclass CloseTabsAfterArgs : IActionArgs
{
UInt32 Index { get; };
};
}

View File

@@ -11,9 +11,9 @@ 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;
using namespace winrt::Microsoft::Terminal::TerminalControl;
using namespace winrt::Microsoft::Terminal::TerminalConnection;
using namespace ::TerminalApp;
@@ -334,4 +334,65 @@ 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);
}
}
}
void TerminalPage::_HandleCloseOtherTabs(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& actionArgs)
{
if (const auto& realArgs = actionArgs.ActionArgs().try_as<TerminalApp::CloseOtherTabsArgs>())
{
uint32_t index = realArgs.Index();
// Remove tabs after the current one
while (_tabs.Size() > index + 1)
{
_RemoveTabViewItemByIndex(_tabs.Size() - 1);
}
// Remove all of them leading up to the selected tab
while (_tabs.Size() > 1)
{
_RemoveTabViewItemByIndex(0);
}
actionArgs.Handled(true);
}
}
void TerminalPage::_HandleCloseTabsAfter(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& actionArgs)
{
if (const auto& realArgs = actionArgs.ActionArgs().try_as<TerminalApp::CloseTabsAfterArgs>())
{
uint32_t index = realArgs.Index();
// Remove tabs after the current one
while (_tabs.Size() > index + 1)
{
_RemoveTabViewItemByIndex(_tabs.Size() - 1);
}
// TODO:GH#7182 For whatever reason, if you run this action
// when the tab that's currently focused is _before_ the `index`
// param, then the tabs will expand to fill the entire width of the
// tab row, until you mouse over them. Probably has something to do
// with tabs not resizing down until there's a mouse exit event.
actionArgs.Handled(true);
}
}
}

View File

@@ -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;
}

View File

@@ -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 };

View File

@@ -9,12 +9,12 @@
using namespace winrt::Microsoft::Terminal;
using namespace winrt::TerminalApp;
using namespace winrt::Microsoft::Terminal::Settings;
using namespace winrt::Microsoft::Terminal::TerminalControl;
namespace winrt::TerminalApp::implementation
{
void AppKeyBindings::SetKeyBinding(const TerminalApp::ActionAndArgs& actionAndArgs,
const Settings::KeyChord& chord)
const KeyChord& chord)
{
_keyShortcuts[chord] = actionAndArgs;
}
@@ -25,7 +25,7 @@ namespace winrt::TerminalApp::implementation
// - chord: the keystroke to remove the action for.
// Return Value:
// - <none>
void AppKeyBindings::ClearKeyBinding(const Settings::KeyChord& chord)
void AppKeyBindings::ClearKeyBinding(const KeyChord& chord)
{
_keyShortcuts.erase(chord);
}
@@ -67,7 +67,7 @@ namespace winrt::TerminalApp::implementation
return { nullptr };
}
bool AppKeyBindings::TryKeyChord(const Settings::KeyChord& kc)
bool AppKeyBindings::TryKeyChord(const KeyChord& kc)
{
const auto keyIter = _keyShortcuts.find(kc);
if (keyIter != _keyShortcuts.end())
@@ -87,19 +87,19 @@ namespace winrt::TerminalApp::implementation
// - Takes the KeyModifier flags from Terminal and maps them to the WinRT types which are used by XAML
// Return Value:
// - a Windows::System::VirtualKeyModifiers object with the flags of which modifiers used.
Windows::System::VirtualKeyModifiers AppKeyBindings::ConvertVKModifiers(Settings::KeyModifiers modifiers)
Windows::System::VirtualKeyModifiers AppKeyBindings::ConvertVKModifiers(KeyModifiers modifiers)
{
Windows::System::VirtualKeyModifiers keyModifiers = Windows::System::VirtualKeyModifiers::None;
if (WI_IsFlagSet(modifiers, Settings::KeyModifiers::Ctrl))
if (WI_IsFlagSet(modifiers, KeyModifiers::Ctrl))
{
keyModifiers |= Windows::System::VirtualKeyModifiers::Control;
}
if (WI_IsFlagSet(modifiers, Settings::KeyModifiers::Shift))
if (WI_IsFlagSet(modifiers, KeyModifiers::Shift))
{
keyModifiers |= Windows::System::VirtualKeyModifiers::Shift;
}
if (WI_IsFlagSet(modifiers, Settings::KeyModifiers::Alt))
if (WI_IsFlagSet(modifiers, KeyModifiers::Alt))
{
// note: Menu is the Alt VK_MENU
keyModifiers |= Windows::System::VirtualKeyModifiers::Menu;

View File

@@ -20,10 +20,10 @@ namespace winrt::TerminalApp::implementation
{
struct KeyChordHash
{
std::size_t operator()(const winrt::Microsoft::Terminal::Settings::KeyChord& key) const
std::size_t operator()(const winrt::Microsoft::Terminal::TerminalControl::KeyChord& key) const
{
std::hash<int32_t> keyHash;
std::hash<winrt::Microsoft::Terminal::Settings::KeyModifiers> modifiersHash;
std::hash<winrt::Microsoft::Terminal::TerminalControl::KeyModifiers> modifiersHash;
std::size_t hashedKey = keyHash(key.Vkey());
std::size_t hashedMods = modifiersHash(key.Modifiers());
return hashedKey ^ hashedMods;
@@ -32,7 +32,7 @@ namespace winrt::TerminalApp::implementation
struct KeyChordEquality
{
bool operator()(const winrt::Microsoft::Terminal::Settings::KeyChord& lhs, const winrt::Microsoft::Terminal::Settings::KeyChord& rhs) const
bool operator()(const winrt::Microsoft::Terminal::TerminalControl::KeyChord& lhs, const winrt::Microsoft::Terminal::TerminalControl::KeyChord& rhs) const
{
return lhs.Modifiers() == rhs.Modifiers() && lhs.Vkey() == rhs.Vkey();
}
@@ -42,15 +42,15 @@ namespace winrt::TerminalApp::implementation
{
AppKeyBindings() = default;
bool TryKeyChord(winrt::Microsoft::Terminal::Settings::KeyChord const& kc);
bool TryKeyChord(winrt::Microsoft::Terminal::TerminalControl::KeyChord const& kc);
void SetKeyBinding(TerminalApp::ActionAndArgs const& actionAndArgs,
winrt::Microsoft::Terminal::Settings::KeyChord const& chord);
void ClearKeyBinding(winrt::Microsoft::Terminal::Settings::KeyChord const& chord);
Microsoft::Terminal::Settings::KeyChord GetKeyBindingForAction(TerminalApp::ShortcutAction const& action);
Microsoft::Terminal::Settings::KeyChord GetKeyBindingForActionWithArgs(TerminalApp::ActionAndArgs const& actionAndArgs);
winrt::Microsoft::Terminal::TerminalControl::KeyChord const& chord);
void ClearKeyBinding(winrt::Microsoft::Terminal::TerminalControl::KeyChord const& chord);
Microsoft::Terminal::TerminalControl::KeyChord GetKeyBindingForAction(TerminalApp::ShortcutAction const& action);
Microsoft::Terminal::TerminalControl::KeyChord GetKeyBindingForActionWithArgs(TerminalApp::ActionAndArgs const& actionAndArgs);
static Windows::System::VirtualKeyModifiers ConvertVKModifiers(winrt::Microsoft::Terminal::Settings::KeyModifiers modifiers);
static Windows::System::VirtualKeyModifiers ConvertVKModifiers(winrt::Microsoft::Terminal::TerminalControl::KeyModifiers modifiers);
// Defined in AppKeyBindingsSerialization.cpp
std::vector<::TerminalApp::SettingsLoadWarnings> LayerJson(const Json::Value& json);
@@ -59,7 +59,7 @@ namespace winrt::TerminalApp::implementation
void SetDispatch(const winrt::TerminalApp::ShortcutActionDispatch& dispatch);
private:
std::unordered_map<winrt::Microsoft::Terminal::Settings::KeyChord, TerminalApp::ActionAndArgs, KeyChordHash, KeyChordEquality> _keyShortcuts;
std::unordered_map<winrt::Microsoft::Terminal::TerminalControl::KeyChord, TerminalApp::ActionAndArgs, KeyChordHash, KeyChordEquality> _keyShortcuts;
winrt::TerminalApp::ShortcutActionDispatch _dispatch{ nullptr };

View File

@@ -5,15 +5,15 @@ import "../ShortcutActionDispatch.idl";
namespace TerminalApp
{
[default_interface] runtimeclass AppKeyBindings : Microsoft.Terminal.Settings.IKeyBindings
[default_interface] runtimeclass AppKeyBindings : Microsoft.Terminal.TerminalControl.IKeyBindings
{
AppKeyBindings();
void SetKeyBinding(ActionAndArgs actionAndArgs, Microsoft.Terminal.Settings.KeyChord chord);
void ClearKeyBinding(Microsoft.Terminal.Settings.KeyChord chord);
void SetKeyBinding(ActionAndArgs actionAndArgs, Microsoft.Terminal.TerminalControl.KeyChord chord);
void ClearKeyBinding(Microsoft.Terminal.TerminalControl.KeyChord chord);
Microsoft.Terminal.Settings.KeyChord GetKeyBindingForAction(ShortcutAction action);
Microsoft.Terminal.Settings.KeyChord GetKeyBindingForActionWithArgs(ActionAndArgs actionAndArgs);
Microsoft.Terminal.TerminalControl.KeyChord GetKeyBindingForAction(ShortcutAction action);
Microsoft.Terminal.TerminalControl.KeyChord GetKeyBindingForActionWithArgs(ActionAndArgs actionAndArgs);
void SetDispatch(ShortcutActionDispatch dispatch);
}

View File

@@ -12,9 +12,8 @@
#include "KeyChordSerialization.h"
#include "Utils.h"
#include "JsonUtils.h"
#include <winrt/Microsoft.Terminal.Settings.h>
using namespace winrt::Microsoft::Terminal::Settings;
using namespace winrt::Microsoft::Terminal::TerminalControl;
using namespace winrt::TerminalApp;
static constexpr std::string_view KeysKey{ "keys" };

View File

@@ -15,7 +15,6 @@ using namespace winrt::Windows::UI::Xaml::Controls;
using namespace winrt::Windows::UI::Core;
using namespace winrt::Windows::System;
using namespace winrt::Microsoft::Terminal;
using namespace winrt::Microsoft::Terminal::Settings;
using namespace winrt::Microsoft::Terminal::TerminalControl;
using namespace ::TerminalApp;
@@ -474,7 +473,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 +503,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 +518,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 +973,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 +983,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

View File

@@ -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();

View File

@@ -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();

View File

@@ -17,7 +17,6 @@
#include "WslDistroGenerator.h"
#include "AzureCloudShellGenerator.h"
using namespace winrt::Microsoft::Terminal::Settings;
using namespace ::TerminalApp;
using namespace winrt::Microsoft::Terminal::TerminalControl;
using namespace winrt::TerminalApp;
@@ -93,7 +92,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() };
}

View File

@@ -54,12 +54,12 @@ public:
static const CascadiaSettings& GetCurrentAppSettings();
std::tuple<GUID, winrt::Microsoft::Terminal::Settings::TerminalSettings> BuildSettings(const winrt::TerminalApp::NewTerminalArgs& newTerminalArgs) const;
winrt::Microsoft::Terminal::Settings::TerminalSettings BuildSettings(GUID profileGuid) const;
std::tuple<GUID, winrt::TerminalApp::TerminalSettings> BuildSettings(const winrt::TerminalApp::NewTerminalArgs& newTerminalArgs) const;
winrt::TerminalApp::TerminalSettings BuildSettings(GUID profileGuid) const;
GlobalAppSettings& GlobalSettings();
std::basic_string_view<Profile> GetProfiles() const noexcept;
gsl::span<const Profile> GetProfiles() const noexcept;
winrt::TerminalApp::AppKeyBindings GetKeybindings() const noexcept;

View File

@@ -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));
}
}

View File

@@ -10,8 +10,7 @@
using namespace ::Microsoft::Console;
using namespace TerminalApp;
using namespace winrt::Microsoft::Terminal::Settings;
using namespace winrt::Microsoft::Terminal::TerminalControl;
using namespace winrt::TerminalApp;
static constexpr std::string_view NameKey{ "name" };
static constexpr std::string_view ForegroundKey{ "foreground" };
@@ -105,9 +104,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 +124,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 +176,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);
}

View File

@@ -15,8 +15,7 @@ Author(s):
--*/
#pragma once
#include <winrt/Microsoft.Terminal.Settings.h>
#include <winrt/Microsoft.Terminal.TerminalControl.h>
#include "TerminalSettings.h"
#include "../../inc/conattrs.hpp"
// fwdecl unittest classes
@@ -38,7 +37,7 @@ public:
ColorScheme(std::wstring name, til::color defaultFg, til::color defaultBg, til::color cursorColor);
~ColorScheme();
void ApplyScheme(winrt::Microsoft::Terminal::Settings::TerminalSettings terminalSettings) const;
void ApplyScheme(winrt::TerminalApp::TerminalSettings terminalSettings) const;
static ColorScheme FromJson(const Json::Value& json);
bool ShouldBeLayered(const Json::Value& json) const;

View File

@@ -7,10 +7,11 @@
#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 +36,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);
}
}

View File

@@ -5,7 +5,6 @@
#include "CommandPalette.h"
#include "CommandPalette.g.cpp"
#include <winrt/Microsoft.Terminal.Settings.h>
using namespace winrt;
using namespace winrt::TerminalApp;
@@ -187,14 +186,16 @@ namespace winrt::TerminalApp::implementation
{
const auto actionAndArgs = command.Action();
_dispatch.DoAction(actionAndArgs);
_close();
TraceLoggingWrite(
g_hTerminalAppProvider, // handle to TerminalApp tracelogging provider
"CommandPaletteDispatchedAction",
TraceLoggingDescription("Event emitted when the user selects an action in the Command Palette"),
TraceLoggingUInt32(_searchBox().Text().size(), "SearchTextLength", "Number of characters in the search string"),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
_close();
}
}
@@ -271,16 +272,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 +305,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 +345,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()]);
}
}

View File

@@ -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();

View File

@@ -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 -->

View File

@@ -7,11 +7,10 @@
#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 +43,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 +133,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 +198,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:

View File

@@ -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
@@ -51,7 +46,7 @@ public:
static GlobalAppSettings FromJson(const Json::Value& json);
void LayerJson(const Json::Value& json);
void ApplyToSettings(winrt::Microsoft::Terminal::Settings::TerminalSettings& settings) const noexcept;
void ApplyToSettings(winrt::TerminalApp::TerminalSettings& settings) const noexcept;
std::vector<TerminalApp::SettingsLoadWarnings> GetKeybindingsWarnings() const;
@@ -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;
};

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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

View File

@@ -4,7 +4,7 @@
#include "pch.h"
#include "KeyChordSerialization.h"
using namespace winrt::Microsoft::Terminal::Settings;
using namespace winrt::Microsoft::Terminal::TerminalControl;
static constexpr std::wstring_view CTRL_KEY{ L"ctrl" };
static constexpr std::wstring_view SHIFT_KEY{ L"shift" };
@@ -99,7 +99,7 @@ static const std::unordered_map<std::wstring_view, int32_t> vkeyNamePairs {
// - hstr: the string to parse into a keychord.
// Return Value:
// - a newly constructed KeyChord
winrt::Microsoft::Terminal::Settings::KeyChord KeyChordSerialization::FromString(const winrt::hstring& hstr)
KeyChord KeyChordSerialization::FromString(const winrt::hstring& hstr)
{
std::wstring wstr{ hstr };
@@ -201,7 +201,7 @@ winrt::Microsoft::Terminal::Settings::KeyChord KeyChordSerialization::FromString
}
}
return winrt::Microsoft::Terminal::Settings::KeyChord{ modifiers, vkey };
return KeyChord{ modifiers, vkey };
}
// Function Description:

View File

@@ -2,11 +2,11 @@
// Licensed under the MIT license.
#pragma once
#include <winrt/Microsoft.Terminal.Settings.h>
#include <winrt/Microsoft.Terminal.TerminalControl.h>
class KeyChordSerialization final
{
public:
static winrt::Microsoft::Terminal::Settings::KeyChord FromString(const winrt::hstring& str);
static winrt::hstring ToString(const winrt::Microsoft::Terminal::Settings::KeyChord& chord);
static winrt::Microsoft::Terminal::TerminalControl::KeyChord FromString(const winrt::hstring& str);
static winrt::hstring ToString(const winrt::Microsoft::Terminal::TerminalControl::KeyChord& chord);
};

View File

@@ -7,11 +7,11 @@
#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;
using namespace winrt::Windows::UI::Xaml::Media;
using namespace winrt::Microsoft::Terminal::Settings;
using namespace winrt::Microsoft::Terminal::TerminalControl;
using namespace winrt::Microsoft::Terminal::TerminalConnection;
using namespace winrt::TerminalApp;
@@ -921,6 +921,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

View File

@@ -51,7 +51,7 @@ public:
void ClearActive();
void SetActive();
void UpdateSettings(const winrt::Microsoft::Terminal::Settings::TerminalSettings& settings,
void UpdateSettings(const winrt::TerminalApp::TerminalSettings& settings,
const GUID& profile);
void ResizeContent(const winrt::Windows::Foundation::Size& newSize);
void Relayout();
@@ -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();

View File

@@ -9,9 +9,11 @@
#include <DefaultSettings.h>
#include "LegacyProfileGeneratorNamespaces.h"
#include "TerminalSettingsSerializationHelpers.h"
using namespace TerminalApp;
using namespace winrt::Microsoft::Terminal::Settings;
using namespace winrt::TerminalApp;
using namespace winrt::Microsoft::Terminal::TerminalControl;
using namespace winrt::Windows::UI::Xaml;
using namespace ::Microsoft::Console;
@@ -52,57 +54,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 +199,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 +300,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 +316,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 +344,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 +366,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 +636,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 +701,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 +716,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;
}

View File

@@ -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
{
@@ -52,7 +46,7 @@ public:
~Profile();
winrt::Microsoft::Terminal::Settings::TerminalSettings CreateTerminalSettings(const std::unordered_map<std::wstring, ColorScheme>& schemes) const;
winrt::TerminalApp::TerminalSettings CreateTerminalSettings(const std::unordered_map<std::wstring, ColorScheme>& schemes) const;
Json::Value GenerateStub() const;
static Profile FromJson(const Json::Value& json);
@@ -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;
@@ -144,7 +122,7 @@ private:
bool _snapOnInput;
bool _altGrAliasing;
uint32_t _cursorHeight;
winrt::Microsoft::Terminal::Settings::CursorStyle _cursorShape;
winrt::Microsoft::Terminal::TerminalControl::CursorStyle _cursorShape;
std::wstring _commandline;
std::wstring _fontFace;
@@ -159,13 +137,13 @@ 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::TerminalControl::ScrollbarState> _scrollbarState;
CloseOnExitMode _closeOnExitMode;
std::wstring _padding;
std::optional<std::wstring> _icon;
winrt::Microsoft::Terminal::Settings::TextAntialiasingMode _antialiasingMode;
winrt::Microsoft::Terminal::TerminalControl::TextAntialiasingMode _antialiasingMode;
friend class TerminalAppLocalTests::SettingsTests;
friend class TerminalAppLocalTests::ProfileTests;

View File

@@ -538,11 +538,23 @@
</data>
<data name="RenameTabCommandKey" xml:space="preserve">
<value>Rename tab to "{0}"</value>
<comment>{0} will be replaced with user-defined string</comment>
<comment>{0} will be replaced with a user-defined string</comment>
</data>
<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 a user-defined commandline</comment>
</data>
<data name="CloseOtherTabsCommandKey" xml:space="preserve">
<value>Close tabs other than index {0}</value>
<comment>{0} will be replaced with a number</comment>
</data>
<data name="CloseTabsAfterCommandKey" xml:space="preserve">
<value>Close tabs after index {0}</value>
<comment>{0} will be replaced with a number</comment>
</data>
<data name="CrimsonColorButton.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
<value>Crimson</value>
</data>

View 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;
};
};

View File

@@ -194,6 +194,21 @@ namespace winrt::TerminalApp::implementation
_RenameTabHandlers(*this, *eventArgs);
break;
}
case ShortcutAction::ExecuteCommandline:
{
_ExecuteCommandlineHandlers(*this, *eventArgs);
break;
}
case ShortcutAction::CloseOtherTabs:
{
_CloseOtherTabsHandlers(*this, *eventArgs);
break;
}
case ShortcutAction::CloseTabsAfter:
{
_CloseTabsAfterHandlers(*this, *eventArgs);
break;
}
default:
return false;
}

View File

@@ -54,6 +54,9 @@ 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);
TYPED_EVENT(CloseOtherTabs, TerminalApp::ShortcutActionDispatch, TerminalApp::ActionEventArgs);
TYPED_EVENT(CloseTabsAfter, TerminalApp::ShortcutActionDispatch, TerminalApp::ActionEventArgs);
// clang-format on
private:

View File

@@ -35,11 +35,14 @@ namespace TerminalApp
ToggleFocusMode,
ToggleFullscreen,
ToggleAlwaysOnTop,
OpenSettings,
SetTabColor,
OpenTabColorPicker,
OpenSettings,
RenameTab,
ToggleCommandPalette
ExecuteCommandline,
ToggleCommandPalette,
CloseOtherTabs,
CloseTabsAfter
};
[default_interface] runtimeclass ActionAndArgs {
@@ -84,5 +87,8 @@ 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;
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, ActionEventArgs> CloseOtherTabs;
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, ActionEventArgs> CloseTabsAfter;
}
}

View File

@@ -12,7 +12,6 @@
using namespace winrt;
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Core;
using namespace winrt::Microsoft::Terminal::Settings;
using namespace winrt::Microsoft::Terminal::TerminalControl;
using namespace winrt::Windows::System;
@@ -875,6 +874,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<>);

View File

@@ -40,12 +40,13 @@ 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);
void NavigateFocus(const winrt::TerminalApp::Direction& direction);
void UpdateSettings(const winrt::Microsoft::Terminal::Settings::TerminalSettings& settings, const GUID& profile);
void UpdateSettings(const winrt::TerminalApp::TerminalSettings& settings, const GUID& profile);
winrt::hstring GetActiveTitle() const;
void Shutdown();

View File

@@ -67,7 +67,6 @@
<!-- The midl compiler however, _will_ aggregate our winmd dependencies
somehow. So make sure to only include top-level dependencies here (don't
include Settings and Connection, since Control will include them for us) -->
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalSettings\TerminalSettings.vcxproj" />
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalConnection\TerminalConnection.vcxproj" />
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalControl\TerminalControl.vcxproj" />
<!-- Reference TerminalAppLib here, so we can use it's TerminalApp.winmd as

View File

@@ -32,7 +32,6 @@ using namespace winrt::Windows::UI::Text;
using namespace winrt::Microsoft::Terminal;
using namespace winrt::Microsoft::Terminal::TerminalControl;
using namespace winrt::Microsoft::Terminal::TerminalConnection;
using namespace winrt::Microsoft::Terminal::Settings;
using namespace ::TerminalApp;
using namespace ::Microsoft::Console;
@@ -46,7 +45,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();
}
@@ -166,7 +166,28 @@ namespace winrt::TerminalApp::implementation
_newTabButton.Click([weakThis{ get_weak() }](auto&&, auto&&) {
if (auto page{ weakThis.get() })
{
page->_OpenNewTab(nullptr);
// if alt is pressed, open a pane
const CoreWindow window = CoreWindow::GetForCurrentThread();
const auto rAltState = window.GetKeyState(VirtualKey::RightMenu);
const auto lAltState = window.GetKeyState(VirtualKey::LeftMenu);
const bool altPressed = WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) ||
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);
// Check for DebugTap
bool debugTap = page->_settings->GlobalSettings().DebugFeaturesEnabled() &&
WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) &&
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);
if (altPressed && !debugTap)
{
page->_SplitPane(TerminalApp::SplitState::Automatic,
TerminalApp::SplitType::Manual,
nullptr);
}
else
{
page->_OpenNewTab(nullptr);
}
}
});
_tabView.SelectionChanged({ this, &TerminalPage::_OnTabSelectionChanged });
@@ -223,7 +244,7 @@ namespace winrt::TerminalApp::implementation
if (_startupState == StartupState::NotInitialized)
{
_startupState = StartupState::InStartup;
if (_startupActions.empty())
if (_startupActions.Size() == 0)
{
_OpenNewTab(nullptr);
@@ -231,22 +252,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 +282,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();
}
}
@@ -590,7 +625,7 @@ namespace winrt::TerminalApp::implementation
// currently displayed, it will be shown.
// Arguments:
// - settings: the TerminalSettings object to use to create the TerminalControl with.
void TerminalPage::_CreateNewTabFromSettings(GUID profileGuid, TerminalSettings settings)
void TerminalPage::_CreateNewTabFromSettings(GUID profileGuid, TerminalApp::TerminalSettings settings)
{
// Initialize the new tab
@@ -691,7 +726,7 @@ namespace winrt::TerminalApp::implementation
// Return value:
// - the desired connection
TerminalConnection::ITerminalConnection TerminalPage::_CreateConnectionFromSettings(GUID profileGuid,
winrt::Microsoft::Terminal::Settings::TerminalSettings settings)
TerminalApp::TerminalSettings settings)
{
const auto* const profile = _settings->FindProfile(profileGuid);
@@ -857,6 +892,9 @@ namespace winrt::TerminalApp::implementation
_actionDispatch->SetTabColor({ this, &TerminalPage::_HandleSetTabColor });
_actionDispatch->OpenTabColorPicker({ this, &TerminalPage::_HandleOpenTabColorPicker });
_actionDispatch->RenameTab({ this, &TerminalPage::_HandleRenameTab });
_actionDispatch->ExecuteCommandline({ this, &TerminalPage::_HandleExecuteCommandline });
_actionDispatch->CloseOtherTabs({ this, &TerminalPage::_HandleCloseOtherTabs });
_actionDispatch->CloseTabsAfter({ this, &TerminalPage::_HandleCloseTabsAfter });
}
// Method Description:
@@ -1168,7 +1206,7 @@ namespace winrt::TerminalApp::implementation
}
}
winrt::Microsoft::Terminal::TerminalControl::TermControl TerminalPage::_GetActiveControl()
TermControl TerminalPage::_GetActiveControl()
{
if (auto index{ _GetFocusedTabIndex() })
{
@@ -1331,7 +1369,7 @@ namespace winrt::TerminalApp::implementation
try
{
auto focusedTab = _GetStrongTabImpl(*indexOpt);
winrt::Microsoft::Terminal::Settings::TerminalSettings controlSettings;
TerminalApp::TerminalSettings controlSettings;
GUID realGuid;
bool profileFound = false;
@@ -1364,19 +1402,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 };
@@ -1464,21 +1503,21 @@ namespace winrt::TerminalApp::implementation
// Return Value:
// - a string representation of the key modifiers for the shortcut
//NOTE: This needs to be localized with https://github.com/microsoft/terminal/issues/794 if XAML framework issue not resolved before then
static std::wstring _FormatOverrideShortcutText(Settings::KeyModifiers modifiers)
static std::wstring _FormatOverrideShortcutText(KeyModifiers modifiers)
{
std::wstring buffer{ L"" };
if (WI_IsFlagSet(modifiers, Settings::KeyModifiers::Ctrl))
if (WI_IsFlagSet(modifiers, KeyModifiers::Ctrl))
{
buffer += L"Ctrl+";
}
if (WI_IsFlagSet(modifiers, Settings::KeyModifiers::Shift))
if (WI_IsFlagSet(modifiers, KeyModifiers::Shift))
{
buffer += L"Shift+";
}
if (WI_IsFlagSet(modifiers, Settings::KeyModifiers::Alt))
if (WI_IsFlagSet(modifiers, KeyModifiers::Alt))
{
buffer += L"Alt+";
}
@@ -1492,7 +1531,7 @@ namespace winrt::TerminalApp::implementation
// Arguments:
// - MenuFlyoutItem that will be displayed, and a KeyChord to map an accelerator
void TerminalPage::_SetAcceleratorForMenuItem(WUX::Controls::MenuFlyoutItem& menuItem,
const winrt::Microsoft::Terminal::Settings::KeyChord& keyChord)
const KeyChord& keyChord)
{
#ifdef DEP_MICROSOFT_UI_XAML_708_FIXED
// work around https://github.com/microsoft/microsoft-ui-xaml/issues/708 in case of VK_OEM_COMMA
@@ -1551,7 +1590,7 @@ namespace winrt::TerminalApp::implementation
// Arguments:
// - copiedData: the new string content to place on the clipboard.
winrt::fire_and_forget TerminalPage::_CopyToClipboardHandler(const IInspectable /*sender*/,
const winrt::Microsoft::Terminal::TerminalControl::CopyToClipboardEventArgs copiedData)
const CopyToClipboardEventArgs copiedData)
{
co_await winrt::resume_foreground(Dispatcher(), CoreDispatcherPriority::High);
@@ -1778,7 +1817,7 @@ namespace winrt::TerminalApp::implementation
tab->SetFocused(true);
// Raise an event that our title changed
_titleChangeHandlers(*this, Title());
_titleChangeHandlers(*this, tab->GetActiveTitle());
// Raise an event that our titlebar color changed
std::optional<Windows::UI::Color> color = tab->GetTabColor();
@@ -1930,9 +1969,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 +2235,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*/)
{

View File

@@ -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();
@@ -117,8 +118,8 @@ namespace winrt::TerminalApp::implementation
void _CreateNewTabFlyout();
void _OpenNewTabDropdown();
void _OpenNewTab(const winrt::TerminalApp::NewTerminalArgs& newTerminalArgs);
void _CreateNewTabFromSettings(GUID profileGuid, winrt::Microsoft::Terminal::Settings::TerminalSettings settings);
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _CreateConnectionFromSettings(GUID profileGuid, winrt::Microsoft::Terminal::Settings::TerminalSettings settings);
void _CreateNewTabFromSettings(GUID profileGuid, TerminalApp::TerminalSettings settings);
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _CreateConnectionFromSettings(GUID profileGuid, TerminalApp::TerminalSettings settings);
void _SettingsButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
void _FeedbackButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
@@ -159,7 +160,7 @@ namespace winrt::TerminalApp::implementation
void _SplitPane(const winrt::TerminalApp::SplitState splitType, const winrt::TerminalApp::SplitType splitMode = winrt::TerminalApp::SplitType::Manual, const winrt::TerminalApp::NewTerminalArgs& newTerminalArgs = nullptr);
void _ResizePane(const Direction& direction);
void _ScrollPage(int delta);
void _SetAcceleratorForMenuItem(Windows::UI::Xaml::Controls::MenuFlyoutItem& menuItem, const winrt::Microsoft::Terminal::Settings::KeyChord& keyChord);
void _SetAcceleratorForMenuItem(Windows::UI::Xaml::Controls::MenuFlyoutItem& menuItem, const winrt::Microsoft::Terminal::TerminalControl::KeyChord& keyChord);
winrt::fire_and_forget _CopyToClipboardHandler(const IInspectable sender, const winrt::Microsoft::Terminal::TerminalControl::CopyToClipboardEventArgs copiedData);
winrt::fire_and_forget _PasteFromClipboardHandler(const IInspectable sender,
@@ -221,7 +222,10 @@ 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);
void _HandleCloseOtherTabs(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleCloseTabsAfter(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
// Make sure to hook new actions up in _RegisterActionCallbacks!
#pragma endregion

View File

@@ -6,7 +6,7 @@
#include "TerminalSettings.g.cpp"
namespace winrt::Microsoft::Terminal::Settings::implementation
namespace winrt::TerminalApp::implementation
{
uint32_t TerminalSettings::GetColorTableEntry(int32_t index) const noexcept
{

View File

@@ -19,7 +19,7 @@ Author(s):
#include <DefaultSettings.h>
#include <conattrs.hpp>
namespace winrt::Microsoft::Terminal::Settings::implementation
namespace winrt::TerminalApp::implementation
{
struct TerminalSettings : TerminalSettingsT<TerminalSettings>
{
@@ -49,7 +49,7 @@ namespace winrt::Microsoft::Terminal::Settings::implementation
GETSET_PROPERTY(bool, SnapOnInput, true);
GETSET_PROPERTY(bool, AltGrAliasing, true);
GETSET_PROPERTY(uint32_t, CursorColor, DEFAULT_CURSOR_COLOR);
GETSET_PROPERTY(CursorStyle, CursorShape, CursorStyle::Vintage);
GETSET_PROPERTY(Microsoft::Terminal::TerminalControl::CursorStyle, CursorShape, Microsoft::Terminal::TerminalControl::CursorStyle::Vintage);
GETSET_PROPERTY(uint32_t, CursorHeight, DEFAULT_CURSOR_HEIGHT);
GETSET_PROPERTY(hstring, WordDelimiters, DEFAULT_WORD_DELIMITERS);
GETSET_PROPERTY(bool, CopyOnSelect, false);
@@ -78,7 +78,7 @@ namespace winrt::Microsoft::Terminal::Settings::implementation
BackgroundImageVerticalAlignment,
winrt::Windows::UI::Xaml::VerticalAlignment::Center);
GETSET_PROPERTY(IKeyBindings, KeyBindings, nullptr);
GETSET_PROPERTY(Microsoft::Terminal::TerminalControl::IKeyBindings, KeyBindings, nullptr);
GETSET_PROPERTY(hstring, Commandline);
GETSET_PROPERTY(hstring, StartingDirectory);
@@ -86,9 +86,9 @@ namespace winrt::Microsoft::Terminal::Settings::implementation
GETSET_PROPERTY(bool, SuppressApplicationTitle);
GETSET_PROPERTY(hstring, EnvironmentVariables);
GETSET_PROPERTY(ScrollbarState, ScrollState, ScrollbarState::Visible);
GETSET_PROPERTY(Microsoft::Terminal::TerminalControl::ScrollbarState, ScrollState, Microsoft::Terminal::TerminalControl::ScrollbarState::Visible);
GETSET_PROPERTY(TextAntialiasingMode, AntialiasingMode, TextAntialiasingMode::Grayscale);
GETSET_PROPERTY(Microsoft::Terminal::TerminalControl::TextAntialiasingMode, AntialiasingMode, Microsoft::Terminal::TerminalControl::TextAntialiasingMode::Grayscale);
GETSET_PROPERTY(bool, RetroTerminalEffect, false);
GETSET_PROPERTY(bool, ForceFullRepaintRendering, false);
@@ -102,7 +102,7 @@ namespace winrt::Microsoft::Terminal::Settings::implementation
};
}
namespace winrt::Microsoft::Terminal::Settings::factory_implementation
namespace winrt::TerminalApp::factory_implementation
{
BASIC_FACTORY(TerminalSettings);
}

View File

@@ -1,10 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import "ICoreSettings.idl";
import "IControlSettings.idl";
namespace Microsoft.Terminal.Settings
namespace TerminalApp
{
// Class Description:
// TerminalSettings encapsulates all settings that control the
@@ -15,8 +12,8 @@ namespace Microsoft.Terminal.Settings
// The TerminalControl will pull settings it requires from this object,
// and pass along the Core properties to the terminal core.
[default_interface]
runtimeclass TerminalSettings : ICoreSettings,
IControlSettings
runtimeclass TerminalSettings : Microsoft.Terminal.TerminalControl.ICoreSettings,
Microsoft.Terminal.TerminalControl.IControlSettings
{
TerminalSettings();
};

View 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.TerminalControl.h>
#include <winrt/TerminalApp.h>
JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::TerminalControl::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::TerminalControl::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::TerminalControl::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 },
};
};

View File

@@ -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();
}

View File

@@ -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

View File

@@ -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",

View File

@@ -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" />
@@ -116,6 +117,9 @@
<ClInclude Include="../AzureCloudShellGenerator.h" />
<ClInclude Include="../TelnetGenerator.h" />
<ClInclude Include="../ColorHelper.h" />
<ClInclude Include="../TerminalSettings.h">
<DependentUpon>../TerminalSettings.idl</DependentUpon>
</ClInclude>
<ClInclude Include="pch.h" />
<ClInclude Include="../ShortcutActionDispatch.h">
<DependentUpon>../ShortcutActionDispatch.idl</DependentUpon>
@@ -178,8 +182,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" />
@@ -187,6 +189,9 @@
<ClCompile Include="../Pane.LayoutSizeNode.cpp" />
<ClCompile Include="../ColorHelper.cpp" />
<ClCompile Include="../DebugTapConnection.cpp" />
<ClCompile Include="../TerminalSettings.cpp">
<DependentUpon>../TerminalSettings.idl</DependentUpon>
</ClCompile>
<ClCompile Include="pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
@@ -257,6 +262,7 @@
<Midl Include="../Command.idl" />
<Midl Include="../CommandKeyChordVisibilityConverter.idl" />
<Midl Include="../Tab.idl" />
<Midl Include="../TerminalSettings.idl" />
</ItemGroup>
<!-- ========================= Misc Files ======================== -->
<ItemGroup>
@@ -296,12 +302,6 @@
private=false and CopyLocalSatelliteAssemblies=false, so that we don't
propagate them upwards (which can make referencing this project result in
duplicate type definitions)-->
<Reference Include="Microsoft.Terminal.Settings">
<HintPath>$(_BinRoot)TerminalSettings\Microsoft.Terminal.Settings.winmd</HintPath>
<IsWinMDFile>true</IsWinMDFile>
<Private>false</Private>
<CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
</Reference>
<Reference Include="Microsoft.Terminal.TerminalConnection">
<HintPath>$(_BinRoot)TerminalConnection\Microsoft.Terminal.TerminalConnection.winmd</HintPath>
<IsWinMDFile>true</IsWinMDFile>

View File

@@ -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>
@@ -63,7 +59,7 @@
<ClCompile Include="../Commandline.cpp" />
<ClCompile Include="../ColorHelper.cpp" />
<ClCompile Include="../DebugTapConnection.cpp" />
<ClCompile Include="../CommandSerialization.cpp">
<ClCompile Include="../TerminalSettings.cpp">
<Filter>settings</Filter>
</ClCompile>
</ItemGroup>
@@ -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>
@@ -120,7 +119,7 @@
<ClInclude Include="../TelnetGenerator.h">
<Filter>profileGeneration</Filter>
</ClInclude>
<ClInclude Include="../CommandSerialization.h">
<ClInclude Include="../TerminalSettings.h">
<Filter>settings</Filter>
</ClInclude>
</ItemGroup>
@@ -140,10 +139,14 @@
<Midl Include="../Tab.idl">
<Filter>tab</Filter>
</Midl>
<Midl Include="../IF7Listener.idl" />
<Midl Include="../Command.idl">
<Filter>commandPalette</Filter>
</Midl>
<Midl Include="../IDirectKeyListener.idl" />
<Midl Include="../CommandKeyChordVisibilityConverter.idl" />
<Midl Include="../TerminalSettings.idl">
<Filter>settings</Filter>
</Midl>
</ItemGroup>
<ItemGroup>
<None Include="../packages.config" />

View File

@@ -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>

View File

@@ -0,0 +1,61 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import "IKeyBindings.idl";
namespace Microsoft.Terminal.TerminalControl
{
enum ScrollbarState
{
Visible = 0,
Hidden
};
enum TextAntialiasingMode
{
Grayscale = 0,
Cleartype,
Aliased
};
// Class Description:
// TerminalSettings encapsulates all settings that control the
// TermControl's behavior. In these settings there is both the entirety
// of the Core ITerminalSettings interface, and any additional settings
// for specifically the control.
interface IControlSettings requires ICoreSettings
{
String ProfileName { get; };
Boolean UseAcrylic { get; };
Double TintOpacity { get; };
ScrollbarState ScrollState { get; };
String FontFace { get; };
Int32 FontSize { get; };
Windows.UI.Text.FontWeight FontWeight { get; };
String Padding { get; };
Microsoft.Terminal.TerminalControl.IKeyBindings KeyBindings { get; };
Boolean CopyOnSelect { get; };
String Commandline { get; };
String StartingDirectory { get; };
String EnvironmentVariables { get; };
String BackgroundImage { get; };
Double BackgroundImageOpacity { get; };
Windows.UI.Xaml.Media.Stretch BackgroundImageStretchMode { get; };
Windows.UI.Xaml.HorizontalAlignment BackgroundImageHorizontalAlignment { get; };
Windows.UI.Xaml.VerticalAlignment BackgroundImageVerticalAlignment { get; };
UInt32 SelectionBackground { get; };
TextAntialiasingMode AntialiasingMode { get; };
Boolean RetroTerminalEffect { get; };
Boolean ForceFullRepaintRendering { get; };
Boolean SoftwareRendering { get; };
};
}

View File

@@ -3,7 +3,7 @@
import "KeyChord.idl";
namespace Microsoft.Terminal.Settings
namespace Microsoft.Terminal.TerminalControl
{
// [default_interface]
interface IKeyBindings

View File

@@ -6,7 +6,7 @@
#include "KeyChord.g.cpp"
namespace winrt::Microsoft::Terminal::Settings::implementation
namespace winrt::Microsoft::Terminal::TerminalControl::implementation
{
KeyChord::KeyChord() noexcept :
_modifiers{ 0 },
@@ -15,25 +15,25 @@ namespace winrt::Microsoft::Terminal::Settings::implementation
}
KeyChord::KeyChord(bool ctrl, bool alt, bool shift, int32_t vkey) noexcept :
_modifiers{ (ctrl ? Settings::KeyModifiers::Ctrl : Settings::KeyModifiers::None) |
(alt ? Settings::KeyModifiers::Alt : Settings::KeyModifiers::None) |
(shift ? Settings::KeyModifiers::Shift : Settings::KeyModifiers::None) },
_modifiers{ (ctrl ? TerminalControl::KeyModifiers::Ctrl : TerminalControl::KeyModifiers::None) |
(alt ? TerminalControl::KeyModifiers::Alt : TerminalControl::KeyModifiers::None) |
(shift ? TerminalControl::KeyModifiers::Shift : TerminalControl::KeyModifiers::None) },
_vkey{ vkey }
{
}
KeyChord::KeyChord(Settings::KeyModifiers const& modifiers, int32_t vkey) noexcept :
KeyChord::KeyChord(TerminalControl::KeyModifiers const& modifiers, int32_t vkey) noexcept :
_modifiers{ modifiers },
_vkey{ vkey }
{
}
Settings::KeyModifiers KeyChord::Modifiers() noexcept
TerminalControl::KeyModifiers KeyChord::Modifiers() noexcept
{
return _modifiers;
}
void KeyChord::Modifiers(Settings::KeyModifiers const& value) noexcept
void KeyChord::Modifiers(TerminalControl::KeyModifiers const& value) noexcept
{
_modifiers = value;
}

View File

@@ -5,26 +5,26 @@
#include "KeyChord.g.h"
namespace winrt::Microsoft::Terminal::Settings::implementation
namespace winrt::Microsoft::Terminal::TerminalControl::implementation
{
struct KeyChord : KeyChordT<KeyChord>
{
KeyChord() noexcept;
KeyChord(Settings::KeyModifiers const& modifiers, int32_t vkey) noexcept;
KeyChord(TerminalControl::KeyModifiers const& modifiers, int32_t vkey) noexcept;
KeyChord(bool ctrl, bool alt, bool shift, int32_t vkey) noexcept;
Settings::KeyModifiers Modifiers() noexcept;
void Modifiers(Settings::KeyModifiers const& value) noexcept;
TerminalControl::KeyModifiers Modifiers() noexcept;
void Modifiers(TerminalControl::KeyModifiers const& value) noexcept;
int32_t Vkey() noexcept;
void Vkey(int32_t value) noexcept;
private:
Settings::KeyModifiers _modifiers;
TerminalControl::KeyModifiers _modifiers;
int32_t _vkey;
};
}
namespace winrt::Microsoft::Terminal::Settings::factory_implementation
namespace winrt::Microsoft::Terminal::TerminalControl::factory_implementation
{
struct KeyChord : KeyChordT<KeyChord, implementation::KeyChord>
{

View File

@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
namespace Microsoft.Terminal.Settings
namespace Microsoft.Terminal.TerminalControl
{
[flags]
enum KeyModifiers

View File

@@ -31,14 +31,14 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
InitializeComponent();
// Create a CoreTextEditingContext for since we are acting like a custom edit control
auto manager = Core::CoreTextServicesManager::GetForCurrentView();
auto manager = CoreTextServicesManager::GetForCurrentView();
_editContext = manager.CreateEditContext();
// InputPane is manually shown inside of TermControl.
_editContext.InputPaneDisplayPolicy(Core::CoreTextInputPaneDisplayPolicy::Manual);
_editContext.InputPaneDisplayPolicy(CoreTextInputPaneDisplayPolicy::Manual);
// set the input scope to Text because this control is for any text.
_editContext.InputScope(Core::CoreTextInputScope::Text);
_editContext.InputScope(CoreTextInputScope::Text);
_textRequestedRevoker = _editContext.TextRequested(winrt::auto_revoke, { this, &TSFInputControl::_textRequestedHandler });

View File

@@ -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;
@@ -24,7 +25,6 @@ using namespace winrt::Windows::UI::Core;
using namespace winrt::Windows::UI::ViewManagement;
using namespace winrt::Windows::UI::Input;
using namespace winrt::Windows::System;
using namespace winrt::Microsoft::Terminal::Settings;
using namespace winrt::Windows::ApplicationModel::DataTransfer;
// The minimum delay between updates to the scroll bar's values.
@@ -55,12 +55,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
return initialized;
}
TermControl::TermControl() :
TermControl(Settings::TerminalSettings{}, TerminalConnection::ITerminalConnection{ nullptr })
{
}
TermControl::TermControl(Settings::IControlSettings settings, TerminalConnection::ITerminalConnection connection) :
TermControl::TermControl(IControlSettings settings, TerminalConnection::ITerminalConnection connection) :
_connection{ connection },
_initializedTerminal{ false },
_settings{ settings },
@@ -241,7 +236,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// - newSettings: New settings values for the profile in this terminal.
// Return Value:
// - <none>
winrt::fire_and_forget TermControl::UpdateSettings(Settings::IControlSettings newSettings)
winrt::fire_and_forget TermControl::UpdateSettings(IControlSettings newSettings)
{
_settings = newSettings;
auto weakThis{ get_weak() };
@@ -628,7 +623,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());
@@ -737,7 +732,11 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
const auto ch = e.Character();
const auto scanCode = gsl::narrow_cast<WORD>(e.KeyStatus().ScanCode);
const auto modifiers = _GetPressedModifierKeys();
auto modifiers = _GetPressedModifierKeys();
if (e.KeyStatus().IsExtendedKey)
{
modifiers |= ControlKeyStates::EnhancedKey;
}
const bool handled = _terminal->SendCharEvent(ch, scanCode, modifiers);
e.Handled(handled);
}
@@ -826,9 +825,13 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
return;
}
const auto modifiers = _GetPressedModifierKeys();
auto modifiers = _GetPressedModifierKeys();
const auto vkey = gsl::narrow_cast<WORD>(e.OriginalKey());
const auto scanCode = gsl::narrow_cast<WORD>(e.KeyStatus().ScanCode);
if (e.KeyStatus().IsExtendedKey)
{
modifiers |= ControlKeyStates::EnhancedKey;
}
// Alt-Numpad# input will send us a character once the user releases
// Alt, so we should be ignoring the individual keydowns. The character
@@ -2274,28 +2277,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 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 +2355,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 +2394,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:

View File

@@ -7,7 +7,6 @@
#include "CopyToClipboardEventArgs.g.h"
#include "PasteFromClipboardEventArgs.g.h"
#include <winrt/Microsoft.Terminal.TerminalConnection.h>
#include <winrt/Microsoft.Terminal.Settings.h>
#include "../../renderer/base/Renderer.hpp"
#include "../../renderer/dx/DxRenderer.hpp"
#include "../../renderer/uia/UiaRenderer.hpp"
@@ -56,10 +55,9 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
struct TermControl : TermControlT<TermControl>
{
TermControl();
TermControl(Settings::IControlSettings settings, TerminalConnection::ITerminalConnection connection);
TermControl(IControlSettings settings, TerminalConnection::ITerminalConnection connection);
winrt::fire_and_forget UpdateSettings(Settings::IControlSettings newSettings);
winrt::fire_and_forget UpdateSettings(IControlSettings newSettings);
hstring Title();
hstring GetProfileName() const;
@@ -100,7 +98,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(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 ScrollbarState& scrollState,
const winrt::hstring& padding,
const uint32_t dpi);
// clang-format off
// -------------------------------- WinRT Events ---------------------------------
@@ -131,7 +136,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
std::unique_ptr<::Microsoft::Console::Render::DxEngine> _renderEngine;
std::unique_ptr<::Microsoft::Console::Render::UiaEngine> _uiaEngine;
Settings::IControlSettings _settings;
IControlSettings _settings;
bool _focused;
std::atomic<bool> _closing;

View File

@@ -2,6 +2,7 @@
// Licensed under the MIT license.
import "IMouseWheelListener.idl";
import "IControlSettings.idl";
namespace Microsoft.Terminal.TerminalControl
{
@@ -32,12 +33,11 @@ namespace Microsoft.Terminal.TerminalControl
[default_interface] runtimeclass TermControl : Windows.UI.Xaml.Controls.UserControl, IDirectKeyListener, IMouseWheelListener
{
TermControl();
TermControl(Microsoft.Terminal.Settings.IControlSettings settings, Microsoft.Terminal.TerminalConnection.ITerminalConnection connection);
TermControl(Microsoft.Terminal.TerminalControl.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.TerminalControl.IControlSettings settings, UInt32 dpi);
void UpdateSettings(Microsoft.Terminal.Settings.IControlSettings newSettings);
void UpdateSettings(Microsoft.Terminal.TerminalControl.IControlSettings newSettings);
event TitleChangedEventArgs TitleChanged;
event FontSizeChangedEventArgs FontSizeChanged;

View File

@@ -19,7 +19,6 @@
projects compile properly when they depend on this "Microsoft.winmd."
-->
<CppWinRTNamespaceMergeDepth>3</CppWinRTNamespaceMergeDepth>
<!--
DON'T REDIRECT OUR OUTPUT.
Setting this will tell cppwinrt.build.post.props to copy our output from
@@ -28,12 +27,13 @@
<NoOutputRedirection>true</NoOutputRedirection>
<XamlComponentResourceLocation>nested</XamlComponentResourceLocation>
</PropertyGroup>
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
<ItemGroup>
<ClInclude Include="pch.h" />
<ClInclude Include="KeyChord.h">
<DependentUpon>KeyChord.idl</DependentUpon>
</ClInclude>
<ClInclude Include="SearchBoxControl.h">
<DependentUpon>SearchBoxControl.xaml</DependentUpon>
</ClInclude>
@@ -54,6 +54,9 @@
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="init.cpp" />
<ClCompile Include="KeyChord.cpp">
<DependentUpon>KeyChord.idl</DependentUpon>
</ClCompile>
<ClCompile Include="SearchBoxControl.cpp">
<DependentUpon>SearchBoxControl.xaml</DependentUpon>
</ClCompile>
@@ -71,6 +74,9 @@
<ClCompile Include="XamlUiaTextRange.cpp" />
</ItemGroup>
<ItemGroup>
<Midl Include="KeyChord.idl" />
<Midl Include="IKeyBindings.idl" />
<Midl Include="IControlSettings.idl" />
<Midl Include="SearchBoxControl.idl">
<DependentUpon>SearchBoxControl.xaml</DependentUpon>
</Midl>
@@ -100,10 +106,6 @@
<ProjectReference Include="..\..\renderer\uia\lib\uia.vcxproj" />
<ProjectReference Include="..\..\terminal\parser\lib\parser.vcxproj" />
<ProjectReference Include="$(OpenConsoleDir)src\terminal\input\lib\terminalinput.vcxproj" />
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalSettings\TerminalSettings.vcxproj">
<Private>false</Private>
<CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
</ProjectReference>
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalCore\lib\TerminalCore-lib.vcxproj" />
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalConnection\TerminalConnection.vcxproj">
<Private>false</Private>
@@ -125,9 +127,7 @@
<SubType>Designer</SubType>
</Page>
</ItemGroup>
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
<ItemDefinitionGroup>
<Link>
<AdditionalDependencies>dwrite.lib;dxgi.lib;d2d1.lib;d3d11.lib;shcore.lib;winmm.lib;pathcch.lib;propsys.lib;uiautomationcore.lib;Shlwapi.lib;ntdll.lib;user32.lib;kernel32.lib;%(AdditionalDependencies)</AdditionalDependencies>
@@ -136,6 +136,41 @@
<AdditionalIncludeDirectories>$(OpenConsoleDir)src\cascadia\inc;$(OpenConsoleDir)src\types\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<Import Project="$(SolutionDir)build\rules\CollectWildcardResources.targets" />
<!--
We're linking a static library that has the same root namespace as us. Unfortunately,
if we allow a winmd that contains our namespace into the to-be-XamlCompiled reference list
the xaml compiler will become upset with us and fail to find any types defined in _this_
version of the namespace. Therefore, we have to strip them out.
Because ReferencePath is an important item group in MSBuild, we're going to make sure to
restore it the moment Xaml is done with it.
We have to use a static library with the _same_ namespace as ours because there's a weird
invariant somewhere in WinRT that every library project produce a single namespace. We're
going to let TerminalCore (static lib) contribute to our namespace, but when we do that
we get the xaml compiler complaining.
-->
<PropertyGroup>
<MarkupCompilePass1DependsOn>$(MarkupCompilePass1DependsOn);OpenConsoleStripDuplicateWinmdFromReferencesBeforePass1</MarkupCompilePass1DependsOn>
<MarkupCompilePass2DependsOn>$(MarkupCompilePass2DependsOn);OpenConsoleStripDuplicateWinmdFromReferencesBeforePass2</MarkupCompilePass2DependsOn>
</PropertyGroup>
<Target Name="OpenConsoleStripDuplicateWinmdFromReferencesBeforePass1">
<ItemGroup>
<!-- Back up the references we're about to delete -->
<OCReferencePathToRestore Include="@(ReferencePath)" Condition="'%(Filename)' == '$(RootNamespace)'"/>
<ReferencePath Remove="@(OCReferencePathToRestore)"/>
</ItemGroup>
</Target>
<Target Name="OpenConsoleRestoreDuplicateWinmdToReferences" AfterTargets="MarkupCompilePass2">
<ItemGroup>
<!-- Put them back. -->
<ReferencePath Include="@(OCReferencePathToRestore)"/>
</ItemGroup>
</Target>
<Target Name="OpenConsoleStripDuplicateWinmdFromReferencesBeforePass2">
<ItemGroup>
<!-- Nobody else uses this item group, it's safe to never restore. -->
<WinMDReferenceToCompile Remove="@(WinMDReferenceToCompile)" Condition="'%(Filename)' == '$(RootNamespace)'"/>
</ItemGroup>
</Target>
</Project>

View File

@@ -15,8 +15,6 @@
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
<ClCompile Include="TermControlAutomationPeer.cpp" />
<ClCompile Include="XamlUiaTextRange.cpp" />
<ClCompile Include="TermControlUiaProvider.cpp" />
<ClCompile Include="UiaTextRange.cpp" />
<ClCompile Include="SearchBoxControl.cpp" />
<ClCompile Include="init.cpp" />
<ClCompile Include="ThrottledFunc.cpp" />
@@ -26,15 +24,15 @@
<ClInclude Include="TermControl.h" />
<ClInclude Include="TermControlAutomationPeer.h" />
<ClInclude Include="XamlUiaTextRange.h" />
<ClInclude Include="TermControlUiaProvider.hpp" />
<ClInclude Include="ThrottledFunc.h" />
<ClInclude Include="UiaTextRange.hpp" />
</ItemGroup>
<ItemGroup>
<Midl Include="TermControl.idl" />
<Midl Include="TermControlAutomationPeer.idl" />
<Midl Include="SearchBoxControl.idl" />
<Midl Include="TSFInputControl.idl" />
<Midl Include="IMouseWheelListener.idl" />
<Midl Include="IControlSettings.idl" />
</ItemGroup>
<ItemGroup>
<None Include="TerminalControl.def" />
@@ -42,6 +40,8 @@
</ItemGroup>
<ItemGroup>
<Page Include="SearchBoxControl.xaml" />
<Page Include="TermControl.xaml" />
<Page Include="TSFInputControl.xaml" />
</ItemGroup>
<ItemGroup>
<Natvis Include="$(SolutionDir)tools\ConsoleTypes.natvis" />
@@ -51,4 +51,4 @@
<Filter>Resources</Filter>
</PRIResource>
</ItemGroup>
</Project>
</Project>

View File

@@ -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();
}
});

Some files were not shown because too many files have changed in this diff Show More