Compare commits

..

113 Commits

Author SHA1 Message Date
Dustin L. Howett
a223d3f5fa Migrate spelling-0.0.21 changes from main 2021-06-14 19:20:30 +02:00
Dustin L. Howett
efd305e9eb Migrate spelling-0.0.19 changes from main 2021-06-14 19:20:30 +02:00
kovdu
428f2a752b Cancel the preview of nested commands when moving back on the stack. (#10392)
<!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? -->
## Summary of the Pull Request
Immediately cancelling the preview when the user is navigating back from a nested command.

<!-- Other than the issue solved, is this relevant to any other issues/existing PRs? -->
## References

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
* [x] Closes #10165
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [ ] 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.
* [ ] 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

<!-- Provide a more detailed description of the PR, other things fixed or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments
Basically 2 changes are done here:
- Allow the click handler to run for the back button when the button has focus and user hits the enter key (similarly as hitting space now).
- Now immediately cancelling the preview when the user is navigating back. Felt nicer to do it immediately at that point then keeping the preview active until the user hits cancel to close the palette. So the preview is already cancelled at step **5** instead of 6 as mentioned in the reproduction steps here https://github.com/microsoft/terminal/issues/10165#issue-899838383. But of course let me know if you're not agreeing here 😀 .

<!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
- Open 'Color Scheme' and verified preview is still working fine when selecting different schemes.
- After tabbing back to the Back button verified that when hitting enter or space the preview is cancelled and the original color scheme is being used again.
- Then after going back to 'Color Scheme' previews are still working ok.
- After hitting Enter on one of the Color Schemes the scheme still becomes active as before.

(cherry picked from commit 1cc383f865)
2021-07-07 13:12:45 -05:00
kovdu
37850bfe7f Don't dispatch the Toggle Command Palette action to keep the just closed Command Palette closed. (#10423)
An exception was introduced for the 'Toggle Command Palette' action to **not** being dispatched. Otherwise the command palette that was just closed will become visible again.

## PR Checklist
* [x] Closes #10240
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA

## Detailed Description of the Pull Request / Additional comments
- Selecting the `Toggle command palette` item in the command palette will now properly close the command palette.
- Opening and closing the Command Palette through shortcut keys is still working fine.
- Other command palette items are still working fine as well.

(cherry picked from commit 813f385c08)
2021-07-07 13:11:22 -05:00
Dustin L. Howett
3042ac5d2c Localize the shell extension's menu item (#10446)
This commit introduces localization for the "Open in Windows Terminal"
menu item and differentiates it based on compile-time branding (rather
than runtime detection!).

@leonMSFT's tray icon pull request had the excellent idea to use the
TerminalApp's resource compartment for auxiliary resources for projects
that can't otherwise be localized the same way. Doing localization in
the shell extension (or WindowsTerminal.exe) would require us to use
MUIRCT and split the build process up to support mui files. That's a
huge amount of work... but this is *not* a huge amount of work.

Fixes #6112

(cherry picked from commit b6593216f2)
2021-07-07 13:08:03 -05:00
Chester Liu
1934e72d6e Don't notify a11y event when in ConPTY mode (#10537)
Don't notify a11y event when in ConPTY mode

In support of #10528

(cherry picked from commit 59239e3b07)
2021-07-07 13:05:54 -05:00
Dustin L. Howett
caeb5b5db7 wpf: fix the TerminalTheme struct to marshal the same on all platforms (#10486)
The CursorStyle enum is declared as being of type `uint` on the C# side,
but as `size_t` on the C++ side. There's a C# size_t impostor we could
use, System.UIntPtr, but I don't want to risk changing the public API of
TerminalTheme and I don't know if it can be used as a base type for an
enum.

Anyway, since we don't have more than four billion cursor types I chose
to narrow the field to a uint32_t and unpack it in TerminalSetTheme.

Fixes #10485

(cherry picked from commit 2770228e09)
2021-07-07 13:04:57 -05:00
Carlos Zamora
cedbee42e0 [A11y] Initialize and copy _blockRange in UIA Clone (#10544)
## Summary of the Pull Request
#7960 was caused by `UiaTextRangeBase::_blockRange` not being initialized, thus pointing to random memory. In most cases, we initialize it properly in `RuntimeClassInitialize`, however, the copying version of `RuntimeClassInitialize` doesn't actually copy it over, resulting in it still containing random memory.

NVDA (and other screen readers) occasionally use `Clone` (really just the copy initializer), resulting in this bug occurring randomly.

## PR Checklist
* [X] Closes #7960
* [X] Tests added/passed

## Validation Steps Performed
Test failed before the change, but passes after the change.

(cherry picked from commit 79a18f0825)
2021-07-07 12:57:32 -05:00
WSLUser
7f63434055 Add Settings UI enum to json schema (#10489)
Noticed the json schema was listing the option as invalid even though it's accepted by WT. So added it to schema to remove the error.

## PR Checklist
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [x] Schema updated.

## Validation Steps Performed
No longer shows as invalid in VSCode.

(cherry picked from commit e3b7a44b13)
2021-07-07 12:57:31 -05:00
Dustin L. Howett
bbc4a904ee Update Cascadia Code to 2106.17 (#10455)
This update brings some significant changes to the Cascadia family:

* Arabic and Hebrew support
* Italics (the new ones, not the cursive ones)
* Tweaked letterforms and fixed interpolation values for the upright
  faces.

Since we now have four font files, this commit also relocates them to a
much more reasonable place (res/fonts/) and tidies up the build and
exclude rules to make them more extensible in the future.

(cherry picked from commit c90de69250)
2021-07-07 12:57:31 -05:00
Dustin L. Howett
dbf9ee93a3 Add support for branch- and branding-based feature flagging (#10361)
This pull request implements a "feature flagging" system that will let
us turn Terminal and conhost features on/off by branch, "release" status
or branding (Dev, Preview, etc.).

It's loosely modelled after the Windows OS concept of "Velocity," but
only insofar as it is driven by an XML document and there's a tool that
emits a header file for you to include.

It only supports toggling features at compile time, and the feature flag
evaluators are intended to be fully constant expressions.

Features are added to `src\features.xml` and marked with a "stage". For
now, the only stages available are `AlwaysDisabled` and `AlwaysEnabled`.
Features can be toggled to different states using branch and branding
tokens, as documented in the included feature flag docs.

For a given feature Feature_XYZ, we will emit two fixtures visible to
the compiler:

1. A preprocessor define `TIL_FEATURE_XYZ_ENABLED` (usable from MIDL,
   C++ and C)
2. A feature class type `Feature_XYZ` with a static constexpr member
   `IsEnabled()` (usable from C++, designed for `if constexpr()`).

Like Velocity, we rely on the compiler to eliminate dead code caused by
things that compile down to `if constexpr (false)`. :)

Michael suggested that we could use `WindowsInbox` as a branding to
determine when we were being built inside Windows to supplant our use of
the `__INSIDE_WINDOWS` preprocessor token. It was brilliant.

Design Decisions
----------------

* Emitting the header as part of an MSBuild project
   * WHY: This allows the MSBuild engine to ensure that the build is
     only run once, even in a parallel build situation.
* Only having one feature flag document for the entire project
   * WHY: Ease.
* Forcibly including `TilFeatureStaging` with `/FI` for all CL compiler
  invocations.
   * WHY: If this is a project-wide feature system, we should make it as
     easy as possible to use.
* Emitting preprocessor definitions instead of constexpr/consteval
   * WHY: Removing entire functions/includes is impossible with `if
     constexpr`.
   * WHY: MIDL cannot use a `static constexpr bool`, but it can rely on
     the C preprocessor to remove text.
* Using MSBuild to emit the text instead of PowerShell
   * WHY: This allows us to leverage MSBuild's `WriteOnlyWhenDifferent`
     task parameter to avoid changing the file's modification time when
     it would have resulted in the same contents. This lets us use the
     same FeatureStaging header across multiple builds and multiple
     branches and brandings _assuming that they do not result in a
     feature flag change_.
   * The risk in using a force-include is always that it, for some
     reason, determines that the entire project is out of date. We've
     gone to great lengths to make sure that it only does so if the
     features _actually materially changed_.

(cherry picked from commit 31a39b3b12)
2021-07-07 12:28:42 -05:00
Leonard Hecker
ebabf32f71 Fix racy access to _tsfTryRedrawCanvas in TermControl in v1.8 2021-07-03 01:37:35 +02:00
Mike Griese
6617eb33ac Don't throw in GetProposedDimensions (#10260)
I cannot for the life of me repro the original bug. I've got fonts with bad permissions SxS, I've tried installing a font twice, I've tried stopping the font cache service. No idea how to manually repro the original bug.

BUT theoretically, this function should never throw. So lets just switch this to a `LOG_IF_FAILED`, and hope that this goes away?

* [x] Fixes #10211?
* [x] built & ran manually.

Unclear if this can get cherry-picked trivially to 1.8. Code's pretty trivial though so if we need another PR for that, it can be arranged.

(cherry picked from commit 89ca2ae05f)
2021-05-28 17:28:01 -05:00
Dustin Howett
4a3f173e17 Revert "Disable path validation, add warning (#10045)"
This reverts commit 3c30877294.
2021-05-28 11:59:19 -05:00
Dustin Howett
646904c3c2 Fix a bad backport in a3de0d0ec 2021-05-24 17:35:18 -05:00
Dustin Howett
cb4c4f7b73 [STABLE ONLY] Combined revert of Environment Block Changes
Revert "Fix environment block creation (#7401)"

This reverts commit 7886f16714.

(cherry picked from commit e46ba65665)

Revert "Always create a new environment block before we spawn a process (#7243)"

This reverts commit 849243af99.

References #7418

(cherry picked from commit 4204d2535c)
(cherry picked from commit f8e8572c23)
2021-05-24 17:13:53 -05:00
PankajBhojwani
3c64fcd12b Allow trailing semicolon when parsing OSC 9;4 (#10024)
## Summary of the Pull Request
When we parse OSC 9;4, allow a trailing semicolon (i.e. allow `9;4;` or something like `9;4;3;`).

## PR Checklist
* [x] Closes #9960
* [X] Tests added/passed

## Validation Steps Performed
OSC 9;4 sequences with or without trailing semicolons work

(cherry picked from commit f518235599)
2021-05-24 17:08:48 -05:00
Leon Liang
7264d5cc43 Fix tabColor arg crash in CommandPalette (#10096)
While a user is formulating their hex string for a `tabColor` arg
in the CommandPalette, we try to parse the string one char at
a time as it comes in. `ColorFromHexString` doesn't like anything
except a well formed hex string so it'll throw. We can probably eat
any error that comes out of this because we should only care to set
the TabColor once the string provided is a valid hex str.

Closes #10053

(cherry picked from commit f7458a31fd)
2021-05-24 17:07:17 -05:00
Leonard Hecker
5756236362 Split ThrottledFunc into Leading and Trailing variants (#10133)
This replaces `ThrottledFunc` with two variants:
* `ThrottledFuncLeading` invokes the callback immediately and blocks further calls for the given duration
* `ThrottledFuncTrailing` blocks calls for the given duration and then invokes the callback

* #9270 - `ThrottledFuncLeading` will allow the pane to flash immediately for a BEL, but block further BELs until the animation finished

* [x] I work here
* [ ] Tests added/passed

* [x] Ensured scrolling still works

(cherry picked from commit 13f0b8e007)
2021-05-24 17:04:05 -05:00
Dustin L. Howett
6e907fd5f2 Attempt to heal settings files damaged by #9962 (#10143)
The bug that caused #9962 resulted in folks getting profiles written to
their settings that didn't contain any identifying information (name or
guid), sometimes multiple times.

These profiles look (somewhat) like this:

```json
{ "colorScheme": "Campbell" },
{},
```

An empty profile serves no purpose -- it shows up in the list as being
named "Default", and it only launches CMD (unless the commandline is the
thing that the user successfully changed.)

We can heal the settings file by simply ignoring those profiles that
have *no identifying information* (a guid or a name that can be
converted into a guid).

Validation
----------
I created a number of profiles that fit this format and made sure that
they were ignored on load and destroyed on save.

## PR Checklist
* [x] Closes an annoyance we discovered after 9962.

(cherry picked from commit 84f6a29d89)
2021-05-24 17:02:26 -05:00
kovdu
afd6a87de5 Clear all state tracking nested commands when switching command mode (#10164)
Went for option 2 proposed here:
https://github.com/microsoft/terminal/issues/10140#issuecomment-845193132.

Disabling back space in the nested entry didn't felt as the nicest
solution.  Instead now all state that keeps track of nested commands is
cleared when switching beteen modes.

## Validation Steps Performed
- Validated the specified issue is fixed by this change:. now after
  entering a sub command and hitting backspace the palette no longer
  shows the sub command item (here `< Select color scheme...` ).
- Validated that switching between all modes (command line, actions, tab
  search & tab switch) still work as expected.
- Validated as well that all modes still work as expected.

Didn't add unit tests, but happy to try that out if this would be
required.

Closes #10140

(cherry picked from commit dd348dccda)
2021-05-24 16:58:06 -05:00
Mike Griese
a3de0d0ece Hook up the keybindings to the SUI, redux (#10121)
## Summary of the Pull Request

This is a redux of #8882.

From the original:

>  This is really similar to what we're doing with the `CommandPalette`. We're adding a ~~Preview~~`KeyDown` handler to the SUI `MainPage`, that connects to `TerminalPage::_HandleKey`. That allows the SUI a chance to search the keymap to dispatch actions for keybindings, similar to how the command palette does it.
>
> This also means it's now possible for the SUI to invoke _all_ the actions available to the Terminal. This includes the ones like `IncreaseFontSize`, which require a _Terminal_ to actually do something. So we have to make sure all the calls to `_GetActiveControl` actually check that the result is non-null before using it.
>
> A bunch of the actions do nothing now from a SUI tab, others behave _weird_. Like "Rename tab" / "Open Tab Renamer" do nothing. "Duplicate Tab" again does nothing - we try making a new settings tab, which just focuses the settings tab again. "Copy text" definitely does nothing, same with paste.

I don't know why I thought this wouldn't work. I thought we'd have to do this in `PreviewKeyDown` or something, which led to [weirdness](https://github.com/microsoft/terminal/pull/8882#issuecomment-767088554). Turns out, we don't need it to be in `PreviewKeyDown`. It can just be in the SUI's `KeyDown`.

## References
* Original: #8882
* Workaround was in #8885

## PR Checklist
* [x] Closes #8767
* [x] I work here
* [x] Tests added/passed
* [n/a] Requires documentation to be updated

## Detailed Description of the Pull Request / Additional comments

The special case handler from #8885 is no longer needed

## Validation Steps Performed

* Switching tabs with Ctrl+Tab works
* Command palette works
* fullscreen, focus mode works
* close window works
* copy paste on Ctrl+C/V works, even when bound
* Select all text in textboxes works
* tab navigation through UI elements works

(cherry picked from commit 52560ff818)

# Conflicts:
#	src/cascadia/TerminalApp/TerminalPage.cpp
#	src/cascadia/TerminalApp/TerminalPage.h
2021-05-24 14:23:34 -05:00
Mike Griese
a2ab200e88 Don't yeet focus to the control when the tab renamer is opened (#10114)
This is a hotfix to #10048. When the tab renamer is opened, we need to make sure to not immediately steal focus from it.

* [x] closes #10112
* [x] I work here
* [x] tested manually

(cherry picked from commit 24f80bd9ba)
2021-05-24 12:31:29 -05:00
Dustin Howett
90e78ec074 Revert "Revert "In specific scenarios, focus the active control (#10048)""
This reverts commit c8ab75b265.
2021-05-24 12:31:16 -05:00
Mike Griese
3c30877294 Disable path validation, add warning (#10045)
This is primarily being done to unblock #9223.

Prior to this, we'd validate that the user's `startingDirectory` existed
here. If it was invalid, we'd gracefully fall back to `%USERPROFILE%`.

However, that could cause hangs when combined with WSL. When the WSL
filesystem is slow to respond, we'll end up waiting indefinitely for
their filesystem driver to respond. This can result in the whole terminal
becoming unresponsive.

Similarly, with #9223 we want users to be able to specify WSL paths in a
profile, but this bit of validation logic totally prevents that from working,
because it'll just replace the path with `%USERPROFILE%`.

If the path is eventually invalid, we'll display warning in the
`ConptyConnection`, when the process fails to launch.

Closes #9541
Closes #9114

![image](https://user-images.githubusercontent.com/18356694/117318675-426d2d00-ae50-11eb-9cc0-0b23c397472c.png)

(cherry picked from commit bfc4838042)
2021-05-24 11:51:24 -05:00
Dustin L. Howett
e593c46679 Emit fixup debug info for internal tooling (#10151)
See MSFT-33187224 for more information.

This may impact debuggability; I have no idea how to tell.

(cherry picked from commit 89af44488f)
2021-05-24 11:50:07 -05:00
Michael Niksa
a736712da9 Set keyword flags on all tracelog events (#10098)
Set keyword flags on all events so those sharing a provider with
telemetry do not fire unless tracing is enabled

## PR Checklist
* [x] Closes #10093
* [x] I work here
* [x] Tests passed
* [x] Documentation added in `til.h` about how keywords work and at the
  only other site of keywords we define in the Host project tracing
  files.

## Detailed Description of the Pull Request / Additional comments
I initially thought that we would need to split providers here to
accomplish this... but @DHowett helped me realize that might be a lot of
additional metadata and bloat binary size. So with help from a friend
from fundamentals, I realized that we could use Keywords to
differentiate here. We can no longer define 0 keywords as that
represents an any/all scenario. Every `TraceLoggingWrite` event now
needs a keyword. When our events have a keyword, they're not included in
any trace. Additionally, when we have an explicit keyword to check that
is different from the ones used for the telemetry pipeline, we can
ensure that we only do "hard work" to generate debug trace data when an
"ALL" type listener like TraceView or Windows Performance Recorder with
our profiles is listening to these providers for ALL keyworded events.

## Validation Steps Performed
- [x] - Built with full release build config to confirm performance is
  worse than dev builds BECAUSE of the telemetry event collector camping
  our provider and triggering full trace event generation on shared
  providers.
- [x] - Built with full release build config to enable statistics
  collection and validated trace event collection is excluded and trace
  event short-circuits work with this change.
- [x] - Checked that TraceView still sees both telemetry and tracing
  events
- [x] - Checked that WPR with our .wprp profile sees both telemetry and
  tracing events

(cherry picked from commit 66fdc645f7)

# Conflicts:
#	src/cascadia/Remoting/Monarch.cpp
#	src/cascadia/Remoting/Peasant.cpp
#	src/cascadia/WindowsTerminal/IslandWindow.cpp
2021-05-17 16:39:09 -05:00
Dustin L. Howett
0499604eee Fix a number of shutdown crashes in TermControl (#10115)
1. The TSFInputControl may get a layout event after it has been removed
   from service (and no longer has a XAML tree)
   * Two fixes:
      * first, guard the layour updater from accessing detached xaml
	objects
      * second, shut down all pending throttled functions during close
	(not destruction!¹)
2. The TermControlAutomationPeer may be destructed before its events
   fire.
3. The TermControlAutomationPeer may receive a notification after it has
   been detached from XAML (and therefore has no dispatcher).

¹ Close happens before the control is removed from the XAML tree;
destruction happens some time later. We must detach all UI-bound events
in Close so that they don't fire between when we detach and when we
destruct.

Fixes MSFT-32496693
Fixes MSFT-32496158
Fixes MSFT-32509759
Fixes MSFT-32871913

(cherry picked from commit 661fde5937)
2021-05-17 14:23:05 -05:00
Dustin Howett
c8ab75b265 Revert "In specific scenarios, focus the active control (#10048)"
This reverts commit b345d39c1d.
2021-05-14 12:55:18 -05:00
Mike Griese
b345d39c1d In specific scenarios, focus the active control (#10048)
A redo of #6290. That PR was overkill. In that one, we'd toss focus back to the active control any time that the tab view item got focus. That's maybe not the _best_ solution.

Instead, this PR is precision strikes. We're re-using a lot of what we already have from #9260.
* When the context menu is closed, yeet focus to the control.
* When the renamer is dismissed, yeet focus to the control.
* When the TabViewItem is tapped (meaning no one else handled it), yeet focus to the control.

* [x] I work here
* [ ] This is UI so it doesn't have tests
* [x] Closes #3609
* [x] Closes #5750
* [x] Closes #6680

* [x] focus the window by clicking on the tab -> Control is focused.
* [x] Open the color picker with the context menu, can move the focus inside the picker with the arrow keys.
* [x] Dismiss the picker with esc -> Control is focused.
* [x] Dismiss the picker with enter -> Control is focused.
* [x] Dismiss the renamer with esc -> Control is focused.
* [x] Dismiss the renamer with enter -> Control is focused.
* [x] Dismiss the context menu with esc -> Control is focused.
* [x] Start renaming, then click on the tab -> Rename is committed, Control is focused.
* [x] Start renaming, then click on the text box -> focus is still in the text box

(cherry picked from commit 8564b269c4)
2021-05-14 12:26:58 -05:00
MPela
c4e02e7274 Add Close menu items to the context menu flyout (#9859)
## Summary of the Pull Request
Add the "Close other tabs"/"Close tabs to the right" menu items straight to the tab context menu to work around #8238.
We can't add them into a dedicated sub-menu until the upstream crash is fixed.

## References
#8238

## PR Checklist
* [X] Closes #8238
* [X] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [ ] Tests added/passed
* [ ] Documentation updated
* [ ] Schema updated.
* [ ] 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.

## Detailed Description of the Pull Request / Additional comments
Moved the creation of the close menu items to a single function. Once the originating crash is fixed, the sub-menu can be restored by just replacing a few lines of code.

## Validation Steps Performed
![immagine](https://user-images.githubusercontent.com/1140981/115059601-0dbc2480-9ee7-11eb-9889-d9ef8e6e7613.png)

(cherry picked from commit 2065fa7b76)
2021-05-14 12:25:14 -05:00
Dustin Howett
cb7f481c95 Revert "[RELEASE ONLY] Remove the close submenu (#9102)"
This reverts commit c951a70208.
2021-05-14 12:25:08 -05:00
Don-Vito
69397b3797 Teach CmdPal search to use user locale (#9943)
## PR Checklist
* [x] Closes https://github.com/microsoft/terminal/issues/9941
* [x] CLA signed.

## Detailed Description of the Pull Request / Additional comments
The bug is due to us using std::tolower, while the default locale is not user's locale.
The fix here is to use the same approach as upon sorting: lstrcmpi.
While there are additional methods to do locale aware comparison,
here we convert chars to string and call lstrcmpi.
While this approach seems somewhat inefficient it ensures consistency
(with the order of locales that lstrcmi tries to apply internally).

(cherry picked from commit cb55cec275)
2021-05-14 12:22:04 -05:00
MPela
e7ec200f34 Fix dropdown showing in up direction (#10009)
## Summary of the Pull Request
Let the dropdown menu open downwards if there's enough space, when clicking on the down arrow.

## PR Checklist
* [X] Closes #8924
* [X] CLA signed.

## Detailed Description of the Pull Request / Additional comments
Set the placement of the flyout to BottomEdgeAlignedLeft, as was done when opening the menu from the key binding.

## Validation Steps Performed
Manual tests

(cherry picked from commit 2b4c20bd6e)
2021-05-14 12:22:03 -05:00
Javier
ba07f65eb9 [WPF] Allows setting the WPF control background when setting the terminal theme (#10026)
When syncing terminals across users (i.e. Liveshare shared terminals),
the terminal size is synced. This leads to having unused space around
the terminal which is the same color as the terminal's background
causing confusion as to what space is usable within the terminal.

Instead this change allows consumers to set the background color of the
control, separate from the terminal renderer's background, which makes
it easier to identify the edges of the terminal.

(cherry picked from commit 31414aa364)
2021-05-14 12:22:03 -05:00
Carlos Zamora
0e810d2c35 Serialize stub for dynamic profiles (#9964)
#9962 was caused by a serialization bug. _Technically_, `ToJson` works
as intended: if the current layer has any values set, write them out to
the json. However, on first load, the dynamic profile `Profile` objects
are actually empty (because they inherit from base layer, then the
dynamic profile generator). This means that `ToJson` writes the dynamic
profiles as empty objects `{}`. Then, on reload, we see that the dynamic
profiles aren't in the JSON, and we write them again.

To get around this issue, we added a simple check to `Profile::ToJson`:
if we have a source, make sure we write out the name, guid, hidden, and
source. This is intended to align with `Profile::GenerateStub`.

Closes #9962

(cherry picked from commit 8f93f76214)
2021-05-14 12:22:02 -05:00
Mike Griese
ed3feef213 Add a success dialog to window renaming (#9808)
I added a `RenameSucceededText` property to the `TerminalPage` which returns the
formatted message `Successfully renamed window to "{WindowNameForDisplay()}"`

This _doesn't_ pop the dialog when you `wt -w foo` for the first time. Only
_subsequent_ renames.

## References
* Added in #9662
* Closes #9804

(cherry picked from commit aa54de1d64)
2021-05-14 12:22:02 -05:00
Don-Vito
ca71ac4100 Fix tab and hyperlink tooltips to wrap long text (#9913)
## PR Checklist
* [x] Closes https://github.com/microsoft/terminal/issues/9869
* [x] CLA signed.
* [ ] Tests added/passed
* [ ] Documentation updated.
* [ ] Schema updated.
* [ ] I've discussed this with core contributors already.

(cherry picked from commit 6b4f70e985)
2021-05-14 12:22:02 -05:00
PankajBhojwani
fba9493dbb Fix for configuring starting directory in SUI when defaults sets it to null (#9862)
## Summary of the Pull Request
Remove an unnecessary check in `Profiles.cpp` that was preventing us from enabling the text box and browse button when the user unchecks 'use parent process directory'

## PR Checklist
* [x] Closes #9847
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [ ] 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 work here

## Validation Steps Performed
Played around with it and it works.

(cherry picked from commit ad34291632)
2021-05-14 12:22:01 -05:00
Don-Vito
ac864d2c65 Delay close tab on middle-click till pointer released (#9842)
## PR Checklist
* [x] Closes https://github.com/microsoft/terminal/issues/9836
* [x] CLA signed.
* [ ] Tests added/passed
* [ ] Documentation updated.
* [ ] Schema updated.
* [ ] I've discussed this with core contributors already.

Not sure what is the reason for handling right button.
But delaying it to PointerReleased seems not to regress anything.

(cherry picked from commit 05e7ea1423)
2021-05-14 12:22:01 -05:00
Don-Vito
89032b8b1c Fix rename window handler to mark action as handled (#9809)
## PR Checklist
* [x] Closes https://github.com/microsoft/terminal/issues/9803
* [x] CLA signed.
* [ ] Tests added/passed
* [ ] Documentation updated.
* [ ] Schema updated.
* [ ] I've discussed this with core contributors already.

(cherry picked from commit cdbcc17458)
2021-05-14 12:22:00 -05:00
Don-Vito
c2cb365c62 Fix profile name generation to allocate unique name (#9816)
## PR Checklist
* [x] Closes https://github.com/microsoft/terminal/issues/9714
* [x] CLA signed.
* [ ] Tests added/passed
* [ ] Documentation updated.
* [ ] Schema updated.
* [ ] I've discussed this with core contributors already.

## Detailed Description of the Pull Request / Additional comments
Attempts to generate a name Profile X, where X is the index of the new profile (1-based).
As long as name is already taken, generates new name by incrementing X by 1

(cherry picked from commit 3368e602fd)
2021-05-14 12:22:00 -05:00
Don-Vito
76a609b3ae Limit terminal warning bells to one per second (#9812)
<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
* [x] Closes https://github.com/microsoft/terminal/issues/9776
* [x] CLA signed.
* [ ] Tests added/passed
* [ ] Documentation updated.
* [ ] Schema updated.
* [ ] I've discussed this with core contributors already.

## Detailed Description of the Pull Request / Additional comments
Use `ThrottledFunc` in `TermControl` to limit bell emission callback to one per second.

(cherry picked from commit 9a2d27e9f6)
2021-05-14 12:21:55 -05:00
Leonard Hecker
99e0f95318 Fix crash on exit introduced in ac265aa (#10042)
ControlCore::AttachUiaEngine receives a IRenderEngine as a raw pointer,
which TermControl owns. We must ensure that we first destroy the
ControlCore before the UiaEngine instance (both owned by TermControl).
Otherwise a deallocated IRenderEngine is accessed when
ControlCore calls Renderer::TriggerTeardown.

This crash was introduced in #10031.

* [x] I work here
* [x] Tests added/passed

* Run accevent.exe to cause a UiaEngine to be attached to a TermControl.
* Close the current tab
* Ensured no crashes occur

(cherry picked from commit 43040ef9d0)
(cherry picked from commit ad45139bb4)
2021-05-14 12:17:46 -05:00
Leonard Hecker
2c0889223a Backport hotfix ac265aa to v1.8 (#10033)
ControlCore's _renderer (IRenderTarget) is allocated as std::unique_ptr,
but is given to Terminal::CreateFromSettings as a reference.
ControlCore::Close deallocates the _renderer, but if ThrottledFuncs
are still scheduled to call ControlCore::UpdatePatternLocations
it'll cause Terminal::UpdatePatterns to be called, which in turn ends up
accessing the deallocated IRenderTarget reference and lead to a crash.

A proper solution with shared pointers is nontrivial and should be
attempted at a later point in time. This solution moves the teardown of
the _renderer into ControlCore::~ControlCore, where we can be certain
that no further strong references are held by ThrottledFuncs.
2021-05-04 17:54:29 -05:00
Dustin L. Howett
aad3855287 Work around a compiler bug w/ coroutines and exceptions (#9893)
There is a bug in the compiler that we trip over when we handle the
exception generated by Package::Current inside a coroutine. It appears
to destruct an invalid instance of winrt::factory_guard_count.

Learned from the compiler folks: "coroutine frame pointer wasn't being
stored ... properly".

Fixes #9821

(cherry picked from commit 21b2e01643)
2021-04-19 16:15:35 -05:00
Kayla Cinnamon
4c09a235cc doc: Add tabColor to JSON schema (#9843)
(cherry picked from commit 8bcb47339d)
2021-04-15 12:32:42 -05:00
Dustin L. Howett
8078db5fd5 Only update render appearance settings if there's a renderer (#9798)
I ran into this crash when I just opened a new tab.

Fixes MSFT-32485023

(cherry picked from commit dab52c46a2)
2021-04-13 16:17:52 -05:00
Evan Koschik
f2afb223f5 Fix restore window position when exiting fullscreen (#9737)
<!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? -->
## Summary of the Pull Request

This change cleans up the Fullscreen implementation for both conhost and Terminal, improving the restore position (where the window goes when exiting fullscreen).

Prior to this change the window wasn't guaranteed to restore somewhere on the window's current monitor when exiting fullscreen. With this change the window will restore always to its current monitor, at a reasonable location (and will 'double restore' (to fullscreen->maximize->restore) after monitor changes while fullscreen, which is the expected user behavior.

<!-- Other than the issue solved, is this relevant to any other issues/existing PRs? -->
## References

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
* [x] Closes #9746
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [ ] 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.
* [ ] 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

<!-- Provide a more detailed description of the PR, other things fixed or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

A fullscreen window's monitor can change.
 - Win+Shift+left/right migrates a window between monitors.
 - User could open settings, display, and move the monitor or change its DPI.
 - The monitor could be unplugged.
 - The session could be remote and be disconnected.

A fullscreen window stores a 'restore position' when entering fullscreen, used to move the window back 'where it was'. BUT, its unexpected for the window to exit fullscreen and jump to another monitor. This means its previous position must be migrated from the old monitor's work area to the new monitor's work area.

If a window is maximized, it is sized to the work area. Like with fullscreen, a maximized window has a 'restore position', though unlike with fullscreen the restore position for maximized is stored by the system itself. Migration in cases where a maximized (or fullscreen) window's monitor changes is also taken care of by the system. To restore 'safely' to maximized (after changing window styles) a window must only `SetWindowPos(SWP_FRAMECHANGED)`. While technically a maximized window that becomes fullscreen 'is still maximized' (from Win32's perspective), its prudent to also `ShowWindow(SW_MAXIMIZED)` prior to `SWP_FRAMECHANGED` (to explicitly make the window maximized).

If not restoring to maximized, the restore position is adjusted by the new/ old work area. Additionally, the new/ old window DPI is used to adjust the size of the window by the DPI change (keeping the window's logical size the same).
 - The work area origin is checked first (shifting window rect by the change in origin)
 - The DPI is checked next, changing right/ bottom (size only)
 - Each edge of the window is compared against the corresponding edge of the work area, nudging the window back on-screen if hanging offscreen. By shifting right before left, bottom before top, the top-left is guaranteed on-screen.

<!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

Tried it out. Seemed to work on my machine.
Jk, ran conhost/ terminal on mixed DPI system, max (or not), fullscreen, win+shift+left/ exit fullscreen/ maximize. Monitor unplug, etc.

(cherry picked from commit bc1ff0b71a)
(cherry picked from commit 14a7e45280)
2021-04-13 15:44:42 -05:00
Dustin L. Howett
9882e9f835 Remove the splash screen (to save 100kb (compressed!)) (#9795)
We're a Centennial application; we can't even _use_ the splash screen.

(cherry picked from commit ab6f41f4bd)
2021-04-13 12:52:42 -05:00
Dustin L. Howett
6072be5436 [RELEASE ONLY] Remove the close submenu (#9102)
There's a platform issue that causes it to crash.
Fixes #8944.

(cherry picked from commit 5fdd1560fd)
(cherry picked from commit c951a70208)
2021-04-13 09:14:05 -05:00
Dustin Howett
bc368b7c29 Disable handoff registration (only) for preview in 1.8
Handoff will still be accepted if the registry is set up properly.
2021-04-13 09:11:21 -05:00
Dustin Howett
2219014385 Merge remote-tracking branch 'openconsole/inbox' into main 2021-04-13 09:06:06 -05:00
Don-Vito
b8e36bae9f Prevent mouse dragging from dismissing existing selection (#9790)
## PR Checklist
* [x] Closes https://github.com/microsoft/terminal/issues/9787
* [x] CLA signed.
* [ ] Tests added/passed
* [ ] Documentation updated.
* [ ] Schema updated.
* [ ] I've discussed this with core contributors already. 

## Validation Steps Performed
* [x] single click = no selection
* [x] single click and drag = selection starting from first point
* [x] single click in unfocused pane and drag = focus pane, selection starting from first point
* [x] double-click = selects a whole word
* [x] triple-click = selects a whole line
* [x] double-click and drag = selects a whole word, drag selects whole words
* [x] triple-click and drag = selects a whole line, drag selects whole lines
* [x] Shift single-click = defines start point
* [x] second Shift single-click = defines end point
* [x] Shift double-click = selects entire word
* [x] Shift triple-click = selects entire line
* [x] Shift double-click and drag = selects entire word, drag selects whole words
* [x] Mouse mode: Shift single-click = defines start point
* [x] Mouse mode: second Shift single-click = defines end point
* [x] Mouse mode: Shift double-click = selects entire word
* [x] Mouse mode: Shift triple-click = selects entire line
* [x] Mouse mode: Shift double-click and drag = selects entire word, drag selects whole words
* [x] With existing selection: single-click outside the selection and drag = establishes a new selection starting from the click point 
* [x] Click-drag to set selection, shift-click-drag outside of selection = extend selection while dragging
2021-04-12 23:32:22 +00:00
PankajBhojwani
7df4b3c823 Fix for some profiles.defaults settings not working (#9764)
Fix for profiles.defaults.colorScheme not working 
Fix for background image only showing up after a settings reload

Closes #9761
2021-04-12 18:21:56 +00:00
Dustin L. Howett
8f79f7c4c8 Propagate the hosting HWND to the new IFileDialogs (#9789)
This is required to maintain the modality of the dialogs, which we lost
when we moved from Pickers to IFileDialog. The HWND hosting Window API
we dreamed up is incompatible with IModalDialog, because IModalDialog
requires the HWND immediately upon `Show`. We're smuggling it in a
uint64, as is tradition.

zadjii-msft noticed this in #9760.
2021-04-12 16:55:51 +00:00
James Holderness
83bd241cd2 Remove unused methods in ConGetSet and SCREEN_INFORMATION (#9772)
This PR removes the `GetConsoleCursorInfo` and `SetConsoleCursorInfo`
methods from the `ConGetSet` interface, and the `GetScrollingRegion`
method from the `SCREEN_INFORMATION` class. None of these methods are
used anymore.

PR #2764 removed the last usage of `GetScrollingRegion`. 

The `Get/SetConsoleCursorInfo` methods don't seem to have ever been used
in the time that the terminal code has been open source, so whatever
purpose they originally served must have been replaced a long time ago.

The `GetScrollingRegion` method was originally called from the
`ScrollRegion` function, but that usage was removed in PR #2764 when the
margin handling was moved to a higher level.

I've checked that the code still compiles.

Closes #9771
2021-04-12 15:35:14 +00:00
hessedoneen
9a276c6371 Bind Ctrl+Numpad Plus,Minus to the font size controls (#9753)
"ctrl+numpad_plus" command now increases font size and
"ctrl+numpad_minus" command now decreases font size.

Before this only "ctrl+=" and "ctrl+-" controlled font size. Increase in
font size follows previous convention where zooms in arbitrarily large,
but decrease in font size is capped.

## Validation Steps Performed
I first ran "ctrl+=" and "ctrl+-" in my terminal to verify its behavior,
then compared that against "ctrl+numpad_plus" and "ctrl+"numpad_minus".
Both increased and decreased the font size by the same amount, and both
appeared to have a cap for how small they could get, but did not appear
to have a cap for how big they could get.

Closes #7518
2021-04-12 15:07:46 +00:00
Don-Vito
ca9e5e0fb0 Trigger taskbar progress evaluation upon pane activation (#9779)
Trigger TaskbarProgressChanged every time we switch between active panes
(to update the tab header if required).

Closes #9743
2021-04-12 15:05:52 +00:00
Don-Vito
912bd4dadb Remove the icon grid column from the Actions page (#9780)
Closes #9715
2021-04-12 15:04:27 +00:00
Don-Vito
a9a58f7156 Handle switch to tab binding even if tab doesn't exist (#9781)
Closes #9635
2021-04-12 15:03:59 +00:00
Breece W
d367c6b6b0 Smoothen close caption button animation (#9763)
The red close button animation fades to gray then to transparent, when
standard behavior skips the gray part. I manually tested in light/dark/high
contrast mode.

Closes #9762
2021-04-12 13:13:18 +00:00
Dustin L. Howett
959c423e7a Replace Windows.Storage.Pickers with Common File Dialogs (#9760)
Using Pickers from an elevated application yields an
ERROR_ACCESS_DENIED. Of course it does: it was designed for the modern
app platform.

Using the common dialog infrastructure has some downsides¹, but it
doesn't crash and is just as flexible.

I've added some fun templated functions that help us with the
complexity.

Fixes #8957

¹You've got to use raw COM, and it runs in-proc instead of out-of-proc.

## Validation Steps Performed
I tested every picker.
2021-04-12 13:12:08 +00:00
Dustin L. Howett
b310b1cffc Give our NavigationView's acrylic a fallback color (#9752)
It will be a different color than the background, so it will look less
weird when it's unfocused. It also fixes the bug where the navigation
menu is transparent when acrylic is disabled systemwide.

Fixes #9337
2021-04-09 10:43:15 +00:00
PankajBhojwani
9e83655b08 Add support for a profile to specify an "unfocused" appearance (#8392)
This pull request adds an appearance configuration object to our
settings model and app lib, allowing the control to be rendered
differently depending on its state, and then uses it to add support for
an "unfocused" appearance that the terminal will use when it's not in
focus.

To accomplish this, we isolated the appearance-related settings from
Profile (into AppearanceConfig) and TerminalSettings (into the
IControlAppearance and ICoreAppearance interfaces). A bunch of work was
done to make inheritance work.

The unfocused appearance inherits from the focused one _for that
profile_. This is important: If you define a
defaults.unfocusedAppearance, it will apply all of defaults' settings to
any leaf profile when a terminal in that profile is out of focus.

Specified in #8345 
Closes #3062
Closes #2316
2021-04-08 22:46:16 +00:00
Michael Niksa
7f5a19b627 Reduce instances of font fallback dialog (#9734)
Reduce instances of font fallback dialog through package font loading,
basic name trimming, and revised fallback test

- Adjusts the font dialog to only show when we attempt last-chance
  resolution from our hardcoded list of font names with a flag instead
  of with a string comparison by name
- Adds a resolution step to trim the font name by word from the end and
  retry to attempt to resolve a proper font that just has a weight
  suffix
- Adds a second font collection to font loading that will attempt to
  locate all TTF files sitting next to our binary, like in our package

- [x] Wrote my font preference in the JSON as `Cascadia Code Heavy` and
  watched it quietly resolve to just `Cascadia Code` without the dialog.
- [x] Put a font that isn't registered with the system into the layout
  directory for the package, set it as my desired font in Terminal, and
  watched it load just fine.
- [x] Try a font name with different casing and see if dialog doesn't
  pop anymore
- [x] Try a font with different (localized) names like MS ゴシック and
  see if dialog doesn't pop anymore
- [x] Check Win7 with WPF target

Closes #9375
2021-04-08 17:49:07 +00:00
Dustin Howett
8e7a866b06 Merged PR 5903250: [Git2Git] Merged PR 5895926: conhost: fix two moderately high-hitting Watsons
This commit fixes two issues:

1. We were pushing ConsoleWaitBlocks into the wait queue before they
   were fully constructed. This resulted in the wait being called before
   it even had an API message in it. [MSFT-24113101]
2. Drawing DBCS characters and resizing (a lot) would cause a crash
   because of an invalid DBCS cell state. This hits in both Terminal and
   conhost. [MSFT-17364373] [GH-4907]

The DBCS state check was promoted from an NT_ASSERT (which never fired
in release) to a FAIL_FAST in !1794053. The console kept chugging along
without failing in fre for all those years.

Fixes MSFT-24113101
Fixes MSFT-17364373
Fixes GH-4907 Retrieved from https://microsoft.visualstudio.com os.2020 OS official/rs_wdx_dxp_windev 108e746630749aa7851dd813b19e013ae31ef0db
2021-04-08 17:29:47 +00:00
Chester Liu
ed1cd32f1f Disable warning about multi-line paste when brackted paste is on (#9586)
Closes #7006
2021-04-07 18:13:50 +00:00
Mike Griese
361877cf1b Match the RequestedTheme of our TeachingTips to the set theme (#9732)
## Summary of the Pull Request

Make sure that the window renamer and other toasts follow the requested app theme. We accomplish this by doing something similar to what we do with ContentDialogs. Since TeachingTips aren't in the same XAML root, we have to traverse the entire tree upwards setting RequestedTheme. If we don't, then we'll update the background color of the TeachingTip, but not the text inside it. 

## References
* Added in #9662 and #9523 

## PR Checklist
* [x] Closes #9717
* [x] I work here
* [n/a] Tests added/passed
* [n/a] Requires documentation to be updated

## Validation Steps Performed
Tested with system theme light & dark, and `theme` set to `light, dark, and unset, and verified that they worked as expected.
2021-04-07 15:27:41 +00:00
Mike Griese
cdf2630204 Add support for LightDismissing the renamer (#9733)
## Summary of the Pull Request

Huh, I guess I missed making the window renamer light-dismissable. This is a oneline fix for that.

Light dismissing is treated as a _cancel_, not as a commit. 

## References
* Added in #9662

## PR Checklist
* [x] Closes #9718 
* [x] I work here
* [n/a] Tests added/passed
* [n/a] Requires documentation to be updated

## Validation Steps Performed

This feels right.
2021-04-07 00:12:41 +00:00
Mike Griese
c3f968b6c6 Stylize the renamer Action button as a Accent button (#9728)
Closes #9719
2021-04-06 14:25:18 -05:00
Don-Vito
e80e9b9e96 Fix marking of new selection start (#9727)
## PR Checklist
* [x] Closes https://github.com/microsoft/terminal/issues/9725
* [x] CLA signed. 
* [ ] Tests added/passed
* [ ] Documentation updated. 
* [ ] Schema updated.
* [ ] I've discussed this with core contributors already. 

## Validation Steps Performed
* [x] single click = no selection
* [x] single click and drag = selection starting from first point
* [x] single click in unfocused pane and drag = focus pane, selection starting from first point
* [x] double-click = selects a whole word
* [x] triple-click = selects a whole line
* [x] double-click and drag = selects a whole word, drag selects whole words
* [x] triple-click and drag = selects a whole line, drag selects whole lines
* [x] Shift single-click = defines start point
* [x] second Shift single-click = defines end point
* [x] Shift double-click = selects entire word
* [x] Shift triple-click = selects entire line
* [x] Shift double-click and drag = selects entire word, drag selects whole words
* [x] Mouse mode: Shift single-click = defines start point
* [x] Mouse mode: second Shift single-click = defines end point
* [x] Mouse mode: Shift double-click = selects entire word
* [x] Mouse mode: Shift triple-click = selects entire line
* [x] Mouse mode: Shift double-click and drag = selects entire word, drag selects whole words
* [x] With existing selection: single-click outside the selection and drag = establishes a new selection starting from the click point
2021-04-06 19:17:26 +00:00
Mike Griese
6ca35b4445 Manually handle Enter and Escape in the Window Renamer (#9730)
## Summary of the Pull Request

In exactly the same fashion as the tab renamer, handle <kbd>Enter</kbd> for committing the rename, and <kbd>Escape</kbd> for dismissing the rename.

## References
* Added in #9662

## PR Checklist
* [x] Closes #9720
* [x] I work here
* [n/a] Tests added/passed
* [n/a] Requires documentation to be updated

## Validation Steps Performed
Played with it - this feels good.
2021-04-06 19:12:08 +00:00
Mike Griese
b0c07ef1eb Add identifyWindow(s?) to the schema (#9726)
Fixes #9721.
2021-04-06 14:11:29 -05:00
Leonard Hecker
faf372f165 Resolve circular reference in ThrottledFunc (#9729)
## Summary of the Pull Request

ThrottledFunc previously created a DispatcherTimer whose Tick callback holds a strong reference to the DispatcherTimer itself.
This causes a reference cycle, inadvertently leaking timer instances.

## PR Checklist

* [x] Closes #7710
* [x] I work here

## Detailed Description of the Pull Request / Additional comments

I've initially wanted to remove the `ThrottledFunc<>` optimization, but it turns out that this causes a 3% slowdown. That's definitely not a lot, but enough that we can just keep the optimization for the time being.
I've moved the implementation from the .cpp file into the header regardless since the two implementations are extremely similar and it's easier that way to keep them in line.

## Validation Steps Performed

I've ensured that the scrollbar still updates its length when I add new lines to a newly created tab.
2021-04-06 19:07:49 +00:00
Ikko Ashimine
ebd07d7125 Fix typo in replace.h (#9679)
occurences -> occurrences
2021-04-06 10:38:51 -05:00
Mike Griese
24b9a7a247 Create a control unittesting project (#9677)
Does what it says on the can.

This is a follow up to #9472. Now that we have a control .lib, we can add tests for it. 

Unfortunately, the `TermControl` itself is a horrible mess. So this new unittest lib is empty for now. I'm working on actual tests as a part of #6842, but this PR is here to keep the diffs smaller.

Also, apparently `server.vcxproj` had the wrong GUID in it.

* [x] I work here
* [x] Adds tests
2021-04-05 16:07:55 +00:00
Dustin L. Howett
6f754a61eb Fix bad merge in c585a93fc (CloseRequested) that broke closing (#9695)
This fixes right-click > "Close" on TerminalTab

Fixes bug bash issue.
2021-04-05 12:25:06 +00:00
Mike Griese
fb597ed304 Add support for renaming windows (#9662)
## Summary of the Pull Request

This PR adds support for renaming windows.

![window-renaming-000](https://user-images.githubusercontent.com/18356694/113034344-9a30be00-9157-11eb-9443-975f3c294f56.gif)
![window-renaming-001](https://user-images.githubusercontent.com/18356694/113034452-b5033280-9157-11eb-9e35-e5ac80fef0bc.gif)


It does so through two new actions:
* `renameWindow` takes a `name` parameter, and attempts to set the window's name
  to the provided name. This is useful if you always want to hit <kbd>F3</kbd>
  and rename a window to "foo" (READ: probably not that useful)
* `openWindowRenamer` is more interesting: it opens a `TeachingTip` with a
  `TextBox`. When the user hits Ok, it'll request a rename for the provided
  value. This lets the user pick a new name for the window at runtime.

In both cases, if there's already a window with that name, then the monarch will
reject the rename, and pop a `Toast` in the window informing the user that the
rename failed. Nifty!

## References
* Builds on the toasts from #9523
* #5000 - process model megathread

## PR Checklist
* [x] Closes https://github.com/microsoft/terminal/projects/5#card-50771747
* [x] I work here
* [x] Tests addded (and pass with the help of #9660)
* [ ] Requires documentation to be updated

## Detailed Description of the Pull Request / Additional comments

I'm sending this PR while finishing up the tests. I figured I'll have time to sneak them in before I get the necessary reviews.

> PAIN: We can't immediately focus the textbox in the TeachingTip. It's
> not technically focusable until it is opened. However, it doesn't
> provide an even tto tell us when it is opened. That's tracked in
> microsoft/microsoft-ui-xaml#1607. So for now, the user _needs_ to
> click on the text box manually.
> We're also not using a ContentDialog for this, because in Xaml
> Islands a text box in a ContentDialog won't recieve _any_ keypresses.
> Fun!

## Validation Steps Performed

I've been playing with 

```json
        { "keys": "f1", "command": "identifyWindow" },
        { "keys": "f2", "command": "identifyWindows" },
        { "keys": "f3", "command": "openWindowRenamer" },
        { "keys": "f4", "command": { "action": "renameWindow", "name": "foo" } },
        { "keys": "f5", "command": { "action": "renameWindow", "name": "bar" } },
```

and they seem to work as expected
2021-04-02 16:00:04 +00:00
Dustin L. Howett
4b7d955012 dx: add support for inverting all types of cursor (#9665)
This commit introduces support for inverting all types of cursor.

To invert the display without re-rendering any text, we draw the cursor
into a command list and then compose the command list with the existing
renderer using the MASK_INVERT composition flag.

This wouldn't normally work with our renderer because there is no
_background_ color to invert in some cases (such as when acrylic is in
use.)

To work around that, we're taking advantage of @zadjii-msft's two-pass
cursor renderer.

To properly invert the cursor over a transparent background:
(Examples are given below for two cursor types, but this applies to all
of them.)

First, we'll draw a "backplate" in the user's requested background color
(with the alpha channel set to 0xFF). (`firstPass` == true)

    EMPTY BOX  FILLED BOX
    =====      =====
    =   =      =====
    =   =      =====
    =   =      =====
    =====      =====

Second, the glyph is drawn (outside of the cursor renderer).

    EMPTY BOX  FILLED BOX
    ==A==      ==A==
    =A A=      =A=A=
    AAAAA      AAAAA
    A   A      A===A
    A===A      A===A

Last, we'll draw the cursor again in all white and use that as the
*mask* for inverting the already-drawn pixels. (`firstPass` == false) (#
= mask, a = inverted A)

    EMPTY BOX  FILLED BOX
    ##a##      ##a##
    #A A#      #a#a#
    aAAAa      aaaaa
    a   a      a###a
    a###a      a###a

Related to #9610

## Validation Steps Performed
Manual visual validation in all configurations.
2021-04-02 11:18:06 +00:00
Kayla Cinnamon
fd99b012f5 Remove default terminal item from SUI (#9671)
Looks like we forgot to comment out the Default terminal setting from the UI.

Commented it out here.
2021-03-31 18:50:40 +00:00
Mike Griese
c09472347c Add X Macro for fun and for profit (#9667)
**Summary of the Pull Request**

This PR adds an X Macro for defining our ShortcutActions. This means that you can add the action in one place, and have the macro synthesize all sorts of boilerplate for you!

From the `AllShortcutActions.h` file:

> For a clearer explanation of how this file should be used, see:
> https://en.wikipedia.org/wiki/X_Macro
>
> Include this file to be able to quickly define some code in the exact same
> way for _every single shortcut action_. To use:
>
> 1. Include this file
> 2. Define the ON_ALL_ACTIONS macro with what you want each action to show up
>    as. Ex:
>
>    #define ON_ALL_ACTIONS(action) void action##Handler();
>
> 3. Then, use the ALL_SHORTCUT_ACTIONS macro to get the ON_ALL_ACTIONS marcro
>    repeated once for every ShortcutAction
>
> This is used in KeyMapping.idl, ShortcutAction.*, TerminalPage.*, etc. to
> reduce the number of places where we must copy-paste boiler-plate code for
> each action. This is _NOT_ something that should be used when any individual
> case should be customized.

**PR Checklist**
* [x] Scratches an itch
* [x] I work here
* [x] Tests passed
* [n/a] Requires documentation to be updated

**Detailed Description of the Pull Request / Additional comments**

Originally I had this blocked as a follow up to #9662. However, I've grown tired after a month of merging main into this branch, and I'm just shipping it separately. It will inevitably conflict with anyone who has actions in flight currently.

**Validation Steps Performed**
The code still builds exactly as before!
2021-03-31 16:38:25 +00:00
Dustin L. Howett
07c5735471 Move Branding into common props (#9668) 2021-03-30 18:08:53 -05:00
Dustin Howett
940254dd57 Fix variable shadowing in exemain from bad merge
Signed-off-by: Michael Niksa <miniksa@microsoft.com>
2021-03-30 17:43:48 -05:00
Dustin Howett
9d729a50b0 Merge remote-tracking branch 'openconsole/inbox' into HEAD 2021-03-30 16:10:49 -05:00
Gabriel C
8f16fdd817 Fix rightmost tab corner (#9575)
Before and after:
![image](https://user-images.githubusercontent.com/78622729/111928492-710b9000-8abc-11eb-9760-0ecd3eb038fb.png)

The bottom right corner appeared without a radius because of the custom padding.
2021-03-30 21:03:33 +00:00
Mike Griese
5ab78fcafb Another fix for the localtests, March 2021 edition (#9660)
Broadly, the tests were broken by #7489 because there were no `_startupActions`. They relied on the removed codepath that assumed `wt.exe` always set actions, or `AppCommandlineArgs::ValidateStartupCommands` created one by default.

* [x] fixes #9659
* [x] I work here
* [x] the tests pass again
2021-03-30 20:38:43 +00:00
Mike Griese
69df0de6bf Add a sample outline shader (#9646)
I accidentally the whole thing

## References
* Heavily inspired by the original rasterbars shader.

## PR Checklist
* [x] Closes #9010
2021-03-30 15:37:53 -05:00
Dustin L. Howett
295fa38295 Introduce MS.Term.Core.Color to replace W.U.Color for Core/Control/TSM (#9658)
This pull request introduces Microsoft.Terminal.Core.Color as an
alternative to both Windows.UI.Color and uint32_t/COLORREF in the
TerminalCore, ...Control, ...SettingsModel and ...SettingsEditor layers.

M.T.C.Color is trivially convertible to/from til::color and therefore
to/from COLORREF, W.U.Color, and any other color representation we might
need².

I've replaced almost every use of W.U.Color and uint32_t-as-color in the
above layers, with minor exception¹.

The need for this work is twofold.

First: We cannot bear a dependency from TerminalCore (which should,
on paper, be Windows 7 compatible) on Windows.UI or any other WinRT
namespace.

This work removes one big dependency on Windows.UI, but it does not go
all the way.

Second: TerminalCore chose to communicate mostly in packed uint32s
(COLORREF), which was inherently lossy and dangerous.

¹ The UI layers (TerminalControl, TerminalApp) still use
Windows.UI.Color as they are intimately connected to the UWP XAML UI.

² In the future, we might even be able to *use* the alpha channel...

## PR Checklist
* [x] I ran into the need for this when I introduced cursor inversion
* [X] Fixes a longstanding itch

## Validation Steps Performed
Built and ran all tests for the impacted layers, even the local ones!
2021-03-30 20:15:49 +00:00
Mike Griese
03ea0f49ad Add an action for identifying windows (#9523)
## Summary of the Pull Request

This is a follow up to #9300. Now that we have names on our windows, it would be nice to see who is named what. So this adds two actions:

* `identifyWindow`: This action will pop up a little toast (#8592) displaying the name and ID of the window, and is bound by default.
![identify-window-toast-000](https://user-images.githubusercontent.com/18356694/111529085-bf710580-872f-11eb-8880-b0b617596cfc.gif)

* `identifyWindows`: This action will request that ALL windows pop up that toast. This is meant to feel like the "Identify" button on the Windows display settings. However, sometimes, it's wonky. 
  ![teaching-tip-dismiss-001](https://user-images.githubusercontent.com/18356694/111529292-fe06c000-872f-11eb-8d4a-5688e4ce1175.gif)
  That's being tracked upstream on https://github.com/microsoft/microsoft-ui-xaml/issues/4382
  Because it's so wonky, we won't bind that by default. Maybe if we get that fixed, then we'll change the default binding from `identifyWindow` to `identifyWindows`


## References

## PR Checklist
* [x] Closes https://github.com/microsoft/terminal/projects/5#card-51431492
* [x] I work here
* [x] Tests added/passed
* [ ] Requires documentation to be updated

## Detailed Description of the Pull Request / Additional comments

You may note that there are some macros to make interacting with lots and lots of actions easier. There's a lot of boilerplate whenever you need to make a new action, so I thought: "Can we make that easier?" 

Turns out you can make it a _LOT_ easier, but that work is still behind another PR after this one. Get excited
2021-03-30 16:08:03 +00:00
Don-Vito
c585a93fc9 Separate between Close Tab Requested and Tab Closed flows (#9574)
## Summary of the Pull Request
Currently, both when the tab is already closed, and when there is a
request to close a tab (might be rejected), we go through the same flow
in TerminalPage.

This might leave the system in inconsistent state, as the side-effects
of closing will persist even if the closing was aborted.

This PR separates between the two flows, by introducing a CloseRequested
event to the TabBase.

This event is used to inform the upper tier (the terminal page) about
the request and to trigger the same logic that happens when the tab is
closed directly from the terminal page (e.g., by clicking close on the
tab view).

The Closed event will be  used only to handle the actual closing of the
tab. It will ensure that the tab gets removed from the terminal page if
required.

As a result, it a read-only pane will be closed non-interactively (aka
connection exits), the tab closed flow will be invoked, and no user
prompt will be shown.

## References

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
* [x] Closes https://github.com/microsoft/terminal/issues/9572
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [ ] Tests added/passed
* [ ] Documentation updated. 
* [ ] Schema updated.
* [ ] I've discussed this with core contributors already.
2021-03-30 15:58:35 +00:00
Carlos Zamora
19fb9b21da Remove Base Layer from Settings UI (#9655)
Removes base layer (aka profiles.defaults) from the Settings UI. `SettingContainer` was also updated to not present a revert arrow when overriding a base layer value.

The new experience is now as follows:
- The revert arrow will only appear if you are overriding a value from a fragment extension.
- Users are still able to fully interact with `profiles.defaults` in their settings.json. Doing so still propagates those changes to their profiles as normal. In this case, the Settings UI presents the base layer value as the one that you selected.

#6800 - Settings UI Epic
Closes #9539
2021-03-30 15:15:37 +00:00
Michael Niksa
c7d2a818b0 Change TAEF nuget package to use new Microsoft.Taef name; Update to 10.58 release build version. (#9656)
Change TAEF nuget package to use new Microsoft.Taef name; Update to 10.58 release build version.

## PR Checklist
* [x] Closes email from Phil letting us know TAEF has a new release build and a rename.
* [x] Closes annoying duplicate TAEF import warning in `Parser.UnitTests.vcxproj`
* [x] I work here.
* [ ] Need to see the tests run off CI to confirm this is fine for those environments and Helix

## Validation Steps Performed
* [x] Build/run tests locally
* [ ] Build/run unit and feature tests in CI
* [ ] Build/run Helix-lab tests in CI
2021-03-30 10:58:11 +00:00
kovdu
cf5dc285a9 Sort fonts alphabetically (#9653)
Validated this only by following the issue description and verified fonts appeared to be sorted alphabetically now.

Closes #9594
2021-03-29 15:36:28 -07:00
Mike Griese
3323dc5724 Auto-format our XAML files and enforce in CI (#9589)
This adds [`XamlStyler.Console`] to our solution, and calls it when we
format the code, to also format
our .xaml files. 
* `XamlStyler.Console` is a dotnet tool so it needs to be restored with
  `dotnet tool restore`
* I've added a set of rules to approximately follow [@cmaneu's XAML guidelines].
  Those guidelines also recommend things based on the code-behind, which
  this tool can't figure out, but also _don't matter that much_.
* There's an extra step to strip BOMs from the output, since Xaml Styler
  adds a BOM by default. Some had them before and others didn't. BOMs
  have been nothing but trouble though.

[`XamlStyler.Console`]: https://github.com/Xavalon/XamlStyler
[@cmaneu's XAML guidelines]: https://github.com/cmaneu/xaml-coding-guidelines
2021-03-29 17:09:38 -05:00
Don-Vito
c19aa89123 Add splitMode to the args for split-pane (as -D for duplicate) (#9652)
Closes #9579
2021-03-29 17:06:05 -05:00
Michael Niksa
d6954244ad Fix ARM64 build by defining WIN32 (#9654)
By default from ARM64 architecture projects, `WIN32` is not defined. It
is supposed to be for this proxy stub to work. So I've set it with the
preprocessor for this project.

## PR Checklist
* [x] Closes release build failure after #7489 
* [x] I work here.
* [x] Built on my machine.

## Detailed Description of the Pull Request / Additional comments

`WIN32` appears to convey two meanings depending on who you are:
- To most of Windows, `WIN32` appears to mean the Win32 API surface and
  sometimes the major OS version that goes with it. (Specifically in
  contrast to 16-bit Windows.)
- To others, `WIN32` appears to mean a 32-bit processor or a synonym of
  `x86`.

This is generally not a problem for a few reasons:
- VS defines `WIN32` in the default targets/props only for the `x86`
  processor type. **BUT**
- Windows defines `WIN32` if it's not already defined in both
  `minwinbase.h` and `ole2.h` which generally speaking manage to get
  compiled into practically everything especially since `minwinbase.h`
  tends to sneak itself in somehow through `windows.h` and that's
  **THE** include to use the Windows API surface.
- Windows also defines `WIN32` for itself unconditionally and relatively
  globally when building itself.

However, it's a problem here because: 
- `rpcproxy.h` is the only header included in `dlldata.c`, a file
  generated automatically by `midl.exe` in the SDK when making a proxy
  stub.
- `rpcproxy.h` only defines its contents for a proxy when `WIN32` or
  `_M_AMD64` are found.
- Therefore, it's defined pretty naturally for x86 and AMD64 targets
  from VS, but not for ARM64.
- ARM64 support is pretty new and those who are attempting to build for
  ARM64 and against the public SDK with Visual Studio for a classic COM
  proxy... seems like a relatively unlikely combination.

I will follow up with the Visual Studio, Windows SDK, and MIDL/COM teams
to try to remove this pitfall from the public tooling. But for now, this
is the fix.
2021-03-29 16:43:58 -05:00
Dragos Sambotin
477c4d9986 Merged PR 5854784: [Git2Git] When V1 console fails to load, fail upwards to V2
This change introduces a fallback from v1 to v2 for SKUs of Windows that do not compose v1.

Related work items: MSFT-29352720
2021-03-29 15:40:27 -05:00
Michael Niksa
b67f40f4c4 Switch to create function as it will open if exists and create if not. 2021-03-29 15:35:16 -05:00
Dustin Howett
25527789c0 Reflect testlist change from OS 2021-03-29 15:24:43 -05:00
Mike Griese
eac3eea484 Add a --colorScheme param to new-tab, split-pane (#9602)
This is entirely self-serving. In my go-to config, I like having some of
the panes for a given profile in a different color scheme. This will let
a user pass `--colorScheme <scheme name>` to manually override the
scheme for that profile. Neat!
2021-03-29 20:04:39 +00:00
Mike Griese
ba543c0696 minor refactor: Move Tab management into its own file (#9629)
I think we can all agree that `TerminalPage.cpp` is an unruly beast of a
file. It's got everything. It does everything. It can sometimes be a bit
hard to work with, because of simply how big it is. This PR tries to
alleviate this by making `TerminalPage.cpp` just a little smaller. It
does so by moving pretty much everything related to tab management into
its own file, `TabManagement.cpp`. These methods that have moved are all
the same as they were before, and they're still members of
`TerminalPage`. But now they're all in one place. 

I tried to move all the references to `_tabs` in `TerminalPage.cpp`, but
there's still a few that I left behind. Mostly because I felt that
moving those would be too gnarly a code change for an otherwise simple
cut&paste PR.

There are a few new methods I introduced:
* `_TabDragStarted` and `_TabDragCompleted`: These were lambdas before,
  promoted to full methods. 
* `_DismissTabContextMenus`: Remove all the right-click context menus
  from the tabs
* `_FocusCurrentTab`: This one's a bit trickier, we were actually doing
  this in a few different places, so I tried consolidating.
* `_HasMultipleTabs`: This doesn't need explaining.
* `_RemoveAllTabs`: Really, just encapsulation for the sake of removing
  a `_tabs` from `TerminalPage.cpp`
* `_ResizeTabContent`: Really, just encapsulation for the sake of
  removing a `_tabs` from `TerminalPage.cpp`

In the future, some enterprising young soul could try promoting that
file to its own class, and hiding `_tabs` (and `_mruTabs`) inside it.
Probably would need to take a reference to TerminalPage's `_tabView` and
`_newTabButton`. I'm not doing that right now, because I already hate
the idea of the ...

>  920 additions and 847 deletions.

... I'm making you look at already.

## Other thoughts

Some of the calls might be a little arbitrary - `_OpenNewTab` and
`_CreateNewTabFromSettings` probably should stay in `TerminalPage`? Or
at least elements of those might need to get split up better. Similarly
`TerminalPage::_OpenSettingsUI` stayed in `TerminalPage.cpp`, but it
does a lot of the same work as `_CreateNewTabFromSettings`. I'm not
saying this is the definitive places for these methods - it's code we're
working with, not stone ☺️
2021-03-29 19:53:39 +00:00
Dustin Howett
8470857016 Fix the preview package manifest
We missed this one.
2021-03-29 12:23:57 -05:00
Don-Vito
704836e45d Prevent arrow keys from dismissing tab renamer (#9633)
## PR Checklist
* [x] Closes https://github.com/microsoft/terminal/issues/9632
* [x] CLA signed. 
* [ ] Tests added/passed
* [ ] Documentation updated. 
* [ ] Schema updated.
* [ ] I've discussed this with core contributors already.
2021-03-29 17:23:09 +00:00
Mike Griese
5a78566628 Make TerminalConnection depend on OpenConsoleProxy (#9649)
Building code merged with `main` this morning, and I hit a error where
the `TerminalConnection` project needed `ITerminalHandoff` something or
other, but that hadn't been built yet. I suspect that's because the
`OpenConsoleProxy` project needs to be built first, but it isn't set as
a dependency.

I suspect once that project is built once, this isn't ever an issue, but
I hadn't done that yet.

This fixed the build for me locally. 

* [x] fixes local dev builds
* [x] also updates the name of the TerminalControl project in the .sln,
      because apparently VS didn't like that.
2021-03-29 11:57:18 -05:00
Chester Liu
806d992a06 Prefer constexpr over const across the codebase (#9587)
Supports #2941

Previous PRs: #3362 #3416
2021-03-29 16:03:16 +00:00
Dustin L. Howett
12275c8599 Add a Fuzzing configuration and a version of conhost that can be fuzzed (#9604)
This commit introduces a new build configuration, "Fuzzing", which
enables the new address sanitizer (shipped in VS 16.9) and code
coverage over the entire solution. Only a small subset of projects
(those comprising original conhost, right now) are selected to build in
this configuration, and even then only in Fuzzing|x64.

It also adds a fuzzing-adapted build of conhost, which makes no server
connections and handles no client applications. To do this, I've
replicated a bit of the console startup routine into fuzzmain.cpp and
made up some fake data. This is the bare minimum required to boot up
Win32 interactivity (or VT interactivity!) and pretend that a process
has connected.

If we don't pretend that a process has connected, "conhost" will exit
immediately. If we don't forge the process list, conhost will exit. If
we can't provide a server handle, we can't provide a "device comm".

Minor changes were necessary to server/host such that they would accept
a preexisting "device comm". We use this new behavior to provide a
"null" one that only hangs up threads and otherwise responds to requests
successfully.

This fuzzing-adapted build links LLVM's libFuzzer, which is an excellent
coverage-based fuzzer that will produce a corpus of inputs that exercise
unique codepaths. Eventually, we can use this to generate known-"good"
inputs for anything.

I've gone ahead and added a fuzz function that yeets bytes directly into
WriteCharsLegacy, which was the original reason I went down this path.

The implementation of LLVMFuzzerTestOneInput should be replaced with
whatever you want to fuzz.
2021-03-29 14:23:30 +00:00
Dustin L. Howett
ea3e56db81 Add some read and write locks around pattern tree manipulation (#9618)
We have been seeing some crashes (#9410) originating from a
use-after-free or a double-free in the renderer. The renderer is
iterating over the dirty rects from the render engine¹ and the rect list
is being freed out from under it.

Things like this are usually the result of somebody manipulating the
renderer's state outside of lock.

Therefore, this pull request introduces some targeted locking fixes
around manipulation of the pattern buffer (which, in turn, changes the
renderer state.)

¹ This was not a problem until #8621, which made the renderer return a
span instead of a copy for the list of dirty rects.

## Validation

I ran Terminal under App Verifier, and introduced a manul delay (under
lock) in the renderer such that the invalid map would definitely have
been invalidated between the renderer taking the lock and the renderer
handling the frame. AppVerif failed us without these locking changes,
and did not do so once they were introduced.

Closes #9410.
2021-03-26 22:11:08 +00:00
Michael Niksa
906edf7002 Implement Default Terminal (#7489)
- Implements the default application behavior and handoff mechanisms
  between console and terminal. The inbox portion is done already. This
  adds the ability for our OpenConsole.exe to accept the incoming server
  connection from the Windows OS, stand up a PTY session, start the
  Windows Terminal as a listener for an incoming connection, and then
  send it the incoming PTY connection for it to launch a tab.
- The tab is launched with default settings at the moment.
- You must configure the default application using the `conhost.exe`
  propsheet or with the registry keys. Finishing the setting inside
  Windows Terminal will be a todo after this is complete. The OS
  Settings panel work to surface this setting is a dependency delivered
  by another team and you will not see it here.

## Validation Steps Performed
- [x] Manual adjust of registry keys to the delegation conhost/terminal
  behavior
- [x] Adjustment of the delegation options with the propsheet
- [x] Launching things from the run box manually and watching them show
  in Terminal
- [x] Launching things from shortcuts and watching them show in the
  Terminal   

Documentation on how it works will be a TODO post completion in #9462

References #7414 - Default Terminal spec

Closes #492
2021-03-26 17:09:49 -05:00
Dustin L. Howett
690fbbbc27 Add a WPR profile containing all of Terminal's ETW providers (#9630) 2021-03-26 11:46:14 -05:00
John Stephens
005b8cc5e0 Link to WinMM.Lib for PlaySound with 19041 (#9624)
The PlaySound functions were removed from OneCoreUAP_apiset.Lib in Windows 10 SDK 19041 because they did not actually belong there. Link to WinMM.Lib for PlaySoundW.

### Validation Steps Performed

* Built for x64 from repository root with: `MSBuild.exe -property:TargetPlatformVersion=10.0.19041.0`
* Installed CascadiaPackage_0.0.1.0_x64_Debug.msix and launched on 19042.867
2021-03-26 11:35:17 -05:00
Don-Vito
e02d9a48e3 Fix wrong item template selection in CmdPal (#9487)
There seems to be a bug in WinUI (see microsoft/microsoft-ui-xaml#2121)
that results in heterogeneous `ModernCollectionBasePanel`  configured
with `DataTemplateSelector` and virtualization enabled to recycle a
container even if its `ContentTemplate` is wrong.

I considered few options of handling this:
* Disabling virtualization (by replacing item container template with
  some non-virtualizing panel (e.g., `StackPanel`,
  `VirtualizingStackPanel` with `VirtualizationMode`=`Standard`)
* Replacing `DataTemplateSelector` approach with `ChoosingItemContainer`
  event handling approach, which allows you to manage the item container
  (`ListViewItem`) allocation process.

I have chosen the last one, as it should limit the amount of
allocations, and might allow optimizations in the future.
 
The solution introduces:
* A container for `ListViewItem`s in the form of a map of sets:
  * The key of this map is a data template (e.g., `TabItemDataTemplate`)
  * The value in the set is the container
* `ChoosingItemContainer` event handler that looks for available item in
  the container or creates a new one
* `ContainerContentChanging` event handler that returns the recycled
  item to the container

Closes #9288
2021-03-24 20:11:35 -05:00
Dustin L. Howett
da1e1a693e WriteCharsLegacy: Add some notes in comments and rename WC_ECHO (#9605)
This commit clarifies some things inside WriteCharsLegacy by adding
comments and renaming parameter and enum names. It does not change any
logic.

`WC_ECHO` was used extensively to mean only one thing: whether to print
a control character like `\x18` (Ctrl+X>) as `^X`. It's been renamed
to make that abundantly clear.
2021-03-24 16:26:50 -05:00
365 changed files with 13030 additions and 7659 deletions

12
.config/dotnet-tools.json Normal file
View File

@@ -0,0 +1,12 @@
{
"version": 1,
"isRoot": true,
"tools": {
"XamlStyler.Console": {
"version": "3.2008.4",
"commands": [
"xstyler"
]
}
}
}

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="vswhere" version="2.6.7" />
</packages>
</packages>

17
.vscode/tasks.json vendored
View File

@@ -16,7 +16,9 @@
"${workspaceFolder}\\OpenConsole.sln",
"/p:Configuration=${input:configChoice}",
"/p:Platform=${input:platformChoice}",
"/p:AppxSymbolPackageEnabled=false", // This takes a long time, so false if we don't really need it.
"/t:$target",
"/m", // Parallel builds
"/verbosity:minimal"
],
"problemMatcher": ["$msCompile"],
@@ -46,8 +48,7 @@
],
"problemMatcher": ["$msCompile"],
"group": {
"kind": "build",
"isDefault": true
"kind": "build"
}
},
{
@@ -57,6 +58,18 @@
"args": [
],
"problemMatcher": ["$msCompile"],
},
{
"type": "process",
"label": "Run Code Format",
"command": "powershell.exe",
"args": [
"-Command",
"Import-Module ${workspaceFolder}\\tools\\OpenConsole.psm1;",
"Set-MsBuildDevEnvironment;",
"Invoke-CodeFormat",
],
"problemMatcher": ["$msCompile"],
}
],
"inputs":[

File diff suppressed because it is too large Load Diff

41
XamlStyler.json Normal file
View File

@@ -0,0 +1,41 @@
{
"AttributesTolerance": 1,
"KeepFirstAttributeOnSameLine": true,
"MaxAttributeCharactersPerLine": 0,
"MaxAttributesPerLine": 1,
"NewlineExemptionElements": "RadialGradientBrush, GradientStop, LinearGradientBrush, ScaleTransform, SkewTransform, RotateTransform, TranslateTransform, Trigger, Condition, Setter",
"SeparateByGroups": false,
"AttributeIndentation": 0,
"AttributeIndentationStyle": 1,
"RemoveDesignTimeReferences": false,
"EnableAttributeReordering": true,
"AttributeOrderingRuleGroups": [
"x:Class",
"xmlns, xmlns:x",
"xmlns:*",
"x:Key, Key, x:Name, Name, x:Uid, Uid, Title",
"Grid.Row, Grid.RowSpan, Grid.Column, Grid.ColumnSpan, Canvas.Left, Canvas.Top, Canvas.Right, Canvas.Bottom",
"Width, Height, MinWidth, MinHeight, MaxWidth, MaxHeight",
"Margin, Padding, HorizontalAlignment, VerticalAlignment, HorizontalContentAlignment, VerticalContentAlignment, Panel.ZIndex",
"*:*, *",
"PageSource, PageIndex, Offset, Color, TargetName, Property, Value, StartPoint, EndPoint",
"mc:Ignorable, d:IsDataSource, d:LayoutOverrides, d:IsStaticText",
"Storyboard.*, From, To, Duration"
],
"FirstLineAttributes": "",
"OrderAttributesByName": true,
"PutEndingBracketOnNewLine": false,
"RemoveEndingTagOfEmptyElement": true,
"SpaceBeforeClosingSlash": true,
"RootElementLineBreakRule": 0,
"ReorderVSM": 2,
"ReorderGridChildren": false,
"ReorderCanvasChildren": false,
"ReorderSetters": 0,
"FormatMarkupExtension": true,
"NoNewLineMarkupExtensions": "x:Bind, Binding",
"ThicknessSeparator": 2,
"ThicknessAttributes": "Margin, Padding, BorderThickness, ThumbnailClipMargin",
"FormatOnSave": true,
"CommentPadding": 2,
}

View File

@@ -19,8 +19,8 @@ New-Item -ItemType Directory -Force -Path $payloadDir
# Copy files from nuget packages
Copy-Item "$nugetPackagesDir\microsoft.windows.apps.test.1.0.181203002\lib\netcoreapp2.1\*.dll" $payloadDir
Copy-Item "$nugetPackagesDir\taef.redist.wlk.10.57.200731005-develop\build\Binaries\$Platform\*" $payloadDir
Copy-Item "$nugetPackagesDir\taef.redist.wlk.10.57.200731005-develop\build\Binaries\$Platform\CoreClr\*" $payloadDir
Copy-Item "$nugetPackagesDir\Microsoft.Taef.10.58.210305002\build\Binaries\$Platform\*" $payloadDir
Copy-Item "$nugetPackagesDir\Microsoft.Taef.10.58.210305002\build\Binaries\$Platform\CoreClr\*" $payloadDir
New-Item -ItemType Directory -Force -Path "$payloadDir\.NETCoreApp2.1\"
Copy-Item "$nugetPackagesDir\runtime.win-$Platform.microsoft.netcore.app.2.1.0\runtimes\win-$Platform\lib\netcoreapp2.1\*" "$payloadDir\.NETCoreApp2.1\"
Copy-Item "$nugetPackagesDir\runtime.win-$Platform.microsoft.netcore.app.2.1.0\runtimes\win-$Platform\native\*" "$payloadDir\.NETCoreApp2.1\"

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="MUXCustomBuildTasks" version="1.0.48" targetFramework="native" />
<package id="TAEF.Redist.Wlk" version="10.57.200731005-develop" targetFramework="native" />
<package id="Microsoft.Taef" version="10.58.210305002" targetFramework="native" />
<package id="microsoft.windows.apps.test" version="1.0.181203002" targetFramework="native" />
<package id="runtime.win-x86.microsoft.netcore.app" version="2.1.0" targetFramework="native" />
<package id="runtime.win-x64.microsoft.netcore.app" version="2.1.0" targetFramework="native" />

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="MUXCustomBuildTasks" version="1.0.48" targetFramework="native" />
<package id="TAEF.Redist.Wlk" version="10.57.200731005-develop" targetFramework="native" />
<package id="Microsoft.Taef" version="10.58.210305002" targetFramework="native" />
</packages>

View File

@@ -12,4 +12,4 @@ steps:
inputs:
targetType: filePath
filePath: build\Helix\GenerateTestProjFile.ps1
arguments: -TestFile '${{ parameters.testFilePath }}' -OutputProjFile '$(Build.ArtifactStagingDirectory)\${{ parameters.outputProjFileName }}' -JobTestSuiteName '${{ parameters.testSuite }}' -TaefPath '$(Build.SourcesDirectory)\build\Helix\packages\taef.redist.wlk.10.57.200731005-develop\build\Binaries\x86' -TaefQuery '${{ parameters.taefQuery }}'
arguments: -TestFile '${{ parameters.testFilePath }}' -OutputProjFile '$(Build.ArtifactStagingDirectory)\${{ parameters.outputProjFileName }}' -JobTestSuiteName '${{ parameters.testSuite }}' -TaefPath '$(Build.SourcesDirectory)\build\Helix\packages\Microsoft.Taef.10.58.210305002\build\Binaries\x86' -TaefQuery '${{ parameters.taefQuery }}'

View File

@@ -36,7 +36,7 @@ jobs:
matrix: ${{ parameters.matrix }}
variables:
artifactsDir: $(Build.SourcesDirectory)\Artifacts
taefPath: $(Build.SourcesDirectory)\build\Helix\packages\taef.redist.wlk.10.57.200731005-develop\build\Binaries\$(buildPlatform)
taefPath: $(Build.SourcesDirectory)\build\Helix\packages\Microsoft.Taef.10.58.210305002\build\Binaries\$(buildPlatform)
helixCommonArgs: '/binaryLogger:$(Build.SourcesDirectory)/${{parameters.name}}.$(buildPlatform).$(buildConfiguration).binlog /p:HelixBuild=$(Build.BuildId).$(buildPlatform).$(buildConfiguration) /p:Platform=$(buildPlatform) /p:Configuration=$(buildConfiguration) /p:HelixType=${{parameters.helixType}} /p:TestSuite=${{parameters.testSuite}} /p:ProjFilesPath=$(Build.ArtifactStagingDirectory) /p:rerunPassesRequiredToAvoidFailure=${{parameters.rerunPassesRequiredToAvoidFailure}}'

View File

@@ -0,0 +1,97 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="16.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- THIS PROJECT CANNOT BE LOADED INTO THE SOLUTION. -->
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Release|Any CPU">
<Configuration>Release</Configuration>
<Platform>AnyCPU</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Fuzzing|Any CPU">
<Configuration>Fuzzing</Configuration>
<Platform>AnyCPU</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="AuditMode|Any CPU">
<Configuration>AuditMode</Configuration>
<Platform>AnyCPU</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|Any CPU">
<Configuration>Debug</Configuration>
<Platform>AnyCPU</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>d97c3c61-53cd-4e72-919b-9a0940e038f9</ProjectGuid>
</PropertyGroup>
<PropertyGroup>
<IntermediateOutputPath>$(SolutionDir)obj\$(Configuration)\GenerateFeatureFlags\</IntermediateOutputPath>
<OpenConsoleCommonOutDir>$(SolutionDir)bin\$(Configuration)\</OpenConsoleCommonOutDir>
<_WTBrandingName Condition="'$(WindowsTerminalBranding)'=='Preview'">Preview</_WTBrandingName>
<_WTBrandingName Condition="'$(WindowsTerminalBranding)'=='Release'">Release</_WTBrandingName>
<_WTBrandingName Condition="'$(_WTBrandingName)'==''">Dev</_WTBrandingName>
</PropertyGroup>
<Target Name="_GenerateBranchAndBrandingCache">
<Exec Command="git.exe rev-parse --abbrev-ref HEAD"
CustomWarningRegularExpression="^fatal:.*"
ConsoleToMsBuild="true"
IgnoreExitCode="true">
<Output TaskParameter="ConsoleOutput" ItemName="_GitBranchLines" />
</Exec>
<ItemGroup>
<_BrandingLines Include="$(_WTBrandingName)" />
</ItemGroup>
<WriteLinesToFile File="$(IntermediateOutputPath)branch_branding_cache.txt"
Lines="@(_GitBranchLines);@(_BrandingLines)"
Overwrite="true"
WriteOnlyWhenDifferent="true" />
<ItemGroup>
<FileWrites Include="$(IntermediateOutputPath)branch_branding_cache.txt" />
<_BranchBrandingCacheFiles Include="$(IntermediateOutputPath)branch_branding_cache.txt" />
</ItemGroup>
</Target>
<Target Name="_RunFeatureFlagScript"
Inputs="@(FeatureFlagFile);@(_BranchBrandingCacheFiles)"
Outputs="$(OpenConsoleCommonOutDir)\inc\TilFeatureStaging.h"
DependsOnTargets="_GenerateBranchAndBrandingCache">
<MakeDir Directories="$(OpenConsoleCommonOutDir)\inc" />
<Exec
Command="powershell -NoLogo -NoProfile -NonInteractive -ExecutionPolicy ByPass -Command &quot;$(SolutionDir)\tools\Generate-FeatureStagingHeader.ps1&quot; -Path &quot;%(FeatureFlagFile.FullPath)&quot; -Branding $(_WTBrandingName)"
ConsoleToMsBuild="true"
StandardOutputImportance="low">
<Output TaskParameter="ConsoleOutput" ItemName="_FeatureFlagFileLines" />
</Exec>
<!--
We gather the feature flag output in MSBuild and emit the file so that we can take advantage of
WriteOnlyWhenDifferent. Doing this ensures that we don't rebuild the world when the branch changes
(if it results in a new TilFeatureStaging.h that would have had the same content/features as the previous one)
-->
<WriteLinesToFile File="$(OpenConsoleCommonOutDir)\inc\TilFeatureStaging.h"
Lines="@(_FeatureFlagFileLines)"
Overwrite="true"
WriteOnlyWhenDifferent="true" />
<ItemGroup>
<FileWrites Include="$(OpenConsoleCommonOutDir)\inc\TilFeatureStaging.h" />
</ItemGroup>
</Target>
<Target Name="Build" DependsOnTargets="_RunFeatureFlagScript" />
<Target Name="Clean">
<Delete Files="$(OpenConsoleCommonOutDir)\inc\TilFeatureStaging.h" />
</Target>
<ItemGroup>
<FeatureFlagFile Include="$(SolutionDir)\src\features.xml" />
</ItemGroup>
</Project>

View File

@@ -3,12 +3,24 @@
# Checks for code formatting errors. Will throw exception if any are found.
function Invoke-CheckBadCodeFormatting() {
Import-Module ./tools/OpenConsole.psm1
Invoke-CodeFormat
# Don't run the XAML formatter in this step - even if it changes nothing,
# it'll still touch all the .xaml files.
Invoke-CodeFormat -IgnoreXaml
# returns a non-zero exit code if there are any diffs in the tracked files in the repo
git diff-index --quiet HEAD --
if ($lastExitCode -eq 1) {
# Write the list of files that need updating to the log
git diff-index --name-only HEAD
throw "code formatting bad, run Invoke-CodeFormat on branch"
}
# Manually check the formatting of our .xaml files, without touching them.
Verify-XamlFormat
}
Invoke-CheckBadCodeFormatting

View File

@@ -19,6 +19,7 @@
"/.github/",
"/samples/",
"/res/terminal/",
"/res/fonts/",
"/doc/specs/",
"/doc/cascadia/",
"/doc/user-docs/",
@@ -38,6 +39,7 @@
".wrn",
".rec",
".err",
"XamlStyler.json",
".xlsx"
]
}

View File

@@ -12,9 +12,9 @@ Use the [TAEF Verify Macros for C++](https://docs.microsoft.com/en-us/windows-ha
### Running Tests
If you have Visual Studio and related C++ components installed, and you have successfully restored NuGets, you should have the TAEF test runner `te.exe` available locally as part of the `Taef.Redist.Wlk` package.
If you have Visual Studio and related C++ components installed, and you have successfully restored NuGets, you should have the TAEF test runner `te.exe` available locally as part of the `Microsoft.Taef` package.
> Note that you cannot easily run TAEF tests directly through Visual Studio. The `Taef.Redist.Wlk` NuGet package comes with an adapter that will let you browse and execute TAEF tests inside of Visual Studio, but its performance and reliability prevent us from recommending it here.
> Note that you cannot easily run TAEF tests directly through Visual Studio. The `Microsoft.Taef` NuGet package comes with an adapter that will let you browse and execute TAEF tests inside of Visual Studio, but its performance and reliability prevent us from recommending it here.
In a "normal" CMD environment, `te.exe` may not be directly available. Try the following command to set up the development enviroment first:

View File

@@ -166,7 +166,7 @@ should be working just the same as before.
Now that you have a static library project, you can start building your unittest
dll. Start by creating a new directory for your unittest code, and creating a
`.vcxproj` for a TAEF unittest dll. For the Terminal solution, we use the TAEF
nuget package `Taef.Redist.Wlk`.
nuget package `Microsoft.Taef`.
### Referencing your C++/WinRT static lib

View File

@@ -52,6 +52,117 @@
}
]
},
"AppearanceConfig": {
"properties": {
"colorScheme": {
"description": "The name of a color scheme to use when unfocused.",
"type": "string"
},
"foreground": {
"$ref": "#/definitions/Color",
"default": "#cccccc",
"description": "Sets the text color when unfocused. Overrides \"foreground\" from the color scheme. Uses hex color format: \"#rrggbb\".",
"type": ["string", "null"]
},
"background": {
"$ref": "#/definitions/Color",
"default": "#0c0c0c",
"description": "Sets the background color of the text when unfocused. Overrides \"background\" from the color scheme. Uses hex color format: \"#rrggbb\".",
"type": ["string", "null"]
},
"selectionBackground": {
"oneOf": [
{"$ref": "#/definitions/Color"},
{ "type": "null" }
],
"description": "Sets the background color of selected text when unfocused. Overrides selectionBackground set in the color scheme. Uses hex color format: \"#rrggbb\"."
},
"cursorColor": {
"oneOf": [
{ "$ref": "#/definitions/Color" },
{"type": "null"}
],
"description": "Sets the color of the cursor when unfocused. Overrides the cursor color from the color scheme. Uses hex color format: \"#rrggbb\"."
},
"cursorShape": {
"default": "bar",
"description": "Sets the shape of the cursor when unfocused. Possible values:\n -\"bar\" ( ┃, default )\n -\"doubleUnderscore\" ( ‗ )\n -\"emptyBox\" ( ▯ )\n -\"filledBox\" ( █ )\n -\"underscore\" ( ▁ )\n -\"vintage\" ( ▃ )",
"enum": [
"bar",
"doubleUnderscore",
"emptyBox",
"filledBox",
"underscore",
"vintage"
],
"type": "string"
},
"cursorHeight": {
"description": "Sets the percentage height of the cursor (when unfocused) starting from the bottom. Only works when cursorShape is set to \"vintage\". Accepts values from 1-100.",
"maximum": 100,
"minimum": 1,
"type": ["integer","null"],
"default": 25
},
"backgroundImage": {
"description": "Sets the file location of the image to draw over the window background when unfocused.",
"oneOf": [
{
"type": ["string", null]
},
{
"enum": [
"desktopWallpaper"
]
}
],
"type": [ "string", "null" ]
},
"backgroundImageOpacity": {
"default": 1.0,
"description": "Sets the transparency of the background image when unfocused. Accepts floating point values from 0-1.",
"maximum": 1.0,
"minimum": 0.0,
"type": "number"
},
"backgroundImageStretchMode": {
"default": "uniformToFill",
"description": "Sets how the background image is resized to fill the window when unfocused.",
"enum": [
"fill",
"none",
"uniform",
"uniformToFill"
],
"type": "string"
},
"backgroundImageAlignment": {
"default": "center",
"enum": [
"bottom",
"bottomLeft",
"bottomRight",
"center",
"left",
"right",
"top",
"topLeft",
"topRight"
],
"description": "Sets how the background image aligns to the boundaries of the window when unfocused. Possible values: \"center\", \"left\", \"top\", \"right\", \"bottom\", \"topLeft\", \"topRight\", \"bottomLeft\", \"bottomRight\"",
"type": "string"
},
"experimental.retroTerminalEffect": {
"description": "When set to true, enable retro terminal effects when unfocused. This is an experimental feature, and its continued existence is not guaranteed.",
"type": "boolean"
},
"experimental.pixelShaderPath": {
"description": "Use to set a path to a pixel shader to use with the Terminal when unfocused. Overrides `experimental.retroTerminalEffect`. This is an experimental feature, and its continued existence is not guaranteed.",
"type": "string"
}
},
"type": "object"
},
"ProfileGuid": {
"default": "{}",
"pattern": "^\\{[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}\\}$",
@@ -77,6 +188,8 @@
"duplicateTab",
"find",
"findMatch",
"identifyWindow",
"identifyWindows",
"moveFocus",
"moveTab",
"newTab",
@@ -85,12 +198,14 @@
"openNewTabDropdown",
"openSettings",
"openTabColorPicker",
"openWindowRenamer",
"paste",
"prevTab",
"renameTab",
"openTabRenamer",
"resetFontSize",
"resizePane",
"renameWindow",
"scrollDown",
"scrollDownPage",
"scrollUp",
@@ -227,6 +342,10 @@
"type": "boolean",
"default": "false",
"description": "When set to true, tabTitle overrides the default title of the tab and any title change messages from the application will be suppressed. When set to false, tabTitle behaves as normal"
},
"colorScheme": {
"description": "The name of a color scheme to use, instead of the one specified by the profile",
"type": "string"
}
},
"type": "object"
@@ -422,12 +541,14 @@
"action": { "type": "string", "pattern": "openSettings" },
"target": {
"type": "string",
"default": "settingsFile",
"description": "The settings file to open.",
"default": "settingsUI",
"description": "Opens Settings UI or settings file.",
"enum": [
"settingsFile",
"defaultsFile",
"allFiles"
"allFiles",
"settingsUI"
]
}
}
@@ -647,6 +768,38 @@
}
]
},
"RenameTabAction": {
"description": "Arguments corresponding to a renameTab Action",
"allOf": [
{ "$ref": "#/definitions/ShortcutAction" },
{
"properties": {
"action": { "type": "string", "pattern": "renameTab" },
"title": {
"type": "string",
"default": "",
"description": "A title to assign to the tab. If omitted or null, this action will restore the tab's title to the original value."
}
}
}
]
},
"RenameWindowAction": {
"description": "Arguments corresponding to a renameWindow Action",
"allOf": [
{ "$ref": "#/definitions/ShortcutAction" },
{
"properties": {
"action": { "type": "string", "pattern": "renameWindow" },
"name": {
"type": "string",
"default": "",
"description": "A name to assign to the window."
}
}
}
]
},
"Keybinding": {
"additionalProperties": false,
"properties": {
@@ -675,6 +828,8 @@
{ "$ref": "#/definitions/NewWindowAction" },
{ "$ref": "#/definitions/NextTabAction" },
{ "$ref": "#/definitions/PrevTabAction" },
{ "$ref": "#/definitions/RenameTabAction" },
{ "$ref": "#/definitions/RenameWindowAction" },
{ "type": "null" }
]
},
@@ -958,6 +1113,11 @@
"description": "Sets the background color of the text. Overrides \"background\" from the color scheme. Uses hex color format: \"#rrggbb\".",
"type": ["string", "null"]
},
"unfocusedAppearance": {
"$ref": "#/definitions/AppearanceConfig",
"description": "Sets the appearance of the terminal when it is unfocused.",
"type": ["object", "null"]
},
"backgroundImage": {
"description": "Sets the file location of the image to draw over the window background.",
"oneOf": [
@@ -1189,6 +1349,11 @@
"type": "boolean",
"default": false
},
"tabColor": {
"$ref": "#/definitions/Color",
"description": "Sets the color of the profile's tab. Using the tab color picker will override this color.",
"type": ["string", "null"]
},
"tabTitle": {
"description": "If set, will replace the name as the title to pass to the shell on startup. Some shells (like bash) may choose to ignore this initial value, while others (cmd, powershell) may use this value over the lifetime of the application.",
"type": ["string", "null"]

65
doc/feature_flags.md Normal file
View File

@@ -0,0 +1,65 @@
# til::feature
Feature flags are controlled by an XML document stored at `src/features.xml`.
## Example Document
```xml
<?xml version="1.0"?>
<featureStaging xmlns="http://microsoft.com/TilFeatureStaging-Schema.xsd">
<feature>
<!-- This will produce Feature_XYZ::IsEnabled() and TIL_FEATURE_XYZ_ENABLED (preprocessor) -->
<name>Feature_XYZ</name>
<description>Does a cool thing</description>
<!-- GitHub deliverable number; optional -->
<id>1234</id>
<!-- Whether the feature defaults to enabled or disabled -->
<stage>AlwaysEnabled|AlwaysDisabled</stage>
<!-- Branch wildcards where the feature should be *DISABLED* -->
<alwaysDisabledBranchTokens>
<branchToken>branch/with/wildcard/*</branchToken>
<!-- ... more branchTokens ... -->
</alwaysDisabledBranchTokens>
<!-- Just like alwaysDisabledBranchTokens, but for *ENABLING* the feature. -->
<alwaysEnabledBranchTokens>
<branchToken>...</branchToken>
</alwaysEnabledBranchTokens>
<!-- Brandings where the feature should be *DISABLED* -->
<alwaysDisabledBrandingTokens>
<!-- Valid brandings include Dev, Preview, Release, WindowsInbox -->
<brandingToken>Release</brandingToken>
<!-- ... more brandingTokens ... -->
</alwaysDisabledBrandingTokens>
<!-- Just like alwaysDisabledBrandingTokens, but for *ENABLING* the feature -->
<alwaysEnabledBrandingTokens>
<branchToken>...</branchToken>
</alwaysEnabledBrandingTokens>
<!-- Unequivocally disable this feature in Release -->
<alwaysDisabledReleaseTokens />
</feature>
</featureStaging>
```
## Notes
Features that are disabled for Release using `alwaysDisabledReleaseTokens` are
*always* disabled in Release, even if they come from a branch that would have
been enabled by the wildcard.
### Precedence
1. `alwaysDisabledReleaseTokens`
2. Enabled branches
3. Disabled branches
* The longest branch token that matches your branch will win.
3. Enabled brandings
4. Disabled brandings
5. The feature's default state

Binary file not shown.

Binary file not shown.

View File

@@ -6,16 +6,3 @@ The images in this directory do not fall under the same [license](https://raw.gi
of the Windows Terminal code.
Please consult the [license](./LICENSE) in this directory for terms applicable to the image assets in this directory.
## Fonts
The fonts in this directory do not fall under the same [license](https://raw.githubusercontent.com/microsoft/terminal/main/LICENSE) as the rest
of the Windows Terminal code.
Please consult the [license](https://raw.githubusercontent.com/microsoft/cascadia-code/main/LICENSE) in the
[microsoft/cascadia-code](https://github.com/microsoft/cascadia-code) repository for terms applicable to the fonts in this directory.
### Fonts Included
* Cascadia Code, Cascadia Mono (2102.25)
* from microsoft/cascadia-code@911dc421f333e3b72b97381d16fee5b71eb48f04

BIN
res/fonts/CascadiaCode.ttf Normal file

Binary file not shown.

Binary file not shown.

BIN
res/fonts/CascadiaMono.ttf Normal file

Binary file not shown.

Binary file not shown.

12
res/fonts/README.md Normal file
View File

@@ -0,0 +1,12 @@
# Windows Terminal and Console Assets (Fonts)
The fonts in this directory do not fall under the same [license](https://raw.githubusercontent.com/microsoft/terminal/main/LICENSE) as the rest
of the Windows Terminal code.
Please consult the [license](https://raw.githubusercontent.com/microsoft/cascadia-code/main/LICENSE) in the
[microsoft/cascadia-code](https://github.com/microsoft/cascadia-code) repository for terms applicable to the fonts in this directory.
### Fonts Included
* Cascadia Code, Cascadia Mono (2106.17)
* from microsoft/cascadia-code@fb0bce69c1c12f6c298b8bc1c1d181868f5daa9a

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

View File

@@ -1,8 +1,6 @@
<Application x:Class="GUIConsole.Wpf.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>
<Application x:Class="GUIConsole.Wpf.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources />
</Application>

View File

@@ -1,84 +1,100 @@
<Window x:Class="GUIConsole.Wpf.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800"
Background="#C7000000"
AllowsTransparency="True"
WindowStyle="None"
MouseDown="Window_MouseDown"
BorderThickness="1"
BorderBrush="LightSlateGray"
KeyDown="Window_KeyDown"
Loaded="Window_Loaded">
<Window.Resources>
<Style x:Key="TitleBarButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="FontFamily" Value="Segoe MDL2 Assets"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Height" Value="32"/>
<Setter Property="Width" Value="46"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="BorderThickness" Value="0"/>
</Style>
<Style x:Key="CloseButtonStyle" TargetType="{x:Type Button}" BasedOn="{StaticResource TitleBarButtonStyle}">
<Setter Property="Content" Value="&#xE10A;"/>
<!--Remove the default Button template's Triggers, otherwise they'll override our trigger below.-->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Button.Background" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="TitleBarTitle" Grid.Column="0" VerticalAlignment="Center" Padding="10 0" Foreground="White">
GUIConsole
</TextBlock>
<StackPanel x:Name="TitleBarButtons" Grid.Column="1" Orientation="Horizontal" VerticalAlignment="Top">
<Button x:Name="MinimizeButton"
Click="MinimizeButton_Click"
Content="&#xE108;"
Style="{StaticResource TitleBarButtonStyle}"/>
<Button x:Name="MaximizeRestoreButton"
Click="MaximizeRestoreButton_Click"
Content="&#xE922;"
FontSize="12"
Style="{StaticResource TitleBarButtonStyle}"/>
<Button x:Name="CloseButton"
Click="CloseButton_Click"
FontSize="12"
Style="{StaticResource CloseButtonStyle}"/>
</StackPanel>
</Grid>
<ScrollViewer x:Name="TerminalHistoryViewer" Grid.Row="1" ScrollChanged="ScrollViewer_ScrollChanged">
<TextBlock x:Name="TerminalHistoryBlock" FontFamily="Consolas" TextWrapping="Wrap" Foreground="White"/>
</ScrollViewer>
</Grid>
</Window>
<Window x:Class="GUIConsole.Wpf.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="800"
Height="450"
AllowsTransparency="True"
Background="#C7000000"
BorderBrush="LightSlateGray"
BorderThickness="1"
KeyDown="Window_KeyDown"
Loaded="Window_Loaded"
MouseDown="Window_MouseDown"
WindowStyle="None"
mc:Ignorable="d">
<Window.Resources>
<Style x:Key="TitleBarButtonStyle"
TargetType="{x:Type Button}">
<Setter Property="Background" Value="Transparent" />
<Setter Property="FontFamily" Value="Segoe MDL2 Assets" />
<Setter Property="FontSize" Value="14" />
<Setter Property="Height" Value="32" />
<Setter Property="Width" Value="46" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="White" />
<Setter Property="BorderThickness" Value="0" />
</Style>
<Style x:Key="CloseButtonStyle"
BasedOn="{StaticResource TitleBarButtonStyle}"
TargetType="{x:Type Button}">
<Setter Property="Content" Value="&#xE10A;" />
<!-- Remove the default Button template's Triggers, otherwise they'll override our trigger below. -->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Button.Background" Value="Red" />
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock x:Name="TitleBarTitle"
Grid.Column="0"
Padding="10,0"
VerticalAlignment="Center"
Foreground="White">
GUIConsole
</TextBlock>
<StackPanel x:Name="TitleBarButtons"
Grid.Column="1"
VerticalAlignment="Top"
Orientation="Horizontal">
<Button x:Name="MinimizeButton"
Click="MinimizeButton_Click"
Content="&#xE108;"
Style="{StaticResource TitleBarButtonStyle}" />
<Button x:Name="MaximizeRestoreButton"
Click="MaximizeRestoreButton_Click"
Content="&#xE922;"
FontSize="12"
Style="{StaticResource TitleBarButtonStyle}" />
<Button x:Name="CloseButton"
Click="CloseButton_Click"
FontSize="12"
Style="{StaticResource CloseButtonStyle}" />
</StackPanel>
</Grid>
<ScrollViewer x:Name="TerminalHistoryViewer"
Grid.Row="1"
ScrollChanged="ScrollViewer_ScrollChanged">
<TextBlock x:Name="TerminalHistoryBlock"
FontFamily="Consolas"
Foreground="White"
TextWrapping="Wrap" />
</ScrollViewer>
</Grid>
</Window>

View File

@@ -0,0 +1,66 @@
// A minimal pixel shader that outlines text
// The terminal graphics as a texture
Texture2D shaderTexture;
SamplerState samplerState;
// Terminal settings such as the resolution of the texture
cbuffer PixelShaderSettings {
// The number of seconds since the pixel shader was enabled
float Time;
// UI Scale
float Scale;
// Resolution of the shaderTexture
float2 Resolution;
// Background color as rgba
float4 Background;
};
// A pixel shader is a program that given a texture coordinate (tex) produces a color.
// tex is an x,y tuple that ranges from 0,0 (top left) to 1,1 (bottom right).
// Just ignore the pos parameter.
float4 main(float4 pos : SV_POSITION, float2 tex : TEXCOORD) : SV_TARGET
{
// Read the color value at the current texture coordinate (tex)
// float4 is tuple of 4 floats, rgba
float4 color = shaderTexture.Sample(samplerState, tex);
// Read the color value at some offset, will be used as shadow. For the best
// effect, read the colors offset on the left, right, top, bottom of this
// fragment, as well as on the corners of this fragment.
//
// You could get away with fewer samples, but the resulting outlines will be
// blurrier.
//left, right, top, bottom:
float4 leftColor = shaderTexture.Sample(samplerState, tex+1.0*Scale*float2( 1.0, 0.0)/Resolution.y);
float4 rightColor = shaderTexture.Sample(samplerState, tex+1.0*Scale*float2(-1.0, 0.0)/Resolution.y);
float4 topColor = shaderTexture.Sample(samplerState, tex+1.0*Scale*float2( 0.0, 1.0)/Resolution.y);
float4 bottomColor = shaderTexture.Sample(samplerState, tex+1.0*Scale*float2( 0.0, -1.0)/Resolution.y);
// Corners
float4 topLeftColor = shaderTexture.Sample(samplerState, tex+1.0*Scale*float2( 1.0, 1.0)/Resolution.y);
float4 topRightColor = shaderTexture.Sample(samplerState, tex+1.0*Scale*float2(-1.0, 1.0)/Resolution.y);
float4 bottomLeftColor = shaderTexture.Sample(samplerState, tex+1.0*Scale*float2( 1.0, -1.0)/Resolution.y);
float4 bottomRightColor = shaderTexture.Sample(samplerState, tex+1.0*Scale*float2(-1.0, -1.0)/Resolution.y);
// Now, if any of those adjacent cells has text in it, then the *color vec4
// will have a non-zero .w (which is used for alpha). Use that alpha value
// to add some black to the current fragment.
//
// This will result in only coloring fragments adjacent to text, but leaving
// background images (for example) untouched.
float3 outlineColor = float3(0, 0, 0);
float4 result = color;
result = result + float4(outlineColor, leftColor.w);
result = result + float4(outlineColor, rightColor.w);
result = result + float4(outlineColor, topColor.w);
result = result + float4(outlineColor, bottomColor.w);
result = result + float4(outlineColor, topLeftColor.w);
result = result + float4(outlineColor, topRightColor.w);
result = result + float4(outlineColor, bottomLeftColor.w);
result = result + float4(outlineColor, bottomRightColor.w);
return result;
}

View File

@@ -43,6 +43,7 @@
<EventProvider Id="EventProvider-Microsoft.Windows.Console.Server" Name="1A541C01-589A-496E-85A7-A9E02170166D"/>
<EventProvider Id="EventProvider-Microsoft.Windows.Console.VirtualTerminal.Parser" Name="c9ba2a84-d3ca-5e19-2bd6-776a0910cb9d"/>
<EventProvider Id="EventProvider-Microsoft.Windows.Console.Render.VtEngine" Name="c9ba2a95-d3ca-5e19-2bd6-776a0910cb9d"/>
<EventProvider Id="EventProvider-Microsoft.Windows.Console.UIA" Name="e7ebce59-2161-572d-b263-2f16a6afb9e5"/>
<!-- Now define some profiles. We'll call them by ID when collecting. Also, the Base is where it is inheriting from and is a .wprpi file built... -->
<!-- ... into WPR automatically. Go look in the WPR install directory or in the documentation to find it. -->
<Profile Id="ConsolePerfProfile.Verbose.File" Base="GeneralProfile.Light.File" LoggingMode="File" Name="ConsolePerfProfile" DetailLevel="Verbose" Description="Console Performance default profile">
@@ -66,6 +67,7 @@
<EventProviderId Value="EventProvider-Microsoft.Windows.Console.Server"/>
<EventProviderId Value="EventProvider-Microsoft.Windows.Console.VirtualTerminal.Parser"/>
<EventProviderId Value="EventProvider-Microsoft.Windows.Console.Render.VtEngine"/>
<EventProviderId Value="EventProvider-Microsoft.Windows.Console.UIA"/>
</EventProviders>
</EventCollectorId>
</Collectors>

34
src/Terminal.wprp Normal file
View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<WindowsPerformanceRecorder Version="1.0" Author="Microsoft Corporation" Copyright="Microsoft Corporation" Company="Microsoft Corporation">
<Profiles>
<EventCollector Id="EventCollector_Terminal" Name="Terminal">
<BufferSize Value="64" />
<Buffers Value="4" />
</EventCollector>
<EventProvider Id="EventProvider_TerminalControl" Name="28c82e50-57af-5a86-c25b-e39cd990032b" />
<EventProvider Id="EventProvider_TerminalConnection" Name="e912fe7b-eeb6-52a5-c628-abe388e5f792" />
<EventProvider Id="EventProvider_TerminalSettingsModel" Name="be579944-4d33-5202-e5d6-a7a57f1935cb" />
<EventProvider Id="EventProvider_TerminalApp" Name="24a1622f-7da7-5c77-3303-d850bd1ab2ed" />
<EventProvider Id="EventProvider_TerminalWin32Host" Name="56c06166-2e2e-5f4d-7ff3-74f4b78c87d6" />
<EventProvider Id="EventProvider_TerminalRemoting" Name="d6f04aad-629f-539a-77c1-73f5c3e4aa7b" />
<EventProvider Id="EventProvider_TerminalDirectX" Name="c93e739e-ae50-5a14-78e7-f171e947535d" />
<Profile Id="Terminal.Verbose.File" Name="Terminal" Description="Terminal" LoggingMode="File" DetailLevel="Verbose">
<Collectors>
<EventCollectorId Value="EventCollector_Terminal">
<EventProviders>
<EventProviderId Value="EventProvider_TerminalControl" />
<EventProviderId Value="EventProvider_TerminalConnection" />
<EventProviderId Value="EventProvider_TerminalSettingsModel" />
<EventProviderId Value="EventProvider_TerminalApp" />
<EventProviderId Value="EventProvider_TerminalWin32Host" />
<EventProviderId Value="EventProvider_TerminalRemoting" />
<EventProviderId Value="EventProvider_TerminalDirectX" />
</EventProviders>
</EventCollectorId>
</Collectors>
</Profile>
<Profile Id="Terminal.Light.File" Name="Terminal" Description="Terminal" Base="Terminal.Verbose.File" LoggingMode="File" DetailLevel="Light" />
<Profile Id="Terminal.Verbose.Memory" Name="Terminal" Description="Terminal" Base="Terminal.Verbose.File" LoggingMode="Memory" DetailLevel="Verbose" />
<Profile Id="Terminal.Light.Memory" Name="Terminal" Description="Terminal" Base="Terminal.Verbose.File" LoggingMode="Memory" DetailLevel="Light" />
</Profiles>
</WindowsPerformanceRecorder>

View File

@@ -4,6 +4,36 @@
#include "precomp.h"
#include "AttrRow.hpp"
// Routine Description:
// - constructor
// Arguments:
// - cchRowWidth - the length of the default text attribute
// - attr - the default text attribute
// Return Value:
// - constructed object
ATTR_ROW::ATTR_ROW(const UINT cchRowWidth, const TextAttribute attr) noexcept
{
try
{
_list.emplace_back(TextAttributeRun(cchRowWidth, attr));
}
catch (...)
{
FAIL_FAST_CAUGHT_EXCEPTION();
}
_cchRowWidth = cchRowWidth;
}
// Routine Description:
// - Sets all properties of the ATTR_ROW to default values
// Arguments:
// - attr - The default text attributes to use on text in this row.
void ATTR_ROW::Reset(const TextAttribute attr)
{
_list.clear();
_list.emplace_back(TextAttributeRun(_cchRowWidth, attr));
}
// Routine Description:
// - Takes an existing row of attributes, and changes the length so that it fills the NewWidth.
// If the new size is bigger, then the last attr is extended to fill the NewWidth.
@@ -15,12 +45,46 @@
// - <none>, throws exceptions on failures.
void ATTR_ROW::Resize(const size_t newWidth)
{
mybase::resize(gsl::narrow<UINT>(newWidth));
}
THROW_HR_IF(E_INVALIDARG, 0 == newWidth);
void ATTR_ROW::Reset(const TextAttribute attr)
{
mybase::fill(attr);
// Easy case. If the new row is longer, increase the length of the last run by how much new space there is.
if (newWidth > _cchRowWidth)
{
// Get the attribute that covers the final column of old width.
const auto runPos = FindAttrIndex(_cchRowWidth - 1, nullptr);
auto& run = _list.at(runPos);
// Extend its length by the additional columns we're adding.
run.SetLength(run.GetLength() + newWidth - _cchRowWidth);
// Store that the new total width we represent is the new width.
_cchRowWidth = newWidth;
}
// harder case: new row is shorter.
else
{
// Get the attribute that covers the final column of the new width
size_t CountOfAttr = 0;
const auto runPos = FindAttrIndex(newWidth - 1, &CountOfAttr);
auto& run = _list.at(runPos);
// CountOfAttr was given to us as "how many columns left from this point forward are covered by the returned run"
// So if the original run was B5 covering a 5 size OldWidth and we have a NewWidth of 3
// then when we called FindAttrIndex, it returned the B5 as the pIndexedRun and a 2 for how many more segments it covers
// after and including the 3rd column.
// B5-2 = B3, which is what we desire to cover the new 3 size buffer.
run.SetLength(run.GetLength() - CountOfAttr + 1);
// Store that the new total width we represent is the new width.
_cchRowWidth = newWidth;
// Erase segments after the one we just updated.
_list.erase(_list.cbegin() + runPos + 1, _list.cend());
// NOTE: Under some circumstances here, we have leftover run segments in memory or blank run segments
// in memory. We're not going to waste time redimensioning the array in the heap. We're just noting that the useful
// portions of it have changed.
}
}
// Routine Description:
@@ -33,7 +97,7 @@ void ATTR_ROW::Reset(const TextAttribute attr)
// - will throw on error
TextAttribute ATTR_ROW::GetAttrByColumn(const size_t column) const
{
return mybase::at(gsl::narrow<UINT>(column));
return GetAttrByColumn(column, nullptr);
}
// Routine Description:
@@ -48,10 +112,73 @@ TextAttribute ATTR_ROW::GetAttrByColumn(const size_t column) const
TextAttribute ATTR_ROW::GetAttrByColumn(const size_t column,
size_t* const pApplies) const
{
UINT applies = 0;
const auto attr = mybase::at(gsl::narrow<UINT>(column), applies);
*pApplies = applies;
return attr;
THROW_HR_IF(E_INVALIDARG, column >= _cchRowWidth);
const auto runPos = FindAttrIndex(column, pApplies);
return _list.at(runPos).GetAttributes();
}
// Routine Description:
// - reports how many runs we have stored (to be used for some optimizations
// Return Value:
// - Count of runs. 1 means we have 1 color to represent the entire row.
size_t ATTR_ROW::GetNumberOfRuns() const noexcept
{
return _list.size();
}
// Routine Description:
// - This routine finds the nth attribute in this ATTR_ROW.
// Arguments:
// - index - which attribute to find
// - applies - on output, contains corrected length of indexed attr.
// for example, if the attribute string was { 5, BLUE } and the requested
// index was 3, CountOfAttr would be 2.
// Return Value:
// - const reference to attribute run object
size_t ATTR_ROW::FindAttrIndex(const size_t index, size_t* const pApplies) const
{
FAIL_FAST_IF(!(index < _cchRowWidth)); // The requested index cannot be longer than the total length described by this set of Attrs.
size_t cTotalLength = 0;
FAIL_FAST_IF(!(_list.size() > 0)); // There should be a non-zero and positive number of items in the array.
// Scan through the internal array from position 0 adding up the lengths that each attribute applies to
auto runPos = _list.cbegin();
do
{
cTotalLength += runPos->GetLength();
if (cTotalLength > index)
{
// If we've just passed up the requested index with the length we added, break early
break;
}
runPos++;
} while (runPos < _list.cend());
// we should have broken before falling out the while case.
// if we didn't break, then this ATTR_ROW wasn't filled with enough attributes for the entire row of characters
FAIL_FAST_IF(runPos >= _list.cend());
// The remaining iterator position is the position of the attribute that is applicable at the position requested (index)
// Calculate its remaining applicability if requested
// The length on which the found attribute applies is the total length seen so far minus the index we were searching for.
FAIL_FAST_IF(!(cTotalLength > index)); // The length of all attributes we counted up so far should be longer than the index requested or we'll underflow.
if (nullptr != pApplies)
{
const auto attrApplies = cTotalLength - index;
FAIL_FAST_IF(!(attrApplies > 0)); // An attribute applies for >0 characters
// MSFT: 17130145 - will restore this and add a better assert to catch the real issue.
//FAIL_FAST_IF(!(attrApplies <= _cchRowWidth)); // An attribute applies for a maximum of the total length available to us
*pApplies = attrApplies;
}
return runPos - _list.cbegin();
}
// Routine Description:
@@ -61,11 +188,11 @@ TextAttribute ATTR_ROW::GetAttrByColumn(const size_t column,
std::vector<uint16_t> ATTR_ROW::GetHyperlinks()
{
std::vector<uint16_t> ids;
for (const auto& run : *this)
for (const auto& run : _list)
{
if (run.IsHyperlink())
if (run.GetAttributes().IsHyperlink())
{
ids.emplace_back(run.GetHyperlinkId());
ids.emplace_back(run.GetAttributes().GetHyperlinkId());
}
}
return ids;
@@ -80,8 +207,10 @@ std::vector<uint16_t> ATTR_ROW::GetHyperlinks()
// - <none>
bool ATTR_ROW::SetAttrToEnd(const UINT iStart, const TextAttribute attr)
{
mybase::fill(attr, iStart);
return true;
size_t const length = _cchRowWidth - iStart;
const TextAttributeRun run(length, attr);
return SUCCEEDED(InsertAttrRuns({ &run, 1 }, iStart, _cchRowWidth - 1, _cchRowWidth));
}
// Method Description:
@@ -94,7 +223,13 @@ bool ATTR_ROW::SetAttrToEnd(const UINT iStart, const TextAttribute attr)
// - <none>
void ATTR_ROW::ReplaceAttrs(const TextAttribute& toBeReplacedAttr, const TextAttribute& replaceWith) noexcept
{
mybase::replace(toBeReplacedAttr, replaceWith);
for (auto& run : _list)
{
if (run.GetAttributes() == toBeReplacedAttr)
{
run.SetAttributes(replaceWith);
}
}
}
// Routine Description:
@@ -113,12 +248,392 @@ void ATTR_ROW::ReplaceAttrs(const TextAttribute& toBeReplacedAttr, const TextAtt
// otherwise STATUS_SUCCESS if we were successful.
[[nodiscard]] HRESULT ATTR_ROW::InsertAttrRuns(const gsl::span<const TextAttributeRun> newAttrs,
const size_t iStart,
const size_t /*iEnd*/,
const size_t /*cBufferWidth*/)
try
const size_t iEnd,
const size_t cBufferWidth)
{
mybase::replace(iStart, mybase::npos, newAttrs.begin(), newAttrs.end());
// Definitions:
// Existing Run = The run length encoded color array we're already storing in memory before this was called.
// Insert Run = The run length encoded color array that someone is asking us to inject into our stored memory run.
// New Run = The run length encoded color array that we have to allocate and rebuild to store internally
// which will replace Existing Run at the end of this function.
// Example:
// cBufferWidth = 10.
// Existing Run: R3 -> G5 -> B2
// Insert Run: Y1 -> N1 at iStart = 5 and iEnd = 6
// (rgInsertAttrs is a 2 length array with Y1->N1 in it and cInsertAttrs = 2)
// Final Run: R3 -> G2 -> Y1 -> N1 -> G1 -> B2
// We'll need to know what the last valid column is for some calculations versus iEnd
// because iEnd is specified to us as an inclusive index value.
// Do the -1 math here now so we don't have to have -1s scattered all over this function.
const size_t iLastBufferCol = cBufferWidth - 1;
// If the insertion size is 1, do some pre-processing to
// see if we can get this done quickly.
if (newAttrs.size() == 1)
{
// Get the new color attribute we're trying to apply
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.
if (_list.size() == 1 && _list.at(0).GetAttributes() == NewAttr)
{
return S_OK;
}
// .. otherwise if we internally have a list of 2 or more and we're about to insert a single color
// it's possible that we just walk left-to-right through the row and find a quick exit.
else if (iStart >= 0 && iStart == iEnd)
{
// First we try to find the run where the insertion happens, using lowerBound and upperBound to track
// where we are currently at.
const auto begin = _list.begin();
size_t lowerBound = 0;
size_t upperBound = 0;
for (size_t i = 0; i < _list.size(); i++)
{
const auto curr = begin + i;
upperBound += curr->GetLength();
if (iStart >= lowerBound && iStart < upperBound)
{
// The run that we try to insert into has the same color as the new one.
// e.g.
// AAAAABBBBBBBCCC
// ^
// AAAAABBBBBBBCCC
//
// 'B' is the new color and '^' represents where iStart is. We don't have to
// do anything.
if (curr->GetAttributes() == NewAttr)
{
return S_OK;
}
// If the current run has length of exactly one, we can simply change the attribute
// of the current run.
// e.g.
// AAAAABCCCCCCCCC
// ^
// AAAAADCCCCCCCCC
//
// Here 'D' is the new color.
if (curr->GetLength() == 1)
{
curr->SetAttributes(NewAttr);
return S_OK;
}
// If the insertion happens at current run's lower boundary...
if (iStart == lowerBound && i > 0)
{
const auto prev = std::prev(curr, 1);
// ... and the previous run has the same color as the new one, we can
// just adjust the counts in the existing two elements in our internal list.
// e.g.
// AAAAABBBBBBBCCC
// ^
// AAAAAABBBBBBCCC
//
// Here 'A' is the new color.
if (NewAttr == prev->GetAttributes())
{
prev->IncrementLength();
curr->DecrementLength();
// If we just reduced the right half to zero, just erase it out of the list.
if (curr->GetLength() == 0)
{
_list.erase(curr);
}
return S_OK;
}
}
// If the insertion happens at current run's upper boundary...
if (iStart == upperBound - 1 && i + 1 < _list.size())
{
// ...then let's try our luck with the next run if possible. This is basically the opposite
// of what we did with the previous run.
// e.g.
// AAAAAABBBBBBCCC
// ^
// AAAAABBBBBBBCCC
//
// Here 'B' is the new color.
const auto next = std::next(curr, 1);
if (NewAttr == next->GetAttributes())
{
curr->DecrementLength();
next->IncrementLength();
if (curr->GetLength() == 0)
{
_list.erase(curr);
}
return S_OK;
}
}
}
// Advance one run in the _list.
lowerBound = upperBound;
// The lowerBound is larger than iStart, which means we fail to find an early exit at the run
// where the insertion happens. We can just break out.
if (lowerBound > iStart)
{
break;
}
}
}
}
// If we're about to cover the entire existing run with a new one, we can also make an optimization.
if (iStart == 0 && iEnd == iLastBufferCol)
{
// Just dump what we're given over what we have and call it a day.
_list.assign(newAttrs.begin(), newAttrs.end());
return S_OK;
}
// In the worst case scenario, we will need a new run that is the length of
// The existing run in memory + The new run in memory + 1.
// This worst case occurs when we inject a new item in the middle of an existing run like so
// Existing R3->B5->G2, Insertion Y2 starting at 5 (in the middle of the B5)
// becomes R3->B2->Y2->B1->G2.
// The original run was 3 long. The insertion run was 1 long. We need 1 more for the
// fact that an existing piece of the run was split in half (to hold the latter half).
const size_t cNewRun = _list.size() + newAttrs.size() + 1;
decltype(_list) newRun;
newRun.reserve(cNewRun);
// We will start analyzing from the beginning of our existing run.
// Use some pointers to keep track of where we are in walking through our runs.
// Get the existing run that we'll be updating/manipulating.
const auto existingRun = _list.begin();
auto pExistingRunPos = existingRun;
const auto pExistingRunEnd = _list.end();
auto pInsertRunPos = newAttrs.begin();
size_t cInsertRunRemaining = newAttrs.size();
size_t iExistingRunCoverage = 0;
// Copy the existing run into the new buffer up to the "start index" where the new run will be injected.
// If the new run starts at 0, we have nothing to copy from the beginning.
if (iStart != 0)
{
// While we're less than the desired insertion position...
while (iExistingRunCoverage < iStart)
{
// Add up how much length we can cover by copying an item from the existing run.
iExistingRunCoverage += pExistingRunPos->GetLength();
// Copy it to the new run buffer and advance both pointers.
newRun.push_back(*pExistingRunPos++);
}
// When we get to this point, we've copied full segments from the original existing run
// into our new run buffer. We will have 1 or more full segments of color attributes and
// we MIGHT have to cut the last copied segment's length back depending on where the inserted
// attributes will fall in the final/new run.
// Some examples:
// - Starting with the original string R3 -> G5 -> B2
// - 1. If the insertion is Y5 at start index 3
// We are trying to get a result/final/new run of R3 -> Y5 -> B2.
// We just copied R3 to the new destination buffer and we cang skip down and start inserting the new attrs.
// - 2. If the insertion is Y3 at start index 5
// We are trying to get a result/final/new run of R3 -> G2 -> Y3 -> B2.
// We just copied R3 -> G5 to the new destination buffer with the code above.
// But the insertion is going to cut out some of the length of the G5.
// We need to fix this up below so it says G2 instead to leave room for the Y3 to fit in
// the new/final run.
// Fetch out the length so we can fix it up based on the below conditions.
size_t length = newRun.back().GetLength();
// If we've covered more cells already than the start of the attributes to be inserted...
if (iExistingRunCoverage > iStart)
{
// ..then subtract some of the length of the final cell we copied.
// We want to take remove the difference in distance between the cells we've covered in the new
// run and the insertion point.
// (This turns G5 into G2 from Example 2 just above)
length -= (iExistingRunCoverage - iStart);
}
// Now we're still on that "last cell copied" into the new run.
// If the color of that existing copied cell matches the color of the first segment
// of the run we're about to insert, we can just increment the length to extend the coverage.
if (newRun.back().GetAttributes() == pInsertRunPos->GetAttributes())
{
length += pInsertRunPos->GetLength();
// Since the color matched, we have already "used up" part of the insert run
// and can skip it in our big "memcopy" step below that will copy the bulk of the insert run.
cInsertRunRemaining--;
pInsertRunPos++;
}
// We're done manipulating the length. Store it back.
newRun.back().SetLength(length);
}
// Bulk copy the majority (or all, depending on circumstance) of the insert run into the final run buffer.
std::copy_n(pInsertRunPos, cInsertRunRemaining, std::back_inserter(newRun));
// We're technically done with the insert run now and have 0 remaining, but won't bother updating its pointers
// and counts any further because we won't use them.
// Now we need to move our pointer for the original existing run forward and update our counts
// on how many cells we could have copied from the source before finishing off the new run.
while (iExistingRunCoverage <= iEnd)
{
FAIL_FAST_IF(!(pExistingRunPos != pExistingRunEnd));
iExistingRunCoverage += pExistingRunPos->GetLength();
pExistingRunPos++;
}
// If we still have original existing run cells remaining, copy them into the final new run.
if (pExistingRunPos != pExistingRunEnd || iExistingRunCoverage != (iEnd + 1))
{
// We advanced the existing run pointer and its count to on or past the end of what the insertion run filled in.
// If this ended up being past the end of what the insertion run covers, we have to account for the cells after
// the insertion run but before the next piece of the original existing run.
// The example in this case is if we had...
// Existing Run = R3 -> G5 -> B2 -> X5
// Insert Run = Y2 @ iStart = 7 and iEnd = 8
// ... then at this point in time, our states would look like...
// New Run so far = R3 -> G4 -> Y2
// Existing Run Pointer is at X5
// Existing run coverage count at 3 + 5 + 2 = 10.
// However, in order to get the final desired New Run
// (which is R3 -> G4 -> Y2 -> B1 -> X5)
// we would need to grab a piece of that B2 we already skipped past.
// iExistingRunCoverage = 10. iEnd = 8. iEnd+1 = 9. 10 > 9. So we skipped something.
if (iExistingRunCoverage > (iEnd + 1))
{
// Back up the existing run pointer so we can grab the piece we skipped.
pExistingRunPos--;
// If the color matches what's already in our run, just increment the count value.
// This case is slightly off from the example above. This case is for if the B2 above was actually Y2.
// That Y2 from the existing run is the same color as the Y2 we just filled a few columns left in the final run
// so we can just adjust the final run's column count instead of adding another segment here.
if (newRun.back().GetAttributes() == pExistingRunPos->GetAttributes())
{
size_t length = newRun.back().GetLength();
length += (iExistingRunCoverage - (iEnd + 1));
newRun.back().SetLength(length);
}
else
{
// If the color didn't match, then we just need to copy the piece we skipped and adjust
// its length for the discrepancy in columns not yet covered by the final/new run.
// Move forward to a blank spot in the new run
newRun.emplace_back();
// Copy the existing run's color information to the new run
newRun.back().SetAttributes(pExistingRunPos->GetAttributes());
// Adjust the length of that copied color to cover only the reduced number of columns needed
// now that some have been replaced by the insert run.
newRun.back().SetLength(iExistingRunCoverage - (iEnd + 1));
}
// Now that we're done recovering a piece of the existing run we skipped, move the pointer forward again.
pExistingRunPos++;
}
// OK. In this case, we didn't skip anything. The end of the insert run fell right at a boundary
// in columns that was in the original existing run.
// However, the next piece of the original existing run might happen to have the same color attribute
// as the final piece of what we just copied.
// As an example...
// Existing Run = R3 -> G5 -> B2.
// Insert Run = B5 @ iStart = 3 and iEnd = 7
// New Run so far = R3 -> B5
// New Run desired when done = R3 -> B7
// Existing run pointer is on B2.
// We want to merge the 2 from the B2 into the B5 so we get B7.
else if (newRun.back().GetAttributes() == pExistingRunPos->GetAttributes())
{
// Add the value from the existing run into the current new run position.
size_t length = newRun.back().GetLength();
length += pExistingRunPos->GetLength();
newRun.back().SetLength(length);
// Advance the existing run position since we consumed its value and merged it in.
pExistingRunPos++;
}
// Now bulk copy any segments left in the original existing run
if (pExistingRunPos < pExistingRunEnd)
{
std::copy_n(pExistingRunPos, (pExistingRunEnd - pExistingRunPos), std::back_inserter(newRun));
}
}
// OK, phew. We're done. Now we just need to free the existing run and store the new run in its place.
_list.swap(newRun);
return S_OK;
}
CATCH_RETURN()
// Routine Description:
// - packs a vector of TextAttribute into a vector of TextAttributeRun
// Arguments:
// - attrs - text attributes to pack
// Return Value:
// - packed text attribute run
std::vector<TextAttributeRun> ATTR_ROW::PackAttrs(const std::vector<TextAttribute>& attrs)
{
std::vector<TextAttributeRun> runs;
if (attrs.empty())
{
return runs;
}
for (auto attr : attrs)
{
if (runs.empty() || runs.back().GetAttributes() != attr)
{
runs.emplace_back(TextAttributeRun(1, attr));
}
else
{
runs.back().SetLength(runs.back().GetLength() + 1);
}
}
return runs;
}
ATTR_ROW::const_iterator ATTR_ROW::begin() const noexcept
{
return AttrRowIterator(this);
}
ATTR_ROW::const_iterator ATTR_ROW::end() const noexcept
{
return AttrRowIterator::CreateEndIterator(this);
}
ATTR_ROW::const_iterator ATTR_ROW::cbegin() const noexcept
{
return AttrRowIterator(this);
}
ATTR_ROW::const_iterator ATTR_ROW::cend() const noexcept
{
return AttrRowIterator::CreateEndIterator(this);
}
bool operator==(const ATTR_ROW& a, const ATTR_ROW& b) noexcept
{
return (a._list.size() == b._list.size() &&
a._list.data() == b._list.data() &&
a._cchRowWidth == b._cchRowWidth);
}

View File

@@ -20,27 +20,37 @@ Revision History:
#pragma once
#include "til/rle.h"
#include "TextAttributeRun.hpp"
#include "AttrRowIterator.hpp"
class ATTR_ROW final : public til::rle<TextAttribute, unsigned int>
class ATTR_ROW final
{
public:
using mybase = til::rle<TextAttribute, unsigned int>;
using const_iterator = typename AttrRowIterator;
using const_iterator = mybase::const_iterator;
using const_reverse_iterator = mybase::const_reverse_iterator;
ATTR_ROW(const UINT cchRowWidth, const TextAttribute attr)
noexcept;
using mybase::mybase; // use base constructor
~ATTR_ROW() = default;
ATTR_ROW(const ATTR_ROW&) = default;
ATTR_ROW& operator=(const ATTR_ROW&) = default;
ATTR_ROW(ATTR_ROW&&)
noexcept = default;
ATTR_ROW& operator=(ATTR_ROW&&) noexcept = default;
TextAttribute GetAttrByColumn(const size_t column) const;
TextAttribute GetAttrByColumn(const size_t column,
size_t* const pApplies) const;
size_t GetNumberOfRuns() const noexcept;
size_t FindAttrIndex(const size_t index,
size_t* const pApplies) const;
std::vector<uint16_t> GetHyperlinks();
bool SetAttrToEnd(const unsigned int iStart, const TextAttribute attr);
bool SetAttrToEnd(const UINT iStart, const TextAttribute attr);
void ReplaceAttrs(const TextAttribute& toBeReplacedAttr, const TextAttribute& replaceWith) noexcept;
void Resize(const size_t newWidth);
@@ -50,19 +60,26 @@ public:
const size_t iEnd,
const size_t cBufferWidth);
using mybase::begin;
using mybase::cbegin;
using mybase::cend;
using mybase::end;
static std::vector<TextAttributeRun> PackAttrs(const std::vector<TextAttribute>& attrs);
using mybase::operator==;
const_iterator begin() const noexcept;
const_iterator end() const noexcept;
const_iterator cbegin() const noexcept;
const_iterator cend() const noexcept;
friend bool operator==(const ATTR_ROW& a, const ATTR_ROW& b) noexcept;
friend class AttrRowIterator;
friend class ROW;
private:
void Reset(const TextAttribute attr);
boost::container::small_vector<TextAttributeRun, 1> _list;
size_t _cchRowWidth;
#ifdef UNIT_TESTING
friend class AttrRowTests;
friend class CommonState;
#endif
};

View File

@@ -22,23 +22,29 @@ Revision History:
#include "TextAttribute.hpp"
class TextAttributeRun final : public std::pair<TextAttribute, unsigned int>
class TextAttributeRun final
{
public:
using mybase = std::pair<TextAttribute, unsigned int>;
using mybase::mybase;
TextAttributeRun(const size_t cchLength, const TextAttribute attr) :
mybase(attr, gsl::narrow<unsigned int>(cchLength))
TextAttributeRun() = default;
TextAttributeRun(const size_t cchLength, const TextAttribute attr) noexcept :
_cchLength(gsl::narrow<unsigned int>(cchLength))
{
SetAttributes(attr);
}
size_t GetLength() const noexcept { return mybase::second; }
void SetLength(const size_t cchLength) noexcept { mybase::second = gsl::narrow<unsigned int>(cchLength); }
void IncrementLength() noexcept { mybase::second++; }
void DecrementLength() noexcept { mybase::second--; }
size_t GetLength() const noexcept { return _cchLength; }
void SetLength(const size_t cchLength) noexcept { _cchLength = gsl::narrow<unsigned int>(cchLength); }
void IncrementLength() noexcept { _cchLength++; }
void DecrementLength() noexcept { _cchLength--; }
const TextAttribute& GetAttributes() const noexcept { return mybase::first; }
void SetAttributes(const TextAttribute textAttribute) noexcept { mybase::first = textAttribute; }
const TextAttribute& GetAttributes() const noexcept { return _attributes; }
void SetAttributes(const TextAttribute textAttribute) noexcept { _attributes = textAttribute; }
private:
unsigned int _cchLength{ 0 };
TextAttribute _attributes{ 0 };
#ifdef UNIT_TESTING
friend class AttrRowTests;
#endif
};

View File

@@ -11,6 +11,7 @@
<Import Project="$(SolutionDir)src\common.build.pre.props" />
<ItemGroup>
<ClCompile Include="..\AttrRow.cpp" />
<ClCompile Include="..\AttrRowIterator.cpp" />
<ClCompile Include="..\cursor.cpp" />
<ClCompile Include="..\OutputCell.cpp" />
<ClCompile Include="..\OutputCellIterator.cpp" />
@@ -33,6 +34,7 @@
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\AttrRow.hpp" />
<ClInclude Include="..\AttrRowIterator.hpp" />
<ClInclude Include="..\cursor.h" />
<ClInclude Include="..\DbcsAttribute.hpp" />
<ClInclude Include="..\ICharRow.hpp" />

View File

@@ -30,6 +30,7 @@ PRECOMPILED_INCLUDE = ..\precomp.h
SOURCES= \
..\AttrRow.cpp \
..\AttrRowIterator.cpp \
..\cursor.cpp \
..\OutputCell.cpp \
..\OutputCellIterator.cpp \

View File

@@ -281,9 +281,9 @@ bool TextBuffer::_AssertValidDoubleByteSequence(const DbcsAttribute dbcsAttribut
// - false otherwise (out of memory)
bool TextBuffer::_PrepareForDoubleByteSequence(const DbcsAttribute dbcsAttribute)
{
// Assert the buffer state is ready for this character
// This function corrects most errors. If this is false, we had an uncorrectable one.
FAIL_FAST_IF(!(_AssertValidDoubleByteSequence(dbcsAttribute))); // Shouldn't be uncorrectable sequences unless something is very wrong.
// This function corrects most errors. If this is false, we had an uncorrectable one which
// older versions of conhost simply let pass by unflinching.
LOG_HR_IF(E_NOT_VALID_STATE, !(_AssertValidDoubleByteSequence(dbcsAttribute))); // Shouldn't be uncorrectable sequences unless something is very wrong.
bool fSuccess = true;
// Now compensate if we don't have enough space for the upcoming double byte sequence

View File

@@ -15,8 +15,8 @@ Author(s):
#pragma once
#include "AttrRowIterator.hpp"
#include "CharRow.hpp"
#include "AttrRow.hpp"
#include "OutputCellView.hpp"
#include "../../types/inc/viewport.hpp"
@@ -55,7 +55,7 @@ protected:
OutputCellView _view;
const ROW* _pRow;
ATTR_ROW::const_iterator _attrIter;
AttrRowIterator _attrIter;
const TextBuffer& _buffer;
const Microsoft::Console::Types::Viewport _bounds;
bool _exceeded;

View File

@@ -0,0 +1,731 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "WexTestClass.h"
#include "../../../inc/consoletaeftemplates.hpp"
#include "../textBuffer.hpp"
using namespace WEX::Common;
using namespace WEX::Logging;
using namespace WEX::TestExecution;
namespace WEX
{
namespace TestExecution
{
template<>
class VerifyOutputTraits<TextAttributeRun>
{
public:
static WEX::Common::NoThrowString ToString(const TextAttributeRun& tar)
{
return WEX::Common::NoThrowString().Format(
L"Length:%d, attr:%s",
tar.GetLength(),
VerifyOutputTraits<TextAttribute>::ToString(tar.GetAttributes()).GetBuffer());
}
};
template<>
class VerifyCompareTraits<TextAttributeRun, TextAttributeRun>
{
public:
static bool AreEqual(const TextAttributeRun& expected, const TextAttributeRun& actual)
{
return expected.GetAttributes() == actual.GetAttributes() &&
expected.GetLength() == actual.GetLength();
}
static bool AreSame(const TextAttributeRun& expected, const TextAttributeRun& actual)
{
return &expected == &actual;
}
static bool IsLessThan(const TextAttributeRun&, const TextAttributeRun&) = delete;
static bool IsGreaterThan(const TextAttributeRun&, const TextAttributeRun&) = delete;
static bool IsNull(const TextAttributeRun& object)
{
return object.GetAttributes().IsLegacy() && object.GetAttributes().GetLegacyAttributes() == 0 &&
object.GetLength() == 0;
}
};
}
}
class AttrRowTests
{
ATTR_ROW* pSingle;
ATTR_ROW* pChain;
short _sDefaultLength = 80;
short _sDefaultChainLength = 6;
short sChainSegLength;
short sChainLeftover;
short sChainSegmentsNeeded;
WORD __wDefaultAttr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
WORD __wDefaultChainAttr = BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY;
TextAttribute _DefaultAttr = TextAttribute(__wDefaultAttr);
TextAttribute _DefaultChainAttr = TextAttribute(__wDefaultChainAttr);
TEST_CLASS(AttrRowTests);
TEST_METHOD_SETUP(MethodSetup)
{
pSingle = new ATTR_ROW(_sDefaultLength, _DefaultAttr);
// Segment length is the expected length divided by the row length
// E.g. row of 80, 4 segments, 20 segment length each
sChainSegLength = _sDefaultLength / _sDefaultChainLength;
// Leftover is spaces that don't fit evenly
// E.g. row of 81, 4 segments, 1 leftover length segment
sChainLeftover = _sDefaultLength % _sDefaultChainLength;
// Start with the number of segments we expect
sChainSegmentsNeeded = _sDefaultChainLength;
// If we had a remainder, add one more segment
if (sChainLeftover)
{
sChainSegmentsNeeded++;
}
// Create the chain
pChain = new ATTR_ROW(_sDefaultLength, _DefaultAttr);
pChain->_list.resize(sChainSegmentsNeeded);
// Attach all chain segments that are even multiples of the row length
for (short iChain = 0; iChain < _sDefaultChainLength; iChain++)
{
TextAttributeRun* pRun = &pChain->_list[iChain];
pRun->SetAttributes(TextAttribute{ gsl::narrow_cast<WORD>(iChain) }); // Just use the chain position as the value
pRun->SetLength(sChainSegLength);
}
if (sChainLeftover > 0)
{
// If we had a leftover, then this chain is one longer than we expected (the default length)
// So use it as the index (because indices start at 0)
TextAttributeRun* pRun = &pChain->_list[_sDefaultChainLength];
pRun->SetAttributes(_DefaultChainAttr);
pRun->SetLength(sChainLeftover);
}
return true;
}
TEST_METHOD_CLEANUP(MethodCleanup)
{
delete pSingle;
delete pChain;
return true;
}
TEST_METHOD(TestInitialize)
{
// Properties needed for test
const WORD wAttr = FOREGROUND_RED | BACKGROUND_BLUE;
TextAttribute attr = TextAttribute(wAttr);
// Cases to test
ATTR_ROW* pTestItems[]{ pSingle, pChain };
// Loop cases
for (UINT iIndex = 0; iIndex < ARRAYSIZE(pTestItems); iIndex++)
{
ATTR_ROW* pUnderTest = pTestItems[iIndex];
pUnderTest->Reset(attr);
VERIFY_ARE_EQUAL(pUnderTest->_list.size(), 1u);
VERIFY_ARE_EQUAL(pUnderTest->_list[0].GetAttributes(), attr);
VERIFY_ARE_EQUAL(pUnderTest->_list[0].GetLength(), (unsigned int)_sDefaultLength);
}
}
// Routine Description:
// - Packs an array of words representing attributes into the more compact storage form used by the row.
// Arguments:
// - rgAttrs - Array of words representing the attribute associated with each character position in the row.
// - cRowLength - Length of preceding array.
// - outAttrRun - reference to unique_ptr that will contain packed attr run on success.
// Return Value:
// - Success if success. Buffer too small if row length is incorrect.
HRESULT PackAttrs(_In_reads_(cRowLength) const TextAttribute* const rgAttrs,
const size_t cRowLength,
_Inout_ std::unique_ptr<TextAttributeRun[]>& outAttrRun,
_Out_ size_t* const cOutAttrRun)
{
RETURN_HR_IF(E_NOT_SUFFICIENT_BUFFER, cRowLength == 0);
// first count up the deltas in the array
size_t cDeltas = 1;
const TextAttribute* pPrevAttr = &rgAttrs[0];
for (size_t i = 1; i < cRowLength; i++)
{
const TextAttribute* pCurAttr = &rgAttrs[i];
if (*pCurAttr != *pPrevAttr)
{
cDeltas++;
}
pPrevAttr = pCurAttr;
}
// This whole situation was too complicated with a one off holder for one row run
// new method:
// delete the old buffer
// make a new buffer, one run + one run for each change
// set the values for each run one run index at a time
std::unique_ptr<TextAttributeRun[]> attrRun = std::make_unique<TextAttributeRun[]>(cDeltas);
RETURN_HR_IF_NULL(E_OUTOFMEMORY, attrRun);
TextAttributeRun* pCurrentRun = attrRun.get();
pCurrentRun->SetAttributes(rgAttrs[0]);
pCurrentRun->SetLength(1);
for (size_t i = 1; i < cRowLength; i++)
{
if (pCurrentRun->GetAttributes() == rgAttrs[i])
{
pCurrentRun->SetLength(pCurrentRun->GetLength() + 1);
}
else
{
pCurrentRun++;
pCurrentRun->SetAttributes(rgAttrs[i]);
pCurrentRun->SetLength(1);
}
}
attrRun.swap(outAttrRun);
*cOutAttrRun = cDeltas;
return S_OK;
}
NoThrowString LogRunElement(_In_ TextAttributeRun& run)
{
return NoThrowString().Format(L"%wc%d", run.GetAttributes().GetLegacyAttributes(), run.GetLength());
}
void LogChain(_In_ PCWSTR pwszPrefix,
boost::container::small_vector_base<TextAttributeRun>& chain)
{
NoThrowString str(pwszPrefix);
if (chain.size() > 0)
{
str.Append(LogRunElement(chain[0]));
for (size_t i = 1; i < chain.size(); i++)
{
str.AppendFormat(L"->%s", (const wchar_t*)(LogRunElement(chain[i])));
}
}
Log::Comment(str);
}
void LogChain(_In_ PCWSTR pwszPrefix,
std::vector<TextAttributeRun>& chain)
{
NoThrowString str(pwszPrefix);
if (chain.size() > 0)
{
str.Append(LogRunElement(chain[0]));
for (size_t i = 1; i < chain.size(); i++)
{
str.AppendFormat(L"->%s", (const wchar_t*)(LogRunElement(chain[i])));
}
}
Log::Comment(str);
}
void DoTestInsertAttrRuns(UINT& uiStartPos, WORD& ch1, UINT& uiChar1Length, WORD& ch2, UINT& uiChar2Length)
{
Log::Comment(String().Format(L"StartPos: %d, Char1: %wc, Char1Length: %d, Char2: %wc, Char2Length: %d",
uiStartPos,
ch1,
uiChar1Length,
ch2,
uiChar2Length));
bool const fUseStr2 = (ch2 != L'0');
// Set up our "original row" that we are going to try to insert into.
// This will represent a 10 column run of R3->B5->G2 that we will use for all tests.
ATTR_ROW originalRow{ static_cast<UINT>(_sDefaultLength), _DefaultAttr };
originalRow._list.resize(3);
originalRow._cchRowWidth = 10;
originalRow._list[0].SetAttributes(TextAttribute{ 'R' });
originalRow._list[0].SetLength(3);
originalRow._list[1].SetAttributes(TextAttribute{ 'B' });
originalRow._list[1].SetLength(5);
originalRow._list[2].SetAttributes(TextAttribute{ 'G' });
originalRow._list[2].SetLength(2);
LogChain(L"Original: ", originalRow._list);
// Set up our "insertion run"
size_t cInsertRow = 1;
if (fUseStr2)
{
cInsertRow++;
}
std::vector<TextAttributeRun> insertRow;
insertRow.resize(cInsertRow);
insertRow[0].SetAttributes(TextAttribute{ ch1 });
insertRow[0].SetLength(uiChar1Length);
if (fUseStr2)
{
insertRow[1].SetAttributes(TextAttribute{ ch2 });
insertRow[1].SetLength(uiChar2Length);
}
LogChain(L"Insert: ", insertRow);
Log::Comment(NoThrowString().Format(L"At Index: %d", uiStartPos));
UINT uiTotalLength = uiChar1Length;
if (fUseStr2)
{
uiTotalLength += uiChar2Length;
}
VERIFY_IS_TRUE((uiStartPos + uiTotalLength) >= 1); // assert we won't underflow.
UINT const uiEndPos = uiStartPos + uiTotalLength - 1;
// Calculate our expected final/result run by unpacking original, laying our insertion on it at the index
// then using our pack function to repack it.
// This method is easy to understand and very reliable, but its performance is bad.
// The InsertAttrRuns method we test against below is hard to understand but very high performance in production.
// - 1. Unpack
std::vector<TextAttribute> unpackedOriginal = { originalRow.begin(), originalRow.end() };
// - 2. Overlay insertion
UINT uiInsertedCount = 0;
UINT uiInsertIndex = 0;
// --- Walk through the unpacked run from start to end....
for (UINT uiUnpackedIndex = uiStartPos; uiUnpackedIndex <= uiEndPos; uiUnpackedIndex++)
{
// Pull the item from the insert run to analyze.
TextAttributeRun run = insertRow[uiInsertIndex];
// Copy the attribute from the run into the unpacked array
unpackedOriginal[uiUnpackedIndex] = run.GetAttributes();
// Increment how many times we've copied this particular portion of the run
uiInsertedCount++;
// If we've now inserted enough of them to match the length, advance the insert index and reset the counter.
if (uiInsertedCount >= run.GetLength())
{
uiInsertIndex++;
uiInsertedCount = 0;
}
}
// - 3. Pack.
std::unique_ptr<TextAttributeRun[]> packedRun;
size_t cPackedRun = 0;
VERIFY_SUCCEEDED(PackAttrs(unpackedOriginal.data(), originalRow._cchRowWidth, packedRun, &cPackedRun));
// Now send parameters into InsertAttrRuns and get its opinion on the subject.
VERIFY_SUCCEEDED(originalRow.InsertAttrRuns({ insertRow.data(), insertRow.size() }, uiStartPos, uiEndPos, (UINT)originalRow._cchRowWidth));
// Compare and ensure that the expected and actual match.
VERIFY_ARE_EQUAL(cPackedRun, originalRow._list.size(), L"Ensure that number of array elements required for RLE are the same.");
std::vector<TextAttributeRun> packedRunExpected;
std::copy_n(packedRun.get(), cPackedRun, std::back_inserter(packedRunExpected));
LogChain(L"Expected: ", packedRunExpected);
LogChain(L"Actual: ", originalRow._list);
for (size_t testIndex = 0; testIndex < cPackedRun; testIndex++)
{
VERIFY_ARE_EQUAL(packedRun[testIndex], originalRow._list[testIndex]);
}
}
TEST_METHOD(TestInsertAttrRunsSingle)
{
UINT const uiTestRunLength = 10;
UINT uiStartPos = 0;
WORD ch1 = L'0';
UINT uiChar1Length = 0;
WORD ch2 = L'0';
UINT uiChar2Length = 0;
Log::Comment(L"Test inserting a single item of a variable length into the run.");
WORD rgch1Options[] = { L'X', L'R', L'G', L'B' };
for (size_t iCh1Option = 0; iCh1Option < ARRAYSIZE(rgch1Options); iCh1Option++)
{
ch1 = rgch1Options[iCh1Option];
for (UINT iCh1Length = 1; iCh1Length <= uiTestRunLength; iCh1Length++)
{
uiChar1Length = iCh1Length;
// We can't try to insert a run that's longer than would fit.
// If the run is of length 10 and we're trying to insert a length of 10,
// we can only insert at position 0.
// For the run length of 10 and an insert length of 9, we can try positions 0 and 1.
// And so on...
UINT const uiMaxPos = uiTestRunLength - uiChar1Length;
for (UINT iStartPos = 0; iStartPos < uiMaxPos; iStartPos++)
{
uiStartPos = iStartPos;
DoTestInsertAttrRuns(uiStartPos, ch1, uiChar1Length, ch2, uiChar2Length);
}
}
}
}
TEST_METHOD(TestInsertAttrRunsMultiple)
{
UINT const uiTestRunLength = 10;
UINT uiStartPos = 0;
WORD ch1 = L'0';
UINT uiChar1Length = 0;
WORD ch2 = L'0';
UINT uiChar2Length = 0;
Log::Comment(L"Test inserting a multiple item run with each piece having variable length into the existing run.");
WORD rgch1Options[] = { L'X', L'R', L'G', L'B' };
for (size_t iCh1Option = 0; iCh1Option < ARRAYSIZE(rgch1Options); iCh1Option++)
{
ch1 = rgch1Options[iCh1Option];
UINT const uiMaxCh1Length = uiTestRunLength - 1; // leave at least 1 space for the second piece of the insert run.
for (UINT iCh1Length = 1; iCh1Length <= uiMaxCh1Length; iCh1Length++)
{
uiChar1Length = iCh1Length;
WORD rgch2Options[] = { L'Y' };
for (size_t iCh2Option = 0; iCh2Option < ARRAYSIZE(rgch2Options); iCh2Option++)
{
ch2 = rgch2Options[iCh2Option];
// When choosing the length of the second item, it can't be bigger than the remaining space in the run
// when accounting for the length of the first piece chosen.
// For example if the total run length is 10 and the first piece chosen was 8 long,
// the second piece can only be 1 or 2 long.
UINT const uiMaxCh2Length = uiTestRunLength - uiMaxCh1Length;
for (UINT iCh2Length = 1; iCh2Length <= uiMaxCh2Length; iCh2Length++)
{
uiChar2Length = iCh2Length;
// We can't try to insert a run that's longer than would fit.
// If the run is of length 10 and we're trying to insert a total length of 10,
// we can only insert at position 0.
// For the run length of 10 and an insert length of 9, we can try positions 0 and 1.
// And so on...
UINT const uiMaxPos = uiTestRunLength - (uiChar1Length + uiChar2Length);
for (UINT iStartPos = 0; iStartPos <= uiMaxPos; iStartPos++)
{
uiStartPos = iStartPos;
DoTestInsertAttrRuns(uiStartPos, ch1, uiChar1Length, ch2, uiChar2Length);
}
}
}
}
}
}
TEST_METHOD(TestUnpackAttrs)
{
Log::Comment(L"Checking unpack of a single color for the entire length");
{
const std::vector<TextAttribute> attrs{ pSingle->begin(), pSingle->end() };
for (auto& attr : attrs)
{
VERIFY_ARE_EQUAL(attr, _DefaultAttr);
}
}
Log::Comment(L"Checking unpack of the multiple color chain");
const std::vector<TextAttribute> attrs{ pChain->begin(), pChain->end() };
short cChainRun = 0; // how long we've been looking at the current piece of the chain
short iChainSegIndex = 0; // which piece of the chain we should be on right now
for (auto& attr : attrs)
{
// by default the chain was assembled above to have the chain segment index be the attribute
TextAttribute MatchingAttr = TextAttribute(iChainSegIndex);
// However, if the index is greater than the expected chain length, a remainder piece was made with a default attribute
if (iChainSegIndex >= _sDefaultChainLength)
{
MatchingAttr = _DefaultChainAttr;
}
VERIFY_ARE_EQUAL(attr, MatchingAttr);
// Add to the chain run
cChainRun++;
// If the chain run is greater than the length the segments were specified to be
if (cChainRun >= sChainSegLength)
{
// reset to 0
cChainRun = 0;
// move to the next chain segment down the line
iChainSegIndex++;
}
}
}
TEST_METHOD(TestReverseIteratorWalkFromMiddle)
{
// GH #3409, walking backwards through color range runs out of bounds
// We're going to create an attribute row with assorted colors and varying lengths
// just like the row of text on the Ubuntu prompt line that triggered this bug being found.
// Then we're going to walk backwards through the iterator like a selection-expand-to-left
// operation and ensure we don't run off the bounds.
// walk the chain, from index, stepSize at a time
// ensure we don't crash
auto testWalk = [](ATTR_ROW* chain, size_t index, int stepSize) {
// move to starting index
auto iter = chain->cbegin();
iter += index;
// Now walk backwards in a loop until 0.
while (iter)
{
iter -= stepSize;
}
Log::Comment(L"We made it through without crashing!");
};
// take one step of size stepSize on the chain
// index is where we start from
// expectedAttribute is what we expect to read here
auto verifyStep = [](ATTR_ROW* chain, size_t index, int stepSize, TextAttribute expectedAttribute) {
// move to starting index
auto iter = chain->cbegin();
iter += index;
// Now step backwards
iter -= stepSize;
VERIFY_ARE_EQUAL(expectedAttribute, *iter);
};
Log::Comment(L"Reverse iterate through ubuntu prompt");
{
// Create attr row representing a buffer that's 121 wide.
auto chain = std::make_unique<ATTR_ROW>(121, _DefaultAttr);
// The repro case had 4 chain segments.
chain->_list.resize(4);
// The color 10 went for the first 18.
chain->_list[0].SetAttributes(TextAttribute(0xA));
chain->_list[0].SetLength(18);
// Default color for the next 1
chain->_list[1].SetAttributes(TextAttribute());
chain->_list[1].SetLength(1);
// Color 12 for the next 29
chain->_list[2].SetAttributes(TextAttribute(0xC));
chain->_list[2].SetLength(29);
// Then default color to end the run
chain->_list[3].SetAttributes(TextAttribute());
chain->_list[3].SetLength(73);
// The sum of the lengths should be 121.
VERIFY_ARE_EQUAL(chain->_cchRowWidth, chain->_list[0]._cchLength + chain->_list[1]._cchLength + chain->_list[2]._cchLength + chain->_list[3]._cchLength);
auto index = chain->_list[0].GetLength();
auto stepSize = 1;
testWalk(chain.get(), index, stepSize);
}
Log::Comment(L"Reverse iterate across a text run in the chain");
{
// Create attr row representing a buffer that's 3 wide.
auto chain = std::make_unique<ATTR_ROW>(3, _DefaultAttr);
// The repro case had 3 chain segments.
chain->_list.resize(3);
// The color 10 went for the first 1.
chain->_list[0].SetAttributes(TextAttribute(0xA));
chain->_list[0].SetLength(1);
// The color 11 for the next 1
chain->_list[1].SetAttributes(TextAttribute(0xB));
chain->_list[1].SetLength(1);
// Color 12 for the next 1
chain->_list[2].SetAttributes(TextAttribute(0xC));
chain->_list[2].SetLength(1);
// The sum of the lengths should be 3.
VERIFY_ARE_EQUAL(chain->_cchRowWidth, chain->_list[0]._cchLength + chain->_list[1]._cchLength + chain->_list[2]._cchLength);
// on 'ABC', step from B to A
auto index = 1;
auto stepSize = 1;
verifyStep(chain.get(), index, stepSize, TextAttribute(0xA));
}
Log::Comment(L"Reverse iterate across two text runs in the chain");
{
// Create attr row representing a buffer that's 3 wide.
auto chain = std::make_unique<ATTR_ROW>(3, _DefaultAttr);
// The repro case had 3 chain segments.
chain->_list.resize(3);
// The color 10 went for the first 1.
chain->_list[0].SetAttributes(TextAttribute(0xA));
chain->_list[0].SetLength(1);
// The color 11 for the next 1
chain->_list[1].SetAttributes(TextAttribute(0xB));
chain->_list[1].SetLength(1);
// Color 12 for the next 1
chain->_list[2].SetAttributes(TextAttribute(0xC));
chain->_list[2].SetLength(1);
// The sum of the lengths should be 3.
VERIFY_ARE_EQUAL(chain->_cchRowWidth, chain->_list[0]._cchLength + chain->_list[1]._cchLength + chain->_list[2]._cchLength);
// on 'ABC', step from C to A
auto index = 2;
auto stepSize = 2;
verifyStep(chain.get(), index, stepSize, TextAttribute(0xA));
}
}
TEST_METHOD(TestSetAttrToEnd)
{
const WORD wTestAttr = FOREGROUND_BLUE | BACKGROUND_GREEN;
TextAttribute TestAttr = TextAttribute(wTestAttr);
Log::Comment(L"FIRST: Set index to > 0 to test making/modifying chains");
const short iTestIndex = 50;
VERIFY_IS_TRUE(iTestIndex >= 0 && iTestIndex < _sDefaultLength);
Log::Comment(L"SetAttrToEnd for single color applied to whole string.");
pSingle->SetAttrToEnd(iTestIndex, TestAttr);
// Was 1 (single), should now have 2 segments
VERIFY_ARE_EQUAL(pSingle->_list.size(), 2u);
VERIFY_ARE_EQUAL(pSingle->_list[0].GetAttributes(), _DefaultAttr);
VERIFY_ARE_EQUAL(pSingle->_list[0].GetLength(), (unsigned int)(_sDefaultLength - (_sDefaultLength - iTestIndex)));
VERIFY_ARE_EQUAL(pSingle->_list[1].GetAttributes(), TestAttr);
VERIFY_ARE_EQUAL(pSingle->_list[1].GetLength(), (unsigned int)(_sDefaultLength - iTestIndex));
Log::Comment(L"SetAttrToEnd for existing chain of multiple colors.");
pChain->SetAttrToEnd(iTestIndex, TestAttr);
// From 7 segments down to 5.
VERIFY_ARE_EQUAL(pChain->_list.size(), 5u);
// Verify chain colors and lengths
VERIFY_ARE_EQUAL(TextAttribute(0), pChain->_list[0].GetAttributes());
VERIFY_ARE_EQUAL(pChain->_list[0].GetLength(), (unsigned int)13);
VERIFY_ARE_EQUAL(TextAttribute(1), pChain->_list[1].GetAttributes());
VERIFY_ARE_EQUAL(pChain->_list[1].GetLength(), (unsigned int)13);
VERIFY_ARE_EQUAL(TextAttribute(2), pChain->_list[2].GetAttributes());
VERIFY_ARE_EQUAL(pChain->_list[2].GetLength(), (unsigned int)13);
VERIFY_ARE_EQUAL(TextAttribute(3), pChain->_list[3].GetAttributes());
VERIFY_ARE_EQUAL(pChain->_list[3].GetLength(), (unsigned int)11);
VERIFY_ARE_EQUAL(TestAttr, pChain->_list[4].GetAttributes());
VERIFY_ARE_EQUAL(pChain->_list[4].GetLength(), (unsigned int)30);
Log::Comment(L"SECOND: Set index to 0 to test replacing anything with a single");
ATTR_ROW* pTestItems[]{ pSingle, pChain };
for (UINT iIndex = 0; iIndex < ARRAYSIZE(pTestItems); iIndex++)
{
ATTR_ROW* pUnderTest = pTestItems[iIndex];
pUnderTest->SetAttrToEnd(0, TestAttr);
// should be down to 1 attribute set from beginning to end of string
VERIFY_ARE_EQUAL(pUnderTest->_list.size(), 1u);
// singular pair should contain the color
VERIFY_ARE_EQUAL(pUnderTest->_list[0].GetAttributes(), TestAttr);
// and its length should be the length of the whole string
VERIFY_ARE_EQUAL(pUnderTest->_list[0].GetLength(), (unsigned int)_sDefaultLength);
}
}
TEST_METHOD(TestTotalLength)
{
ATTR_ROW* pTestItems[]{ pSingle, pChain };
for (UINT iIndex = 0; iIndex < ARRAYSIZE(pTestItems); iIndex++)
{
ATTR_ROW* pUnderTest = pTestItems[iIndex];
const size_t Result = pUnderTest->_cchRowWidth;
VERIFY_ARE_EQUAL((short)Result, _sDefaultLength);
}
}
TEST_METHOD(TestResize)
{
pSingle->Resize(240);
pChain->Resize(240);
pSingle->Resize(255);
pChain->Resize(255);
pSingle->Resize(255);
pChain->Resize(255);
pSingle->Resize(60);
pChain->Resize(60);
pSingle->Resize(60);
pChain->Resize(60);
VERIFY_THROWS_SPECIFIC(pSingle->Resize(0), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_INVALIDARG; });
VERIFY_THROWS_SPECIFIC(pChain->Resize(0), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_INVALIDARG; });
}
};

View File

@@ -6,10 +6,11 @@
<RootNamespace>TextBufferUnitTests</RootNamespace>
<ProjectName>TextBuffer.Unit.Tests</ProjectName>
<TargetName>TextBuffer.Unit.Tests</TargetName>
<ConfigurationType>DynamicLibrary</ConfigurationType>
<ConfigurationType>DynamicLibrary</ConfigurationType>
</PropertyGroup>
<Import Project="$(SolutionDir)src\common.build.pre.props" />
<ItemGroup>
<ClCompile Include="AttrRowTests.cpp" />
<ClCompile Include="ReflowTests.cpp" />
<ClCompile Include="TextColorTests.cpp" />
<ClCompile Include="TextAttributeTests.cpp" />

View File

@@ -14,6 +14,7 @@ DLLDEF =
SOURCES = \
$(SOURCES) \
AttrRowTests.cpp \
ReflowTests.cpp \
TextColorTests.cpp \
TextAttributeTests.cpp \

View File

@@ -70,6 +70,7 @@
<ItemGroup>
<ProjectReference Include="..\WindowsTerminal\WindowsTerminal.vcxproj" />
<ProjectReference Include="..\..\host\exe\Host.EXE.vcxproj" />
<ProjectReference Include="..\..\host\proxy\Host.Proxy.vcxproj" />
<ProjectReference Include="..\TerminalAzBridge\TerminalAzBridge.vcxproj" />
<ProjectReference Include="..\ShellExtension\WindowsTerminalShellExt.vcxproj" />
<ProjectReference Include="..\wt\wt.vcxproj" />

View File

@@ -54,7 +54,6 @@
<uap:ShowOn Tile="square310x310Logo"/>
</uap:ShowNameOnTiles>
</uap:DefaultTile>
<uap:SplashScreen Image="Images\SplashScreen.png"/>
</uap:VisualElements>
<Extensions>
@@ -74,9 +73,43 @@
<uap3:Name>com.microsoft.windows.terminal.settings</uap3:Name>
</uap3:AppExtensionHost>
</uap3:Extension>
<uap3:Extension Category="windows.appExtension">
<uap3:AppExtension Name="com.microsoft.windows.console.host"
Id="OpenConsole-Dev"
DisplayName="OpenConsole Dev"
Description="Console host built from microsoft/terminal open source repository"
PublicFolder="Public">
<uap3:Properties>
<Clsid>{1F9F2BF5-5BC3-4F17-B0E6-912413F1F451}</Clsid>
</uap3:Properties>
</uap3:AppExtension>
</uap3:Extension>
<uap3:Extension Category="windows.appExtension">
<uap3:AppExtension Name="com.microsoft.windows.terminal.host"
Id="Terminal-Dev"
DisplayName="Windows Terminal Dev"
Description="Terminal host built from microsoft/terminal open source repository"
PublicFolder="Public">
<uap3:Properties>
<Clsid>{051F34EE-C1FD-4B19-AF75-9BA54648434C}</Clsid>
</uap3:Properties>
</uap3:AppExtension>
</uap3:Extension>
<com:Extension Category="windows.comInterface">
<com:ComInterface>
<com:ProxyStub Id="DEC4804D-56D1-4F73-9FBE-6828E7C85C56" DisplayName="OpenConsoleHandoffProxy" Path="OpenConsoleProxy.dll"/>
<com:Interface Id="2B607BC1-43EB-40C3-95AE-2856ADDB7F23" ProxyStubClsid="DEC4804D-56D1-4F73-9FBE-6828E7C85C56"/>
<com:Interface Id="FA1E3AB4-9AEC-4A3C-96CA-E6078C30BD74" ProxyStubClsid="DEC4804D-56D1-4F73-9FBE-6828E7C85C56"/>
</com:ComInterface>
</com:Extension>
<com:Extension Category="windows.comServer">
<com:ComServer>
<com:ExeServer DisplayName="OpenConsole" Executable="OpenConsole.exe">
<com:Class Id="1F9F2BF5-5BC3-4F17-B0E6-912413F1F451"/>
</com:ExeServer>
<com:ExeServer DisplayName="WindowsTerminal" Executable="WindowsTerminal.exe">
<com:Class Id="051F34EE-C1FD-4B19-AF75-9BA54648434C"/>
</com:ExeServer>
<com:SurrogateServer DisplayName="WindowsTerminalShellExt">
<com:Class Id="52065414-e077-47ec-a3ac-1cc5455e1b54" Path="WindowsTerminalShellExt.dll" ThreadingModel="STA"/>
</com:SurrogateServer>

View File

@@ -55,7 +55,6 @@
<uap:ShowOn Tile="square310x310Logo"/>
</uap:ShowNameOnTiles>
</uap:DefaultTile>
<uap:SplashScreen Image="Images\SplashScreen.png"/>
</uap:VisualElements>
<Extensions>
@@ -75,8 +74,45 @@
Enabled="false"
DisplayName="ms-resource:AppNamePre" />
</uap5:Extension>
<!-- DISABLED FOR 1.8
<uap3:Extension Category="windows.appExtension">
<uap3:AppExtension Name="com.microsoft.windows.console.host"
Id="OpenConsole-Pre"
DisplayName="OpenConsole Preview"
Description="Console host built from microsoft/terminal open source repository"
PublicFolder="Public">
<uap3:Properties>
<Clsid>{06EC847C-C0A5-46B8-92CB-7C92F6E35CD5}</Clsid>
</uap3:Properties>
</uap3:AppExtension>
</uap3:Extension>
<uap3:Extension Category="windows.appExtension">
<uap3:AppExtension Name="com.microsoft.windows.terminal.host"
Id="Terminal-Pre"
DisplayName="Windows Terminal Preview"
Description="Terminal host built from microsoft/terminal open source repository"
PublicFolder="Public">
<uap3:Properties>
<Clsid>{86633F1F-6454-40EC-89CE-DA4EBA977EE2}</Clsid>
</uap3:Properties>
</uap3:AppExtension>
</uap3:Extension>
-->
<com:Extension Category="windows.comInterface">
<com:ComInterface>
<com:ProxyStub Id="1833E661-CC81-4DD0-87C6-C2F74BD39EFA" DisplayName="OpenConsoleHandoffProxy" Path="OpenConsoleProxy.dll"/>
<com:Interface Id="2B607BC1-43EB-40C3-95AE-2856ADDB7F23" ProxyStubClsid="1833E661-CC81-4DD0-87C6-C2F74BD39EFA"/>
<com:Interface Id="FA1E3AB4-9AEC-4A3C-96CA-E6078C30BD74" ProxyStubClsid="1833E661-CC81-4DD0-87C6-C2F74BD39EFA"/>
</com:ComInterface>
</com:Extension>
<com:Extension Category="windows.comServer">
<com:ComServer>
<com:ExeServer DisplayName="OpenConsole" Executable="OpenConsole.exe">
<com:Class Id="06EC847C-C0A5-46B8-92CB-7C92F6E35CD5"/>
</com:ExeServer>
<com:ExeServer DisplayName="WindowsTerminal" Executable="WindowsTerminal.exe">
<com:Class Id="86633F1F-6454-40EC-89CE-DA4EBA977EE2"/>
</com:ExeServer>
<com:SurrogateServer DisplayName="WindowsTerminalShellExt">
<com:Class Id="02db545a-3e20-46de-83a5-1329b1e88b6b" Path="WindowsTerminalShellExt.dll" ThreadingModel="STA"/>
</com:SurrogateServer>
@@ -106,8 +142,10 @@
<Extensions>
<uap7:Extension Category="windows.sharedFonts">
<uap7:SharedFonts>
<uap4:Font File="Cascadia.ttf" />
<uap4:Font File="CascadiaCode.ttf" />
<uap4:Font File="CascadiaCodeItalic.ttf" />
<uap4:Font File="CascadiaMono.ttf" />
<uap4:Font File="CascadiaMonoItalic.ttf" />
</uap7:SharedFonts>
</uap7:Extension>
</Extensions>

View File

@@ -55,7 +55,6 @@
<uap:ShowOn Tile="square310x310Logo"/>
</uap:ShowNameOnTiles>
</uap:DefaultTile>
<uap:SplashScreen Image="Images\SplashScreen.png"/>
</uap:VisualElements>
<Extensions>
@@ -75,9 +74,43 @@
Enabled="false"
DisplayName="ms-resource:AppName" />
</uap5:Extension>
<!-- <uap3:Extension Category="windows.appExtension">
<uap3:AppExtension Name="com.microsoft.windows.console.host"
Id="OpenConsole"
DisplayName="OpenConsole"
Description="Console host built from microsoft/terminal open source repository"
PublicFolder="Public">
<uap3:Properties>
<Clsid>{2EACA947-7F5F-4CFA-BA87-8F7FBEEFBE69}</Clsid>
</uap3:Properties>
</uap3:AppExtension>
</uap3:Extension>
<uap3:Extension Category="windows.appExtension">
<uap3:AppExtension Name="com.microsoft.windows.terminal.host"
Id="Terminal"
DisplayName="Windows Terminal"
Description="Terminal host built from microsoft/terminal open source repository"
PublicFolder="Public">
<uap3:Properties>
<Clsid>{E12CFF52-A866-4C77-9A90-F570A7AA2C6B}</Clsid>
</uap3:Properties>
</uap3:AppExtension>
</uap3:Extension>
<com:Extension Category="windows.comInterface">
<com:ComInterface>
<com:ProxyStub Id="3171DE52-6EFA-4AEF-8A9F-D02BD67E7A4F" DisplayName="OpenConsoleHandoffProxy" Path="OpenConsoleProxy.dll"/>
<com:Interface Id="2B607BC1-43EB-40C3-95AE-2856ADDB7F23" ProxyStubClsid="3171DE52-6EFA-4AEF-8A9F-D02BD67E7A4F"/>
<com:Interface Id="FA1E3AB4-9AEC-4A3C-96CA-E6078C30BD74" ProxyStubClsid="3171DE52-6EFA-4AEF-8A9F-D02BD67E7A4F"/>
</com:ComInterface>
</com:Extension> -->
<com:Extension Category="windows.comServer">
<com:ComServer>
<!-- <com:ExeServer DisplayName="OpenConsole" Executable="OpenConsole.exe">
<com:Class Id="2EACA947-7F5F-4CFA-BA87-8F7FBEEFBE69"/>
</com:ExeServer>
<com:ExeServer DisplayName="WindowsTerminal" Executable="WindowsTerminal.exe">
<com:Class Id="E12CFF52-A866-4C77-9A90-F570A7AA2C6B"/>
</com:ExeServer> -->
<com:SurrogateServer DisplayName="WindowsTerminalShellExt">
<com:Class Id="9f156763-7844-4dc4-b2b1-901f640f5155" Path="WindowsTerminalShellExt.dll" ThreadingModel="STA"/>
</com:SurrogateServer>
@@ -107,8 +140,10 @@
<Extensions>
<uap7:Extension Category="windows.sharedFonts">
<uap7:SharedFonts>
<uap4:Font File="Cascadia.ttf" />
<uap4:Font File="CascadiaCode.ttf" />
<uap4:Font File="CascadiaCodeItalic.ttf" />
<uap4:Font File="CascadiaMono.ttf" />
<uap4:Font File="CascadiaMonoItalic.ttf" />
</uap7:SharedFonts>
</uap7:Extension>
</Extensions>

View File

@@ -12,13 +12,9 @@
<Link>Images\%(RecursiveDir)%(FileName)%(Extension)</Link>
</Content>
<!-- Fonts -->
<Content Include="$(OpenConsoleDir)res\Cascadia.ttf" Condition="'$(WindowsTerminalOfficialBuild)'=='true'">
<Content Include="$(OpenConsoleDir)res\fonts\*.ttf" Condition="'$(WindowsTerminalOfficialBuild)'=='true'">
<DeploymentContent>true</DeploymentContent>
<Link>%(RecursiveDir)%(FileName)%(Extension)</Link>
</Content>
<Content Include="$(OpenConsoleDir)res\CascadiaMono.ttf" Condition="'$(WindowsTerminalOfficialBuild)'=='true'">
<DeploymentContent>true</DeploymentContent>
<Link>%(RecursiveDir)%(FileName)%(Extension)</Link>
<Link>%(FileName)%(Extension)</Link>
</Content>
<!-- Profile Icons -->
<Content Include="$(OpenConsoleDir)src\cascadia\CascadiaPackage\ProfileIcons\**\*">

View File

@@ -338,34 +338,34 @@ namespace SettingsModelLocalTests
// verify profile defaults
Log::Comment(L"Profile Defaults");
VERIFY_ARE_EQUAL(newName, settings->ProfileDefaults().ColorSchemeName());
VERIFY_IS_TRUE(settings->ProfileDefaults().HasColorSchemeName());
VERIFY_ARE_EQUAL(newName, settings->ProfileDefaults().DefaultAppearance().ColorSchemeName());
VERIFY_IS_TRUE(settings->ProfileDefaults().DefaultAppearance().HasColorSchemeName());
// verify all other profiles
const auto& profiles{ settings->AllProfiles() };
{
const auto& prof{ profiles.GetAt(0) };
Log::Comment(prof.Name().c_str());
VERIFY_ARE_EQUAL(newName, prof.ColorSchemeName());
VERIFY_IS_TRUE(prof.HasColorSchemeName());
VERIFY_ARE_EQUAL(newName, prof.DefaultAppearance().ColorSchemeName());
VERIFY_IS_TRUE(prof.DefaultAppearance().HasColorSchemeName());
}
{
const auto& prof{ profiles.GetAt(1) };
Log::Comment(prof.Name().c_str());
VERIFY_ARE_EQUAL(newName, prof.ColorSchemeName());
VERIFY_IS_TRUE(prof.HasColorSchemeName());
VERIFY_ARE_EQUAL(newName, prof.DefaultAppearance().ColorSchemeName());
VERIFY_IS_TRUE(prof.DefaultAppearance().HasColorSchemeName());
}
{
const auto& prof{ profiles.GetAt(2) };
Log::Comment(prof.Name().c_str());
VERIFY_ARE_EQUAL(newName, prof.ColorSchemeName());
VERIFY_IS_FALSE(prof.HasColorSchemeName());
VERIFY_ARE_EQUAL(newName, prof.DefaultAppearance().ColorSchemeName());
VERIFY_IS_FALSE(prof.DefaultAppearance().HasColorSchemeName());
}
{
const auto& prof{ profiles.GetAt(3) };
Log::Comment(prof.Name().c_str());
VERIFY_ARE_EQUAL(L"Scheme 2", prof.ColorSchemeName());
VERIFY_IS_TRUE(prof.HasColorSchemeName());
VERIFY_ARE_EQUAL(L"Scheme 2", prof.DefaultAppearance().ColorSchemeName());
VERIFY_IS_TRUE(prof.DefaultAppearance().HasColorSchemeName());
}
}
}

View File

@@ -1311,9 +1311,9 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(3u, settings->_allProfiles.Size());
VERIFY_ARE_EQUAL(2u, settings->_globals->ColorSchemes().Size());
VERIFY_ARE_EQUAL(L"schemeOne", settings->_allProfiles.GetAt(0).ColorSchemeName());
VERIFY_ARE_EQUAL(L"InvalidSchemeName", settings->_allProfiles.GetAt(1).ColorSchemeName());
VERIFY_ARE_EQUAL(L"Campbell", settings->_allProfiles.GetAt(2).ColorSchemeName());
VERIFY_ARE_EQUAL(L"schemeOne", settings->_allProfiles.GetAt(0).DefaultAppearance().ColorSchemeName());
VERIFY_ARE_EQUAL(L"InvalidSchemeName", settings->_allProfiles.GetAt(1).DefaultAppearance().ColorSchemeName());
VERIFY_ARE_EQUAL(L"Campbell", settings->_allProfiles.GetAt(2).DefaultAppearance().ColorSchemeName());
settings->_ValidateAllSchemesExist();
@@ -1323,9 +1323,9 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(3u, settings->_allProfiles.Size());
VERIFY_ARE_EQUAL(2u, settings->_globals->ColorSchemes().Size());
VERIFY_ARE_EQUAL(L"schemeOne", settings->_allProfiles.GetAt(0).ColorSchemeName());
VERIFY_ARE_EQUAL(L"Campbell", settings->_allProfiles.GetAt(1).ColorSchemeName());
VERIFY_ARE_EQUAL(L"Campbell", settings->_allProfiles.GetAt(2).ColorSchemeName());
VERIFY_ARE_EQUAL(L"schemeOne", settings->_allProfiles.GetAt(0).DefaultAppearance().ColorSchemeName());
VERIFY_ARE_EQUAL(L"Campbell", settings->_allProfiles.GetAt(1).DefaultAppearance().ColorSchemeName());
VERIFY_ARE_EQUAL(L"Campbell", settings->_allProfiles.GetAt(2).DefaultAppearance().ColorSchemeName());
}
void DeserializationTests::ValidateColorSchemeInCommands()
@@ -1543,7 +1543,7 @@ namespace SettingsModelLocalTests
settings->_ParseJsonString(settingsJson, false);
settings->LayerJson(settings->_userSettings);
VERIFY_ARE_NOT_EQUAL(0u, settings->_allProfiles.Size());
VERIFY_ARE_EQUAL(expectedPath, settings->_allProfiles.GetAt(0).ExpandedBackgroundImagePath());
VERIFY_ARE_EQUAL(expectedPath, settings->_allProfiles.GetAt(0).DefaultAppearance().ExpandedBackgroundImagePath());
}
void DeserializationTests::TestProfileBackgroundImageWithDesktopWallpaper()
{
@@ -1564,8 +1564,8 @@ namespace SettingsModelLocalTests
auto settings = winrt::make_self<implementation::CascadiaSettings>();
settings->_ParseJsonString(settingsJson, false);
settings->LayerJson(settings->_userSettings);
VERIFY_ARE_EQUAL(expectedBackgroundImagePath, settings->_allProfiles.GetAt(0).BackgroundImagePath());
VERIFY_ARE_NOT_EQUAL(expectedBackgroundImagePath, settings->_allProfiles.GetAt(0).ExpandedBackgroundImagePath());
VERIFY_ARE_EQUAL(expectedBackgroundImagePath, settings->_allProfiles.GetAt(0).DefaultAppearance().BackgroundImagePath());
VERIFY_ARE_NOT_EQUAL(expectedBackgroundImagePath, settings->_allProfiles.GetAt(0).DefaultAppearance().ExpandedBackgroundImagePath());
}
void DeserializationTests::TestCloseOnExitParsing()
{

View File

@@ -117,14 +117,14 @@ namespace SettingsModelLocalTests
const auto profile2Json = VerifyParseSucceeded(profile2String);
auto profile0 = implementation::Profile::FromJson(profile0Json);
VERIFY_IS_NOT_NULL(profile0->Foreground());
VERIFY_ARE_EQUAL(til::color(0, 0, 0), til::color{ profile0->Foreground().Value() });
VERIFY_IS_NOT_NULL(profile0->DefaultAppearance().Foreground());
VERIFY_ARE_EQUAL(til::color(0, 0, 0), til::color{ profile0->DefaultAppearance().Foreground().Value() });
VERIFY_IS_NOT_NULL(profile0->Background());
VERIFY_ARE_EQUAL(til::color(1, 1, 1), til::color{ profile0->Background().Value() });
VERIFY_IS_NOT_NULL(profile0->DefaultAppearance().Background());
VERIFY_ARE_EQUAL(til::color(1, 1, 1), til::color{ profile0->DefaultAppearance().Background().Value() });
VERIFY_IS_NOT_NULL(profile0->SelectionBackground());
VERIFY_ARE_EQUAL(til::color(1, 1, 1), til::color{ profile0->SelectionBackground().Value() });
VERIFY_IS_NOT_NULL(profile0->DefaultAppearance().SelectionBackground());
VERIFY_ARE_EQUAL(til::color(1, 1, 1), til::color{ profile0->DefaultAppearance().SelectionBackground().Value() });
VERIFY_ARE_EQUAL(L"profile0", profile0->Name());
@@ -135,14 +135,14 @@ namespace SettingsModelLocalTests
auto profile1{ profile0->CreateChild() };
profile1->LayerJson(profile1Json);
VERIFY_IS_NOT_NULL(profile1->Foreground());
VERIFY_ARE_EQUAL(til::color(2, 2, 2), til::color{ profile1->Foreground().Value() });
VERIFY_IS_NOT_NULL(profile1->DefaultAppearance().Foreground());
VERIFY_ARE_EQUAL(til::color(2, 2, 2), til::color{ profile1->DefaultAppearance().Foreground().Value() });
VERIFY_IS_NOT_NULL(profile1->Background());
VERIFY_ARE_EQUAL(til::color(1, 1, 1), til::color{ profile1->Background().Value() });
VERIFY_IS_NOT_NULL(profile1->DefaultAppearance().Background());
VERIFY_ARE_EQUAL(til::color(1, 1, 1), til::color{ profile1->DefaultAppearance().Background().Value() });
VERIFY_IS_NOT_NULL(profile1->Background());
VERIFY_ARE_EQUAL(til::color(1, 1, 1), til::color{ profile1->Background().Value() });
VERIFY_IS_NOT_NULL(profile1->DefaultAppearance().Background());
VERIFY_ARE_EQUAL(til::color(1, 1, 1), til::color{ profile1->DefaultAppearance().Background().Value() });
VERIFY_ARE_EQUAL(L"profile1", profile1->Name());
@@ -154,14 +154,14 @@ namespace SettingsModelLocalTests
auto profile2{ profile1->CreateChild() };
profile2->LayerJson(profile2Json);
VERIFY_IS_NOT_NULL(profile2->Foreground());
VERIFY_ARE_EQUAL(til::color(3, 3, 3), til::color{ profile2->Foreground().Value() });
VERIFY_IS_NOT_NULL(profile2->DefaultAppearance().Foreground());
VERIFY_ARE_EQUAL(til::color(3, 3, 3), til::color{ profile2->DefaultAppearance().Foreground().Value() });
VERIFY_IS_NOT_NULL(profile2->Background());
VERIFY_ARE_EQUAL(til::color(1, 1, 1), til::color{ profile2->Background().Value() });
VERIFY_IS_NOT_NULL(profile2->DefaultAppearance().Background());
VERIFY_ARE_EQUAL(til::color(1, 1, 1), til::color{ profile2->DefaultAppearance().Background().Value() });
VERIFY_IS_NOT_NULL(profile2->SelectionBackground());
VERIFY_ARE_EQUAL(til::color(2, 2, 2), til::color{ profile2->SelectionBackground().Value() });
VERIFY_IS_NOT_NULL(profile2->DefaultAppearance().SelectionBackground());
VERIFY_ARE_EQUAL(til::color(2, 2, 2), til::color{ profile2->DefaultAppearance().SelectionBackground().Value() });
VERIFY_ARE_EQUAL(L"profile2", profile2->Name());

View File

@@ -126,7 +126,8 @@ namespace SettingsModelLocalTests
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(guid0, guid);
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
@@ -147,7 +148,8 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}", realArgs.TerminalArgs().Profile());
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(guid1, guid);
VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings.Commandline());
VERIFY_ARE_EQUAL(2, termSettings.HistorySize());
@@ -168,7 +170,8 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile());
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(guid1, guid);
VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings.Commandline());
VERIFY_ARE_EQUAL(2, termSettings.HistorySize());
@@ -189,7 +192,8 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile());
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(profile2Guid, guid);
VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline());
VERIFY_ARE_EQUAL(3, termSettings.HistorySize());
@@ -210,7 +214,8 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(L"foo.exe", realArgs.TerminalArgs().Commandline());
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(guid0, guid);
VERIFY_ARE_EQUAL(L"foo.exe", termSettings.Commandline());
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
@@ -232,7 +237,8 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(L"foo.exe", realArgs.TerminalArgs().Commandline());
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(guid1, guid);
VERIFY_ARE_EQUAL(L"foo.exe", termSettings.Commandline());
VERIFY_ARE_EQUAL(2, termSettings.HistorySize());
@@ -251,7 +257,8 @@ namespace SettingsModelLocalTests
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(guid0, guid);
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
@@ -271,7 +278,8 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(L"c:\\foo", realArgs.TerminalArgs().StartingDirectory());
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(guid0, guid);
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
VERIFY_ARE_EQUAL(L"c:\\foo", termSettings.StartingDirectory());
@@ -293,7 +301,8 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile());
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(profile2Guid, guid);
VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline());
VERIFY_ARE_EQUAL(L"c:\\foo", termSettings.StartingDirectory());
@@ -314,7 +323,8 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(L"bar", realArgs.TerminalArgs().TabTitle());
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(guid0, guid);
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
VERIFY_ARE_EQUAL(L"bar", termSettings.StartingTitle());
@@ -336,7 +346,8 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile());
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(profile2Guid, guid);
VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline());
VERIFY_ARE_EQUAL(L"bar", termSettings.StartingTitle());
@@ -360,7 +371,8 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile());
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(guid1, guid);
VERIFY_ARE_EQUAL(L"foo.exe", termSettings.Commandline());
VERIFY_ARE_EQUAL(L"bar", termSettings.StartingTitle());
@@ -398,7 +410,7 @@ namespace SettingsModelLocalTests
{
auto terminalSettings{ TerminalSettings::CreateWithProfileByID(settings, guid1, nullptr) };
VERIFY_ARE_NOT_EQUAL(nullptr, terminalSettings);
VERIFY_ARE_EQUAL(1, terminalSettings.HistorySize());
VERIFY_ARE_EQUAL(1, terminalSettings.DefaultSettings().HistorySize());
}
catch (...)
{
@@ -409,7 +421,7 @@ namespace SettingsModelLocalTests
{
auto terminalSettings{ TerminalSettings::CreateWithProfileByID(settings, guid2, nullptr) };
VERIFY_ARE_NOT_EQUAL(nullptr, terminalSettings);
VERIFY_ARE_EQUAL(2, terminalSettings.HistorySize());
VERIFY_ARE_EQUAL(2, terminalSettings.DefaultSettings().HistorySize());
}
catch (...)
{
@@ -422,7 +434,7 @@ namespace SettingsModelLocalTests
{
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, nullptr, nullptr) };
VERIFY_ARE_NOT_EQUAL(nullptr, termSettings);
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
VERIFY_ARE_EQUAL(1, termSettings.DefaultSettings().HistorySize());
}
catch (...)
{
@@ -461,7 +473,7 @@ namespace SettingsModelLocalTests
{
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, nullptr, nullptr) };
VERIFY_ARE_NOT_EQUAL(nullptr, termSettings);
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
VERIFY_ARE_EQUAL(1, termSettings.DefaultSettings().HistorySize());
}
catch (...)
{
@@ -522,7 +534,8 @@ namespace SettingsModelLocalTests
auto createTerminalSettings = [&](const auto& profile, const auto& schemes) {
auto terminalSettings{ winrt::make_self<implementation::TerminalSettings>() };
terminalSettings->_ApplyProfileSettings(profile, schemes);
terminalSettings->_ApplyProfileSettings(profile);
terminalSettings->_ApplyAppearanceSettings(profile.DefaultAppearance(), schemes);
return terminalSettings;
};

View File

@@ -56,6 +56,8 @@ Author(s):
#include <winrt/Microsoft.UI.Xaml.Controls.h>
#include <winrt/Microsoft.Terminal.Core.h>
// Manually include til after we include Windows.Foundation to give it winrt superpowers
#include "til.h"

View File

@@ -458,6 +458,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor());
VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex());
VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty());
VERIFY_IS_TRUE(myArgs.TerminalArgs().ColorScheme().empty());
}
{
AppCommandlineArgs appArgs{};
@@ -479,6 +480,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex());
VERIFY_IS_FALSE(myArgs.TerminalArgs().Profile().empty());
VERIFY_ARE_EQUAL(L"cmd", myArgs.TerminalArgs().Profile());
VERIFY_IS_TRUE(myArgs.TerminalArgs().ColorScheme().empty());
}
{
AppCommandlineArgs appArgs{};
@@ -500,6 +502,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex());
VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty());
VERIFY_ARE_EQUAL(L"c:\\Foo", myArgs.TerminalArgs().StartingDirectory());
VERIFY_IS_TRUE(myArgs.TerminalArgs().ColorScheme().empty());
}
{
AppCommandlineArgs appArgs{};
@@ -521,6 +524,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex());
VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty());
VERIFY_ARE_EQUAL(L"powershell.exe", myArgs.TerminalArgs().Commandline());
VERIFY_IS_TRUE(myArgs.TerminalArgs().ColorScheme().empty());
}
{
AppCommandlineArgs appArgs{};
@@ -543,6 +547,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty());
auto myCommand = myArgs.TerminalArgs().Commandline();
VERIFY_ARE_EQUAL(L"powershell.exe \"This is an arg with spaces\"", myCommand);
VERIFY_IS_TRUE(myArgs.TerminalArgs().ColorScheme().empty());
}
{
AppCommandlineArgs appArgs{};
@@ -565,6 +570,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty());
auto myCommand = myArgs.TerminalArgs().Commandline();
VERIFY_ARE_EQUAL(L"powershell.exe \"This is an arg with spaces\" another-arg \"more spaces in this one\"", myCommand);
VERIFY_IS_TRUE(myArgs.TerminalArgs().ColorScheme().empty());
}
{
AppCommandlineArgs appArgs{};
@@ -586,6 +592,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex());
VERIFY_IS_FALSE(myArgs.TerminalArgs().Profile().empty());
VERIFY_ARE_EQUAL(L"Windows PowerShell", myArgs.TerminalArgs().Profile());
VERIFY_IS_TRUE(myArgs.TerminalArgs().ColorScheme().empty());
}
{
AppCommandlineArgs appArgs{};
@@ -606,6 +613,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex());
VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty());
VERIFY_ARE_EQUAL(L"wsl -d Alpine", myArgs.TerminalArgs().Commandline());
VERIFY_IS_TRUE(myArgs.TerminalArgs().ColorScheme().empty());
}
{
AppCommandlineArgs appArgs{};
@@ -628,6 +636,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_FALSE(myArgs.TerminalArgs().Profile().empty());
VERIFY_ARE_EQUAL(L"wsl -d Alpine", myArgs.TerminalArgs().Commandline());
VERIFY_ARE_EQUAL(L"1", myArgs.TerminalArgs().Profile());
VERIFY_IS_TRUE(myArgs.TerminalArgs().ColorScheme().empty());
}
{
AppCommandlineArgs appArgs{};
@@ -651,6 +660,31 @@ namespace TerminalAppLocalTests
VERIFY_ARE_EQUAL(til::color(myArgs.TerminalArgs().TabColor().Value()), expectedColor);
VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex());
VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty());
VERIFY_IS_TRUE(myArgs.TerminalArgs().ColorScheme().empty());
}
{
AppCommandlineArgs appArgs{};
std::vector<const wchar_t*> rawCommands{ L"wt.exe", subcommand, L"--colorScheme", L"Vintage" };
const winrt::hstring expectedScheme{ L"Vintage" };
_buildCommandlinesHelper(appArgs, 1u, rawCommands);
VERIFY_ARE_EQUAL(1u, appArgs._startupActions.size());
auto actionAndArgs = appArgs._startupActions.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_NULL(myArgs.TerminalArgs().TabColor());
VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex());
VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty());
VERIFY_IS_FALSE(myArgs.TerminalArgs().ColorScheme().empty());
VERIFY_ARE_EQUAL(expectedScheme, myArgs.TerminalArgs().ColorScheme());
}
}
@@ -680,6 +714,7 @@ namespace TerminalAppLocalTests
auto myArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(myArgs);
VERIFY_ARE_EQUAL(SplitState::Automatic, myArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitType::Manual, myArgs.SplitMode());
VERIFY_IS_NOT_NULL(myArgs.TerminalArgs());
}
{
@@ -699,6 +734,7 @@ namespace TerminalAppLocalTests
auto myArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(myArgs);
VERIFY_ARE_EQUAL(SplitState::Horizontal, myArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitType::Manual, myArgs.SplitMode());
VERIFY_IS_NOT_NULL(myArgs.TerminalArgs());
}
{
@@ -720,6 +756,28 @@ namespace TerminalAppLocalTests
auto myArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(myArgs);
VERIFY_ARE_EQUAL(SplitState::Vertical, myArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitType::Manual, myArgs.SplitMode());
VERIFY_IS_NOT_NULL(myArgs.TerminalArgs());
}
{
AppCommandlineArgs appArgs{};
std::vector<const wchar_t*> rawCommands{ L"wt.exe", subcommand, L"-D" };
auto commandlines = AppCommandlineArgs::BuildCommands(rawCommands);
VERIFY_ARE_EQUAL(1u, commandlines.size());
_buildCommandlinesHelper(appArgs, 1u, rawCommands);
VERIFY_ARE_EQUAL(2u, appArgs._startupActions.size());
// The first action is going to always be a new-tab action
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, appArgs._startupActions.at(0).Action());
// The one we actually want to test here is the SplitPane action we created
auto actionAndArgs = appArgs._startupActions.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_ARE_EQUAL(SplitType::Duplicate, myArgs.SplitMode());
VERIFY_IS_NOT_NULL(myArgs.TerminalArgs());
}
{
@@ -749,6 +807,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_FALSE(myArgs.TerminalArgs().Profile().empty());
VERIFY_ARE_EQUAL(L"wsl -d Alpine", myArgs.TerminalArgs().Commandline());
VERIFY_ARE_EQUAL(L"1", myArgs.TerminalArgs().Profile());
VERIFY_IS_TRUE(myArgs.TerminalArgs().ColorScheme().empty());
}
{
AppCommandlineArgs appArgs{};
@@ -777,6 +836,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_FALSE(myArgs.TerminalArgs().Profile().empty());
VERIFY_ARE_EQUAL(L"wsl -d Alpine", myArgs.TerminalArgs().Commandline());
VERIFY_ARE_EQUAL(L"1", myArgs.TerminalArgs().Profile());
VERIFY_IS_TRUE(myArgs.TerminalArgs().ColorScheme().empty());
}
{
AppCommandlineArgs appArgs{};
@@ -805,6 +865,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_FALSE(myArgs.TerminalArgs().Profile().empty());
VERIFY_ARE_EQUAL(L"wsl -d Alpine -H", myArgs.TerminalArgs().Commandline());
VERIFY_ARE_EQUAL(L"1", myArgs.TerminalArgs().Profile());
VERIFY_IS_TRUE(myArgs.TerminalArgs().ColorScheme().empty());
}
}

View File

@@ -85,6 +85,9 @@ namespace TerminalAppLocalTests
TEST_METHOD(NextMRUTab);
TEST_METHOD(VerifyCommandPaletteTabSwitcherOrder);
TEST_METHOD(TestWindowRenameSuccessful);
TEST_METHOD(TestWindowRenameFailure);
TEST_CLASS_SETUP(ClassSetup)
{
return true;
@@ -260,6 +263,15 @@ namespace TerminalAppLocalTests
page->Create();
Log::Comment(L"Create()'d the page successfully");
// Build a NewTab action, to make sure we start with one. The real
// Terminal will always get one from AppCommandlineArgs.
NewTerminalArgs newTerminalArgs{};
NewTabArgs args{ newTerminalArgs };
ActionAndArgs newTabAction{ ShortcutAction::NewTab, args };
// push the arg onto the front
page->_startupActions.Append(newTabAction);
Log::Comment(L"Added a single newTab action");
auto app = ::winrt::Windows::UI::Xaml::Application::Current();
winrt::TerminalApp::TerminalPage pp = *page;
@@ -276,8 +288,9 @@ namespace TerminalAppLocalTests
// In the real app, this isn't a problem, but doesn't happen
// reliably in the unit tests.
Log::Comment(L"Ensure we set the first tab as the selected one.");
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
page->_tabView.SelectedItem(tab->TabViewItem());
auto tab = page->_tabs.GetAt(0);
auto tabImpl = page->_GetTerminalTabImpl(tab);
page->_tabView.SelectedItem(tabImpl->TabViewItem());
page->_UpdatedSelectedTab(0);
});
VERIFY_SUCCEEDED(result);
@@ -601,7 +614,6 @@ namespace TerminalAppLocalTests
auto result = RunOnUIThread([&page]() {
SplitPaneArgs args{ SplitType::Duplicate };
ActionEventArgs eventArgs{ args };
// eventArgs.Args(args);
page->_HandleSplitPane(nullptr, eventArgs);
auto firstTab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
@@ -939,4 +951,57 @@ namespace TerminalAppLocalTests
// will also dismiss itself immediately when that's called. So we can't
// really inspect the contents of the list in this test, unfortunately.
}
void TabTests::TestWindowRenameSuccessful()
{
auto page = _commonSetup();
page->RenameWindowRequested([&page](auto&&, const winrt::TerminalApp::RenameWindowRequestedArgs args) {
// In the real terminal, this would bounce up to the monarch and
// come back down. Instead, immediately call back and set the name.
page->WindowName(args.ProposedName());
});
bool windowNameChanged = false;
page->PropertyChanged([&page, &windowNameChanged](auto&&, const winrt::WUX::Data::PropertyChangedEventArgs& args) mutable {
if (args.PropertyName() == L"WindowNameForDisplay")
{
windowNameChanged = true;
}
});
TestOnUIThread([&page]() {
page->_RequestWindowRename(winrt::hstring{ L"Foo" });
});
TestOnUIThread([&]() {
VERIFY_ARE_EQUAL(L"Foo", page->_WindowName);
VERIFY_IS_TRUE(windowNameChanged,
L"The window name should have changed, and we should have raised a notification that WindowNameForDisplay changed");
});
}
void TabTests::TestWindowRenameFailure()
{
auto page = _commonSetup();
page->RenameWindowRequested([&page](auto&&, auto&&) {
// In the real terminal, this would bounce up to the monarch and
// come back down. Instead, immediately call back to tell the terminal it failed.
page->RenameFailed();
});
bool windowNameChanged = false;
page->PropertyChanged([&page, &windowNameChanged](auto&&, const winrt::WUX::Data::PropertyChangedEventArgs& args) mutable {
if (args.PropertyName() == L"WindowNameForDisplay")
{
windowNameChanged = true;
}
});
TestOnUIThread([&page]() {
page->_RequestWindowRename(winrt::hstring{ L"Foo" });
});
TestOnUIThread([&]() {
VERIFY_IS_FALSE(windowNameChanged,
L"The window name should not have changed, we should have rejected the change.");
});
}
}

View File

@@ -117,7 +117,7 @@
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestExecutor.WinRTCore">
<HintPath>$(OpenConsoleDir)\packages\Taef.Redist.Wlk.10.57.200731005-develop\lib\Microsoft.VisualStudio.TestPlatform.TestExecutor.WinRTCore.winmd</HintPath>
<HintPath>$(OpenConsoleDir)\packages\Microsoft.Taef.10.58.210305002\lib\Microsoft.VisualStudio.TestPlatform.TestExecutor.WinRTCore.winmd</HintPath>
<IsWinMDFile>true</IsWinMDFile>
<!-- This path is _relative to the .winmd_ -->

View File

@@ -1,8 +1,5 @@
<Application
x:Class="TestHostApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:TestHostApp"
RequestedTheme="Dark">
</Application>
<Application x:Class="TestHostApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:TestHostApp"
RequestedTheme="Dark" />

View File

@@ -45,6 +45,7 @@ Author(s):
#include <winrt/Windows.ui.input.h>
#include <winrt/Windows.UI.Xaml.Controls.h>
#include <winrt/Windows.UI.Xaml.Controls.Primitives.h>
#include <winrt/Windows.UI.Xaml.Data.h>
#include <winrt/Windows.ui.xaml.media.h>
#include <winrt/Windows.ui.xaml.input.h>
#include <winrt/Windows.UI.Xaml.Markup.h>

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