Compare commits

..

62 Commits

Author SHA1 Message Date
Dustin L. Howett
0e8be883c6 Migrate spelling-0.0.21 changes from main 2021-03-10 13:42:32 -08:00
Dustin L. Howett
76f8a74022 Migrate spelling-0.0.19 changes from main 2021-03-10 13:42:32 -08:00
Carlos Zamora
d913907237 Fix Settings UI crash from reloading JSON 2021-03-12 11:39:41 -08:00
PankajBhojwani
a47ed99272 Allow shift+click on a profile to open a new window (#9429)
<!-- 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
Shift+click on a profile to open a new wt window with that profile. Or, shift+click on the '+' button to open a new wt window with the default profile. 

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
* [x] Closes #9395 
* [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

<!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
Manual testing
2021-03-10 18:32:54 +00:00
Dustin Howett
95b031e27c ci: fix spelling issues from inbox merge 2021-03-09 17:46:43 -08:00
Dustin Howett
8f9ccc55d1 Merge inbox changes into main
This includes:
* The property sheet work for #492
* A fix for til::pmr on ARM inside Windows
2021-03-09 17:38:59 -08:00
Dustin Howett
3909cc103a Merged PR 5770253: [Git2Git] Merged PR 5760120: Add propsheet chooser to Windows
[Git2Git] Merged PR 5760120: Add propsheet chooser to Windows

Now the inbox console propsheet can choose which terminal is default

Related work items: MSFT-32007202 #492

Retrieved from https://microsoft.visualstudio.com os.2020 OS official/rs_wdx_dxp_windev d80f506858bd990c267de6cefae7ff55707b3a57
2021-03-10 01:19:04 +00:00
Eric Tian
8358f8d93f Display correct tooltip when window is maximized (#9412)
## Summary of the Pull Request
Instead of displaying "Maximize" in the tooltip for the maximize/restore button even when the window is maximized, it now displays "Restore Down".

## References
Fixes #5693

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

## Validation Steps Performed
Tested manually.
2021-03-09 17:41:29 +00:00
Eugene Samoylov
48d59e8304 [Settings UI] Represent Cursor Height as a slider (#9386)
Change the vintage cursor height number box to a slider.

## References
Related:  #9370

## PR Checklist
* [x] Closes #9377
* [x] zadjii-msft edit: Now _this one_ closes #9175
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [x] Schema updated.
* [ ] 

## Detailed Description of the Pull Request / Additional comments

It seems like the cursor height couldn't be lower than 25 percent regardless of the given value, so I've changed the `MinCursorHeightPercent` in CustomTextRenderer header file.

## Validation Steps Performed
Manual validation

![CursorHeightSlider](https://user-images.githubusercontent.com/39456018/110041939-bf076080-7d66-11eb-8d58-ba9a84922803.gif)
2021-03-09 17:13:32 +00:00
Don-Vito
83f2a3bb3d Fix selection logic with shift on multi-click (#9403)
## PR Checklist
* [x] Closes https://github.com/microsoft/terminal/issues/9382
* [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
The selection with shift is quite broken in 1.6.

It started with #8611 that introduces cell selection on `shift+click`.
This change resulted in the following defect:
`shift+double-click`, `shift+triple-click` select only parts  of the word.
The reason for this is that the first `shift+click` establishes the selection,
while the consequent clicks simply extend it to the relevant boundary
(aka word / line boundary)

However, the logic was broken even before #8611.
For instance, `shift+triple-click` had exactly the same handicap:
`shift+double-click` was establishing the selection and the
third click was simply extending it to the line boundary.

This PR addresses the both defects in the following manner:
upon multi-click that starts new selection we establish
a new selection on every consequent click using appropriate mode
(cell/word/line) rather than trying to extend one.
For this purpose we remember the position that started the selection.
2021-03-08 19:15:46 +00:00
Carlos Zamora
87fa526fb7 Represent font face as a combo box in SUI (#9275)
## Summary of the Pull Request

This replaces the Profiles > Font Face text box with a combo box.

## References
#6800 - Settings UI Epic

## Detailed Description of the Pull Request / Additional comments

- Enumerating the fonts
  - [This doc](https://docs.microsoft.com/en-us/windows/win32/directwrite/font-enumeration) was the main reference used to enumerate the fonts. It was mildly adapted to use WinRT instead of WRL.
- Updating the UI
  - Similar to other combo box settings, `Profiles` keeps a reference to the current value. We use that as a way to update the settings model. If an invalid value is used, we fallback to `Cascadia Mono`.
  - A checkbox was added to let the user select from all of the installed fonts, or just the monospace ones.

## Demo
![Font Face Combo Box Demo](https://user-images.githubusercontent.com/11050425/109342917-6cd3b600-7821-11eb-8df9-fb988b037e02.gif)
2021-03-08 16:45:12 +00:00
Don-Vito
c6a31710d9 Allow configuring suppressApplicationTitle in new tab/pane/window commands (#9392)
## PR Checklist
* [x] Closes https://github.com/microsoft/terminal/issues/9345
* [x] CLA signed. 
* [ ] Tests added/passed
* [ ] Documentation updated - not yet, will be once conceptually approved
* [x] Schema updated.
* [ ] I've discussed this with core contributors already. 

## Detailed Description of the Pull Request / Additional comments
Introduce optional `suppressApplicationTitle` in to `NewTerminalArgs`.
When set (either to true or false) overrides profile configuration.

Introduce `--suppressApplicationTitle` flag to command line arguments.
When provided for sub=command, 
sets the value in the relevant `NewTerminalArgs` to `true`
2021-03-08 15:23:50 +00:00
Don-Vito
19bd0c94e7 Fix TermControl initialization to pre-seed working dir (#9397)
## PR Checklist
* [x] Closes https://github.com/microsoft/terminal/issues/8969
* [x] CLA signed. 
* [ ] Tests added/passed
* [ ] Documentation updated. 
* [ ] Schema updated.
* [ ] I've discussed this with core contributors already.

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

<!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2021-03-08 14:56:36 +00:00
Don-Vito
629c06d0ad Introduce duplicate tab menu (#9388)
## PR Checklist
* [x] Closes https://github.com/microsoft/terminal/issues/9373
* [x] CLA signed. 
* [ ] Tests added/passed
* [ ] Documentation updated. 
* [ ] Schema updated.
* [ ] I've discussed this with core contributors already.
2021-03-08 12:16:56 +00:00
Don-Vito
1202f89399 Fix overflow in scroll-to-bottom (#9389)
## PR Checklist
* [x] Closes https://github.com/microsoft/terminal/issues/9353
* [x] CLA signed. 
* [ ] Tests added/passed
* [ ] Documentation updated. 
* [ ] Schema updated.
* [ ] I've discussed this with core contributors already.
2021-03-05 21:18:54 +00:00
Carlos Zamora
4a95341caf Change SettingsTab close icon to be smaller (#9324)
We were using the wrong close icon for the settings tab. This is a side effect of `TerminalTab` overriding `TabBase`'s implementation.

#6800 - Settings UI Epic

Closes #9317
2021-03-04 19:31:57 +00:00
Mike Griese
3cf7677d17 Replace some of our macros to reduce confusion, increase success (#9376)
As mentioned in https://github.com/microsoft/terminal/issues/9354#issuecomment-790034728

`GETSET_SETTING` is too visually similar to `GETSET_PROPERTY`, but with a _VERY_ different meaning. I think that merely changing the name of the macro would make it harder for us to make this mistake again.
2021-03-04 11:27:03 -08:00
Eugene Samoylov
ac3fecb134 [Settings UI] Clamp vintage cursor height and history size (#9370)
## Summary of the Pull Request
Add `Minimum` and `Maximum` for the cursor height numberbox in the SUI.
Add `Minimum` for the history size numberbox in the SUI.

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

## Validation Steps Performed
Manual validation
2021-03-04 17:47:23 +00:00
PankajBhojwani
5aaf5b4d59 Fix pixel shaders not loading (#9371)
<!-- 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
Fix for #9354 

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
* [x] Closes #9354 
* [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
2021-03-04 17:19:21 +00:00
Carlos Zamora
2961a104af Add localization comments to settings UI (#9367)
We've received feedback from the localization team that the strings need some comments to provide more context.

## References
Closes [MSFT-31929817](https://microsoft.visualstudio.com/OS/_workitems/edit/31929817)
#9328 - Chinese translation issues
#9238 - French translation issues
2021-03-03 16:28:48 -08:00
Michael Niksa
7b1a660e59 Create settings/tasks definitions for VScode builds and registration (#9297)
I wanted to start using VScode. It wasn't easy. I wrote some tasks that allow us to build the various flavors of OpenConsole and Windows Terminal from one of the tasks. I also wrote a task that allows registration of the loose Windows Terminal package and a shortcut one to launch it. 

Also it was grinding away at its own Intellisense forever because it was indexing obj, bin, packages, etc. I excluded those.

Things should be easier now for folks in general. I expect we'll make more task types in the future.
2021-02-26 18:50:15 +00:00
Dustin L. Howett
cb03b97e67 Update Cascadia Code to 2102.25 (#9295)
This update fixes some issues in Cascadia Code's February update:

microsoft/cascadia-code#406 - updated anchor type to lock with the other equals-related ligatures
microsoft/cascadia-code#408 - corrected component used for glyph to align with Unicode
microsoft/cascadia-code#412 - updated locl features removing iacute_j ligature and Catalan substitution
microsoft/cascadia-code#414 - increased overlaps of middle glyph for arrow ligatures
microsoft/cascadia-code#415 - reduces width of macronbelow
microsoft/cascadia-code#416 - rolls back name ID 4 modification as JetBrains cannot process it correctly
microsoft/cascadia-code#428 - rolls back variation of the underline to prevent MVAR table generation

Full changelist:
* Repositioned tilde in related ligatures. Previously it was higher than the standard one.
* Added missing vietnamese anchors on acute and grave (futureproofing).
* Corrected / made consistent greater & less positioning in </> and <$> related ligatures.
* Otherwise reviewed hinting
2021-02-25 14:47:03 -08:00
PankajBhojwani
f26c246c7c Fix updating with fragments ignoring original profile settings (#9293)
Turns out we were adding the fragment source to profiles we update. This
PR fixes it so we keep the original source. 

## Validation Steps Performed
Existing profile settings are maintained

Closes #9290
2021-02-25 22:44:43 +00:00
Carlos Zamora
649c546960 Update dropdown's settings keyboard accelerator (#9294)
Fixes a bug introduced by #9224 where the wrong keyboard accelerator
would appear in the new tab dropdown. We were looking for the "settings
file" version of the action, as opposed to the "settings UI" version. 

## References
#9224 - Settings UI as default
#6800 - Settings UI Epic
2021-02-25 22:44:24 +00:00
PankajBhojwani
a930aa390e Fix settings not updating on reload (#9289)
In #8602, we started passing a child of the `TerminalSettings` to the
control upon tab initialization, but forgot to do the same when new
controls get created on a pane split. 

## Validation Steps Performed
Settings reload with multiple panes works

Closes #9280
2021-02-25 13:00:20 -08:00
Carlos Zamora
d1bf0fcd1e Add help text to 'more options' in command palette (#9271)
Similar to #9262. This creates another data template specifically for
command palette items that open up more options. We leverage the
localization key from #9262 to apply help text to this template
automatically.

Using the data template approach, we now have no need for the
`HasNestedComandsVisibilityConverter`, so that set of files is now
deleted. The logic to detect nested commands was moved to the template
selector.

## Validation Steps Performed
Tested using NVDA.

Addresses #7908 better
2021-02-24 12:02:09 -08:00
Dustin L. Howett
66033dcb01 Exclude MonarchPeasantPackage from AnyCPU/DotNet*Test configs (#9272)
Visual Studio automatically enabled this package to build in all
configurations. This results in a build error when we go to pack the WPF
control.
2021-02-24 11:34:34 -08:00
Dustin Howett
95f63a9100 [PICK] Cherry-pick loc key from 83d35c119 2021-02-23 18:57:57 -08:00
Dustin Howett
6654f0d155 version: bump to 1.4 on master 2021-02-23 16:35:25 -08:00
Carlos Zamora
35e1168bfa Make Settings UI the default experience (#9224)
This makes the settings UI the default settings experience.

As shown below, the following bindings are now default:
- <kbd>ctrl+,</kbd> --> settings ui
- <kbd>ctrl+shift+,</kbd> --> settings.json
- <kbd>ctrl+alt+,</kbd> --> defaults.json

The dropdown settings button aligns with this heuristic:
- click --> settings ui
- shift+click --> settings.json
- alt+click --> defaults.json
- if alt and shift both pressed, open settings.json

#6800 - Settings UI Epic
2021-02-24 00:14:13 +00:00
Carlos Zamora
ef4f2ca03e Update automation properties for tab header control (#9258)
This sets the automation property (name) on the tab view item we expose in XAML.

## PR Checklist
* [x] Closes #9254 

## Validation Steps Performed
Tested under NVDA and Accessibility Insights
2021-02-23 23:40:42 +00:00
Mike Griese
f87596f8f7 Add a simple page for keybindings (#9253)
This was the only thing blocking me from signing off on #9224 in 1.7.

! CHANGE WARNING !
If we bind to `T.S.M.Command`s in XAML, then the compiler gets _very
angry_ at us. It generates two different versions of
`GetReferenceTypeMember_Icon` in `XamlTypeInfo.g.cpp`. Presumably
because there's an Icon on a NavViewItem and an Icon on a Command. We
don't really know why. Fortunately, the fix is "rename Command::Icon" to
"Command::IconPath". It's dumb, but it works. Thanks for the help with
that one Carlos ☺️ 

Unblocks #9224
2021-02-23 23:37:23 +00:00
Dustin L. Howett
17c6f8e9ff TabHeader: put TraceLogging events in the right category (#9257)
Fixes MSFT-31615100
2021-02-23 15:14:04 -08:00
Dustin L. Howett
81d773d2bf PICK: Work around an optimizer issue with SetPixelShaderPath (#9249)
It appears as though the optimizer is generating a sequence of
instructions on x64 that results in a nonsense std::wstring_view being
passed to SetPixelShaderPath when it's converted from a winrt::hstring.

Initially, I suspected that the issue was in us caching `_settings`
before we broke off the coroutine to update settings on the UI thread. I
implemented a quick fix for this (applying values off the new settings
object while also storing it in the control instance), but it didn't
actually lead anywhere. I do think it's the right thing to do for code
health's sake. Pankaj already changed how this works in 1.7: we no
longer (ever) re-seat the `_settings` reference... we only ever change
its parentage. Whether this is right or wrong is not for this paragraph
to discuss.

Eventually, I started looking more closely at the time travel traces. It
seriously looks like the wstring_view is generated wrong to begin with.
The debugger points directly at `return { L"", 0 };` (which is correct),
but the values we get immediately on the other side of the call are
something like `{ 0x7FFFFFFF, 0 }` or `{ 0x0, 0x48454C4C }`.

I moved _just_ the call to SetPixelShaderPath into a separate function.
The bug miraculously disappeared when I marked it **noinline**. It
reappeared when the function was fully inlined.

To avoid any future issues, I moved the whole UI thread body of
UpdateSettings out into its own function, to be called only while on the
UI thread. This fixes the bug.

Closes #8723.
Closes #9064.

I found a repro (update the settings file every 0.5 seconds and resize
the terminal wildly while it's doing so) that would trigger the bug
within ~10 seconds. It stopped doing so.

(cherry picked from commit 91b867102c)
2021-02-22 13:01:51 -08:00
WVVxm
8ad4d1f19a Control 'Touch Keyboard and Handwriting Panel Service' warning (#9015)
Add a setting to turn off the warning if 'Touch Keyboard and
Handwriting Panel Service' is disabled.

The service might not start in some case, and it doesn't affect the
input in some computer.  This PR turn off the warning even if the
service is disabled.  The setting name is  "inputServiceWarning".

## Validation Steps Performed
I manually set the service to "Disabled", restarted the Terminal,
verified the warning up, then set "inputServiceWarning" to false and
restarted the Terminal, and the warning didn't appear.

References #8095
References https://github.com/microsoft/terminal/issues/7886#issuecomment-729350169
2021-02-22 12:08:49 -08:00
Mike Griese
99ffaa8a1a Fix wt --help (#9246)
When the user executes `--help`, make sure we force the creation of a new window, so that the `MessageBox` will actually appear. 

Add tests too. 

* [x] I work here
* [x] Fixes #9230
* [x] Tests added


I'm gonna have to immediately rewrite those tests for https://github.com/microsoft/terminal/projects/5#card-51431478, but this issue is ship-blocking so I don't care
2021-02-22 18:50:39 +00:00
Dustin L. Howett
c9dea60bbe Hide the Command Palette's key binding label from UIA (#9234)
The command palette's list items explicitly specify the "AcceleratorKey"
property (for UIA) and set it to the key binding. Putting it in a label
inside the list item makes Narrator read it out twice ("New Tab ...
ctrl+shift+t ... ctrl+shift+t").

Addresses #7913 (to be closed when a11y re-evaluates)
2021-02-22 18:30:08 +00:00
spiralofhope
a158cc81ae res: prepend XML line to SVG files, to pass strict svg checks (#9226)
A header line is missing from a few `.svg` files, denying their use (via
a security error) in WordPress (if svg support is enabled). The upload
is rejected even though the web browser could display the image if it
were dragged into its own tab.

The `.svg` images appear to have been edited at different times and with
different tools.  Their contents are in a different style.  Two of them
are beautiful and the rest do not follow suit and do not function the
same.

I don't know enough to make them all the same style, but changes can be
made to three of them to make them work the way I was expecting (see
below).

## Validation Steps Performed
- Perform the change with a text editor
- Open a new WordPress post page
- Drag-and-drop the changed file into that WordPress edit box
- (The WordPress media upload dialog appears and the file is uploaded)
- Confirmed that the file does not trigger a "security error" (as seen
  at the top of the right-hand column)
- Confirmed that the image appears as a thumbnail preview

`Terminal_Pre_HC.svg` is not fixable in this way, and I don't understand
svg well enough to troubleshoot easily.
2021-02-19 17:15:04 -08:00
Mike Griese
049e37e514 Add support for the newWindow action (#9208)
Finally implements the `newWindow` action. It does so by
`ShellExecute`ing `wt.exe` with commandline args corresponding to the
ones that would create the same `NewTerminalArgs`. This works with #8898
and #9118 to allow new windows (even with `windowingBehavior:
useExisting`)

This is taken from my auto-elevate branch, hence the references to
elevation

References #5000
References projects/5
References #8898
References #9118
Closes #1051
2021-02-19 23:51:30 +00:00
Carlos Zamora
eb0fb3e822 Introduce setting override tracking and update SettingContainer (#9079)
This PR adds improved override message generation for inheritance in
SUI. The settings model now has an `OriginTag` to be able to denote
where a `Profile` came from. This tag is used in the `SettingContainer`
to generate a more specific override message.

## References
#6800 - SUI Epic
#8919 - SUI Inheritance PR
#8804 - SUI Inheritance (old issue)

## Detailed Description of the Pull Request / Additional comments
- **Terminal Settings Model**
  - Introduced `PROJECTED_SETTING` as a macro to more easily declare the
    functions for each setting
  - Introduced `<setting>OverrideSource` which finds the `Profile` that
    has \<setting\> defined
  - Introduced `OriginTag Profile::Origin {Custom, InBox, Generated}` to
    trace where a profile came from
  - `DefaultProfileUtils` creates profiles for profile generators. So
    that now sets the `Origin` tag to `Generated`
  - `CascadiaSettings::LoadDefaults()` tags all profiles created as
    `InBox`.
  - The view model had to ingest the API change to be able to interact
    with `<setting>OverrideSource`
- **Override Message Generation**
  - The reset button now has a more specific tooltip
  - The reset button now only appears if base layer is being overridden
  - We use the settings model changes to determine the message to
    display for the target

## Validation Steps Performed
Tested the following cases:
- overrides nothing (inherited setting)
- overrides value inherited from...
  - base layer
  - a profile generator
  - in-box profile
- global settings should not have this feature
2021-02-19 23:50:52 +00:00
PankajBhojwani
dfeb855d18 Support the "file" URI scheme (#7526)
We now support the file URI schemes where the hostname is either
"localhost" or the empty string

References #5001

Fixes #7699
2021-02-19 14:32:54 -08:00
Mike Griese
ba8bd006f4 Add centerOnLaunch setting (#9036)
This PR is a resurrection of #8414. @Hegunumo has apparently deleted
their account, but the contribution was still valuable. I'm just here to
get it across the finish line.

This PR adds new global setting `centerOnLaunch`. When set to `true`,
the Terminal window will be centered on the display it opens on. 

So the interactions are like:

* `initialPos: x,y`, `centered: true`, `launchMode: default`
  center on the monitor that x,y is on 

* `initialPos: x,y`, `centered: true`, `launchMode: maximized`
  maximized on the monitor that x,y is on (centered adds nothing)

* `initialPos: <omitted>`, `centered: true`, `launchMode: default`
  center on the default monitor

* `initialPos: <omitted>`, `centered: true`, `launchMode: focus`
  center, focus mode on the default monitor

* `initialPos: <omitted>`, `centered: true`, `launchMode: maximized`
  maximized on the default monitor (centered adds nothing)

## Validation Steps Performed
I've played with it on multiple different monitors, and it seems to work
on all of them.

Closes #8414 (original PR)
Closes #7722 

Co-authored-by: Kiminori Kaburagi <yukawa_hidenori@icloud.com>
2021-02-19 22:30:24 +00:00
Carlos Zamora
177430272c Polish settings UI localized text (#9124)
Updates the following text in the settings UI
- focus follow mouse mode is introduced to be more instructional
- focus follow mouse mode tooltip removed
- avoid double negative in "disable pane animation"

Closes #8900 
Updates #6459 Settings UI text
2021-02-19 14:27:30 -08:00
Mike Griese
69318d3ba1 Add support for the windowingBehavior setting (#9118)
Adds support for the `windowingBehavior` global setting. This setting
controls how mutiple instances of `wt` behave in the absence of the `-w`
parameter. This setting has three values:
* `"useNew"`: (default) Multiple `wt` invocations (without the `-w`
  param) always create new windows. 
* `"useAnyExisting"`: When starting a new `wt`, we'll instead default to
  any existing windows. `wt -w -1` will still create new windows. 
* `"useExisting"`: Similar to `useAnyExisting`, but limits to
  windows on the current desktop. 

The IVirtualDesktopManager interface is _very_ limited. Hence why we
have to track the HWNDs manually, and ask if they're on the current
desktop. 

## Validation Steps Performed
I've been playing with it for a week now. 

References #5000
References projects/5
References #8898
Spec'd in #8135
Closes #2227
Closes https://github.com/microsoft/terminal/projects/5#card-51431448
Closes https://github.com/microsoft/terminal/projects/5#card-51431433
2021-02-19 21:09:17 +00:00
Dustin Howett
ce99c2a349 Merged PR 5704731: Migrate OSS up to e6aa90222
Related work items: MSFT-9667854, MSFT-31783834
2021-02-19 19:45:33 +00:00
Don-Vito
e6aa902224 Fix FilteredCommand tests to run on UI thread (#9209)
Fixes exception in FilteredCommandTests, caused due to
an attempt to instantiate PropertyChangedEventArgs on non-UI thread.

Though the diff might look scary it is just wrapping with `RunOnUIThread`
2021-02-19 18:22:09 +00:00
PankajBhojwani
d12a71cdf9 Always show the bell indicator when BEL is emitted (#9212)
Regardless of whether the bellstyle is set to visual,
show the bell indicator in the tab header
2021-02-19 18:21:35 +00:00
Carlos Zamora
bb0e1d3979 Redesign color schemes page (#9196)
This PR performs a large overall polish of the color schemes page:
- Ensures keyboard navigation is holistically improved (i.e. fully
  accessible, no lost focus, etc...)
- Adds tooltips and automation properties to all controls
- Redesigns the page according to @mdtauk's approved design
  ([link](https://github.com/microsoft/terminal/pull/8997#issuecomment-771623842)).
  Note, there are some minor modifications to the design that were
  approved by @cinnamon-msft.
- Automatically reflow's the color buttons when they do not fit in
  horizontal mode

## Detailed Description of the Pull Request / Additional comments
- Redesign
  - a data template was introduced to make color representation
    consistent and straightforward
  - `ContentControl` is used to hold a reference to the
    `ColorTableEntry` and represent it properly using the aforementioned
    data template.
  - The design is mainly a StackPanel holding two grids: color table &
    functional colors.
  - The color table is populated via code. After much thought, this
    seems to be the easiest way to correctly bind 16 controls that are
    very similar.
  - The functional colors are populated via XAML manually.
  - We need a grid to separate the text and the buttons. This allows for
    scenarios like "selection background is the longest string" to force
    the buttons and text to be aligned.
- Reflow
  - A `VisualStateManager` uses an `AdaptiveTrigger` to change the
    orientation of the color tables' stack panel. The adaptive trigger
    was carefully calculated to align with the navigation view's
    breakpoint.
- Keyboard Navigation
  - (a lesson from `SettingContainer`) `ContentControl` can be focused
    as opposed to the content inside of it. We don't want that, so we
    set `IsTabStop` to false on it. That basically fixes all of our
    keyboard navigation issues in this new design.
- Automation Properties and ToolTips
  - As in my previous PRs, I can't seem to figure out how to bind to a
    control's automation property to its own tooltip. So I had to do
    this in the code and add a few `x:Name` around.

## Validation Steps Performed
- Manually tested...
  - tab navigation
  - accessibility insights
  - NVDA
  - changing color schemes updates color table
- specific scenario:
  - change a color table color and a functional color
  - navigate to a different color scheme
  - navigate back to the first color scheme
  - if the colors persist, the changes were propagated to the settings model

References #8997 - Based on the work from @Chips1234
References #6800 - Settings UI Epic
Closes #8765 - Polish Color Schemes page
Closes #8899 - Automation Properties
Closes #8768 - Keyboard Navigation
2021-02-19 18:20:04 +00:00
Carlos Zamora
2c22b68e15 Enable text search on combo boxes (#9206)
`ComboBox` has a text search function that allows users to type letters, and the `ComboBoxItem` starting with those letters is shown. In order to enable this functionality, the underlying items must be `IStringable`. This exposes a `ToString()` function and fixes all of our issues.

This PR adds the `IStringable` interface to `ColorScheme`, `Profile`, and `EnumEntry`.

## References
#6800 - Settings UI Epic
#8768 - Keyboard Navigation
https://github.com/microsoft/microsoft-ui-xaml/issues/4182 - discussion with WinUI about how to overcome this issue

## Validation Steps Performed
Tested...
- Launch > Default Profile
- Color Schemes > Name
- Profile > Appearance > Color scheme
- Profile > Appearance > Font weight

Also tested radio buttons, but those still don't work, unfortunately. Looks like they don't have the same underlying mechanism.
2021-02-19 18:11:07 +00:00
PankajBhojwani
654c0cc286 Add support for "fragment extensions" (#7632)
Support for fragment extensions, according to the implementation
outlined in #7584 (which calls them proto extensions.)

See #7584 for more information.

## Validation Steps Performed
Self-testing by creating the folder 
`%LOCALAPPDATA%\Microsoft\Windows Terminal\Fragments`
and adding a json file into it to modify and add profiles

Also self-tested with an app extension

Closes #1690
2021-02-19 02:12:16 +00:00
Mike Griese
c07553cb57 A bunch of test fixes (#9192)
A bunch of our local tests regressed recently. I'm unsure as to when
this happened. Clearly, we all do a super good job of running these
tests 😄.
* I had to make sure the call to `AppLogic::CurrentAppSettings` was
  try/caught, because that doesn't work in the tests
* I had to make the `Pointer*` events take a weak pointer to the
  `TerminalPage` because for whatever reason, they'd be called at a
  weird point in the test init, causing the tests to fail. It was weird.
  Almost as if the TerminalPage had been released, but the test logs
  showed it hadn't barely been set up yet? Whatever, this fixes it.
* The `VerifyCommandPaletteTabSwitcherOrder` test needed to take a time
  out, for reasons that are not totally clear to me. That one was flakey
  and I hate it.

### Checklist:
* [x] Doesn't close anything, this is just something I noticed.
* [x] Doesn't require docs to be updated, it's test fixes
* [x] Yea, I ran the tests 

/cc @Don-Vito: The `FilteredCommandTests` all crashed immediately for
me. I'm not sure what's causing that - I _think_ everything we need for
those tests is set up right? The generated `AppxManifest.xml` had all
the right classes listed in it, I really can't be sure what was wrong
there. These tests aren't run in CI so it's not a super big deal, but I
thought I'd let you know.

(cherry picked from commit ccda434f69)
2021-02-18 20:47:14 +00:00
Mike Griese
491cb21722 Add findNext, findPrev actions (#8917)
This PR is a resurrection of #8522. @Hegunumo has apparently deleted
their account, but the contribution was still valuable. I'm just here to
get it across the finish line.

This PR adds new action for navigating to the next & previous search
results. These actions are unbound by default. These actions can be used
from directly within the search dialog also, to immediately navigate the
results. 

Furthermore, if you have a search started, and close the search box,
then press this keybinding, _it will still perform the search_. So you
can just hit <kbd>F3</kbd> repeatedly with the dialog closed to keep
searching new results. Neat!

If you dispatch the action on the key down, then dismiss a selection on
a key up, we'll end up immediately destroying the selection when you
release the bound key. That's annoying. It also bothers @carlos-zamora
in #3758. However, I _think_ we can just only dismiss the selection on a
key up. I _think_ that's fine. It _seems_ fine so far. We've got an
entire release cycle to futz with it.

## Validation Steps Performed
I've played with it all day and it seems _crisp_.

Closes #7695 

Co-authored-by: Kiminori Kaburagi <yukawa_hidenori@icloud.com>
2021-02-18 19:21:35 +00:00
Don-Vito
90699da23b Fix FFM to apply only if tab is focused (#9198)
In the FFM mode, hovering on the pane might dismiss renamer.
To address this we want to make sure that FFM is applied
only if the Terminal Tab is focused.
2021-02-18 17:15:44 +00:00
Chester Liu
eb349935a0 Introduce DxFontRenderData (#9096)
This is my attempt to isolate all the dwrite font related thing by
introducing a new layer - `DxFontRenderData`. This will free
`DxRenderer` & `CustomTextLayout` from the burden of handling fonts &
box effects. The logic is more simplified & streamlined.

In short I just moved everything fonts-related into `DxFontRenderData`
and started from there. There's no modification to code logic. Just pure
structural stuff.

SGR support tracking issue: #6879
Initial Italic support PR: #8580
2021-02-18 06:11:38 +00:00
James Holderness
4c53c595e7 Add support for double-width/double-height lines in conhost (#8664)
This PR adds support for the VT line rendition attributes, which allow
for double-width and double-height line renditions. These renditions are
enabled with the `DECDWL` (double-width line) and `DECDHL`
(double-height line) escape sequences. Both reset to the default
rendition with the `DECSWL` (single-width line) escape sequence. For now
this functionality is only supported by the GDI renderer in conhost.

There are a lot of changes, so this is just a general overview of the
main areas affected.

Previously it was safe to assume that the screen had a fixed width, at
least for a given point in time. But now we need to deal with the
possibility of different lines have different widths, so all the
functions that are constrained by the right border (text wrapping,
cursor movement operations, and sequences like `EL` and `ICH`) now need
to lookup the width of the active line in order to behave correctly.

Similarly it used to be safe to assume that buffer and screen
coordinates were the same thing, but that is no longer true. Lots of
places now need to translate back and forth between coordinate systems
dependent on the line rendition. This includes clipboard handling, the
conhost color selection and search, accessibility location tracking and
screen reading, IME editor positioning, "snapping" the viewport, and of
course all the rendering calculations.

For the rendering itself, I've had to introduce a new
`PrepareLineTransform` method that the render engines can use to setup
the necessary transform matrix for a given line rendition. This is also
now used to handle the horizontal viewport offset, since that could no
longer be achieved just by changing the target coordinates (on a double
width line, the viewport offset may be halfway through a character).

I've also had to change the renderer's existing `InvalidateCursor`
method to take a `SMALL_RECT` rather than a `COORD`, to allow for the
cursor being a variable width. Technically this was already a problem,
because the cursor could occupy two screen cells when over a
double-width character, but now it can be anything between one and four
screen cells (e.g. a double-width character on the double-width line).

In terms of architectural changes, there is now a new `lineRendition`
field in the `ROW` class that keeps track of the line rendition for each
row, and several new methods in the `ROW` and `TextBuffer` classes for
manipulating that state. This includes a few helper methods for handling
the various issues discussed above, e.g. position clamping and
translating between coordinate systems.

## Validation Steps Performed

I've manually confirmed all the double-width and double-height tests in
_Vttest_ are now working as expected, and the _VT100 Torture Test_ now
renders correctly (at least the line rendition aspects). I've also got
my own test scripts that check many of the line rendition boundary cases
and have confirmed that those are now passing.

I've manually tested as many areas of the conhost UI that I could think
of, that might be affected by line rendition, including things like
searching, selection, copying, and color highlighting. For
accessibility, I've confirmed that the _Magnifier_ and _Narrator_
correctly handle double-width lines. And I've also tested the Japanese
IME, which while not perfect, is at least useable.

Closes #7865
2021-02-18 05:44:50 +00:00
Dan Thompson
72cbe59078 Add support for XTPUSHSGR / XTPOPSGR (#1978)
Implement the `XTPUSHSGR` and `XTPOPSGR` control sequences (see #1796).

This change adds a new pair of methods to `ITermDispatch`:
`PushGraphicsRendition` and `PopGraphicsRendition`, and then plumbs the
change through `AdaptDispatch`, `TerminalDispatch`, `ITerminalApi` and
`TerminalApi`.

The stack logic is encapsulated in the `SgrStack` class, to allow it to
be reused between the two APIs (`AdaptDispatch` and `TerminalDispatch`).

Like xterm, only ten levels of nesting are supported.

The stack is implemented as a "ring stack": if you push when the stack
is full, the bottom of the stack will be dropped to make room.

Partial pushes (see the description of `XTPUSHSGR` in Issue #1796) are
implemented per xterm spec.

## Validation Steps Performed
Tests added, plus manual verification of the feature.

Closes #1796
2021-02-17 18:31:52 -08:00
Dustin Howett
ce0505073c Merged PR 5695814: Migrate OSS up to 847749f19
Dustin L. Howett (3)
* Fix the Host Proxy DLL reference in ServerLib (GH-9129)
* ci: fix spelling
* Format the incoming inbox code

Michael Niksa (1)
* Eliminate more transient allocations: Titles and invalid rectangles and bitmap runs and utf8 conversions (GH-8621)

Related work items: MSFT-31755835
2021-02-17 22:04:25 +00:00
Mike Griese
847749f19e Force activate existing windows when running a commandline in them (#9137)
This will make sure to summon the terminal window when running a
commandline in it. 
* If the window is on another desktop, the OS will switch to the desktop
  the window is on. 
* If the window is minimized, it will restore it.

This is taken from my quake mode branch. It works aggressively. 848682a,
fee6473, 342d3f2, 5052d31 all had other attempts at doing this, but they
didn't work reliably. Part of the trick is that I don't _think_ Windows
wants one process to be able to move another process into the
foreground. In this case though, we _do_ want to move ourself into the
foreground, and this `AttachThreadInput` hack seems to be the only way
to do it reliably.

References #5000
Uses code authored for #653

Closes https://github.com/microsoft/terminal/projects/5#card-54636373
2021-02-17 21:37:22 +00:00
Don-Vito
52d1533c4f Ensure hyperlinks de-underline when pointer leaves terminal (#9195)
Introduced PointerExited handler to nullify last hovered cell
(which will also de-underline the hyperlink if required).

Closes #9141
2021-02-17 13:33:53 -08:00
Carlos Zamora
ac3e4bfe56 Fix command palette accessibility (#9143)
Fixes a regression of command palette accessibility. The regression was
introduced in #8377 by setting `IsTabStop` to false. Though the commands
would light up, the focus didn't technically get on the command, so the
screen reader would just read the text box.

## Validation Steps Performed
Opened the command palette while NVDA is active. It now reads the
commands as focus moves on them.
2021-02-17 21:30:45 +00:00
Javier
12eb69f665 wpf: prevent a 0 size when VS builds the Terminal window too early (#9194)
The terminal WPF container might have a (0,0) render size when VS
eagerly attempts to initialize the terminal tool window when it is
pinned during startup, but no actual UI is shown due to the VS welcome
dialog that shows up before VS can build the terminal tool window.

We've fixed this issue previously in other areas but only recently did
we get a complete enough dump to find the corner cases for this issue. 

This should patch all the gaps that cause this bug.

## Validation Steps Performed
I've manually validated the scenarios shown in the user dumps.
2021-02-17 21:00:50 +00:00
Don-Vito
00d1dc99e4 Teach tab to focus terminal after rename (#9162)
## Summary of the Pull Request
After rename ends (either by enter or escape) the rename box
gets collapsed and the focus moves to the next tab stop
in the TabView (e.g., new tab button).

To overcome this:
* Added RenameEnded event to TabHeaderControl
* Forwarded it as RenamerDeactivated from TerminalTab
* Registered in the TerminalPage to focus the active control upon
RenamerDeactivated if focus didn't move to tab menu.

This means, no matter how you close the renamer,
the current terminal gains the focus.

## PR Checklist
* [x] Closes https://github.com/microsoft/terminal/issues/9160
* [x] CLA signed. 
* [ ] Tests added/passed
* [ ] Documentation updated. 
* [ ] Schema updated.
* [ ] I've discussed this with core contributors already.
2021-02-17 20:00:23 +00:00
251 changed files with 7970 additions and 2860 deletions

13
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,13 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
// List of extensions which should be recommended for users of this workspace.
"recommendations": [
"ms-vscode.cpptools"
],
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
"unwantedRecommendations": [
]
}

24
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,24 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Debug OpenConsole by Launching (x64, debug)",
"type": "cppvsdbg",
"request": "launch",
"program": "${workspaceFolder}\\bin\\x64\\debug\\openconsole.exe",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
},
{
"name": "Debug Terminal by Attaching (You go build/register/launch it first.)",
"type": "cppvsdbg",
"request": "attach",
"processId": "${command:pickProcess}"
}
]
}

32
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,32 @@
{
"C_Cpp.default.browse.databaseFilename": "${workspaceFolder}\\.vscode\\.BROWSE.VC.DB",
"C_Cpp.default.browse.path": [
"${workspaceFolder}"
],
"C_Cpp.loggingLevel": "None",
"files.associations": {
"xstring": "cpp",
"*.idl": "cpp",
"array": "cpp",
"future": "cpp",
"istream": "cpp",
"memory": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"utility": "cpp",
"variant": "cpp",
"xlocmes": "cpp",
"xlocmon": "cpp",
"xlocnum": "cpp",
"xloctime": "cpp",
"multi_span": "cpp",
"pointers": "cpp",
"vector": "cpp"
},
"files.exclude": {
"**/bin/**": true,
"**/obj/**": true,
"**/packages/**": true,
"**/generated files/**": true
}
}

107
.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,107 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "process",
"label": "Build Terminal/Console",
"command": "powershell.exe",
"args": [
"-Command",
"Import-Module ${workspaceFolder}\\tools\\OpenConsole.psm1;",
"Set-MsBuildDevEnvironment;",
"$project = switch(\"${input:buildProjectChoice}\"){OpenConsole{\"Conhost\\Host_EXE\"} Terminal{\"Terminal\\CascadiaPackage\"}};",
"$target = switch(\"${input:buildModeChoice}\"){Build{\"\"} Rebuild{\":Rebuild\"} Clean{\":Clean\"}};",
"$target = $project + $target;",
"msbuild",
"${workspaceFolder}\\OpenConsole.sln",
"/p:Configuration=${input:configChoice}",
"/p:Platform=${input:platformChoice}",
"/t:$target",
"/verbosity:minimal"
],
"problemMatcher": ["$msCompile"],
"group": {
"kind": "build",
"isDefault": true
},
"runOptions": {
"reevaluateOnRerun": false,
"instanceLimit": 1,
"runOn": "default"
}
},
{
"type": "process",
"label": "Register Windows Terminal x64 Debug",
"command": "powershell.exe",
"args": [
"-Command",
"Import-Module ${workspaceFolder}\\tools\\OpenConsole.psm1;",
"Set-MsBuildDevEnvironment;",
"Set-Location -Path ${workspaceFolder}\\src\\cascadia\\CascadiaPackage\\AppPackages\\CascadiaPackage_0.0.1.0_x64_Debug_Test;",
"if ((Get-AppxPackage -Name 'WindowsTerminalDev*') -ne $null) { Remove-AppxPackage 'WindowsTerminalDev_0.0.1.0_x64__8wekyb3d8bbwe'};",
"New-Item ..\\loose -Type Directory -Force;",
"makeappx unpack /v /o /p .\\CascadiaPackage_0.0.1.0_x64_Debug.msix /d ..\\Loose\\;",
"Add-AppxPackage -Path ..\\loose\\AppxManifest.xml -Register -ForceUpdateFromAnyVersion -ForceApplicationShutdown"
],
"problemMatcher": ["$msCompile"],
"group": {
"kind": "build",
"isDefault": true
}
},
{
"type": "process",
"label": "Run Windows Terminal Dev",
"command": "wtd.exe",
"args": [
],
"problemMatcher": ["$msCompile"],
}
],
"inputs":[
{
"id": "platformChoice",
"type": "pickString",
"description": "Processor architecture choice",
"options":[
"x64",
"x86",
"arm64"
],
"default": "x64"
},
{
"id": "configChoice",
"type": "pickString",
"description": "Debug or release?",
"options":[
"Debug",
"Release"
],
"default": "Debug"
},
{
"id": "buildModeChoice",
"type": "pickString",
"description": "Build, rebuild, or clean?",
"options":[
"Build",
"Rebuild",
"Clean"
],
"default": "Build"
},
{
"id": "buildProjectChoice",
"type": "pickString",
"description": "OpenConsole or Terminal?",
"options":[
"OpenConsole",
"Terminal"
],
"default": "Terminal"
}
]
}

View File

@@ -2410,8 +2410,6 @@ Global
{21B7EA5E-1EF8-49B6-AC07-11714AF0E37D}.Release|x86.ActiveCfg = Release|Win32
{21B7EA5E-1EF8-49B6-AC07-11714AF0E37D}.Release|x86.Build.0 = Release|Win32
{F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|Any CPU.ActiveCfg = Release|Any CPU
{F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|Any CPU.Build.0 = Release|Any CPU
{F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|Any CPU.Deploy.0 = Release|Any CPU
{F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|ARM.ActiveCfg = Debug|ARM
{F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|ARM.Build.0 = Debug|ARM
{F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|ARM.Deploy.0 = Debug|ARM
@@ -2419,11 +2417,7 @@ Global
{F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|ARM64.Build.0 = Debug|ARM64
{F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|ARM64.Deploy.0 = Debug|ARM64
{F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|DotNet_x64Test.ActiveCfg = Release|Any CPU
{F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|DotNet_x64Test.Build.0 = Release|Any CPU
{F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|DotNet_x64Test.Deploy.0 = Release|Any CPU
{F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|DotNet_x86Test.ActiveCfg = Release|Any CPU
{F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|DotNet_x86Test.Build.0 = Release|Any CPU
{F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|DotNet_x86Test.Deploy.0 = Release|Any CPU
{F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|x64.ActiveCfg = Debug|x64
{F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|x64.Build.0 = Debug|x64
{F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|x64.Deploy.0 = Debug|x64
@@ -2431,8 +2425,6 @@ Global
{F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|x86.Build.0 = Debug|x86
{F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|x86.Deploy.0 = Debug|x86
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|ARM.ActiveCfg = Debug|ARM
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|ARM.Build.0 = Debug|ARM
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|ARM.Deploy.0 = Debug|ARM
@@ -2440,11 +2432,7 @@ Global
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|ARM64.Build.0 = Debug|ARM64
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|ARM64.Deploy.0 = Debug|ARM64
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|DotNet_x64Test.ActiveCfg = Debug|Any CPU
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|DotNet_x64Test.Build.0 = Debug|Any CPU
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|DotNet_x64Test.Deploy.0 = Debug|Any CPU
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|DotNet_x86Test.ActiveCfg = Debug|Any CPU
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|DotNet_x86Test.Build.0 = Debug|Any CPU
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|DotNet_x86Test.Deploy.0 = Debug|Any CPU
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|x64.ActiveCfg = Debug|x64
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|x64.Build.0 = Debug|x64
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|x64.Deploy.0 = Debug|x64
@@ -2452,8 +2440,6 @@ Global
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|x86.Build.0 = Debug|x86
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|x86.Deploy.0 = Debug|x86
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|Any CPU.Build.0 = Release|Any CPU
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|Any CPU.Deploy.0 = Release|Any CPU
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|ARM.ActiveCfg = Release|ARM
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|ARM.Build.0 = Release|ARM
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|ARM.Deploy.0 = Release|ARM
@@ -2461,11 +2447,7 @@ Global
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|ARM64.Build.0 = Release|ARM64
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|ARM64.Deploy.0 = Release|ARM64
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|DotNet_x64Test.ActiveCfg = Release|Any CPU
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|DotNet_x64Test.Build.0 = Release|Any CPU
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|DotNet_x64Test.Deploy.0 = Release|Any CPU
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|DotNet_x86Test.ActiveCfg = Release|Any CPU
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|DotNet_x86Test.Build.0 = Release|Any CPU
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|DotNet_x86Test.Deploy.0 = Release|Any CPU
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|x64.ActiveCfg = Release|x64
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|x64.Build.0 = Release|x64
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|x64.Deploy.0 = Release|x64

View File

@@ -5,7 +5,7 @@
<XesUseOneStoreVersioning>true</XesUseOneStoreVersioning>
<XesBaseYearForStoreVersion>2021</XesBaseYearForStoreVersion>
<VersionMajor>1</VersionMajor>
<VersionMinor>7</VersionMinor>
<VersionMinor>8</VersionMinor>
<VersionInfoProductName>Windows Terminal</VersionInfoProductName>
</PropertyGroup>
</Project>

View File

@@ -76,9 +76,11 @@
"copy",
"duplicateTab",
"find",
"findMatch",
"moveFocus",
"moveTab",
"newTab",
"newWindow",
"nextTab",
"openNewTabDropdown",
"openSettings",
@@ -138,6 +140,13 @@
],
"type": "string"
},
"FindMatchDirection": {
"enum": [
"next",
"prev"
],
"type": "string"
},
"SplitState": {
"enum": [
"vertical",
@@ -213,6 +222,11 @@
"$ref": "#/definitions/Color",
"default": null,
"description": "If provided, will set the tab's color to the given value"
},
"suppressApplicationTitle": {
"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"
}
},
"type": "object"
@@ -559,6 +573,35 @@
}
]
},
"FindMatchAction": {
"description": "Arguments corresponding to a Find Match Action",
"allOf": [
{ "$ref": "#/definitions/ShortcutAction" },
{
"properties": {
"action": { "type": "string", "pattern": "findMatch" },
"direction": {
"$ref": "#/definitions/FindMatchDirection",
"default": "prev",
"description": "The direction to search in. \"prev\" will search upwards in the buffer, and \"next\" will search downwards."
}
}
}
],
"required": [ "direction" ]
},
"NewWindowAction": {
"description": "Arguments corresponding to a New Window Action",
"allOf": [
{ "$ref": "#/definitions/ShortcutAction" },
{ "$ref": "#/definitions/NewTerminalArgs" },
{
"properties": {
"action": { "type":"string", "pattern": "newWindow" }
}
}
]
},
"Keybinding": {
"additionalProperties": false,
"properties": {
@@ -583,6 +626,8 @@
{ "$ref": "#/definitions/ScrollUpAction" },
{ "$ref": "#/definitions/ScrollDownAction" },
{ "$ref": "#/definitions/MoveTabAction" },
{ "$ref": "#/definitions/FindMatchAction" },
{ "$ref": "#/definitions/NewWindowAction" },
{ "type": "null" }
]
},
@@ -630,6 +675,16 @@
"description": "When set to true, tabs are always displayed. When set to false and \"showTabsInTitlebar\" is set to false, tabs only appear after opening a new tab.",
"type": "boolean"
},
"centerOnLaunch": {
"default": false,
"description": "When set to `true`, the terminal window will auto-center itself on the display it opens on. The terminal will use the \"initialPosition\" to determine which display to open on.",
"type": "boolean"
},
"inputServiceWarning": {
"default": true,
"description": "Warning if 'Touch Keyboard and Handwriting Panel Service' is disabled.",
"type": "boolean"
},
"copyOnSelect": {
"default": false,
"description": "When set to true, a selection is immediately copied to your clipboard upon creation. When set to false, the selection persists and awaits further action.",
@@ -812,6 +867,16 @@
"type": "string"
}
]
},
"windowingBehavior": {
"default": "useNew",
"description": "Controls how new terminal instances attach to existing windows. \"useNew\" will always create a new window. \"useExisting\" will create new tabs in the most recently used window on this virtual desktop, and \"useAnyExisting\" will create tabs in the most recent window on any desktop.",
"enum": [
"useNew",
"useExisting",
"useAnyExisting"
],
"type": "string"
}
},
"required": [
@@ -933,9 +998,9 @@
"description": "Sets the color of the cursor. Overrides the cursor color from the color scheme. Uses hex color format: \"#rrggbb\"."
},
"cursorHeight": {
"description": "Sets the percentage height of the cursor starting from the bottom. Only works when cursorShape is set to \"vintage\". Accepts values from 25-100.",
"description": "Sets the percentage height of the cursor starting from the bottom. Only works when cursorShape is set to \"vintage\". Accepts values from 1-100.",
"maximum": 100,
"minimum": 25,
"minimum": 1,
"type": ["integer","null"],
"default": 25
},

Binary file not shown.

Binary file not shown.

View File

@@ -17,5 +17,5 @@ Please consult the [license](https://raw.githubusercontent.com/microsoft/cascadi
### Fonts Included
* Cascadia Code, Cascadia Mono (2102.03)
* from microsoft/cascadia-code@b358d1ba3d1629c113671312b18eab52797cc055
* Cascadia Code, Cascadia Mono (2102.25)
* from microsoft/cascadia-code@911dc421f333e3b72b97381d16fee5b71eb48f04

View File

@@ -1,3 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 13H16V6H2C0.9 6 0 6.9 0 8V13Z" fill="#CCCCCC"/>
<path d="M32 6H16V13H32V6Z" fill="#999999"/>

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@@ -1,3 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="foreground"><stop stop-color="#000000"/></linearGradient>

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -1,3 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="foreground"><stop stop-color="#000000"/></linearGradient>

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1,36 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- LineRendition.hpp
Abstract:
- Enumerated type for the VT line rendition attribute. This determines the
width and height scaling with which each line is rendered.
--*/
#pragma once
enum class LineRendition
{
SingleWidth,
DoubleWidth,
DoubleHeightTop,
DoubleHeightBottom
};
constexpr SMALL_RECT ScreenToBufferLine(const SMALL_RECT& line, const LineRendition lineRendition)
{
// Use shift right to quickly divide the Left and Right by 2 for double width lines.
const auto scale = lineRendition == LineRendition::SingleWidth ? 0 : 1;
return { line.Left >> scale, line.Top, line.Right >> scale, line.Bottom };
}
constexpr SMALL_RECT BufferToScreenLine(const SMALL_RECT& line, const LineRendition lineRendition)
{
// Use shift left to quickly multiply the Left and Right by 2 for double width lines.
const SHORT scale = lineRendition == LineRendition::SingleWidth ? 0 : 1;
return { line.Left << scale, line.Top, (line.Right << scale) + scale, line.Bottom };
}

View File

@@ -21,6 +21,7 @@ ROW::ROW(const SHORT rowId, const unsigned short rowWidth, const TextAttribute f
_rowWidth{ rowWidth },
_charRow{ rowWidth, this },
_attrRow{ rowWidth, fillAttribute },
_lineRendition{ LineRendition::SingleWidth },
_wrapForced{ false },
_doubleBytePadded{ false },
_pParent{ pParent }
@@ -35,6 +36,7 @@ ROW::ROW(const SHORT rowId, const unsigned short rowWidth, const TextAttribute f
// - <none>
bool ROW::Reset(const TextAttribute Attr)
{
_lineRendition = LineRendition::SingleWidth;
_wrapForced = false;
_doubleBytePadded = false;
_charRow.Reset();

View File

@@ -21,6 +21,7 @@ Revision History:
#pragma once
#include "AttrRow.hpp"
#include "LineRendition.hpp"
#include "OutputCell.hpp"
#include "OutputCellIterator.hpp"
#include "CharRow.hpp"
@@ -48,6 +49,9 @@ public:
const ATTR_ROW& GetAttrRow() const noexcept { return _attrRow; }
ATTR_ROW& GetAttrRow() noexcept { return _attrRow; }
LineRendition GetLineRendition() const noexcept { return _lineRendition; }
void SetLineRendition(const LineRendition lineRendition) noexcept { _lineRendition = lineRendition; }
SHORT GetId() const noexcept { return _id; }
void SetId(const SHORT id) noexcept { _id = id; }
@@ -70,6 +74,7 @@ public:
private:
CharRow _charRow;
ATTR_ROW _attrRow;
LineRendition _lineRendition;
SHORT _id;
unsigned short _rowWidth;
// Occurs when the user runs out of text in a given row and we're forced to wrap the cursor to the next line

View File

@@ -38,6 +38,7 @@
<ClInclude Include="..\cursor.h" />
<ClInclude Include="..\DbcsAttribute.hpp" />
<ClInclude Include="..\ICharRow.hpp" />
<ClInclude Include="..\LineRendition.hpp" />
<ClInclude Include="..\OutputCell.hpp" />
<ClInclude Include="..\OutputCellIterator.hpp" />
<ClInclude Include="..\OutputCellRect.hpp" />

View File

@@ -97,7 +97,12 @@ bool Search::FindNext()
// - Takes the found word and selects it in the screen buffer
void Search::Select() const
{
_uiaData.SelectNewRegion(_coordSelStart, _coordSelEnd);
// Convert buffer selection offsets into the equivalent screen coordinates
// required by SelectNewRegion, taking line renditions into account.
const auto& textBuffer = _uiaData.GetTextBuffer();
const auto selStart = textBuffer.BufferToScreenPosition(_coordSelStart);
const auto selEnd = textBuffer.BufferToScreenPosition(_coordSelEnd);
_uiaData.SelectNewRegion(selStart, selEnd);
}
// Routine Description:
@@ -141,7 +146,10 @@ COORD Search::s_GetInitialAnchor(IUiaData& uiaData, const Direction direction)
const COORD textBufferEndPosition = uiaData.GetTextBufferEndPosition();
if (uiaData.IsSelectionActive())
{
auto anchor = uiaData.GetSelectionAnchor();
// Convert the screen position of the selection anchor into an equivalent
// buffer position to start searching, taking line rendition into account.
auto anchor = textBuffer.ScreenToBufferPosition(uiaData.GetSelectionAnchor());
if (direction == Direction::Forward)
{
textBuffer.GetSize().IncrementInBoundsCircular(anchor);

View File

@@ -290,13 +290,14 @@ bool TextBuffer::_PrepareForDoubleByteSequence(const DbcsAttribute dbcsAttribute
// We only need to compensate for leading bytes
if (dbcsAttribute.IsLeading())
{
short const sBufferWidth = GetSize().Width();
const auto cursorPosition = GetCursor().GetPosition();
const auto lineWidth = GetLineWidth(cursorPosition.Y);
// If we're about to lead on the last column in the row, we need to add a padding space
if (GetCursor().GetPosition().X == sBufferWidth - 1)
if (cursorPosition.X == lineWidth - 1)
{
// set that we're wrapping for double byte reasons
auto& row = GetRowByOffset(GetCursor().GetPosition().Y);
auto& row = GetRowByOffset(cursorPosition.Y);
row.SetDoubleBytePadded(true);
// then move the cursor forward and onto the next row
@@ -496,7 +497,7 @@ bool TextBuffer::IncrementCursor()
// Cursor position is stored as logical array indices (starts at 0) for the window
// Buffer Size is specified as the "length" of the array. It would say 80 for valid values of 0-79.
// So subtract 1 from buffer size in each direction to find the index of the final column in the buffer
const short iFinalColumnIndex = GetSize().RightInclusive();
const short iFinalColumnIndex = GetLineWidth(GetCursor().GetPosition().Y) - 1;
// Move the cursor one position to the right
GetCursor().IncrementXPosition(1);
@@ -635,7 +636,7 @@ COORD TextBuffer::GetLastNonSpaceCharacter(std::optional<const Microsoft::Consol
// Return Value:
// - Coordinate position in screen coordinates of the character just before the cursor.
// - NOTE: Will return 0,0 if already in the top left corner
COORD TextBuffer::_GetPreviousFromCursor() const noexcept
COORD TextBuffer::_GetPreviousFromCursor() const
{
COORD coordPosition = GetCursor().GetPosition();
@@ -649,11 +650,11 @@ COORD TextBuffer::_GetPreviousFromCursor() const noexcept
// Otherwise, only if we're not on the top row (e.g. we don't move anywhere in the top left corner. there is no previous)
if (coordPosition.Y > 0)
{
// move the cursor to the right edge
coordPosition.X = GetSize().RightInclusive();
// and up one line
// move the cursor up one line
coordPosition.Y--;
// and to the right edge
coordPosition.X = GetLineWidth(coordPosition.Y) - 1;
}
}
@@ -801,6 +802,78 @@ void TextBuffer::SetCurrentAttributes(const TextAttribute& currentAttributes) no
_currentAttributes = currentAttributes;
}
void TextBuffer::SetCurrentLineRendition(const LineRendition lineRendition)
{
const auto cursorPosition = GetCursor().GetPosition();
const auto rowIndex = cursorPosition.Y;
auto& row = GetRowByOffset(rowIndex);
if (row.GetLineRendition() != lineRendition)
{
row.SetLineRendition(lineRendition);
// If the line rendition has changed, the row can no longer be wrapped.
row.SetWrapForced(false);
// And if it's no longer single width, the right half of the row should be erased.
if (lineRendition != LineRendition::SingleWidth)
{
const auto fillChar = L' ';
auto fillAttrs = GetCurrentAttributes();
fillAttrs.SetStandardErase();
const size_t fillOffset = GetLineWidth(rowIndex);
const size_t fillLength = GetSize().Width() - fillOffset;
const auto fillData = OutputCellIterator{ fillChar, fillAttrs, fillLength };
row.WriteCells(fillData, fillOffset, false);
// We also need to make sure the cursor is clamped within the new width.
GetCursor().SetPosition(ClampPositionWithinLine(cursorPosition));
}
_NotifyPaint(Viewport::FromDimensions({ 0, rowIndex }, { GetSize().Width(), 1 }));
}
}
void TextBuffer::ResetLineRenditionRange(const size_t startRow, const size_t endRow)
{
for (auto row = startRow; row < endRow; row++)
{
GetRowByOffset(row).SetLineRendition(LineRendition::SingleWidth);
}
}
LineRendition TextBuffer::GetLineRendition(const size_t row) const
{
return GetRowByOffset(row).GetLineRendition();
}
bool TextBuffer::IsDoubleWidthLine(const size_t row) const
{
return GetLineRendition(row) != LineRendition::SingleWidth;
}
SHORT TextBuffer::GetLineWidth(const size_t row) const
{
// Use shift right to quickly divide the width by 2 for double width lines.
const auto scale = IsDoubleWidthLine(row) ? 1 : 0;
return GetSize().Width() >> scale;
}
COORD TextBuffer::ClampPositionWithinLine(const COORD position) const
{
const SHORT rightmostColumn = GetLineWidth(position.Y) - 1;
return { std::min(position.X, rightmostColumn), position.Y };
}
COORD TextBuffer::ScreenToBufferPosition(const COORD position) const
{
// Use shift right to quickly divide the X pos by 2 for double width lines.
const auto scale = IsDoubleWidthLine(position.Y) ? 1 : 0;
return { position.X >> scale, position.Y };
}
COORD TextBuffer::BufferToScreenPosition(const COORD position) const
{
// Use shift left to quickly multiply the X pos by 2 for double width lines.
const auto scale = IsDoubleWidthLine(position.Y) ? 1 : 0;
return { position.X << scale, position.Y };
}
// Routine Description:
// - Resets the text contents of this buffer with the default character
// and the default current color attributes
@@ -1425,9 +1498,11 @@ bool TextBuffer::MoveToPreviousGlyph(til::point& pos) const
// - blockSelection: when enabled, only get the rectangular text region,
// as opposed to the text extending to the left/right
// buffer margins
// - bufferCoordinates: when enabled, treat the coordinates as relative to
// the buffer rather than the screen.
// Return Value:
// - the delimiter class for the given char
const std::vector<SMALL_RECT> TextBuffer::GetTextRects(COORD start, COORD end, bool blockSelection) const
const std::vector<SMALL_RECT> TextBuffer::GetTextRects(COORD start, COORD end, bool blockSelection, bool bufferCoordinates) const
{
std::vector<SMALL_RECT> textRects;
@@ -1461,6 +1536,13 @@ const std::vector<SMALL_RECT> TextBuffer::GetTextRects(COORD start, COORD end, b
textRow.Right = (row == lowerCoord.Y) ? lowerCoord.X : bufferSize.RightInclusive();
}
// If we were passed screen coordinates, convert the given range into
// equivalent buffer offsets, taking line rendition into account.
if (!bufferCoordinates)
{
textRow = ScreenToBufferLine(textRow, GetLineRendition(row));
}
_ExpandTextRow(textRow);
textRects.emplace_back(textRow);
}
@@ -2044,7 +2126,6 @@ HRESULT TextBuffer::Reflow(TextBuffer& oldBuffer,
const COORD cOldLastChar = oldBuffer.GetLastNonSpaceCharacter(lastCharacterViewport);
const short cOldRowsTotal = cOldLastChar.Y + 1;
const short cOldColsTotal = oldBuffer.GetSize().Width();
COORD cNewCursorPos = { 0 };
bool fFoundCursorPos = false;
@@ -2056,9 +2137,19 @@ HRESULT TextBuffer::Reflow(TextBuffer& oldBuffer,
{
// Fetch the row and its "right" which is the last printable character.
const ROW& row = oldBuffer.GetRowByOffset(iOldRow);
const short cOldColsTotal = oldBuffer.GetLineWidth(iOldRow);
const CharRow& charRow = row.GetCharRow();
short iRight = gsl::narrow_cast<short>(charRow.MeasureRight());
// If we're starting a new row, try and preserve the line rendition
// from the row in the original buffer.
const auto newBufferPos = newBuffer.GetCursor().GetPosition();
if (newBufferPos.X == 0)
{
auto& newRow = newBuffer.GetRowByOffset(newBufferPos.Y);
newRow.SetLineRendition(row.GetLineRendition());
}
// There is a special case here. If the row has a "wrap"
// flag on it, but the right isn't equal to the width (one
// index past the final valid index in the row) then there

View File

@@ -122,6 +122,16 @@ public:
void SetCurrentAttributes(const TextAttribute& currentAttributes) noexcept;
void SetCurrentLineRendition(const LineRendition lineRendition);
void ResetLineRenditionRange(const size_t startRow, const size_t endRow);
LineRendition GetLineRendition(const size_t row) const;
bool IsDoubleWidthLine(const size_t row) const;
SHORT GetLineWidth(const size_t row) const;
COORD ClampPositionWithinLine(const COORD position) const;
COORD ScreenToBufferPosition(const COORD position) const;
COORD BufferToScreenPosition(const COORD position) const;
void Reset();
[[nodiscard]] HRESULT ResizeTraditional(const COORD newSize) noexcept;
@@ -141,7 +151,7 @@ public:
bool MoveToNextGlyph(til::point& pos, bool allowBottomExclusive = false) const;
bool MoveToPreviousGlyph(til::point& pos) const;
const std::vector<SMALL_RECT> GetTextRects(COORD start, COORD end, bool blockSelection = false) const;
const std::vector<SMALL_RECT> GetTextRects(COORD start, COORD end, bool blockSelection, bool bufferCoordinates) const;
void AddHyperlinkToMap(std::wstring_view uri, uint16_t id);
std::wstring GetHyperlinkUriFromId(uint16_t id) const;
@@ -212,7 +222,7 @@ private:
void _SetFirstRowIndex(const SHORT FirstRowIndex) noexcept;
COORD _GetPreviousFromCursor() const noexcept;
COORD _GetPreviousFromCursor() const;
void _SetWrapOnCurrentRow();
void _AdjustWrapOnCurrentRow(const bool fSet);

View File

@@ -12,7 +12,7 @@
xmlns:desktop5="http://schemas.microsoft.com/appx/manifest/desktop/windows10/5"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5"
IgnorableNamespaces="uap mp rescap">
IgnorableNamespaces="uap mp rescap uap3">
<Identity
Name="WindowsTerminalDev"
@@ -69,6 +69,11 @@
Enabled="false"
DisplayName="ms-resource:AppNameDev" />
</uap5:Extension>
<uap3:Extension Category="windows.appExtensionHost">
<uap3:AppExtensionHost>
<uap3:Name>com.microsoft.windows.terminal.settings</uap3:Name>
</uap3:AppExtensionHost>
</uap3:Extension>
<com:Extension Category="windows.comServer">
<com:ComServer>

View File

@@ -13,7 +13,7 @@
xmlns:desktop4="http://schemas.microsoft.com/appx/manifest/desktop/windows10/4"
xmlns:desktop5="http://schemas.microsoft.com/appx/manifest/desktop/windows10/5"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
IgnorableNamespaces="uap mp rescap">
IgnorableNamespaces="uap mp rescap uap3">
<Identity
Name="Microsoft.WindowsTerminalPreview"
@@ -64,6 +64,11 @@
<desktop:ExecutionAlias Alias="wt.exe" />
</uap3:AppExecutionAlias>
</uap3:Extension>
<uap3:Extension Category="windows.appExtensionHost">
<uap3:AppExtensionHost>
<uap3:Name>com.microsoft.windows.terminal.settings</uap3:Name>
</uap3:AppExtensionHost>
</uap3:Extension>
<uap5:Extension Category="windows.startupTask">
<uap5:StartupTask
TaskId="StartTerminalOnLoginTask"

View File

@@ -13,7 +13,7 @@
xmlns:desktop4="http://schemas.microsoft.com/appx/manifest/desktop/windows10/4"
xmlns:desktop5="http://schemas.microsoft.com/appx/manifest/desktop/windows10/5"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
IgnorableNamespaces="uap mp rescap">
IgnorableNamespaces="uap mp rescap uap3">
<Identity
Name="Microsoft.WindowsTerminal"
@@ -64,6 +64,11 @@
<desktop:ExecutionAlias Alias="wt.exe" />
</uap3:AppExecutionAlias>
</uap3:Extension>
<uap3:Extension Category="windows.appExtensionHost">
<uap3:AppExtensionHost>
<uap3:Name>com.microsoft.windows.terminal.settings</uap3:Name>
</uap3:AppExtensionHost>
</uap3:Extension>
<uap5:Extension Category="windows.startupTask">
<uap5:StartupTask
TaskId="StartTerminalOnLoginTask"

View File

@@ -42,6 +42,8 @@ namespace SettingsModelLocalTests
TEST_METHOD(TestAutogeneratedName);
TEST_METHOD(TestLayerOnAutogeneratedName);
TEST_METHOD(TestGenerateCommandline);
TEST_CLASS_SETUP(ClassSetup)
{
InitializeJsonReader();
@@ -275,14 +277,6 @@ namespace SettingsModelLocalTests
void CommandTests::TestAutogeneratedName()
{
// Tests run in Helix can't report Skipped until GH#7286 is resolved.
// Set ignore flag to make Helix run completely overlook it.
BEGIN_TEST_METHOD_PROPERTIES()
TEST_METHOD_PROPERTY(L"Ignore", L"True")
END_TEST_METHOD_PROPERTIES()
// This test to be corrected as a part of GH#7281
// This test ensures that we'll correctly create commands for actions
// that don't have given names, pursuant to the spec in GH#6532.
@@ -369,4 +363,145 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
}
}
void CommandTests::TestGenerateCommandline()
{
const WEX::TestExecution::DisableVerifyExceptions disableExceptionsScope;
const std::string commands0String{ R"([
{
"name":"action0",
"command": { "action": "newWindow" }
},
{
"name":"action1",
"command": { "action": "newTab", "profile": "foo" }
},
{
"name":"action2",
"command": { "action": "newWindow", "profile": "foo" }
},
{
"name":"action3",
"command": { "action": "newWindow", "commandline": "bar.exe" }
},
{
"name":"action4",
"command": { "action": "newWindow", "commandline": "pop.exe ya ha ha" }
},
{
"name":"action5",
"command": { "action": "newWindow", "commandline": "pop.exe \"ya ha ha\"" }
},
{
"name":"action6",
"command": { "action": "newWindow", "startingDirectory":"C:\\foo", "commandline": "bar.exe" }
},
])" };
const auto commands0Json = VerifyParseSucceeded(commands0String);
IMap<winrt::hstring, Command> commands = winrt::single_threaded_map<winrt::hstring, Command>();
VERIFY_ARE_EQUAL(0u, commands.Size());
auto warnings = implementation::Command::LayerJson(commands, commands0Json);
VERIFY_ARE_EQUAL(0u, warnings.size());
VERIFY_ARE_EQUAL(7u, commands.Size());
{
auto command = commands.Lookup(L"action0");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.Action());
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.Action().Action());
const auto& realArgs = command.Action().Args().try_as<NewWindowArgs>();
VERIFY_IS_NOT_NULL(realArgs);
const auto& terminalArgs = realArgs.TerminalArgs();
VERIFY_IS_NOT_NULL(terminalArgs);
auto cmdline = terminalArgs.ToCommandline();
VERIFY_ARE_EQUAL(L"", cmdline);
}
{
auto command = commands.Lookup(L"action1");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.Action());
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, command.Action().Action());
const auto& realArgs = command.Action().Args().try_as<NewTabArgs>();
VERIFY_IS_NOT_NULL(realArgs);
const auto& terminalArgs = realArgs.TerminalArgs();
VERIFY_IS_NOT_NULL(terminalArgs);
auto cmdline = terminalArgs.ToCommandline();
VERIFY_ARE_EQUAL(L"--profile \"foo\"", cmdline);
}
{
auto command = commands.Lookup(L"action2");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.Action());
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.Action().Action());
const auto& realArgs = command.Action().Args().try_as<NewWindowArgs>();
VERIFY_IS_NOT_NULL(realArgs);
const auto& terminalArgs = realArgs.TerminalArgs();
VERIFY_IS_NOT_NULL(terminalArgs);
auto cmdline = terminalArgs.ToCommandline();
VERIFY_ARE_EQUAL(L"--profile \"foo\"", cmdline);
}
{
auto command = commands.Lookup(L"action3");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.Action());
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.Action().Action());
const auto& realArgs = command.Action().Args().try_as<NewWindowArgs>();
VERIFY_IS_NOT_NULL(realArgs);
const auto& terminalArgs = realArgs.TerminalArgs();
VERIFY_IS_NOT_NULL(terminalArgs);
auto cmdline = terminalArgs.ToCommandline();
VERIFY_ARE_EQUAL(L"-- \"bar.exe\"", cmdline);
}
{
auto command = commands.Lookup(L"action4");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.Action());
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.Action().Action());
const auto& realArgs = command.Action().Args().try_as<NewWindowArgs>();
VERIFY_IS_NOT_NULL(realArgs);
const auto& terminalArgs = realArgs.TerminalArgs();
VERIFY_IS_NOT_NULL(terminalArgs);
auto cmdline = terminalArgs.ToCommandline();
Log::Comment(NoThrowString().Format(
L"cmdline: \"%s\"", cmdline.c_str()));
VERIFY_ARE_EQUAL(L"-- \"pop.exe ya ha ha\"", terminalArgs.ToCommandline());
}
{
auto command = commands.Lookup(L"action5");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.Action());
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.Action().Action());
const auto& realArgs = command.Action().Args().try_as<NewWindowArgs>();
VERIFY_IS_NOT_NULL(realArgs);
const auto& terminalArgs = realArgs.TerminalArgs();
VERIFY_IS_NOT_NULL(terminalArgs);
auto cmdline = terminalArgs.ToCommandline();
Log::Comment(NoThrowString().Format(
L"cmdline: \"%s\"", cmdline.c_str()));
VERIFY_ARE_EQUAL(L"-- \"pop.exe \"ya ha ha\"\"", terminalArgs.ToCommandline());
}
{
auto command = commands.Lookup(L"action6");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.Action());
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.Action().Action());
const auto& realArgs = command.Action().Args().try_as<NewWindowArgs>();
VERIFY_IS_NOT_NULL(realArgs);
const auto& terminalArgs = realArgs.TerminalArgs();
VERIFY_IS_NOT_NULL(terminalArgs);
auto cmdline = terminalArgs.ToCommandline();
Log::Comment(NoThrowString().Format(
L"cmdline: \"%s\"", cmdline.c_str()));
VERIFY_ARE_EQUAL(L"--startingDirectory \"C:\\foo\" -- \"bar.exe\"", terminalArgs.ToCommandline());
}
}
}

View File

@@ -84,7 +84,7 @@ namespace SettingsModelLocalTests
"initialPosition": ",",
"launchMode": "default",
"alwaysOnTop": false,
"inputServiceWarning": true,
"copyOnSelect": false,
"copyFormatting": "all",
"wordDelimiters": " /\\()\"'-.,:;<>~!@#$%^&*|+=[]{}~?\u2502",

View File

@@ -34,6 +34,7 @@ Author(s):
#include <WexTestClass.h>
#include <json.h>
#include "consoletaeftemplates.hpp"
#include "winrtTaefTemplates.hpp"
#include <winrt/Windows.ApplicationModel.Resources.Core.h>
#include "winrt/Windows.UI.Xaml.Markup.h"

View File

@@ -6,16 +6,24 @@
#include "../types/inc/utils.hpp"
#include "../TerminalApp/TerminalPage.h"
#include "../TerminalApp/AppLogic.h"
#include "../TerminalApp/AppCommandlineArgs.h"
#include "../inc/WindowingBehavior.h"
using namespace WEX::Logging;
using namespace WEX::Common;
using namespace WEX::TestExecution;
using namespace winrt;
using namespace winrt::Microsoft::Terminal::Settings::Model;
using namespace winrt::TerminalApp;
using namespace ::TerminalApp;
namespace winrt
{
namespace appImpl = TerminalApp::implementation;
}
namespace TerminalAppLocalTests
{
// TODO:microsoft/terminal#3838:
@@ -64,6 +72,10 @@ namespace TerminalAppLocalTests
TEST_METHOD(TestMultipleSplitPaneSizes);
TEST_METHOD(TestFindTargetWindow);
TEST_METHOD(TestFindTargetWindowHelp);
TEST_METHOD(TestFindTargetWindowVersion);
private:
void _buildCommandlinesHelper(AppCommandlineArgs& appArgs,
const size_t expectedSubcommands,
@@ -620,7 +632,7 @@ namespace TerminalAppLocalTests
{
AppCommandlineArgs appArgs{};
std::vector<const wchar_t*> rawCommands{ L"wt.exe", subcommand, L"--tabColor", L"#009999" };
const auto expectedColor = Microsoft::Console::Utils::ColorFromHexString("#009999");
const auto expectedColor = ::Microsoft::Console::Utils::ColorFromHexString("#009999");
_buildCommandlinesHelper(appArgs, 1u, rawCommands);
@@ -1578,4 +1590,106 @@ namespace TerminalAppLocalTests
}
}
}
void CommandlineTest::TestFindTargetWindow()
{
{
std::vector<winrt::hstring> args{ L"wt.exe" };
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew));
VERIFY_ARE_EQUAL(WindowingBehaviorUseExisting,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting));
VERIFY_ARE_EQUAL(WindowingBehaviorUseAnyExisting,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting));
}
{
std::vector<winrt::hstring> args{ L"wt.exe", L"-w", L"-1" };
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew));
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting));
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting));
}
{
std::vector<winrt::hstring> args{ L"wt.exe", L"-w", L"-12345" };
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew));
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting));
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting));
}
{
std::vector<winrt::hstring> args{ L"wt.exe", L"-w", L"0" };
VERIFY_ARE_EQUAL(WindowingBehaviorUseCurrent,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew));
VERIFY_ARE_EQUAL(WindowingBehaviorUseCurrent,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting));
VERIFY_ARE_EQUAL(WindowingBehaviorUseCurrent,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting));
}
{
std::vector<winrt::hstring> args{ L"wt.exe", L"new-tab" };
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew));
VERIFY_ARE_EQUAL(WindowingBehaviorUseExisting,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting));
VERIFY_ARE_EQUAL(WindowingBehaviorUseAnyExisting,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting));
}
}
void CommandlineTest::TestFindTargetWindowHelp()
{
Log::Comment(L"--help should always create a new window");
// This is a little helper to make sure that these args _always_ return
// UseNew, regardless of the windowing behavior.
auto testHelper = [](auto&& args) {
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew));
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting));
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting));
};
testHelper(std::vector<winrt::hstring>{ L"wt.exe", L"--help" });
testHelper(std::vector<winrt::hstring>{ L"wt.exe", L"new-tab", L"--help" });
testHelper(std::vector<winrt::hstring>{ L"wt.exe", L"-w", L"0", L"new-tab", L"--help" });
testHelper(std::vector<winrt::hstring>{ L"wt.exe", L"new-tab", L";", L"--help" });
}
void CommandlineTest::TestFindTargetWindowVersion()
{
Log::Comment(L"--version should always create a new window");
// This is a little helper to make sure that these args _always_ return
// UseNew, regardless of the windowing behavior.
auto testHelper = [](auto&& args) {
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew));
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting));
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting));
};
testHelper(std::vector<winrt::hstring>{ L"wt.exe", L"--version" });
}
}

View File

@@ -4,6 +4,7 @@
#include "pch.h"
#include "../TerminalApp/CommandLinePaletteItem.h"
#include "../TerminalApp/CommandPalette.h"
#include "../CppWinrtTailored.h"
using namespace Microsoft::Console;
using namespace WEX::Logging;
@@ -29,187 +30,203 @@ namespace TerminalAppLocalTests
void FilteredCommandTests::VerifyHighlighting()
{
const auto paletteItem{ winrt::make<winrt::TerminalApp::implementation::CommandLinePaletteItem>(L"AAAAAABBBBBBCCC") };
{
Log::Comment(L"Testing command name segmentation with no filter");
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
auto segments = filteredCommand->_computeHighlightedName().Segments();
VERIFY_ARE_EQUAL(segments.Size(), 1u);
VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"AAAAAABBBBBBCCC");
VERIFY_IS_FALSE(segments.GetAt(0).IsHighlighted());
}
{
Log::Comment(L"Testing command name segmentation with empty filter");
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
filteredCommand->_Filter = L"";
auto segments = filteredCommand->_computeHighlightedName().Segments();
VERIFY_ARE_EQUAL(segments.Size(), 1u);
VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"AAAAAABBBBBBCCC");
VERIFY_IS_FALSE(segments.GetAt(0).IsHighlighted());
}
{
Log::Comment(L"Testing command name segmentation with filter equals to the string");
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
filteredCommand->_Filter = L"AAAAAABBBBBBCCC";
auto segments = filteredCommand->_computeHighlightedName().Segments();
VERIFY_ARE_EQUAL(segments.Size(), 1u);
VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"AAAAAABBBBBBCCC");
VERIFY_IS_TRUE(segments.GetAt(0).IsHighlighted());
}
{
Log::Comment(L"Testing command name segmentation with filter with first character matching");
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
filteredCommand->_Filter = L"A";
auto segments = filteredCommand->_computeHighlightedName().Segments();
VERIFY_ARE_EQUAL(segments.Size(), 2u);
VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"A");
VERIFY_IS_TRUE(segments.GetAt(0).IsHighlighted());
VERIFY_ARE_EQUAL(segments.GetAt(1).TextSegment(), L"AAAAABBBBBBCCC");
VERIFY_IS_FALSE(segments.GetAt(1).IsHighlighted());
}
{
Log::Comment(L"Testing command name segmentation with filter with other case");
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
filteredCommand->_Filter = L"a";
auto segments = filteredCommand->_computeHighlightedName().Segments();
VERIFY_ARE_EQUAL(segments.Size(), 2u);
VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"A");
VERIFY_IS_TRUE(segments.GetAt(0).IsHighlighted());
VERIFY_ARE_EQUAL(segments.GetAt(1).TextSegment(), L"AAAAABBBBBBCCC");
VERIFY_IS_FALSE(segments.GetAt(1).IsHighlighted());
}
{
Log::Comment(L"Testing command name segmentation with filter matching several characters");
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
filteredCommand->_Filter = L"ab";
auto segments = filteredCommand->_computeHighlightedName().Segments();
VERIFY_ARE_EQUAL(segments.Size(), 4u);
VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"A");
VERIFY_IS_TRUE(segments.GetAt(0).IsHighlighted());
VERIFY_ARE_EQUAL(segments.GetAt(1).TextSegment(), L"AAAAA");
VERIFY_IS_FALSE(segments.GetAt(1).IsHighlighted());
VERIFY_ARE_EQUAL(segments.GetAt(2).TextSegment(), L"B");
VERIFY_IS_TRUE(segments.GetAt(2).IsHighlighted());
VERIFY_ARE_EQUAL(segments.GetAt(3).TextSegment(), L"BBBBBCCC");
VERIFY_IS_FALSE(segments.GetAt(3).IsHighlighted());
}
{
Log::Comment(L"Testing command name segmentation with non matching filter");
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
filteredCommand->_Filter = L"abcd";
auto segments = filteredCommand->_computeHighlightedName().Segments();
VERIFY_ARE_EQUAL(segments.Size(), 1u);
VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"AAAAAABBBBBBCCC");
VERIFY_IS_FALSE(segments.GetAt(0).IsHighlighted());
}
auto result = RunOnUIThread([]() {
const auto paletteItem{ winrt::make<winrt::TerminalApp::implementation::CommandLinePaletteItem>(L"AAAAAABBBBBBCCC") };
{
Log::Comment(L"Testing command name segmentation with no filter");
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
auto segments = filteredCommand->_computeHighlightedName().Segments();
VERIFY_ARE_EQUAL(segments.Size(), 1u);
VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"AAAAAABBBBBBCCC");
VERIFY_IS_FALSE(segments.GetAt(0).IsHighlighted());
}
{
Log::Comment(L"Testing command name segmentation with empty filter");
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
filteredCommand->_Filter = L"";
auto segments = filteredCommand->_computeHighlightedName().Segments();
VERIFY_ARE_EQUAL(segments.Size(), 1u);
VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"AAAAAABBBBBBCCC");
VERIFY_IS_FALSE(segments.GetAt(0).IsHighlighted());
}
{
Log::Comment(L"Testing command name segmentation with filter equals to the string");
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
filteredCommand->_Filter = L"AAAAAABBBBBBCCC";
auto segments = filteredCommand->_computeHighlightedName().Segments();
VERIFY_ARE_EQUAL(segments.Size(), 1u);
VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"AAAAAABBBBBBCCC");
VERIFY_IS_TRUE(segments.GetAt(0).IsHighlighted());
}
{
Log::Comment(L"Testing command name segmentation with filter with first character matching");
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
filteredCommand->_Filter = L"A";
auto segments = filteredCommand->_computeHighlightedName().Segments();
VERIFY_ARE_EQUAL(segments.Size(), 2u);
VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"A");
VERIFY_IS_TRUE(segments.GetAt(0).IsHighlighted());
VERIFY_ARE_EQUAL(segments.GetAt(1).TextSegment(), L"AAAAABBBBBBCCC");
VERIFY_IS_FALSE(segments.GetAt(1).IsHighlighted());
}
{
Log::Comment(L"Testing command name segmentation with filter with other case");
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
filteredCommand->_Filter = L"a";
auto segments = filteredCommand->_computeHighlightedName().Segments();
VERIFY_ARE_EQUAL(segments.Size(), 2u);
VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"A");
VERIFY_IS_TRUE(segments.GetAt(0).IsHighlighted());
VERIFY_ARE_EQUAL(segments.GetAt(1).TextSegment(), L"AAAAABBBBBBCCC");
VERIFY_IS_FALSE(segments.GetAt(1).IsHighlighted());
}
{
Log::Comment(L"Testing command name segmentation with filter matching several characters");
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
filteredCommand->_Filter = L"ab";
auto segments = filteredCommand->_computeHighlightedName().Segments();
VERIFY_ARE_EQUAL(segments.Size(), 4u);
VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"A");
VERIFY_IS_TRUE(segments.GetAt(0).IsHighlighted());
VERIFY_ARE_EQUAL(segments.GetAt(1).TextSegment(), L"AAAAA");
VERIFY_IS_FALSE(segments.GetAt(1).IsHighlighted());
VERIFY_ARE_EQUAL(segments.GetAt(2).TextSegment(), L"B");
VERIFY_IS_TRUE(segments.GetAt(2).IsHighlighted());
VERIFY_ARE_EQUAL(segments.GetAt(3).TextSegment(), L"BBBBBCCC");
VERIFY_IS_FALSE(segments.GetAt(3).IsHighlighted());
}
{
Log::Comment(L"Testing command name segmentation with non matching filter");
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
filteredCommand->_Filter = L"abcd";
auto segments = filteredCommand->_computeHighlightedName().Segments();
VERIFY_ARE_EQUAL(segments.Size(), 1u);
VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"AAAAAABBBBBBCCC");
VERIFY_IS_FALSE(segments.GetAt(0).IsHighlighted());
}
});
VERIFY_SUCCEEDED(result);
}
void FilteredCommandTests::VerifyWeight()
{
const auto paletteItem{ winrt::make<winrt::TerminalApp::implementation::CommandLinePaletteItem>(L"AAAAAABBBBBBCCC") };
{
Log::Comment(L"Testing weight of command with no filter");
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName();
auto weight = filteredCommand->_computeWeight();
VERIFY_ARE_EQUAL(weight, 0);
}
{
Log::Comment(L"Testing weight of command with empty filter");
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
filteredCommand->_Filter = L"";
filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName();
auto weight = filteredCommand->_computeWeight();
VERIFY_ARE_EQUAL(weight, 0);
}
{
Log::Comment(L"Testing weight of command with filter equals to the string");
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
filteredCommand->_Filter = L"AAAAAABBBBBBCCC";
filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName();
auto weight = filteredCommand->_computeWeight();
VERIFY_ARE_EQUAL(weight, 30); // 1 point for the first char and 2 points for the 14 consequent ones + 1 point for the beginning of the word
}
{
Log::Comment(L"Testing weight of command with filter with first character matching");
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
filteredCommand->_Filter = L"A";
filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName();
auto weight = filteredCommand->_computeWeight();
VERIFY_ARE_EQUAL(weight, 2); // 1 point for the first char match + 1 point for the beginning of the word
}
{
Log::Comment(L"Testing weight of command with filter with other case");
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
filteredCommand->_Filter = L"a";
filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName();
auto weight = filteredCommand->_computeWeight();
VERIFY_ARE_EQUAL(weight, 2); // 1 point for the first char match + 1 point for the beginning of the word
}
{
Log::Comment(L"Testing weight of command with filter matching several characters");
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
filteredCommand->_Filter = L"ab";
filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName();
auto weight = filteredCommand->_computeWeight();
VERIFY_ARE_EQUAL(weight, 3); // 1 point for the first char match + 1 point for the beginning of the word + 1 point for the match of "b"
}
auto result = RunOnUIThread([]() {
const auto paletteItem{ winrt::make<winrt::TerminalApp::implementation::CommandLinePaletteItem>(L"AAAAAABBBBBBCCC") };
{
Log::Comment(L"Testing weight of command with no filter");
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName();
auto weight = filteredCommand->_computeWeight();
VERIFY_ARE_EQUAL(weight, 0);
}
{
Log::Comment(L"Testing weight of command with empty filter");
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
filteredCommand->_Filter = L"";
filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName();
auto weight = filteredCommand->_computeWeight();
VERIFY_ARE_EQUAL(weight, 0);
}
{
Log::Comment(L"Testing weight of command with filter equals to the string");
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
filteredCommand->_Filter = L"AAAAAABBBBBBCCC";
filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName();
auto weight = filteredCommand->_computeWeight();
VERIFY_ARE_EQUAL(weight, 30); // 1 point for the first char and 2 points for the 14 consequent ones + 1 point for the beginning of the word
}
{
Log::Comment(L"Testing weight of command with filter with first character matching");
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
filteredCommand->_Filter = L"A";
filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName();
auto weight = filteredCommand->_computeWeight();
VERIFY_ARE_EQUAL(weight, 2); // 1 point for the first char match + 1 point for the beginning of the word
}
{
Log::Comment(L"Testing weight of command with filter with other case");
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
filteredCommand->_Filter = L"a";
filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName();
auto weight = filteredCommand->_computeWeight();
VERIFY_ARE_EQUAL(weight, 2); // 1 point for the first char match + 1 point for the beginning of the word
}
{
Log::Comment(L"Testing weight of command with filter matching several characters");
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
filteredCommand->_Filter = L"ab";
filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName();
auto weight = filteredCommand->_computeWeight();
VERIFY_ARE_EQUAL(weight, 3); // 1 point for the first char match + 1 point for the beginning of the word + 1 point for the match of "b"
}
});
VERIFY_SUCCEEDED(result);
}
void FilteredCommandTests::VerifyCompare()
{
const auto paletteItem{ winrt::make<winrt::TerminalApp::implementation::CommandLinePaletteItem>(L"AAAAAABBBBBBCCC") };
const auto paletteItem2{ winrt::make<winrt::TerminalApp::implementation::CommandLinePaletteItem>(L"BBBBBCCC") };
{
Log::Comment(L"Testing comparison of commands with no filter");
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
const auto filteredCommand2 = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem2);
auto result = RunOnUIThread([]() {
const auto paletteItem{ winrt::make<winrt::TerminalApp::implementation::CommandLinePaletteItem>(L"AAAAAABBBBBBCCC") };
const auto paletteItem2{ winrt::make<winrt::TerminalApp::implementation::CommandLinePaletteItem>(L"BBBBBCCC") };
{
Log::Comment(L"Testing comparison of commands with no filter");
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
const auto filteredCommand2 = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem2);
VERIFY_ARE_EQUAL(filteredCommand->Weight(), filteredCommand2->Weight());
VERIFY_IS_TRUE(winrt::TerminalApp::implementation::FilteredCommand::Compare(*filteredCommand, *filteredCommand2));
}
{
Log::Comment(L"Testing comparison of commands with empty filter");
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
filteredCommand->_Filter = L"";
filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName();
filteredCommand->_Weight = filteredCommand->_computeWeight();
VERIFY_ARE_EQUAL(filteredCommand->Weight(), filteredCommand2->Weight());
VERIFY_IS_TRUE(winrt::TerminalApp::implementation::FilteredCommand::Compare(*filteredCommand, *filteredCommand2));
}
{
Log::Comment(L"Testing comparison of commands with empty filter");
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
filteredCommand->_Filter = L"";
filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName();
filteredCommand->_Weight = filteredCommand->_computeWeight();
const auto filteredCommand2 = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem2);
filteredCommand2->_Filter = L"";
filteredCommand2->_HighlightedName = filteredCommand2->_computeHighlightedName();
filteredCommand2->_Weight = filteredCommand2->_computeWeight();
const auto filteredCommand2 = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem2);
filteredCommand2->_Filter = L"";
filteredCommand2->_HighlightedName = filteredCommand2->_computeHighlightedName();
filteredCommand2->_Weight = filteredCommand2->_computeWeight();
VERIFY_ARE_EQUAL(filteredCommand->Weight(), filteredCommand2->Weight());
VERIFY_IS_TRUE(winrt::TerminalApp::implementation::FilteredCommand::Compare(*filteredCommand, *filteredCommand2));
}
{
Log::Comment(L"Testing comparison of commands with different weights");
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
filteredCommand->_Filter = L"B";
filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName();
filteredCommand->_Weight = filteredCommand->_computeWeight();
VERIFY_ARE_EQUAL(filteredCommand->Weight(), filteredCommand2->Weight());
VERIFY_IS_TRUE(winrt::TerminalApp::implementation::FilteredCommand::Compare(*filteredCommand, *filteredCommand2));
}
{
Log::Comment(L"Testing comparison of commands with different weights");
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
filteredCommand->_Filter = L"B";
filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName();
filteredCommand->_Weight = filteredCommand->_computeWeight();
const auto filteredCommand2 = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem2);
filteredCommand2->_Filter = L"B";
filteredCommand2->_HighlightedName = filteredCommand2->_computeHighlightedName();
filteredCommand2->_Weight = filteredCommand2->_computeWeight();
const auto filteredCommand2 = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem2);
filteredCommand2->_Filter = L"B";
filteredCommand2->_HighlightedName = filteredCommand2->_computeHighlightedName();
filteredCommand2->_Weight = filteredCommand2->_computeWeight();
VERIFY_IS_TRUE(filteredCommand->Weight() < filteredCommand2->Weight()); // Second command gets more points due to the beginning of the word
VERIFY_IS_FALSE(winrt::TerminalApp::implementation::FilteredCommand::Compare(*filteredCommand, *filteredCommand2));
}
VERIFY_IS_TRUE(filteredCommand->Weight() < filteredCommand2->Weight()); // Second command gets more points due to the beginning of the word
VERIFY_IS_FALSE(winrt::TerminalApp::implementation::FilteredCommand::Compare(*filteredCommand, *filteredCommand2));
}
});
VERIFY_SUCCEEDED(result);
}
void FilteredCommandTests::VerifyCompareIgnoreCase()
{
const auto paletteItem{ winrt::make<winrt::TerminalApp::implementation::CommandLinePaletteItem>(L"a") };
const auto paletteItem2{ winrt::make<winrt::TerminalApp::implementation::CommandLinePaletteItem>(L"B") };
{
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
const auto filteredCommand2 = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem2);
auto result = RunOnUIThread([]() {
const auto paletteItem{ winrt::make<winrt::TerminalApp::implementation::CommandLinePaletteItem>(L"a") };
const auto paletteItem2{ winrt::make<winrt::TerminalApp::implementation::CommandLinePaletteItem>(L"B") };
{
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
const auto filteredCommand2 = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem2);
VERIFY_ARE_EQUAL(filteredCommand->Weight(), filteredCommand2->Weight());
VERIFY_IS_TRUE(winrt::TerminalApp::implementation::FilteredCommand::Compare(*filteredCommand, *filteredCommand2));
}
VERIFY_ARE_EQUAL(filteredCommand->Weight(), filteredCommand2->Weight());
VERIFY_IS_TRUE(winrt::TerminalApp::implementation::FilteredCommand::Compare(*filteredCommand, *filteredCommand2));
}
});
VERIFY_SUCCEEDED(result);
}
}

View File

@@ -330,7 +330,7 @@ namespace TerminalAppLocalTests
{
// * Create a tab with a profile with GUID 1
// * Reload the settings so that GUID 1 is no longer in the list of profiles
// * Try calling _DuplicateTabViewItem on tab 1
// * Try calling _DuplicateFocusedTab on tab 1
// * No new tab should be created (and more importantly, the app should not crash)
//
// Created to test GH#2455
@@ -392,7 +392,7 @@ namespace TerminalAppLocalTests
Log::Comment(L"Duplicate the first tab");
result = RunOnUIThread([&page]() {
page->_DuplicateTabViewItem();
page->_DuplicateFocusedTab();
VERIFY_ARE_EQUAL(2u, page->_tabs.Size());
});
VERIFY_SUCCEEDED(result);
@@ -407,7 +407,7 @@ namespace TerminalAppLocalTests
Log::Comment(L"Duplicate the tab, and don't crash");
result = RunOnUIThread([&page]() {
page->_DuplicateTabViewItem();
page->_DuplicateFocusedTab();
VERIFY_ARE_EQUAL(2u, page->_tabs.Size(), L"We should gracefully do nothing here - the profile no longer exists.");
});
VERIFY_SUCCEEDED(result);
@@ -868,13 +868,33 @@ namespace TerminalAppLocalTests
VERIFY_ARE_EQUAL(4u, page->_mruTabs.Size());
Log::Comment(L"give alphabetical names to all switch tab actions");
RunOnUIThread([&page]() {
TestOnUIThread([&page]() {
page->_GetTerminalTabImpl(page->_tabs.GetAt(0))->Title(L"a");
});
TestOnUIThread([&page]() {
page->_GetTerminalTabImpl(page->_tabs.GetAt(1))->Title(L"b");
});
TestOnUIThread([&page]() {
page->_GetTerminalTabImpl(page->_tabs.GetAt(2))->Title(L"c");
});
TestOnUIThread([&page]() {
page->_GetTerminalTabImpl(page->_tabs.GetAt(3))->Title(L"d");
});
TestOnUIThread([&page]() {
Log::Comment(L"Sanity check the titles of our tabs are what we set them to.");
VERIFY_ARE_EQUAL(L"a", page->_tabs.GetAt(0).Title());
VERIFY_ARE_EQUAL(L"b", page->_tabs.GetAt(1).Title());
VERIFY_ARE_EQUAL(L"c", page->_tabs.GetAt(2).Title());
VERIFY_ARE_EQUAL(L"d", page->_tabs.GetAt(3).Title());
VERIFY_ARE_EQUAL(L"d", page->_mruTabs.GetAt(0).Title());
VERIFY_ARE_EQUAL(L"c", page->_mruTabs.GetAt(1).Title());
VERIFY_ARE_EQUAL(L"b", page->_mruTabs.GetAt(2).Title());
VERIFY_ARE_EQUAL(L"a", page->_mruTabs.GetAt(3).Title());
});
Log::Comment(L"Change the tab switch order to MRU switching");
TestOnUIThread([&page]() {
page->_settings.GlobalSettings().TabSwitcherMode(TabSwitcherMode::MostRecentlyUsed);
@@ -897,18 +917,30 @@ namespace TerminalAppLocalTests
Log::Comment(L"Switch to the next MRU tab, which is the third tab");
RunOnUIThread([&page]() {
page->_SelectNextTab(true);
// In the course of a single tick, the Command Palette will:
// * open
// * select the proper tab from the mru's list
// * raise an event for _filteredActionsView().SelectionChanged to
// immediately preview the new tab
// * raise a _SwitchToTabRequestedHandlers event
// * then dismiss itself, because we can't fake holing down an
// anchor key in the tests
});
TestOnUIThread([&page]() {
VERIFY_ARE_EQUAL(L"c", page->_mruTabs.GetAt(0).Title());
VERIFY_ARE_EQUAL(L"d", page->_mruTabs.GetAt(1).Title());
VERIFY_ARE_EQUAL(L"b", page->_mruTabs.GetAt(2).Title());
VERIFY_ARE_EQUAL(L"a", page->_mruTabs.GetAt(3).Title());
});
const auto palette = winrt::get_self<winrt::TerminalApp::implementation::CommandPalette>(page->CommandPalette());
VERIFY_ARE_EQUAL(1u, palette->_switcherStartIdx, L"Verify the index is 1 as we went right");
VERIFY_ARE_EQUAL(winrt::TerminalApp::implementation::CommandPaletteMode::TabSwitchMode, palette->_currentMode, L"Verify we are in the tab switcher mode");
Log::Comment(L"Verify command palette preserves MRU order of tabs");
VERIFY_ARE_EQUAL(4u, palette->_filteredActions.Size());
VERIFY_ARE_EQUAL(L"d", palette->_filteredActions.GetAt(0).Item().Name());
VERIFY_ARE_EQUAL(L"c", palette->_filteredActions.GetAt(1).Item().Name());
VERIFY_ARE_EQUAL(L"b", palette->_filteredActions.GetAt(2).Item().Name());
VERIFY_ARE_EQUAL(L"a", palette->_filteredActions.GetAt(3).Item().Name());
// At this point, the contents of the command palette's _mruTabs list is
// still the _old_ ordering (d, c, b, a). The ordering is only updated
// in TerminalPage::_SelectNextTab, but as we saw before, the palette
// will also dismiss itself immediately when that's called. So we can't
// really inspect the contents of the list in this test, unfortunately.
}
}

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" IgnorableNamespaces="uap mp">
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3" IgnorableNamespaces="uap mp uap3">
<Identity Name="WindowsTerminal.TestHost" Publisher="CN=Windows Terminal Team" Version="1.0.0.0" />
<mp:PhoneIdentity PhoneProductId="fba054a7-f1a1-4cb7-bb21-4949919af2f5" PhonePublisherId="00000000-0000-0000-0000-000000000000" />
<Properties>
@@ -22,6 +22,13 @@
</uap:DefaultTile>
<uap:SplashScreen Image="taef.png" />
</uap:VisualElements>
<Extensions>
<uap3:Extension Category="windows.appExtensionHost">
<uap3:AppExtensionHost>
<uap3:Name>com.microsoft.windows.terminal.settings</uap3:Name>
</uap3:AppExtensionHost>
</uap3:Extension>
</Extensions>
</Application>
</Applications>
</Package>

View File

@@ -34,6 +34,7 @@ Author(s):
#include <WexTestClass.h>
#include <json.h>
#include "consoletaeftemplates.hpp"
#include "winrtTaefTemplates.hpp"
#include <winrt/Windows.ApplicationModel.Resources.Core.h>
#include "winrt/Windows.UI.Xaml.Markup.h"
@@ -61,6 +62,8 @@ Author(s):
#include <regex>
#include <CLI11/CLI11.hpp>
#include <shobjidl_core.h>
// Manually include til after we include Windows.Foundation to give it winrt superpowers
#include "til.h"

View File

@@ -25,8 +25,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
{
struct FindTargetWindowArgs : public FindTargetWindowArgsT<FindTargetWindowArgs>
{
GETSET_PROPERTY(winrt::Microsoft::Terminal::Remoting::CommandlineArgs, Args, nullptr);
GETSET_PROPERTY(int, ResultTargetWindow, -1);
WINRT_PROPERTY(winrt::Microsoft::Terminal::Remoting::CommandlineArgs, Args, nullptr);
WINRT_PROPERTY(int, ResultTargetWindow, -1);
public:
FindTargetWindowArgs(winrt::Microsoft::Terminal::Remoting::CommandlineArgs args) :

View File

@@ -2,6 +2,7 @@
// Licensed under the MIT license.
#include "pch.h"
#include "../inc/WindowingBehavior.h"
#include "Monarch.h"
#include "CommandlineArgs.h"
#include "FindTargetWindowArgs.h"
@@ -20,6 +21,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
Monarch::Monarch() :
_ourPID{ GetCurrentProcessId() }
{
try
{
_desktopManager = winrt::create_instance<IVirtualDesktopManager>(__uuidof(VirtualDesktopManager));
}
CATCH_LOG();
}
// This is a private constructor to be used in unit tests, where we don't
@@ -39,7 +45,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
}
// Method Description:
// - Add the given peasant to the list of peasants we're tracking. This Peasant may have already been assigned an ID. If it hasn't, then give it an ID.
// - Add the given peasant to the list of peasants we're tracking. This
// Peasant may have already been assigned an ID. If it hasn't, then give
// it an ID.
// Arguments:
// - peasant: the new Peasant to track.
// Return Value:
@@ -106,7 +114,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
}
// Method Description:
// - Lookup a peasant by its ID.
// - Lookup a peasant by its ID. If the peasant has died, this will also
// remove the peasant from our list of peasants.
// Arguments:
// - peasantID: The ID Of the peasant to find
// Return Value:
@@ -130,63 +139,239 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
LOG_CAUGHT_EXCEPTION();
// Remove the peasant from the list of peasants
_peasants.erase(peasantID);
// Remove the peasant from the list of MRU windows. They're dead.
// They can't be the MRU anymore.
_clearOldMruEntries(peasantID);
return nullptr;
}
}
// Method Description:
// - Handler for the `Peasant::WindowActivated` event. We'll make a in-proc
// copy of the WindowActivatedArgs from the peasant. That way, we won't
// need to worry about the origin process dying when working with the
// WindowActivatedArgs.
// - If the peasant process dies while we're making this copy, then we'll
// just log it and do nothing. We certainly don't want to track a dead
// peasant.
// - We'll pass that copy of the WindowActivatedArgs to
// _doHandleActivatePeasant, which will actually insert the
// WindowActivatedArgs into the list we're using to track the most recent
// peasants.
// Arguments:
// - args: the WindowActivatedArgs describing when and where the peasant was activated.
// Return Value:
// - <none>
void Monarch::HandleActivatePeasant(const Remoting::WindowActivatedArgs& args)
{
// TODO:projects/5 Use a heap/priority queue per-desktop to track which
// peasant was the most recent per-desktop. When we want to get the most
// recent of all desktops (WindowingBehavior::UseExisting), then use the
// most recent of all desktops.
const auto oldLastActiveTime = _lastActivatedTime.time_since_epoch().count();
const auto newLastActiveTime = args.ActivatedTime().time_since_epoch().count();
// For now, we'll just pay attention to whoever the most recent peasant
// was. We're not too worried about the mru peasant dying. Worst case -
// when the user executes a `wt -w 0`, we won't be able to find that
// peasant, and it'll open in a new window instead of the current one.
if (args.ActivatedTime() > _lastActivatedTime)
// Start by making a local copy of these args. It's easier for us if our
// tracking of these args is all in-proc. That way, the only thing that
// could fail due to the peasant dying is _this first copy_.
winrt::com_ptr<implementation::WindowActivatedArgs> localArgs{ nullptr };
try
{
_mostRecentPeasant = args.PeasantID();
_lastActivatedTime = args.ActivatedTime();
localArgs = winrt::make_self<implementation::WindowActivatedArgs>(args);
// This method will actually do the hard work
_doHandleActivatePeasant(localArgs);
}
catch (...)
{
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_HandleActivatePeasant_Failed",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
}
}
// Method Description:
// - Helper for removing a peasant from the list of MRU peasants. We want to
// do this both when the peasant dies, and also when the peasant is newly
// activated (so that we don't leave an old entry for it in the list).
// Arguments:
// - peasantID: The ID of the peasant to remove from the MRU list
// Return Value:
// - <none>
void Monarch::_clearOldMruEntries(const uint64_t peasantID)
{
auto result = std::find_if(_mruPeasants.begin(),
_mruPeasants.end(),
[peasantID](auto&& other) {
return peasantID == other.PeasantID();
});
if (result != std::end(_mruPeasants))
{
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_RemovedPeasantFromDesktop",
TraceLoggingUInt64(peasantID, "peasantID", "The ID of the peasant"),
TraceLoggingGuid(result->DesktopID(), "desktopGuid", "The GUID of the previous desktop the window was on"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
_mruPeasants.erase(result);
}
}
// Method Description:
// - Actually handle inserting the WindowActivatedArgs into our list of MRU windows.
// Arguments:
// - localArgs: an in-proc WindowActivatedArgs that we should add to our list of MRU windows.
// Return Value:
// - <none>
void Monarch::_doHandleActivatePeasant(const winrt::com_ptr<implementation::WindowActivatedArgs>& localArgs)
{
const auto newLastActiveTime = localArgs->ActivatedTime().time_since_epoch().count();
// * Check all the current lists to look for this peasant.
// remove it from any where it exists.
_clearOldMruEntries(localArgs->PeasantID());
// * If the current desktop doesn't have a vector, add one.
const auto desktopGuid{ localArgs->DesktopID() };
// * Add this args list. By using lower_bound with insert, we can get it
// into exactly the right spot, without having to re-sort the whole
// array.
_mruPeasants.insert(std::lower_bound(_mruPeasants.begin(),
_mruPeasants.end(),
*localArgs,
[](const auto& first, const auto& second) { return first.ActivatedTime() > second.ActivatedTime(); }),
*localArgs);
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_SetMostRecentPeasant",
TraceLoggingUInt64(args.PeasantID(), "peasantID", "the ID of the activated peasant"),
TraceLoggingInt64(oldLastActiveTime, "oldLastActiveTime", "The previous lastActiveTime"),
TraceLoggingInt64(newLastActiveTime, "newLastActiveTime", "The provided args.ActivatedTime()"),
TraceLoggingUInt64(localArgs->PeasantID(), "peasantID", "the ID of the activated peasant"),
TraceLoggingGuid(desktopGuid, "desktopGuid", "The GUID of the desktop the window is on"),
TraceLoggingInt64(newLastActiveTime, "newLastActiveTime", "The provided localArgs->ActivatedTime()"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
}
uint64_t Monarch::_getMostRecentPeasantID()
// Method Description:
// - Retrieves the ID of the MRU peasant window. If requested, will limit
// the search to windows that are on the current desktop.
// Arguments:
// - limitToCurrentDesktop: if true, only return the MRU peasant that's
// actually on the current desktop.
// Return Value:
// - the ID of the most recent peasant, otherwise 0 if we could not find one.
uint64_t Monarch::_getMostRecentPeasantID(const bool limitToCurrentDesktop)
{
if (_mostRecentPeasant != 0)
if (_mruPeasants.empty())
{
return _mostRecentPeasant;
// We haven't yet been told the MRU peasant. Just use the first one.
// This is just gonna be a random one, but really shouldn't happen
// in practice. The WindowManager should set the MRU peasant
// immediately as soon as it creates the monarch/peasant for the
// first window.
if (_peasants.size() > 0)
{
try
{
return _peasants.begin()->second.GetID();
}
catch (...)
{
// This shouldn't really happen. If we're the monarch, then the
// first peasant should also _be us_. So we should be able to
// get our own ID.
return 0;
}
}
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_getMostRecentPeasantID_NoPeasants",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
return 0;
}
// We haven't yet been told the MRU peasant. Just use the first one.
// This is just gonna be a random one, but really shouldn't happen
// in practice. The WindowManager should set the MRU peasant
// immediately as soon as it creates the monarch/peasant for the
// first window.
if (_peasants.size() > 0)
// Here, there's at least one MRU peasant.
//
// We're going to iterate over these peasants until we find one that both:
// 1. Is alive
// 2. Meets our selection criteria (do we care if it is on this desktop?)
//
// If the peasant is dead, then we'll remove it, and try the next one.
// Once we find one that's alive, we'll either:
// * check if we only want a peasant on the current desktop, and if so,
// check if this peasant is on the current desktop.
// - If it isn't on the current desktop, we'll loop again, on the
// following peasant.
// * If we don't care, then we'll just return that one.
//
// We're not just using an iterator because the contents of the list
// might change while we're iterating here (if the peasant is dead we'll
// remove it from the list).
int positionInList = 0;
while (_mruPeasants.cbegin() + positionInList < _mruPeasants.cend())
{
try
const auto mruWindowArgs{ *(_mruPeasants.begin() + positionInList) };
const auto peasant{ _getPeasant(mruWindowArgs.PeasantID()) };
if (!peasant)
{
return _peasants.begin()->second.GetID();
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_Collect_WasDead",
TraceLoggingUInt64(mruWindowArgs.PeasantID(),
"peasantID",
"We thought this peasant was the MRU one, but it was actually already dead."),
TraceLoggingGuid(mruWindowArgs.DesktopID(), "desktopGuid", "The GUID of the desktop the window is on"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
// We'll go through the loop again. We removed the current one
// at positionInList, so the next one in positionInList will be
// a new, different peasant.
continue;
}
catch (...)
if (limitToCurrentDesktop && _desktopManager)
{
// This shouldn't really happen. If we're the monarch, then the
// first peasant should also _be us_. So we should be able to
// get our own ID.
return 0;
// Check if this peasant is actually on this desktop. We can't
// simply get the GUID of the current desktop. We have to ask if
// the HWND is on the current desktop.
BOOL onCurrentDesktop{ false };
// SUCCEEDED_LOG will log if it failed, and return true if it SUCCEEDED
if (SUCCEEDED_LOG(_desktopManager->IsWindowOnCurrentVirtualDesktop(reinterpret_cast<HWND>(mruWindowArgs.Hwnd()),
&onCurrentDesktop)) &&
onCurrentDesktop)
{
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_Collect",
TraceLoggingUInt64(mruWindowArgs.PeasantID(),
"peasantID",
"the ID of the MRU peasant for a desktop"),
TraceLoggingGuid(mruWindowArgs.DesktopID(),
"desktopGuid",
"The GUID of the desktop the window is on"),
TraceLoggingBoolean(limitToCurrentDesktop,
"limitToCurrentDesktop",
"True if we should only search for a window on the current desktop"),
TraceLoggingBool(onCurrentDesktop,
"onCurrentDesktop",
"true if this window was in fact on the current desktop"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
return mruWindowArgs.PeasantID();
}
// If this window wasn't on the current desktop, another one
// might be. We'll increment positionInList below, and try
// again.
}
else
{
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_getMostRecentPeasantID_Found",
TraceLoggingUInt64(mruWindowArgs.PeasantID(), "peasantID", "The ID of the MRU peasant"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
return mruWindowArgs.PeasantID();
}
positionInList++;
}
// Here, we've checked all the windows, and none of them was both alive
// and the most recent (on this desktop). Just return 0 - the caller
// will use this to create a new window.
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_getMostRecentPeasantID_NotFound",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
return 0;
}
@@ -216,16 +401,51 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
// the parsed result.
const auto targetWindow = findWindowArgs->ResultTargetWindow();
// If there's a valid ID returned, then let's try and find the peasant that goes with it.
if (targetWindow >= 0)
{
uint64_t windowID = ::base::saturated_cast<uint64_t>(targetWindow);
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_ProposeCommandline",
TraceLoggingInt64(targetWindow, "targetWindow", "The window ID the args specified"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
if (windowID == 0)
// If there's a valid ID returned, then let's try and find the peasant
// that goes with it. Alternatively, if we were given a magic windowing
// constant, we can use that to look up an appropriate peasant.
if (targetWindow >= 0 ||
targetWindow == WindowingBehaviorUseExisting ||
targetWindow == WindowingBehaviorUseAnyExisting)
{
uint64_t windowID = 0;
switch (targetWindow)
{
windowID = _getMostRecentPeasantID();
case WindowingBehaviorUseCurrent:
case WindowingBehaviorUseExisting:
// TODO:projects/5 for now, just use the MRU window. Technically,
// UseExisting and UseCurrent are different.
// UseCurrent implies that we should try to do the WT_SESSION
// lookup to find the window that spawned this process (then
// fall back to sameDesktop if we can't find a match). For now,
// it's good enough to just try to find a match on this desktop.
windowID = _getMostRecentPeasantID(true);
break;
case WindowingBehaviorUseAnyExisting:
windowID = _getMostRecentPeasantID(false);
break;
default:
windowID = ::base::saturated_cast<uint64_t>(targetWindow);
break;
}
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_ProposeCommandline",
TraceLoggingInt64(windowID,
"windowID",
"The actual peasant ID we evaluated the window ID as"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
// If_getMostRecentPeasantID returns 0 above, then we couldn't find
// a matching window for that style of windowing. _getPeasant will
// return nullptr, and we'll fall through to the "create a new
// window" branch below.
if (auto targetPeasant{ _getPeasant(windowID) })
{
auto result{ winrt::make_self<Remoting::implementation::ProposeCommandlineResult>(false) };
@@ -251,9 +471,13 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_ProposeCommandline_Existing",
TraceLoggingUInt64(windowID, "peasantID", "the ID of the peasant the commandline waws intended for"),
TraceLoggingUInt64(windowID,
"peasantID",
"the ID of the peasant the commandline waws intended for"),
TraceLoggingBoolean(true, "foundMatch", "true if we found a peasant with that ID"),
TraceLoggingBoolean(!result->ShouldCreateWindow(), "succeeded", "true if we successfully dispatched the commandline to the peasant"),
TraceLoggingBoolean(!result->ShouldCreateWindow(),
"succeeded",
"true if we successfully dispatched the commandline to the peasant"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
return *result;
}
@@ -265,7 +489,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_ProposeCommandline_Existing",
TraceLoggingUInt64(windowID, "peasantID", "the ID of the peasant the commandline waws intended for"),
TraceLoggingUInt64(windowID,
"peasantID",
"the ID of the peasant the commandline waws intended for"),
TraceLoggingBoolean(false, "foundMatch", "true if we found a peasant with that ID"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
@@ -275,6 +501,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
}
}
// If we get here, we couldn't find an existing window. Make a new one.
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_ProposeCommandline_NewWindow",
TraceLoggingInt64(targetWindow, "targetWindow", "The provided ID"),
@@ -283,5 +510,4 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
// In this case, no usable ID was provided. Return { true, nullopt }
return winrt::make<Remoting::implementation::ProposeCommandlineResult>(true);
}
}

View File

@@ -6,6 +6,7 @@
#include "Monarch.g.h"
#include "Peasant.h"
#include "../cascadia/inc/cppwinrt_utils.h"
#include "WindowActivatedArgs.h"
// We sure different GUIDs here depending on whether we're running a Release,
// Preview, or Dev build. This ensures that different installs don't
@@ -30,12 +31,6 @@ constexpr GUID Monarch_clsid
}
};
enum class WindowingBehavior : uint64_t
{
UseNew = 0,
UseExisting = 1,
};
namespace RemotingUnitTests
{
class RemotingTests;
@@ -63,17 +58,20 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
uint64_t _nextPeasantID{ 1 };
uint64_t _thisPeasantID{ 0 };
uint64_t _mostRecentPeasant{ 0 };
winrt::Windows::Foundation::DateTime _lastActivatedTime{};
WindowingBehavior _windowingBehavior{ WindowingBehavior::UseNew };
winrt::com_ptr<IVirtualDesktopManager> _desktopManager{ nullptr };
std::unordered_map<uint64_t, winrt::Microsoft::Terminal::Remoting::IPeasant> _peasants;
std::vector<Remoting::WindowActivatedArgs> _mruPeasants;
winrt::Microsoft::Terminal::Remoting::IPeasant _getPeasant(uint64_t peasantID);
uint64_t _getMostRecentPeasantID();
uint64_t _getMostRecentPeasantID(bool limitToCurrentDesktop);
void _peasantWindowActivated(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args);
void _doHandleActivatePeasant(const winrt::com_ptr<winrt::Microsoft::Terminal::Remoting::implementation::WindowActivatedArgs>& args);
void _clearOldMruEntries(const uint64_t peasantID);
friend class RemotingUnitTests::RemotingTests;
};

View File

@@ -16,7 +16,9 @@ namespace Microsoft.Terminal.Remoting
runtimeclass WindowActivatedArgs
{
WindowActivatedArgs(UInt64 peasantID, Guid desktopID, Windows.Foundation.DateTime activatedTime);
WindowActivatedArgs(UInt64 peasantID, UInt64 hwnd, Guid desktopID, Windows.Foundation.DateTime activatedTime);
UInt64 PeasantID { get; };
UInt64 Hwnd { get; };
Guid DesktopID { get; };
Windows.Foundation.DateTime ActivatedTime { get; };
};

View File

@@ -26,8 +26,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
struct ProposeCommandlineResult : public ProposeCommandlineResultT<ProposeCommandlineResult>
{
public:
GETSET_PROPERTY(Windows::Foundation::IReference<uint64_t>, Id);
GETSET_PROPERTY(bool, ShouldCreateWindow, true);
WINRT_PROPERTY(Windows::Foundation::IReference<uint64_t>, Id);
WINRT_PROPERTY(bool, ShouldCreateWindow, true);
public:
ProposeCommandlineResult(bool shouldCreateWindow) :

View File

@@ -18,17 +18,40 @@ Abstract:
namespace winrt::Microsoft::Terminal::Remoting::implementation
{
struct CompareWindowActivatedArgs
{
bool operator()(const Remoting::WindowActivatedArgs& lhs, const Remoting::WindowActivatedArgs& rhs) const
{
return lhs.ActivatedTime() > rhs.ActivatedTime();
}
};
struct WindowActivatedArgs : public WindowActivatedArgsT<WindowActivatedArgs>
{
GETSET_PROPERTY(uint64_t, PeasantID, 0);
GETSET_PROPERTY(winrt::guid, DesktopID, {});
GETSET_PROPERTY(winrt::Windows::Foundation::DateTime, ActivatedTime, {});
WINRT_PROPERTY(uint64_t, PeasantID, 0);
WINRT_PROPERTY(winrt::guid, DesktopID, {});
WINRT_PROPERTY(winrt::Windows::Foundation::DateTime, ActivatedTime, {});
WINRT_PROPERTY(uint64_t, Hwnd, 0);
public:
WindowActivatedArgs(uint64_t peasantID, winrt::guid desktopID, winrt::Windows::Foundation::DateTime timestamp) :
WindowActivatedArgs(uint64_t peasantID,
uint64_t hwnd,
winrt::guid desktopID,
winrt::Windows::Foundation::DateTime timestamp) :
_PeasantID{ peasantID },
_Hwnd{ hwnd },
_DesktopID{ desktopID },
_ActivatedTime{ timestamp } {};
WindowActivatedArgs(uint64_t peasantID,
winrt::guid desktopID,
winrt::Windows::Foundation::DateTime timestamp) :
WindowActivatedArgs(peasantID, 0, desktopID, timestamp){};
WindowActivatedArgs(const Remoting::WindowActivatedArgs& other) :
_PeasantID{ other.PeasantID() },
_Hwnd{ other.Hwnd() },
_DesktopID{ other.DesktopID() },
_ActivatedTime{ other.ActivatedTime() } {};
};
}

View File

@@ -7,11 +7,17 @@
#pragma once
// Block minwindef.h min/max macros to prevent <algorithm> conflict
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
#define NOMCX
#define NOHELP
#define NOCOMM
#include <unknwn.h>
#include <ShObjIdl.h>
// Manually include til after we include Windows.Foundation to give it winrt superpowers
#define BLOCK_TIL
#include <LibraryIncludes.h>
@@ -25,8 +31,6 @@
#include <wil/cppwinrt.h>
#include <unknwn.h>
#include <hstring.h>
#include <winrt/Windows.ApplicationModel.h>

View File

@@ -3,6 +3,7 @@
#include "pch.h"
#include "OpenTerminalHere.h"
#include "../WinRTUtils/inc/WtExeUtils.h"
#include <ShlObj.h>
// TODO GH#6112: Localize these strings
@@ -10,103 +11,10 @@ static constexpr std::wstring_view VerbDisplayName{ L"Open in Windows Terminal"
static constexpr std::wstring_view VerbDevBuildDisplayName{ L"Open in Windows Terminal (Dev Build)" };
static constexpr std::wstring_view VerbName{ L"WindowsTerminalOpenHere" };
static constexpr std::wstring_view WtExe{ L"wt.exe" };
static constexpr std::wstring_view WtdExe{ L"wtd.exe" };
static constexpr std::wstring_view WindowsTerminalExe{ L"WindowsTerminal.exe" };
static constexpr std::wstring_view LocalAppDataAppsPath{ L"%LOCALAPPDATA%\\Microsoft\\WindowsApps\\" };
// This code is aggressively copied from
// https://github.com/microsoft/Windows-classic-samples/blob/master/Samples/
// Win7Samples/winui/shell/appshellintegration/ExplorerCommandVerb/ExplorerCommandVerb.cpp
// Function Description:
// - This is a helper to determine if we're running as a part of the Dev Build
// Package or the release package. We'll need to return different text, icons,
// and use different commandlines depending on which one the user requested.
// - Uses a C++11 "magic static" to make sure this is only computed once.
// - If we can't determine if it's the dev build or not, we'll default to true
// Arguments:
// - <none>
// Return Value:
// - true if we believe this extension is being run in the dev build package.
static bool IsDevBuild()
{
// use C++11 magic statics to make sure we only do this once.
static bool isDevBuild = []() -> bool {
try
{
const auto package{ winrt::Windows::ApplicationModel::Package::Current() };
const auto id = package.Id();
const std::wstring name{ id.FullName() };
// Does our PFN start with WindowsTerminalDev?
return name.rfind(L"WindowsTerminalDev", 0) == 0;
}
CATCH_LOG();
return true;
}();
return isDevBuild;
}
// Function Description:
// - Helper function for getting the path to the appropriate executable to use
// for this instance of the shell extension. If we're running the dev build,
// it should be a `wtd.exe`, but if we're preview or release, we want to make
// sure to get the correct `wt.exe` that corresponds to _us_.
// - If we're unpackaged, this needs to get us `WindowsTerminal.exe`, because
// the `wt*exe` alias won't have been installed for this install.
// Arguments:
// - <none>
// Return Value:
// - the full path to the exe, one of `wt.exe`, `wtd.exe`, or `WindowsTerminal.exe`.
static std::wstring _getExePath()
{
// use C++11 magic statics to make sure we only do this once.
static const std::wstring exePath = []() -> std::wstring {
// First, check a packaged location for the exe. If we've got a package
// family name, that means we're one of the packaged Dev build, packaged
// Release build, or packaged Preview build.
//
// If we're the preview or release build, there's no way of knowing if the
// `wt.exe` on the %PATH% is us or not. Fortunately, _our_ execution alias
// is located in "%LOCALAPPDATA%\Microsoft\WindowsApps\<our package family
// name>", _always_, so we can use that to look up the exe easier.
try
{
const auto package{ winrt::Windows::ApplicationModel::Package::Current() };
const auto id = package.Id();
const std::wstring pfn{ id.FamilyName() };
if (!pfn.empty())
{
const std::filesystem::path windowsAppsPath{ wil::ExpandEnvironmentStringsW<std::wstring>(LocalAppDataAppsPath.data()) };
const std::filesystem::path wtPath = windowsAppsPath / pfn / (IsDevBuild() ? WtdExe : WtExe);
return wtPath;
}
}
CATCH_LOG();
// If we're here, then we couldn't resolve our exe from the package. This
// means we're running unpackaged. We should just use the
// WindowsTerminal.exe that's sitting in the directory next to us.
try
{
HMODULE hModule = GetModuleHandle(nullptr);
THROW_LAST_ERROR_IF(hModule == nullptr);
std::wstring dllPathString;
THROW_IF_FAILED(wil::GetModuleFileNameW(hModule, dllPathString));
const std::filesystem::path dllPath{ dllPathString };
const std::filesystem::path rootDir = dllPath.parent_path();
std::filesystem::path wtPath = rootDir / WindowsTerminalExe;
return wtPath;
}
CATCH_LOG();
return L"wt.exe";
}();
return exePath;
}
// Method Description:
// - This method is called when the user activates the context menu item. We'll
// launch the Terminal using the current working directory.
@@ -148,7 +56,7 @@ HRESULT OpenTerminalHere::Invoke(IShellItemArray* psiItemArray,
siEx.StartupInfo.cb = sizeof(STARTUPINFOEX);
// Append a "\." to the given path, so that this will work in "C:\"
std::wstring cmdline = fmt::format(L"\"{}\" -d \"{}\\.\"", _getExePath(), pszName.get());
std::wstring cmdline = fmt::format(L"\"{}\" -d \"{}\\.\"", GetWtExePath(), pszName.get());
RETURN_IF_WIN32_BOOL_FALSE(CreateProcessW(
nullptr,
cmdline.data(),

View File

@@ -27,7 +27,7 @@ namespace winrt::Microsoft::Terminal::ShellExtension::implementation
struct PlaceholderType : PlaceholderTypeT<PlaceholderType>
{
PlaceholderType() = default;
GETSET_PROPERTY(int32_t, Placeholder, 42);
WINRT_PROPERTY(int32_t, Placeholder, 42);
};
}

View File

@@ -23,7 +23,7 @@ namespace winrt::TerminalApp::implementation
{
Name(command.Name());
KeyChordText(command.KeyChordText());
Icon(command.Icon());
Icon(command.IconPath());
_commandChangedRevoker = command.PropertyChanged(winrt::auto_revoke, [weakThis{ get_weak() }](auto& sender, auto& e) {
auto item{ weakThis.get() };
@@ -40,9 +40,9 @@ namespace winrt::TerminalApp::implementation
{
item->KeyChordText(senderCommand.KeyChordText());
}
else if (changedProperty == L"Icon")
else if (changedProperty == L"IconPath")
{
item->Icon(senderCommand.Icon());
item->Icon(senderCommand.IconPath());
}
}
});

View File

@@ -14,7 +14,7 @@ namespace winrt::TerminalApp::implementation
ActionPaletteItem() = default;
ActionPaletteItem(Microsoft::Terminal::Settings::Model::Command const& command);
GETSET_PROPERTY(Microsoft::Terminal::Settings::Model::Command, Command, nullptr);
WINRT_PROPERTY(Microsoft::Terminal::Settings::Model::Command, Command, nullptr);
private:
Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _commandChangedRevoker;

View File

@@ -5,6 +5,8 @@
#include "App.h"
#include "TerminalPage.h"
#include "../WinRTUtils/inc/WtExeUtils.h"
#include "../../types/inc/utils.hpp"
#include "Utils.h"
using namespace winrt::Windows::ApplicationModel::DataTransfer;
@@ -37,7 +39,7 @@ namespace winrt::TerminalApp::implementation
void TerminalPage::_HandleDuplicateTab(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
_DuplicateTabViewItem();
_DuplicateFocusedTab();
args.Handled(true);
}
@@ -193,6 +195,18 @@ namespace winrt::TerminalApp::implementation
args.Handled(true);
}
void TerminalPage::_HandleFindMatch(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (const auto& realArgs = args.ActionArgs().try_as<FindMatchArgs>())
{
if (const auto& control{ _GetActiveControl() })
{
control.SearchMatch(realArgs.Direction() == FindMatchDirection::Next);
args.Handled(true);
}
}
}
void TerminalPage::_HandleOpenSettings(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
@@ -569,4 +583,89 @@ namespace winrt::TerminalApp::implementation
}
}
// Function Description:
// - Helper to launch a new WT instance. It can either launch the instance
// elevated or unelevated.
// - To launch elevated, it will as the shell to elevate the process for us.
// This might cause a UAC prompt. The elevation is performed on a
// background thread, as to not block the UI thread.
// Arguments:
// - elevate: If true, launch the new Terminal elevated using `runas`
// - newTerminalArgs: A NewTerminalArgs describing the terminal instance
// that should be spawned. The Profile should be filled in with the GUID
// of the profile we want to launch.
// Return Value:
// - <none>
// Important: Don't take the param by reference, since we'll be doing work
// on another thread.
fire_and_forget TerminalPage::_OpenNewWindow(const bool elevate,
const NewTerminalArgs newTerminalArgs)
{
// Hop to the BG thread
co_await winrt::resume_background();
// This will get us the correct exe for dev/preview/release. If you
// don't stick this in a local, it'll get mangled by ShellExecute. I
// have no idea why.
const auto exePath{ GetWtExePath() };
// Build the commandline to pass to wt for this set of NewTerminalArgs
// `-w -1` will ensure a new window is created.
winrt::hstring cmdline{
fmt::format(L"-w -1 new-tab {}",
newTerminalArgs ? newTerminalArgs.ToCommandline().c_str() :
L"")
};
// Build the args to ShellExecuteEx. We need to use ShellExecuteEx so we
// can pass the SEE_MASK_NOASYNC flag. That flag allows us to safely
// call this on the background thread, and have ShellExecute _not_ call
// back to us on the main thread. Without this, if you close the
// Terminal quickly after the UAC prompt, the elevated WT will never
// actually spawn.
SHELLEXECUTEINFOW seInfo{ 0 };
seInfo.cbSize = sizeof(seInfo);
seInfo.fMask = SEE_MASK_NOASYNC;
// `runas` will cause the shell to launch this child process elevated.
// `open` will just run the executable normally.
seInfo.lpVerb = elevate ? L"runas" : L"open";
seInfo.lpFile = exePath.c_str();
seInfo.lpParameters = cmdline.c_str();
seInfo.nShow = SW_SHOWNORMAL;
LOG_IF_WIN32_BOOL_FALSE(ShellExecuteExW(&seInfo));
co_return;
}
void TerminalPage::_HandleNewWindow(const IInspectable& /*sender*/,
const ActionEventArgs& actionArgs)
{
NewTerminalArgs newTerminalArgs{ nullptr };
// If the caller provided NewTerminalArgs, then try to use those
if (actionArgs)
{
if (const auto& realArgs = actionArgs.ActionArgs().try_as<NewWindowArgs>())
{
newTerminalArgs = realArgs.TerminalArgs();
}
}
// Otherwise, if no NewTerminalArgs were provided, then just use a
// default-constructed one. The default-constructed one implies that
// nothing about the launch should be modified (just use the default
// profile).
if (!newTerminalArgs)
{
newTerminalArgs = NewTerminalArgs();
}
auto [profileGuid, settings] = TerminalSettings::BuildSettings(_settings,
newTerminalArgs,
*_bindings);
// Manually fill in the evaluated profile.
newTerminalArgs.Profile(::Microsoft::Console::Utils::GuidToString(profileGuid));
_OpenNewWindow(false, newTerminalArgs);
actionArgs.Handled(true);
}
}

View File

@@ -185,9 +185,9 @@ void AppCommandlineArgs::_buildParser()
maximized->excludes(fullscreen);
focus->excludes(fullscreen);
_app.add_option("-w,--window",
_windowTarget,
RS_A(L"CmdWindowTargetArgDesc"));
_app.add_option<std::optional<int>, int>("-w,--window",
_windowTarget,
RS_A(L"CmdWindowTargetArgDesc"));
// Subcommands
_buildNewTabParser();
@@ -417,6 +417,11 @@ void AppCommandlineArgs::_addNewTerminalArgs(AppCommandlineArgs::NewTerminalSubc
_startingTabColor,
RS_A(L"CmdTabColorArgDesc"));
subcommand.suppressApplicationTitleOption = subcommand.subcommand->add_flag(
"--suppressApplicationTitle,!--useApplicationTitle",
_suppressApplicationTitle,
RS_A(L"CmdSuppressApplicationTitleDesc"));
// Using positionals_at_end allows us to support "wt new-tab -d wsl -d Ubuntu"
// without CLI11 thinking that we've specified -d twice.
// There's an alternate construction where we make all subcommands "prefix commands",
@@ -484,6 +489,11 @@ NewTerminalArgs AppCommandlineArgs::_getNewTerminalArgs(AppCommandlineArgs::NewT
args.TabColor(static_cast<winrt::Windows::UI::Color>(tabColor));
}
if (*subcommand.suppressApplicationTitleOption)
{
args.SuppressApplicationTitle(_suppressApplicationTitle);
}
return args;
}
@@ -522,6 +532,7 @@ void AppCommandlineArgs::_resetStateToDefault()
_startingTitle.clear();
_startingTabColor.clear();
_commandline.clear();
_suppressApplicationTitle = false;
_splitVertical = false;
_splitHorizontal = false;
@@ -854,10 +865,16 @@ void AppCommandlineArgs::FullResetState()
_exitMessage = "";
_shouldExitEarly = false;
_windowTarget = -1;
_windowTarget = std::nullopt;
}
int AppCommandlineArgs::GetTargetWindow() const noexcept
std::optional<int> AppCommandlineArgs::GetTargetWindow() const noexcept
{
// If the user provides _any_ negative number, then treat it as -1, for "use a new window".
if (_windowTarget.has_value() && *_windowTarget < 0)
{
return { -1 };
}
return _windowTarget;
}

View File

@@ -44,7 +44,7 @@ public:
void DisableHelpInExitMessage();
void FullResetState();
int GetTargetWindow() const noexcept;
std::optional<int> GetTargetWindow() const noexcept;
private:
static const std::wregex _commandDelimiterRegex;
@@ -61,6 +61,7 @@ private:
CLI::Option* startingDirectoryOption;
CLI::Option* titleOption;
CLI::Option* tabColorOption;
CLI::Option* suppressApplicationTitleOption;
};
struct NewPaneSubcommand : public NewTerminalSubcommand
@@ -85,6 +86,7 @@ private:
std::string _startingDirectory;
std::string _startingTitle;
std::string _startingTabColor;
bool _suppressApplicationTitle{ false };
winrt::Microsoft::Terminal::Settings::Model::FocusDirection _moveFocusDirection{ winrt::Microsoft::Terminal::Settings::Model::FocusDirection::None };
@@ -106,7 +108,7 @@ private:
std::string _exitMessage;
bool _shouldExitEarly{ false };
int _windowTarget{ -1 };
std::optional<int> _windowTarget{ std::nullopt };
// Are you adding more args or attributes here? If they are not reset in _resetStateToDefault, make sure to reset them in FullResetState
winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs _getNewTerminalArgs(NewTerminalSubcommand& subcommand);

View File

@@ -3,6 +3,7 @@
#include "pch.h"
#include "AppLogic.h"
#include "../inc/WindowingBehavior.h"
#include "AppLogic.g.cpp"
#include <winrt/Microsoft.UI.Xaml.XamlTypeInfo.h>
@@ -172,6 +173,9 @@ namespace winrt::TerminalApp::implementation
// Method Description:
// - Returns the settings currently in use by the entire Terminal application.
// - IMPORTANT! This can throw! Make sure to try/catch this, so that the
// LocalTests don't crash (because their Application::Current() won't be a
// AppLogic)
// Throws:
// - HR E_INVALIDARG if the app isn't up and running.
const CascadiaSettings AppLogic::CurrentAppSettings()
@@ -487,11 +491,15 @@ namespace winrt::TerminalApp::implementation
void AppLogic::_OnLoaded(const IInspectable& /*sender*/,
const RoutedEventArgs& /*eventArgs*/)
{
const auto keyboardServiceIsDisabled = !_IsKeyboardServiceEnabled();
if (keyboardServiceIsDisabled)
if (_settings.GlobalSettings().InputServiceWarning())
{
_root->ShowKeyboardServiceWarning();
const auto keyboardServiceIsDisabled = !_IsKeyboardServiceEnabled();
if (keyboardServiceIsDisabled)
{
_root->ShowKeyboardServiceWarning();
}
}
if (FAILED(_settingsLoadedResult))
{
const winrt::hstring titleKey = USES_RESOURCE(L"InitialJsonParseErrorTitle");
@@ -660,6 +668,16 @@ namespace winrt::TerminalApp::implementation
};
}
bool AppLogic::CenterOnLaunch()
{
if (!_loadedInitialSettings)
{
// Load settings if we haven't already
LoadSettings();
}
return _settings.GlobalSettings().CenterOnLaunch();
}
winrt::Windows::UI::Xaml::ElementTheme AppLogic::GetRequestedTheme()
{
if (!_loadedInitialSettings)
@@ -1194,27 +1212,54 @@ namespace winrt::TerminalApp::implementation
// - args: an array of strings to process as a commandline. These args can contain spaces
// Return Value:
// - 0: We should handle the args "in the current window".
// - -1: We should handle the args in a new window
// - WindowingBehaviorUseNew: We should handle the args in a new window
// - WindowingBehaviorUseExisting: We should handle the args "in
// the current window ON THIS DESKTOP"
// - WindowingBehaviorUseAnyExisting: We should handle the args "in the current
// window ON ANY DESKTOP"
// - anything else: We should handle the commandline in the window with the given ID.
int32_t AppLogic::FindTargetWindow(array_view<const winrt::hstring> args)
{
return AppLogic::_doFindTargetWindow(args, _settings.GlobalSettings().WindowingBehavior());
}
// The main body of this function is a static helper, to facilitate unit-testing
int32_t AppLogic::_doFindTargetWindow(array_view<const winrt::hstring> args,
const Microsoft::Terminal::Settings::Model::WindowingMode& windowingBehavior)
{
::TerminalApp::AppCommandlineArgs appArgs;
const auto result = appArgs.ParseArgs(args);
if (result == 0)
{
return appArgs.GetTargetWindow();
if (!appArgs.GetExitMessage().empty())
{
return WindowingBehaviorUseNew;
}
// TODO:projects/5
//
// In the future, we'll want to use the windowingBehavior setting to
// determine what happens when a window ID wasn't manually provided.
//
// Maybe that'd be a special return value out of here, to tell the
// monarch to do something special:
//
// -1 -> create a new window
// -2 -> find the mru, this desktop
// -3 -> MRU, any desktop (is this not just 0?)
const auto parsedTarget = appArgs.GetTargetWindow();
if (parsedTarget.has_value())
{
// parsedTarget might be -1, if the user explicitly requested -1
// (or any other negative number) on the commandline. So the set
// of possible values here is {-1, 0, +}
return *parsedTarget;
}
else
{
// If the user did not provide any value on the commandline,
// then lookup our windowing behavior to determine what to do
// now.
switch (windowingBehavior)
{
case WindowingMode::UseExisting:
return WindowingBehaviorUseExisting;
case WindowingMode::UseAnyExisting:
return WindowingBehaviorUseAnyExisting;
case WindowingMode::UseNew:
default:
return WindowingBehaviorUseNew;
}
}
}
// Any unsuccessful parse will be a new window. That new window will try
@@ -1228,7 +1273,7 @@ namespace winrt::TerminalApp::implementation
// create a new window. Then, in that new window, we'll try to set the
// StartupActions, which will again fail, returning the correct error
// message.
return -1;
return WindowingBehaviorUseNew;
}
// Method Description:

View File

@@ -8,6 +8,14 @@
#include "Jumplist.h"
#include "../../cascadia/inc/cppwinrt_utils.h"
#ifdef UNIT_TESTING
// fwdecl unittest classes
namespace TerminalAppLocalTests
{
class CommandlineTest;
};
#endif
namespace winrt::TerminalApp::implementation
{
struct AppLogic : AppLogicT<AppLogic, IInitializeWithWindow>
@@ -39,6 +47,7 @@ namespace winrt::TerminalApp::implementation
bool AlwaysOnTop() const;
Windows::Foundation::Size GetLaunchDimensions(uint32_t dpi);
bool CenterOnLaunch();
TerminalApp::InitialPosition GetInitialPosition(int64_t defaultInitialX, int64_t defaultInitialY);
winrt::Windows::UI::Xaml::ElementTheme GetRequestedTheme();
Microsoft::Terminal::Settings::Model::LaunchMode GetLaunchMode();
@@ -89,6 +98,8 @@ namespace winrt::TerminalApp::implementation
::TerminalApp::AppCommandlineArgs _appArgs;
::TerminalApp::AppCommandlineArgs _settingsAppArgs;
int _ParseArgs(winrt::array_view<const hstring>& args);
static int32_t _doFindTargetWindow(winrt::array_view<const hstring> args,
const Microsoft::Terminal::Settings::Model::WindowingMode& windowingBehavior);
void _ShowLoadErrorsDialog(const winrt::hstring& titleKey, const winrt::hstring& contentKey, HRESULT settingsLoadedResult);
void _ShowLoadWarningsDialog();
@@ -124,6 +135,10 @@ namespace winrt::TerminalApp::implementation
FORWARDED_TYPED_EVENT(AlwaysOnTopChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, AlwaysOnTopChanged);
FORWARDED_TYPED_EVENT(RaiseVisualBell, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, RaiseVisualBell);
FORWARDED_TYPED_EVENT(SetTaskbarProgress, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, SetTaskbarProgress);
#ifdef UNIT_TESTING
friend class TerminalAppLocalTests::CommandlineTest;
#endif
};
}

View File

@@ -43,6 +43,7 @@ namespace TerminalApp
Boolean AlwaysOnTop { get; };
Windows.Foundation.Size GetLaunchDimensions(UInt32 dpi);
Boolean CenterOnLaunch { get; };
InitialPosition GetInitialPosition(Int64 defaultInitialX, Int64 defaultInitialY);
Windows.UI.Xaml.ElementTheme GetRequestedTheme();

View File

@@ -14,7 +14,7 @@ namespace winrt::TerminalApp::implementation
CommandLinePaletteItem() = default;
CommandLinePaletteItem(winrt::hstring const& commandLine);
GETSET_PROPERTY(winrt::hstring, CommandLine);
WINRT_PROPERTY(winrt::hstring, CommandLine);
};
}

View File

@@ -48,12 +48,12 @@ namespace winrt::TerminalApp::implementation
void EnableTabSearchMode();
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, NoMatchesText, _PropertyChangedHandlers);
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, SearchBoxPlaceholderText, _PropertyChangedHandlers);
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, PrefixCharacter, _PropertyChangedHandlers);
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, ControlName, _PropertyChangedHandlers);
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, ParentCommandName, _PropertyChangedHandlers);
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, ParsedCommandLineText, _PropertyChangedHandlers);
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, NoMatchesText, _PropertyChangedHandlers);
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, SearchBoxPlaceholderText, _PropertyChangedHandlers);
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, PrefixCharacter, _PropertyChangedHandlers);
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, ControlName, _PropertyChangedHandlers);
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, ParentCommandName, _PropertyChangedHandlers);
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, ParsedCommandLineText, _PropertyChangedHandlers);
TYPED_EVENT(SwitchToTabRequested, winrt::TerminalApp::CommandPalette, winrt::TerminalApp::TabBase);
TYPED_EVENT(CommandLineExecutionRequested, winrt::TerminalApp::CommandPalette, winrt::hstring);

View File

@@ -32,7 +32,6 @@ the MIT License. See LICENSE in the project root for license information. -->
<local:EmptyStringVisibilityConverter x:Key="CommandKeyChordVisibilityConverter"/>
<local:EmptyStringVisibilityConverter x:Key="ParsedCommandLineTextVisibilityConverter"/>
<local:EmptyStringVisibilityConverter x:Key="ParentCommandVisibilityConverter"/>
<local:HasNestedCommandsVisibilityConverter x:Key="HasNestedCommandsVisibilityConverter"/>
<model:IconPathConverter x:Key="IconSourceConverter"/>
<DataTemplate x:Key="GeneralItemTemplate" x:DataType="local:FilteredCommand">
@@ -40,7 +39,6 @@ the MIT License. See LICENSE in the project root for license information. -->
<!-- This HorizontalContentAlignment="Stretch" is important
to make sure it takes the entire width of the line -->
<ListViewItem HorizontalContentAlignment="Stretch"
IsTabStop="False"
AutomationProperties.Name="{x:Bind Item.Name, Mode=OneWay}"
AutomationProperties.AcceleratorKey="{x:Bind Item.KeyChordText, Mode=OneWay}">
@@ -71,7 +69,12 @@ the MIT License. See LICENSE in the project root for license information. -->
<!-- The block for the key chord is only visible
when there's actual text set as the label. See
CommandKeyChordVisibilityConverter for details. -->
CommandKeyChordVisibilityConverter for details.
We're setting the accessibility view on the
border and text block to Raw because otherwise,
Narrator will read out the key chord. Problem is,
it already did that because it was the list item's
"AcceleratorKey". It's redundant. -->
<Border
Grid.Column="2"
Visibility="{x:Bind Item.KeyChordText,
@@ -80,12 +83,78 @@ the MIT License. See LICENSE in the project root for license information. -->
Style="{ThemeResource KeyChordBorderStyle}"
Padding="2,0,2,0"
HorizontalAlignment="Right"
VerticalAlignment="Center">
VerticalAlignment="Center"
AutomationProperties.AccessibilityView="Raw">
<TextBlock
Style="{ThemeResource KeyChordTextBlockStyle}"
FontSize="12"
Text="{x:Bind Item.KeyChordText, Mode=OneWay}" />
Text="{x:Bind Item.KeyChordText, Mode=OneWay}"
AutomationProperties.AccessibilityView="Raw" />
</Border>
</Grid>
</ListViewItem>
</DataTemplate>
<DataTemplate x:Key="NestedItemTemplate" x:DataType="local:FilteredCommand">
<!-- This HorizontalContentAlignment="Stretch" is important
to make sure it takes the entire width of the line -->
<ListViewItem x:Uid="CommandPalette_MoreOptions"
HorizontalContentAlignment="Stretch"
AutomationProperties.Name="{x:Bind Item.Name, Mode=OneWay}"
AutomationProperties.AcceleratorKey="{x:Bind Item.KeyChordText, Mode=OneWay}">
<Grid HorizontalAlignment="Stretch" ColumnSpacing="8" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="16"/>
<!-- icon -->
<ColumnDefinition Width="Auto"/>
<!-- command label -->
<ColumnDefinition Width="*"/>
<!-- key chord -->
<ColumnDefinition Width="16"/>
<!-- gutter for scrollbar -->
</Grid.ColumnDefinitions>
<IconSourceElement
Grid.Column="0"
Width="16"
Height="16"
IconSource="{x:Bind Item.Icon,
Mode=OneWay,
Converter={StaticResource IconSourceConverter}}"/>
<local:HighlightedTextControl
Grid.Column="1"
HorizontalAlignment="Left"
Text="{x:Bind HighlightedName, Mode=OneWay}"/>
<!-- The block for the key chord is only visible
when there's actual text set as the label. See
CommandKeyChordVisibilityConverter for details.
We're setting the accessibility view on the
border and text block to Raw because otherwise,
Narrator will read out the key chord. Problem is,
it already did that because it was the list item's
"AcceleratorKey". It's redundant. -->
<Border
Grid.Column="2"
Visibility="{x:Bind Item.KeyChordText,
Mode=OneWay,
Converter={StaticResource CommandKeyChordVisibilityConverter}}"
Style="{ThemeResource KeyChordBorderStyle}"
Padding="2,0,2,0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
AutomationProperties.AccessibilityView="Raw">
<TextBlock
Style="{ThemeResource KeyChordTextBlockStyle}"
FontSize="12"
Text="{x:Bind Item.KeyChordText, Mode=OneWay}"
AutomationProperties.AccessibilityView="Raw" />
</Border>
<!-- xE70E is ChevronUp. Rotated 90 degrees, it's _ChevronRight_ -->
@@ -93,9 +162,6 @@ the MIT License. See LICENSE in the project root for license information. -->
FontFamily="Segoe MDL2 Assets"
Glyph="&#xE70E;"
HorizontalAlignment="Right"
Visibility="{x:Bind Item,
Mode=OneWay,
Converter={StaticResource HasNestedCommandsVisibilityConverter}}"
Grid.Column="2">
<FontIcon.RenderTransform>
@@ -112,7 +178,6 @@ the MIT License. See LICENSE in the project root for license information. -->
<!-- This HorizontalContentAlignment="Stretch" is important
to make sure it takes the entire width of the line -->
<ListViewItem HorizontalContentAlignment="Stretch"
IsTabStop="False"
AutomationProperties.Name="{x:Bind Item.Name, Mode=OneWay}"
AutomationProperties.AcceleratorKey="{x:Bind Item.KeyChordText, Mode=OneWay}">
@@ -186,7 +251,10 @@ the MIT License. See LICENSE in the project root for license information. -->
</ListViewItem>
</DataTemplate>
<local:PaletteItemTemplateSelector x:Key="PaletteItemTemplateSelector" TabItemTemplate="{StaticResource TabItemTemplate}" GeneralItemTemplate="{StaticResource GeneralItemTemplate}"/>
<local:PaletteItemTemplateSelector x:Key="PaletteItemTemplateSelector"
TabItemTemplate="{StaticResource TabItemTemplate}"
GeneralItemTemplate="{StaticResource GeneralItemTemplate}"
NestedItemTemplate="{StaticResource NestedItemTemplate}"/>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Dark">

View File

@@ -25,10 +25,10 @@ namespace winrt::TerminalApp::implementation
static int Compare(winrt::TerminalApp::FilteredCommand const& first, winrt::TerminalApp::FilteredCommand const& second);
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
OBSERVABLE_GETSET_PROPERTY(winrt::TerminalApp::PaletteItem, Item, _PropertyChangedHandlers, nullptr);
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, Filter, _PropertyChangedHandlers);
OBSERVABLE_GETSET_PROPERTY(winrt::TerminalApp::HighlightedText, HighlightedName, _PropertyChangedHandlers);
OBSERVABLE_GETSET_PROPERTY(int, Weight, _PropertyChangedHandlers);
WINRT_OBSERVABLE_PROPERTY(winrt::TerminalApp::PaletteItem, Item, _PropertyChangedHandlers, nullptr);
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, Filter, _PropertyChangedHandlers);
WINRT_OBSERVABLE_PROPERTY(winrt::TerminalApp::HighlightedText, HighlightedName, _PropertyChangedHandlers);
WINRT_OBSERVABLE_PROPERTY(int, Weight, _PropertyChangedHandlers);
private:
winrt::TerminalApp::HighlightedText _computeHighlightedName();

View File

@@ -1,39 +0,0 @@
#include "pch.h"
#include "HasNestedCommandsVisibilityConverter.h"
#include "HasNestedCommandsVisibilityConverter.g.cpp"
using namespace winrt::Windows;
using namespace winrt::Windows::UI::Xaml;
namespace winrt::TerminalApp::implementation
{
// Method Description:
// - Attempt to convert something into another type. For the
// HasNestedCommandsVisibilityConverter, we're gonna check if `value` is a
// string, and try and convert it into a Visibility value. If the input
// param wasn't a string, or was the empty string, we'll return
// Visibility::Collapsed. Otherwise, we'll return Visible.
// Arguments:
// - value: the input object to attempt to convert into a Visibility.
// Return Value:
// - Visible if the object was a string and wasn't the empty string.
Foundation::IInspectable HasNestedCommandsVisibilityConverter::Convert(Foundation::IInspectable const& value,
Windows::UI::Xaml::Interop::TypeName const& /* targetType */,
Foundation::IInspectable const& /* parameter */,
hstring const& /* language */)
{
const auto paletteItem{ value.try_as<winrt::TerminalApp::ActionPaletteItem>() };
const auto& hasNestedCommands = paletteItem && paletteItem.Command().HasNestedCommands();
return winrt::box_value(hasNestedCommands ? Visibility::Visible : Visibility::Collapsed);
}
// unused for one-way bindings
Foundation::IInspectable HasNestedCommandsVisibilityConverter::ConvertBack(Foundation::IInspectable const& /* value */,
Windows::UI::Xaml::Interop::TypeName const& /* targetType */,
Foundation::IInspectable const& /* parameter */,
hstring const& /* language */)
{
throw hresult_not_implemented();
}
}

View File

@@ -1,27 +0,0 @@
#pragma once
#include "HasNestedCommandsVisibilityConverter.g.h"
#include "../inc/cppwinrt_utils.h"
namespace winrt::TerminalApp::implementation
{
struct HasNestedCommandsVisibilityConverter : HasNestedCommandsVisibilityConverterT<HasNestedCommandsVisibilityConverter>
{
HasNestedCommandsVisibilityConverter() = default;
Windows::Foundation::IInspectable Convert(Windows::Foundation::IInspectable const& value,
Windows::UI::Xaml::Interop::TypeName const& targetType,
Windows::Foundation::IInspectable const& parameter,
hstring const& language);
Windows::Foundation::IInspectable ConvertBack(Windows::Foundation::IInspectable const& value,
Windows::UI::Xaml::Interop::TypeName const& targetType,
Windows::Foundation::IInspectable const& parameter,
hstring const& language);
};
}
namespace winrt::TerminalApp::factory_implementation
{
BASIC_FACTORY(HasNestedCommandsVisibilityConverter);
}

View File

@@ -1,19 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
namespace TerminalApp
{
// See https://docs.microsoft.com/en-us/windows/uwp/data-binding/data-binding-quickstart
// We use the default attribute to declare IValueConverter as the default
// interface. In the listing, HasNestedCommandsVisibilityConverter has only a
// constructor, and no methods, so no default interface is generated for it.
// The default attribute is optimal if you won't be adding instance members
// to HasNestedCommandsVisibilityConverter, because no QueryInterface will be
// required to call the IValueConverter methods
runtimeclass HasNestedCommandsVisibilityConverter : [default] Windows.UI.Xaml.Data.IValueConverter
{
HasNestedCommandsVisibilityConverter();
};
}

View File

@@ -17,8 +17,8 @@ namespace winrt::TerminalApp::implementation
HighlightedTextSegment(winrt::hstring const& text, bool isHighlighted);
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, TextSegment, _PropertyChangedHandlers);
OBSERVABLE_GETSET_PROPERTY(bool, IsHighlighted, _PropertyChangedHandlers);
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, TextSegment, _PropertyChangedHandlers);
WINRT_OBSERVABLE_PROPERTY(bool, IsHighlighted, _PropertyChangedHandlers);
};
struct HighlightedText : HighlightedTextT<HighlightedText>
@@ -27,7 +27,7 @@ namespace winrt::TerminalApp::implementation
HighlightedText(Windows::Foundation::Collections::IObservableVector<winrt::TerminalApp::HighlightedTextSegment> const& segments);
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
OBSERVABLE_GETSET_PROPERTY(Windows::Foundation::Collections::IObservableVector<winrt::TerminalApp::HighlightedTextSegment>, Segments, _PropertyChangedHandlers);
WINRT_OBSERVABLE_PROPERTY(Windows::Foundation::Collections::IObservableVector<winrt::TerminalApp::HighlightedTextSegment>, Segments, _PropertyChangedHandlers);
};
}

View File

@@ -10,6 +10,9 @@
#include "MinMaxCloseControl.h"
#include "MinMaxCloseControl.g.cpp"
#include <LibraryResources.h>
using namespace winrt::Windows::UI::Xaml;
namespace winrt::TerminalApp::implementation
@@ -77,6 +80,7 @@ namespace winrt::TerminalApp::implementation
MinimizeButton().Height(maximizedHeight);
MaximizeButton().Height(maximizedHeight);
CloseButton().Height(maximizedHeight);
MaximizeToolTip().Text(RS_(L"WindowRestoreDownButtonToolTip"));
break;
case WindowVisualState::WindowVisualStateNormal:
@@ -87,6 +91,7 @@ namespace winrt::TerminalApp::implementation
MinimizeButton().Height(windowedHeight);
MaximizeButton().Height(windowedHeight);
CloseButton().Height(windowedHeight);
MaximizeToolTip().Text(RS_(L"WindowMaximizeButtonToolTip"));
break;
}
}

View File

@@ -188,6 +188,13 @@ the MIT License. See LICENSE in the project root for license information. -->
<x:String x:Key="CaptionButtonPathWindowMaximized">M 0 2 h 8 v 8 h -8 v -8 M 2 2 v -2 h 8 v 8 h -2</x:String>
</ResourceDictionary>
</Button.Resources>
<ToolTipService.ToolTip>
<ToolTip>
<TextBlock>
<Run x:Name="MaximizeToolTip"/>
</TextBlock>
</ToolTip>
</ToolTipService.ToolTip>
</Button>
<Button Height="{StaticResource CaptionButtonHeightWindowed}" MinWidth="46.0" Width="46.0"
x:Name="CloseButton"

View File

@@ -12,8 +12,8 @@ namespace winrt::TerminalApp::implementation
public:
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, Name, _PropertyChangedHandlers);
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, Icon, _PropertyChangedHandlers);
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, KeyChordText, _PropertyChangedHandlers);
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, Name, _PropertyChangedHandlers);
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, Icon, _PropertyChangedHandlers);
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, KeyChordText, _PropertyChangedHandlers);
};
}

View File

@@ -30,6 +30,13 @@ namespace winrt::TerminalApp::implementation
{
return TabItemTemplate();
}
else if (const auto actionPaletteItem{ filteredCommand.Item().try_as<winrt::TerminalApp::ActionPaletteItem>() })
{
if (actionPaletteItem.Command().HasNestedCommands())
{
return NestedItemTemplate();
}
}
}
return GeneralItemTemplate();

View File

@@ -15,8 +15,9 @@ namespace winrt::TerminalApp::implementation
Windows::UI::Xaml::DataTemplate SelectTemplateCore(winrt::Windows::Foundation::IInspectable const&, winrt::Windows::UI::Xaml::DependencyObject const&);
Windows::UI::Xaml::DataTemplate SelectTemplateCore(winrt::Windows::Foundation::IInspectable const&);
GETSET_PROPERTY(winrt::Windows::UI::Xaml::DataTemplate, TabItemTemplate);
GETSET_PROPERTY(winrt::Windows::UI::Xaml::DataTemplate, GeneralItemTemplate);
WINRT_PROPERTY(winrt::Windows::UI::Xaml::DataTemplate, TabItemTemplate);
WINRT_PROPERTY(winrt::Windows::UI::Xaml::DataTemplate, NestedItemTemplate);
WINRT_PROPERTY(winrt::Windows::UI::Xaml::DataTemplate, GeneralItemTemplate);
};
}

View File

@@ -8,6 +8,7 @@ namespace TerminalApp
PaletteItemTemplateSelector();
Windows.UI.Xaml.DataTemplate TabItemTemplate;
Windows.UI.Xaml.DataTemplate NestedItemTemplate;
Windows.UI.Xaml.DataTemplate GeneralItemTemplate;
}
}

View File

@@ -211,6 +211,9 @@
<data name="RenameTabText" xml:space="preserve">
<value>Rename Tab</value>
</data>
<data name="DuplicateTabText" xml:space="preserve">
<value>Duplicate Tab</value>
</data>
<data name="InvalidBackgroundImage" xml:space="preserve">
<value>Found a profile with an invalid "backgroundImage". Defaulting that profile to have no background image. Make sure that when setting a "backgroundImage", the value is a valid file path to an image.</value>
<comment>{Locked="\"backgroundImage\""}</comment>
@@ -313,6 +316,10 @@
<data name="CmdTabColorArgDesc" xml:space="preserve">
<value>Open the tab with the specified color, in #rrggbb format</value>
</data>
<data name="CmdSuppressApplicationTitleDesc" xml:space="preserve">
<value>Open the tab with tabTitle overriding default title and suppressing title change messages from the application</value>
<comment>{Locked="\"tabTitle\""}</comment>
</data>
<data name="CmdVersionDesc" xml:space="preserve">
<value>Display the application version</value>
</data>
@@ -350,6 +357,9 @@
<data name="NewPaneRun.Text" xml:space="preserve">
<value>Alt+Click to split the current window</value>
</data>
<data name="NewWindowRun.Text" xml:space="preserve">
<value>Shift+Click to open a new window</value>
</data>
<data name="WindowCloseButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Close</value>
</data>
@@ -359,9 +369,6 @@
<data name="WindowMaximizeButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Maximize</value>
</data>
<data name="WindowMaximizeButton.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
<value>Maximize</value>
</data>
<data name="WindowMinimizeButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Minimize</value>
</data>
@@ -451,7 +458,7 @@
</data>
<data name="CommandPalette_ParsedCommandLine" xml:space="preserve">
<value>Executing command line will invoke the following commands:</value>
<comment>Will be followed by a list of strings describing parsed commands</comment>
<comment>Will be followed by a list of strings describing parsed commands</comment>
</data>
<data name="CommandPalette_FailedParsingCommandLine" xml:space="preserve">
<value>Failed parsing command line:</value>
@@ -559,4 +566,13 @@
<data name="ClipboardTextHeader.Text" xml:space="preserve">
<value>Clipboard contents (preview):</value>
</data>
</root>
<data name="CommandPalette_MoreOptions.[using:Windows.UI.Xaml.Automation]AutomationProperties.HelpText" xml:space="preserve">
<value>More options</value>
</data>
<data name="WindowMaximizeButtonToolTip" xml:space="preserve">
<value>Maximize</value>
</data>
<data name="WindowRestoreDownButtonToolTip" xml:space="preserve">
<value>Restore Down</value>
</data>
</root>

View File

@@ -60,12 +60,6 @@ namespace winrt::TerminalApp::implementation
_NewTabHandlers(*this, eventArgs);
break;
}
case ShortcutAction::NewWindow:
{
_NewWindowHandlers(*this, eventArgs);
break;
}
case ShortcutAction::CloseWindow:
{
_CloseWindowHandlers(*this, eventArgs);
@@ -256,11 +250,21 @@ namespace winrt::TerminalApp::implementation
_BreakIntoDebuggerHandlers(*this, eventArgs);
break;
}
case ShortcutAction::FindMatch:
{
_FindMatchHandlers(*this, eventArgs);
break;
}
case ShortcutAction::TogglePaneReadOnly:
{
_TogglePaneReadOnlyHandlers(*this, eventArgs);
break;
}
case ShortcutAction::NewWindow:
{
_NewWindowHandlers(*this, eventArgs);
break;
}
default:
return false;
}

View File

@@ -27,7 +27,6 @@ namespace winrt::TerminalApp::implementation
TYPED_EVENT(OpenNewTabDropdown, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs);
TYPED_EVENT(DuplicateTab, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs);
TYPED_EVENT(NewTab, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs);
TYPED_EVENT(NewWindow, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs);
TYPED_EVENT(CloseWindow, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs);
TYPED_EVENT(CloseTab, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs);
TYPED_EVENT(ClosePane, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs);
@@ -65,7 +64,9 @@ namespace winrt::TerminalApp::implementation
TYPED_EVENT(TabSearch, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs);
TYPED_EVENT(MoveTab, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs);
TYPED_EVENT(BreakIntoDebugger, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs);
TYPED_EVENT(FindMatch, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs);
TYPED_EVENT(TogglePaneReadOnly, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs);
TYPED_EVENT(NewWindow, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs);
// clang-format on
private:

View File

@@ -13,7 +13,6 @@ namespace TerminalApp
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> NewTab;
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> OpenNewTabDropdown;
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> DuplicateTab;
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> NewWindow;
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> CloseWindow;
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> CloseTab;
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> ClosePane;
@@ -51,6 +50,8 @@ namespace TerminalApp
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> TabSearch;
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> MoveTab;
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> BreakIntoDebugger;
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> FindMatch;
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> TogglePaneReadOnly;
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> NewWindow;
}
}

View File

@@ -49,7 +49,7 @@ namespace winrt::TerminalApp::implementation
Controls::MenuFlyoutItem closeTabMenuItem;
Controls::FontIcon closeSymbol;
closeSymbol.FontFamily(Media::FontFamily{ L"Segoe MDL2 Assets" });
closeSymbol.Glyph(L"\xE8BB");
closeSymbol.Glyph(L"\xE711");
closeTabMenuItem.Click([weakThis](auto&&, auto&&) {
if (auto tab{ weakThis.get() })

View File

@@ -29,16 +29,16 @@ namespace winrt::TerminalApp::implementation
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
// The TabViewIndex is the index this Tab object resides in TerminalPage's _tabs vector.
GETSET_PROPERTY(uint32_t, TabViewIndex, 0);
WINRT_PROPERTY(uint32_t, TabViewIndex, 0);
// The TabViewNumTabs is the number of Tab objects in TerminalPage's _tabs vector.
GETSET_PROPERTY(uint32_t, TabViewNumTabs, 0);
WINRT_PROPERTY(uint32_t, TabViewNumTabs, 0);
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, Title, _PropertyChangedHandlers);
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, Icon, _PropertyChangedHandlers);
OBSERVABLE_GETSET_PROPERTY(bool, ReadOnly, _PropertyChangedHandlers, false);
GETSET_PROPERTY(winrt::Microsoft::UI::Xaml::Controls::TabViewItem, TabViewItem, nullptr);
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, Title, _PropertyChangedHandlers);
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, Icon, _PropertyChangedHandlers);
WINRT_OBSERVABLE_PROPERTY(bool, ReadOnly, _PropertyChangedHandlers, false);
WINRT_PROPERTY(winrt::Microsoft::UI::Xaml::Controls::TabViewItem, TabViewItem, nullptr);
OBSERVABLE_GETSET_PROPERTY(winrt::Windows::UI::Xaml::FrameworkElement, Content, _PropertyChangedHandlers, nullptr);
WINRT_OBSERVABLE_PROPERTY(winrt::Windows::UI::Xaml::FrameworkElement, Content, _PropertyChangedHandlers, nullptr);
protected:
winrt::Windows::UI::Xaml::FocusState _focusState{ winrt::Windows::UI::Xaml::FocusState::Unfocused };

View File

@@ -65,7 +65,7 @@ namespace winrt::TerminalApp::implementation
"TabRenamerOpened",
TraceLoggingDescription("Event emitted when the tab renamer is opened"),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
}
// Method Description:
@@ -93,7 +93,7 @@ namespace winrt::TerminalApp::implementation
TraceLoggingDescription("Event emitted when the tab renamer is closed"),
TraceLoggingBoolean(_renameCancelled, "CancelledRename", "True if the user cancelled the rename, false if they committed."),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
_CloseRenameBox();
if (!_renameCancelled)
@@ -106,7 +106,11 @@ namespace winrt::TerminalApp::implementation
// - Hides the rename box and displays the title text block
void TabHeaderControl::_CloseRenameBox()
{
HeaderRenamerTextBox().Visibility(Windows::UI::Xaml::Visibility::Collapsed);
HeaderTextBlock().Visibility(Windows::UI::Xaml::Visibility::Visible);
if (HeaderRenamerTextBox().Visibility() == Windows::UI::Xaml::Visibility::Visible)
{
HeaderRenamerTextBox().Visibility(Windows::UI::Xaml::Visibility::Collapsed);
HeaderTextBlock().Visibility(Windows::UI::Xaml::Visibility::Visible);
_RenameEndedHandlers(*this, nullptr);
}
}
}

View File

@@ -21,9 +21,11 @@ namespace winrt::TerminalApp::implementation
WINRT_CALLBACK(TitleChangeRequested, TerminalApp::TitleChangeRequestedArgs);
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, Title, _PropertyChangedHandlers);
OBSERVABLE_GETSET_PROPERTY(double, RenamerMaxWidth, _PropertyChangedHandlers);
OBSERVABLE_GETSET_PROPERTY(winrt::TerminalApp::TerminalTabStatus, TabStatus, _PropertyChangedHandlers);
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, Title, _PropertyChangedHandlers);
WINRT_OBSERVABLE_PROPERTY(double, RenamerMaxWidth, _PropertyChangedHandlers);
WINRT_OBSERVABLE_PROPERTY(winrt::TerminalApp::TerminalTabStatus, TabStatus, _PropertyChangedHandlers);
TYPED_EVENT(RenameEnded, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
private:
bool _receivedKeyDown{ false };

View File

@@ -17,5 +17,6 @@ namespace TerminalApp
TerminalTabStatus TabStatus { get; set; };
event TitleChangeRequestedArgs TitleChangeRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> RenameEnded;
}
}

View File

@@ -19,7 +19,7 @@ namespace winrt::TerminalApp::implementation
return _tab.get();
}
OBSERVABLE_GETSET_PROPERTY(winrt::TerminalApp::TerminalTabStatus, TabStatus, _PropertyChangedHandlers);
WINRT_OBSERVABLE_PROPERTY(winrt::TerminalApp::TerminalTabStatus, TabStatus, _PropertyChangedHandlers);
private:
winrt::weak_ref<winrt::TerminalApp::TabBase> _tab;

View File

@@ -37,10 +37,11 @@ the MIT License. See LICENSE in the project root for license information. -->
<ToolTipService.ToolTip>
<ToolTip Placement="Mouse">
<TextBlock IsTextSelectionEnabled="False">
<Run x:Uid="NewTabRun"/> <LineBreak />
<Run x:Uid="NewTabRun"/> <LineBreak/>
<Run x:Uid="NewPaneRun"
FontStyle="Italic">
</Run>
FontStyle="Italic"/> <LineBreak/>
<Run x:Uid="NewWindowRun"
FontStyle="Italic"/>
</TextBlock>
</ToolTip>
</ToolTipService.ToolTip>

View File

@@ -124,9 +124,6 @@
<ClInclude Include="EmptyStringVisibilityConverter.h">
<DependentUpon>EmptyStringVisibilityConverter.idl</DependentUpon>
</ClInclude>
<ClInclude Include="HasNestedCommandsVisibilityConverter.h">
<DependentUpon>HasNestedCommandsVisibilityConverter.idl</DependentUpon>
</ClInclude>
<ClInclude Include="Pane.h" />
<ClInclude Include="ColorHelper.h" />
<ClInclude Include="TerminalSettings.h">
@@ -204,9 +201,6 @@
<ClCompile Include="EmptyStringVisibilityConverter.cpp">
<DependentUpon>EmptyStringVisibilityConverter.idl</DependentUpon>
</ClCompile>
<ClCompile Include="HasNestedCommandsVisibilityConverter.cpp">
<DependentUpon>HasNestedCommandsVisibilityConverter.idl</DependentUpon>
</ClCompile>
<ClCompile Include="Pane.cpp" />
<ClCompile Include="Pane.LayoutSizeNode.cpp" />
<ClCompile Include="ColorHelper.cpp" />
@@ -293,7 +287,6 @@
</Midl>
<Midl Include="FilteredCommand.idl" />
<Midl Include="EmptyStringVisibilityConverter.idl" />
<Midl Include="HasNestedCommandsVisibilityConverter.idl" />
<Midl Include="TerminalSettings.idl" />
</ItemGroup>
<!-- ========================= Misc Files ======================== -->

View File

@@ -93,21 +93,27 @@ namespace winrt::TerminalApp::implementation
winrt::fire_and_forget TerminalPage::SetSettings(CascadiaSettings settings, bool needRefreshUI)
{
_settings = settings;
if (needRefreshUI)
{
_RefreshUIForSettingsReload();
}
// Upon settings update we reload the system settings for scrolling as well.
// TODO: consider reloading this value periodically.
_systemRowsToScroll = _ReadSystemRowsToScroll();
auto weakThis{ get_weak() };
co_await winrt::resume_foreground(Dispatcher());
if (auto page{ weakThis.get() })
{
// Make sure to _UpdateCommandsForPalette before
// _RefreshUIForSettingsReload. _UpdateCommandsForPalette will make
// sure the KeyChordText of Commands is updated, which needs to
// happen before the Settings UI is reloaded and tries to re-read
// those values.
_UpdateCommandsForPalette();
CommandPalette().SetKeyMap(_settings.KeyMap());
if (needRefreshUI)
{
_RefreshUIForSettingsReload();
}
// Upon settings update we reload the system settings for scrolling as well.
// TODO: consider reloading this value periodically.
_systemRowsToScroll = _ReadSystemRowsToScroll();
}
}
@@ -135,8 +141,7 @@ namespace winrt::TerminalApp::implementation
}
CATCH_LOG();
_tabRow.PointerMoved({ this, &TerminalPage::_RestorePointerCursorHandler });
_tabRow.PointerMoved({ get_weak(), &TerminalPage::_RestorePointerCursorHandler });
_tabView.CanReorderTabs(!isElevated);
_tabView.CanDragTabs(!isElevated);
@@ -201,6 +206,13 @@ namespace winrt::TerminalApp::implementation
const bool altPressed = WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) ||
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);
const auto shiftState{ window.GetKeyState(VirtualKey::Shift) };
const auto rShiftState = window.GetKeyState(VirtualKey::RightShift);
const auto lShiftState = window.GetKeyState(VirtualKey::LeftShift);
const auto shiftPressed{ WI_IsFlagSet(shiftState, CoreVirtualKeyStates::Down) ||
WI_IsFlagSet(lShiftState, CoreVirtualKeyStates::Down) ||
WI_IsFlagSet(rShiftState, CoreVirtualKeyStates::Down) };
// Check for DebugTap
bool debugTap = page->_settings.GlobalSettings().DebugFeaturesEnabled() &&
WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) &&
@@ -213,6 +225,10 @@ namespace winrt::TerminalApp::implementation
0.5f,
nullptr);
}
else if (shiftPressed && !debugTap)
{
page->_OpenNewWindow(false, NewTerminalArgs());
}
else
{
page->_OpenNewTab(nullptr);
@@ -261,7 +277,11 @@ namespace winrt::TerminalApp::implementation
// Store cursor, so we can restore it, e.g., after mouse vanishing
// (we'll need to adapt this logic once we make cursor context aware)
_defaultPointerCursor = CoreWindow::GetForCurrentThread().PointerCursor();
try
{
_defaultPointerCursor = CoreWindow::GetForCurrentThread().PointerCursor();
}
CATCH_LOG();
}
// Method Description:
@@ -577,11 +597,16 @@ namespace winrt::TerminalApp::implementation
auto newPaneRun = WUX::Documents::Run();
newPaneRun.Text(RS_(L"NewPaneRun/Text"));
newPaneRun.FontStyle(FontStyle::Italic);
auto newWindowRun = WUX::Documents::Run();
newWindowRun.Text(RS_(L"NewWindowRun/Text"));
newWindowRun.FontStyle(FontStyle::Italic);
auto textBlock = WUX::Controls::TextBlock{};
textBlock.Inlines().Append(newTabRun);
textBlock.Inlines().Append(WUX::Documents::LineBreak{});
textBlock.Inlines().Append(newPaneRun);
textBlock.Inlines().Append(WUX::Documents::LineBreak{});
textBlock.Inlines().Append(newWindowRun);
auto toolTip = WUX::Controls::ToolTip{};
toolTip.Content(textBlock);
@@ -599,6 +624,13 @@ namespace winrt::TerminalApp::implementation
const bool altPressed = WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) ||
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);
const auto shiftState{ window.GetKeyState(VirtualKey::Shift) };
const auto rShiftState = window.GetKeyState(VirtualKey::RightShift);
const auto lShiftState = window.GetKeyState(VirtualKey::LeftShift);
const auto shiftPressed{ WI_IsFlagSet(shiftState, CoreVirtualKeyStates::Down) ||
WI_IsFlagSet(lShiftState, CoreVirtualKeyStates::Down) ||
WI_IsFlagSet(rShiftState, CoreVirtualKeyStates::Down) };
// Check for DebugTap
bool debugTap = page->_settings.GlobalSettings().DebugFeaturesEnabled() &&
WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) &&
@@ -611,6 +643,12 @@ namespace winrt::TerminalApp::implementation
0.5f,
newTerminalArgs);
}
else if (shiftPressed && !debugTap)
{
// Manually fill in the evaluated profile.
newTerminalArgs.Profile(::Microsoft::Console::Utils::GuidToString(page->_settings.GetProfileForArgs(newTerminalArgs)));
page->_OpenNewWindow(false, newTerminalArgs);
}
else
{
page->_OpenNewTab(newTerminalArgs);
@@ -649,7 +687,7 @@ namespace winrt::TerminalApp::implementation
settingsItem.Click({ this, &TerminalPage::_SettingsButtonOnClick });
newTabFlyout.Items().Append(settingsItem);
Microsoft::Terminal::Settings::Model::OpenSettingsArgs args{ SettingsTarget::SettingsFile };
Microsoft::Terminal::Settings::Model::OpenSettingsArgs args{ SettingsTarget::SettingsUI };
Microsoft::Terminal::Settings::Model::ActionAndArgs settingsAction{ ShortcutAction::OpenSettings, args };
const auto settingsKeyChord{ keyBindings.GetKeyBindingForActionWithArgs(settingsAction) };
if (settingsKeyChord)
@@ -838,6 +876,16 @@ namespace winrt::TerminalApp::implementation
}
});
newTabImpl->DuplicateRequested([weakTab, weakThis{ get_weak() }]() {
auto page{ weakThis.get() };
auto tab{ weakTab.get() };
if (page && tab)
{
page->_DuplicateTab(*tab);
}
});
auto tabViewItem = newTabImpl->TabViewItem();
_tabView.TabItems().Append(tabViewItem);
@@ -858,9 +906,22 @@ namespace winrt::TerminalApp::implementation
}
});
newTabImpl->TabRenamerDeactivated([weakThis{ get_weak() }](auto&& /*s*/, auto&& /*e*/) {
if (const auto page{ weakThis.get() })
{
if (!page->_newTabButton.Flyout().IsOpen())
{
if (const auto tab{ page->_GetFocusedTab() })
{
tab.Focus(FocusState::Programmatic);
}
}
}
});
if (debugConnection) // this will only be set if global debugging is on and tap is active
{
TermControl newControl{ settings, debugConnection };
TermControl newControl{ *(winrt::get_self<TerminalSettings>(settings)->CreateChild()), debugConnection };
_RegisterTerminalEvents(newControl, *newTabImpl);
// Split (auto) with the debug tap.
newTabImpl->SplitPane(SplitState::Automatic, 0.5f, profileGuid, newControl);
@@ -967,12 +1028,30 @@ namespace winrt::TerminalApp::implementation
const RoutedEventArgs&)
{
const CoreWindow window = CoreWindow::GetForCurrentThread();
const auto rAltState = window.GetKeyState(VirtualKey::RightMenu);
const auto lAltState = window.GetKeyState(VirtualKey::LeftMenu);
const bool altPressed = WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) ||
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);
const auto target = altPressed ? SettingsTarget::DefaultsFile : SettingsTarget::SettingsFile;
// check alt state
const auto rAltState{ window.GetKeyState(VirtualKey::RightMenu) };
const auto lAltState{ window.GetKeyState(VirtualKey::LeftMenu) };
const bool altPressed{ WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) ||
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down) };
// check shift state
const auto shiftState{ window.GetKeyState(VirtualKey::Shift) };
const auto lShiftState{ window.GetKeyState(VirtualKey::LeftShift) };
const auto rShiftState{ window.GetKeyState(VirtualKey::RightShift) };
const auto shiftPressed{ WI_IsFlagSet(shiftState, CoreVirtualKeyStates::Down) ||
WI_IsFlagSet(lShiftState, CoreVirtualKeyStates::Down) ||
WI_IsFlagSet(rShiftState, CoreVirtualKeyStates::Down) };
auto target{ SettingsTarget::SettingsUI };
if (shiftPressed)
{
target = SettingsTarget::SettingsFile;
}
else if (altPressed)
{
target = SettingsTarget::DefaultsFile;
}
_LaunchSettings(target);
}
@@ -1115,7 +1194,9 @@ namespace winrt::TerminalApp::implementation
_actionDispatch->TabSearch({ this, &TerminalPage::_HandleOpenTabSearch });
_actionDispatch->MoveTab({ this, &TerminalPage::_HandleMoveTab });
_actionDispatch->BreakIntoDebugger({ this, &TerminalPage::_HandleBreakIntoDebugger });
_actionDispatch->FindMatch({ this, &TerminalPage::_HandleFindMatch });
_actionDispatch->TogglePaneReadOnly({ this, &TerminalPage::_HandleTogglePaneReadOnly });
_actionDispatch->NewWindow({ this, &TerminalPage::_HandleNewWindow });
}
// Method Description:
@@ -1186,43 +1267,52 @@ namespace winrt::TerminalApp::implementation
// Method Description:
// - Duplicates the current focused tab
void TerminalPage::_DuplicateTabViewItem()
void TerminalPage::_DuplicateFocusedTab()
{
if (const auto terminalTab{ _GetFocusedTabImpl() })
{
try
{
// TODO: GH#5047 - In the future, we should get the Profile of
// the focused pane, and use that to build a new instance of the
// settings so we can duplicate this tab/pane.
//
// Currently, if the profile doesn't exist anymore in our
// settings, we'll silently do nothing.
//
// In the future, it will be preferable to just duplicate the
// current control's settings, but we can't do that currently,
// because we won't be able to create a new instance of the
// connection without keeping an instance of the original Profile
// object around.
const auto& profileGuid = terminalTab->GetFocusedProfile();
if (profileGuid.has_value())
{
const auto settings{ winrt::make<TerminalSettings>(_settings, profileGuid.value(), *_bindings) };
const auto workingDirectory = terminalTab->GetActiveTerminalControl().WorkingDirectory();
const auto validWorkingDirectory = !workingDirectory.empty();
if (validWorkingDirectory)
{
settings.StartingDirectory(workingDirectory);
}
_CreateNewTabFromSettings(profileGuid.value(), settings);
}
}
CATCH_LOG();
_DuplicateTab(*terminalTab);
}
}
// Method Description:
// - Duplicates specified tab
// Arguments:
// - tab: tab to duplicate
void TerminalPage::_DuplicateTab(const TerminalTab& tab)
{
try
{
// TODO: GH#5047 - In the future, we should get the Profile of
// the focused pane, and use that to build a new instance of the
// settings so we can duplicate this tab/pane.
//
// Currently, if the profile doesn't exist anymore in our
// settings, we'll silently do nothing.
//
// In the future, it will be preferable to just duplicate the
// current control's settings, but we can't do that currently,
// because we won't be able to create a new instance of the
// connection without keeping an instance of the original Profile
// object around.
const auto& profileGuid = tab.GetFocusedProfile();
if (profileGuid.has_value())
{
const auto settings{ winrt::make<TerminalSettings>(_settings, profileGuid.value(), *_bindings) };
const auto workingDirectory = tab.GetActiveTerminalControl().WorkingDirectory();
const auto validWorkingDirectory = !workingDirectory.empty();
if (validWorkingDirectory)
{
settings.StartingDirectory(workingDirectory);
}
_CreateNewTabFromSettings(profileGuid.value(), settings);
}
}
CATCH_LOG();
}
// Method Description:
// - Look for the index of the input tabView in the tabs vector,
// and call _RemoveTab
@@ -1373,8 +1463,8 @@ namespace winrt::TerminalApp::implementation
// Add an event handler for when the terminal wants to set a progress indicator on the taskbar
term.SetTaskbarProgress({ this, &TerminalPage::_SetTaskbarProgressHandler });
term.HidePointerCursor({ this, &TerminalPage::_HidePointerCursorHandler });
term.RestorePointerCursor({ this, &TerminalPage::_RestorePointerCursorHandler });
term.HidePointerCursor({ get_weak(), &TerminalPage::_HidePointerCursorHandler });
term.RestorePointerCursor({ get_weak(), &TerminalPage::_RestorePointerCursorHandler });
// Bind Tab events to the TermControl and the Tab's Pane
hostingTab.Initialize(term);
@@ -1823,7 +1913,7 @@ namespace winrt::TerminalApp::implementation
return;
}
TermControl newControl{ controlSettings, controlConnection };
TermControl newControl{ *(winrt::get_self<TerminalSettings>(controlSettings)->CreateChild()), controlConnection };
// Hookup our event handlers to the new terminal
_RegisterTerminalEvents(newControl, *focusedTab);
@@ -2131,7 +2221,7 @@ namespace winrt::TerminalApp::implementation
try
{
auto parsed = winrt::Windows::Foundation::Uri(eventArgs.Uri().c_str());
if (parsed.SchemeName() == L"http" || parsed.SchemeName() == L"https")
if (_IsUriSupported(parsed))
{
ShellExecute(nullptr, L"open", eventArgs.Uri().c_str(), nullptr, nullptr, SW_SHOWNORMAL);
}
@@ -2168,6 +2258,37 @@ namespace winrt::TerminalApp::implementation
}
}
// Method Description:
// - Determines if the given URI is currently supported
// Arguments:
// - The parsed URI
// Return value:
// - True if we support it, false otherwise
bool TerminalPage::_IsUriSupported(const winrt::Windows::Foundation::Uri& parsedUri)
{
if (parsedUri.SchemeName() == L"http" || parsedUri.SchemeName() == L"https")
{
return true;
}
if (parsedUri.SchemeName() == L"file")
{
const auto host = parsedUri.Host();
// If no hostname was provided or if the hostname was "localhost", Host() will return an empty string
// and we allow it
if (host == L"")
{
return true;
}
// TODO: by the OSC 8 spec, if a hostname (other than localhost) is provided, we _should_ be
// comparing that value against what is returned by GetComputerNameExW and making sure they match.
// However, ShellExecute does not seem to be happy with file URIs of the form
// file://{hostname}/path/to/file.ext
// and so while we could do the hostname matching, we do not know how to actually open the URI
// if its given in that form. So for now we ignore all hostnames other than localhost
}
return false;
}
void TerminalPage::_ControlNoticeRaisedHandler(const IInspectable /*sender*/, const Microsoft::Terminal::TerminalControl::NoticeEventArgs eventArgs)
{
winrt::hstring message = eventArgs.Message();
@@ -3182,8 +3303,15 @@ namespace winrt::TerminalApp::implementation
{
if (_shouldMouseVanish && !_isMouseHidden)
{
CoreWindow::GetForCurrentThread().PointerCursor(nullptr);
_isMouseHidden = true;
if (auto window{ CoreWindow::GetForCurrentThread() })
{
try
{
window.PointerCursor(nullptr);
_isMouseHidden = true;
}
CATCH_LOG();
}
}
}
@@ -3195,8 +3323,15 @@ namespace winrt::TerminalApp::implementation
{
if (_isMouseHidden)
{
CoreWindow::GetForCurrentThread().PointerCursor(_defaultPointerCursor);
_isMouseHidden = false;
if (auto window{ CoreWindow::GetForCurrentThread() })
{
try
{
window.PointerCursor(_defaultPointerCursor);
_isMouseHidden = false;
}
CATCH_LOG();
}
}
}

View File

@@ -155,6 +155,8 @@ namespace winrt::TerminalApp::implementation
void _CreateNewTabFromSettings(GUID profileGuid, TerminalApp::TerminalSettings settings);
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _CreateConnectionFromSettings(GUID profileGuid, TerminalApp::TerminalSettings settings);
winrt::fire_and_forget _OpenNewWindow(const bool elevate, const Microsoft::Terminal::Settings::Model::NewTerminalArgs newTerminalArgs);
bool _displayingCloseDialog{ false };
void _SettingsButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
void _FeedbackButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
@@ -176,7 +178,8 @@ namespace winrt::TerminalApp::implementation
Windows::Foundation::Collections::IVectorView<Microsoft::Terminal::Settings::Model::Profile> profiles,
Windows::Foundation::Collections::IMapView<winrt::hstring, Microsoft::Terminal::Settings::Model::ColorScheme> schemes);
void _DuplicateTabViewItem();
void _DuplicateFocusedTab();
void _DuplicateTab(const TerminalTab& tab);
void _RemoveTabViewItem(const Microsoft::UI::Xaml::Controls::TabViewItem& tabViewItem);
winrt::Windows::Foundation::IAsyncAction _RemoveTab(winrt::TerminalApp::TabBase tab);
winrt::fire_and_forget _RemoveTabs(const std::vector<winrt::TerminalApp::TabBase> tabs);
@@ -217,6 +220,8 @@ namespace winrt::TerminalApp::implementation
const Microsoft::Terminal::TerminalControl::PasteFromClipboardEventArgs eventArgs);
void _OpenHyperlinkHandler(const IInspectable sender, const Microsoft::Terminal::TerminalControl::OpenHyperlinkEventArgs eventArgs);
bool _IsUriSupported(const winrt::Windows::Foundation::Uri& parsedUri);
void _ShowCouldNotOpenDialog(winrt::hstring reason, winrt::hstring uri);
bool _CopyText(const bool singleLine, const Windows::Foundation::IReference<Microsoft::Terminal::TerminalControl::CopyFormat>& formats);
@@ -315,7 +320,9 @@ namespace winrt::TerminalApp::implementation
void _HandleOpenTabSearch(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args);
void _HandleMoveTab(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args);
void _HandleBreakIntoDebugger(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args);
void _HandleFindMatch(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args);
void _HandleTogglePaneReadOnly(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args);
void _HandleNewWindow(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args);
// Make sure to hook new actions up in _RegisterActionCallbacks!
#pragma endregion

View File

@@ -104,6 +104,10 @@ namespace winrt::TerminalApp::implementation
{
settings.StartingTabColor(static_cast<uint32_t>(til::color(newTerminalArgs.TabColor().Value())));
}
if (newTerminalArgs.SuppressApplicationTitle())
{
settings.SuppressApplicationTitle(newTerminalArgs.SuppressApplicationTitle().Value());
}
}
return { profileGuid, settings };

View File

@@ -50,23 +50,24 @@ namespace winrt::TerminalApp::implementation
void ColorTable(std::array<uint32_t, 16> colors);
std::array<uint32_t, 16> ColorTable();
GETSET_SETTING(uint32_t, DefaultForeground, DEFAULT_FOREGROUND_WITH_ALPHA);
GETSET_SETTING(uint32_t, DefaultBackground, DEFAULT_BACKGROUND_WITH_ALPHA);
GETSET_SETTING(uint32_t, SelectionBackground, DEFAULT_FOREGROUND);
GETSET_SETTING(int32_t, HistorySize, DEFAULT_HISTORY_SIZE);
GETSET_SETTING(int32_t, InitialRows, 30);
GETSET_SETTING(int32_t, InitialCols, 80);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, uint32_t, DefaultForeground, DEFAULT_FOREGROUND_WITH_ALPHA);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, uint32_t, DefaultBackground, DEFAULT_BACKGROUND_WITH_ALPHA);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, uint32_t, SelectionBackground, DEFAULT_FOREGROUND);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, int32_t, HistorySize, DEFAULT_HISTORY_SIZE);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, int32_t, InitialRows, 30);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, int32_t, InitialCols, 80);
GETSET_SETTING(bool, SnapOnInput, true);
GETSET_SETTING(bool, AltGrAliasing, true);
GETSET_SETTING(uint32_t, CursorColor, DEFAULT_CURSOR_COLOR);
GETSET_SETTING(Microsoft::Terminal::TerminalControl::CursorStyle, CursorShape, Microsoft::Terminal::TerminalControl::CursorStyle::Vintage);
GETSET_SETTING(uint32_t, CursorHeight, DEFAULT_CURSOR_HEIGHT);
GETSET_SETTING(hstring, WordDelimiters, DEFAULT_WORD_DELIMITERS);
GETSET_SETTING(bool, CopyOnSelect, false);
GETSET_SETTING(bool, FocusFollowMouse, false);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, bool, SnapOnInput, true);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, bool, AltGrAliasing, true);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, uint32_t, CursorColor, DEFAULT_CURSOR_COLOR);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, Microsoft::Terminal::TerminalControl::CursorStyle, CursorShape, Microsoft::Terminal::TerminalControl::CursorStyle::Vintage);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, uint32_t, CursorHeight, DEFAULT_CURSOR_HEIGHT);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, hstring, WordDelimiters, DEFAULT_WORD_DELIMITERS);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, bool, CopyOnSelect, false);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, bool, InputServiceWarning, true);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, bool, FocusFollowMouse, false);
GETSET_SETTING(Windows::Foundation::IReference<uint32_t>, TabColor, nullptr);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, Windows::Foundation::IReference<uint32_t>, TabColor, nullptr);
// When set, StartingTabColor allows to create a terminal with a "sticky" tab color.
// This color is prioritized above the TabColor (that is usually initialized based on profile settings).
@@ -76,50 +77,44 @@ namespace winrt::TerminalApp::implementation
// TODO: to ensure that this property is not populated during settings reload,
// we should consider moving this property to a separate interface,
// passed to the terminal only upon creation.
GETSET_SETTING(Windows::Foundation::IReference<uint32_t>, StartingTabColor, nullptr);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, Windows::Foundation::IReference<uint32_t>, StartingTabColor, nullptr);
// ------------------------ End of Core Settings -----------------------
GETSET_SETTING(hstring, ProfileName);
GETSET_SETTING(bool, UseAcrylic, false);
GETSET_SETTING(double, TintOpacity, 0.5);
GETSET_SETTING(hstring, Padding, DEFAULT_PADDING);
GETSET_SETTING(hstring, FontFace, DEFAULT_FONT_FACE);
GETSET_SETTING(int32_t, FontSize, DEFAULT_FONT_SIZE);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, hstring, ProfileName);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, bool, UseAcrylic, false);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, double, TintOpacity, 0.5);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, hstring, Padding, DEFAULT_PADDING);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, hstring, FontFace, DEFAULT_FONT_FACE);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, int32_t, FontSize, DEFAULT_FONT_SIZE);
GETSET_SETTING(winrt::Windows::UI::Text::FontWeight, FontWeight);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, winrt::Windows::UI::Text::FontWeight, FontWeight);
GETSET_SETTING(hstring, BackgroundImage);
GETSET_SETTING(double, BackgroundImageOpacity, 1.0);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, hstring, BackgroundImage);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, double, BackgroundImageOpacity, 1.0);
GETSET_SETTING(winrt::Windows::UI::Xaml::Media::Stretch,
BackgroundImageStretchMode,
winrt::Windows::UI::Xaml::Media::Stretch::UniformToFill);
GETSET_SETTING(winrt::Windows::UI::Xaml::HorizontalAlignment,
BackgroundImageHorizontalAlignment,
winrt::Windows::UI::Xaml::HorizontalAlignment::Center);
GETSET_SETTING(winrt::Windows::UI::Xaml::VerticalAlignment,
BackgroundImageVerticalAlignment,
winrt::Windows::UI::Xaml::VerticalAlignment::Center);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, winrt::Windows::UI::Xaml::Media::Stretch, BackgroundImageStretchMode, winrt::Windows::UI::Xaml::Media::Stretch::UniformToFill);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, winrt::Windows::UI::Xaml::HorizontalAlignment, BackgroundImageHorizontalAlignment, winrt::Windows::UI::Xaml::HorizontalAlignment::Center);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, winrt::Windows::UI::Xaml::VerticalAlignment, BackgroundImageVerticalAlignment, winrt::Windows::UI::Xaml::VerticalAlignment::Center);
GETSET_SETTING(Microsoft::Terminal::TerminalControl::IKeyBindings, KeyBindings, nullptr);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, Microsoft::Terminal::TerminalControl::IKeyBindings, KeyBindings, nullptr);
GETSET_SETTING(hstring, Commandline);
GETSET_SETTING(hstring, StartingDirectory);
GETSET_SETTING(hstring, StartingTitle);
GETSET_SETTING(bool, SuppressApplicationTitle);
GETSET_SETTING(hstring, EnvironmentVariables);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, hstring, Commandline);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, hstring, StartingDirectory);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, hstring, StartingTitle);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, bool, SuppressApplicationTitle);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, hstring, EnvironmentVariables);
GETSET_SETTING(Microsoft::Terminal::TerminalControl::ScrollbarState, ScrollState, Microsoft::Terminal::TerminalControl::ScrollbarState::Visible);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, Microsoft::Terminal::TerminalControl::ScrollbarState, ScrollState, Microsoft::Terminal::TerminalControl::ScrollbarState::Visible);
GETSET_SETTING(Microsoft::Terminal::TerminalControl::TextAntialiasingMode, AntialiasingMode, Microsoft::Terminal::TerminalControl::TextAntialiasingMode::Grayscale);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, Microsoft::Terminal::TerminalControl::TextAntialiasingMode, AntialiasingMode, Microsoft::Terminal::TerminalControl::TextAntialiasingMode::Grayscale);
GETSET_SETTING(bool, RetroTerminalEffect, false);
GETSET_SETTING(bool, ForceFullRepaintRendering, false);
GETSET_SETTING(bool, SoftwareRendering, false);
GETSET_SETTING(bool, ForceVTInput, false);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, bool, RetroTerminalEffect, false);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, bool, ForceFullRepaintRendering, false);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, bool, SoftwareRendering, false);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, bool, ForceVTInput, false);
GETSET_PROPERTY(hstring, PixelShaderPath);
INHERITABLE_SETTING(TerminalApp::TerminalSettings, hstring, PixelShaderPath);
private:
std::optional<std::array<uint32_t, COLOR_TABLE_SIZE>> _ColorTable;

View File

@@ -104,15 +104,21 @@ namespace winrt::TerminalApp::implementation
if (auto tab{ weakThis.get() })
{
const auto settings{ winrt::TerminalApp::implementation::AppLogic::CurrentAppSettings() };
if (settings.GlobalSettings().TabWidthMode() == winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode::SizeToContent)
try
{
tab->_headerControl.RenamerMaxWidth(HeaderRenameBoxWidthTitleLength);
}
else
{
tab->_headerControl.RenamerMaxWidth(HeaderRenameBoxWidthDefault);
// Make sure to try/catch this, because the LocalTests won't be
// able to use this helper.
const auto settings{ winrt::TerminalApp::implementation::AppLogic::CurrentAppSettings() };
if (settings.GlobalSettings().TabWidthMode() == winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode::SizeToContent)
{
tab->_headerControl.RenamerMaxWidth(HeaderRenameBoxWidthTitleLength);
}
else
{
tab->_headerControl.RenamerMaxWidth(HeaderRenameBoxWidthDefault);
}
}
CATCH_LOG()
}
}
@@ -353,6 +359,7 @@ namespace winrt::TerminalApp::implementation
// Update the control to reflect the changed title
_headerControl.Title(activeTitle);
Automation::AutomationProperties::SetName(tab->TabViewItem(), activeTitle);
_UpdateToolTip();
}
}
@@ -372,7 +379,7 @@ namespace winrt::TerminalApp::implementation
co_await winrt::resume_foreground(control.Dispatcher());
const auto currentOffset = control.GetScrollOffset();
control.ScrollViewport(currentOffset + delta);
control.ScrollViewport(::base::ClampAdd(currentOffset, delta));
}
// Method Description:
@@ -609,6 +616,19 @@ namespace winrt::TerminalApp::implementation
tab->_RecalculateAndApplyReadOnly();
}
});
control.FocusFollowMouseRequested([weakThis](auto&& sender, auto&&) {
if (const auto tab{ weakThis.get() })
{
if (tab->_focusState != FocusState::Unfocused)
{
if (const auto termControl{ sender.try_as<winrt::Microsoft::Terminal::TerminalControl::TermControl>() })
{
termControl.Focus(FocusState::Pointer);
}
}
}
});
}
// Method Description:
@@ -722,25 +742,26 @@ namespace winrt::TerminalApp::implementation
}
});
// Add a PaneRaiseVisualBell event handler to the Pane. When the pane emits this event,
// we need to bubble it all the way to app host. In this part of the chain we bubble it
// from the hosting tab to the page.
// Add a PaneRaiseBell event handler to the Pane
pane->PaneRaiseBell([weakThis](auto&& /*s*/, auto&& visual) {
if (auto tab{ weakThis.get() })
{
if (visual)
{
// If visual is set, we need to bubble this event all the way to app host to flash the taskbar
// In this part of the chain we bubble it from the hosting tab to the page
tab->_TabRaiseVisualBellHandlers();
}
tab->ShowBellIndicator(true);
// Show the bell indicator in the tab header
tab->ShowBellIndicator(true);
// If this tab is focused, activate the bell indicator timer, which will
// remove the bell indicator once it fires
// (otherwise, the indicator is removed when the tab gets focus)
if (tab->_focusState != WUX::FocusState::Unfocused)
{
tab->ActivateBellIndicatorTimer();
}
// If this tab is focused, activate the bell indicator timer, which will
// remove the bell indicator once it fires
// (otherwise, the indicator is removed when the tab gets focus)
if (tab->_focusState != WUX::FocusState::Unfocused)
{
tab->ActivateBellIndicatorTimer();
}
}
});
@@ -820,11 +841,29 @@ namespace winrt::TerminalApp::implementation
renameTabMenuItem.Icon(renameTabSymbol);
}
Controls::MenuFlyoutItem duplicateTabMenuItem;
{
// "Duplicate Tab"
Controls::FontIcon duplicateTabSymbol;
duplicateTabSymbol.FontFamily(Media::FontFamily{ L"Segoe MDL2 Assets" });
duplicateTabSymbol.Glyph(L"\xF5ED");
duplicateTabMenuItem.Click([weakThis](auto&&, auto&&) {
if (auto tab{ weakThis.get() })
{
tab->_DuplicateRequestedHandlers();
}
});
duplicateTabMenuItem.Text(RS_(L"DuplicateTabText"));
duplicateTabMenuItem.Icon(duplicateTabSymbol);
}
// Build the menu
Controls::MenuFlyout newTabFlyout;
Controls::MenuFlyoutSeparator menuSeparator;
newTabFlyout.Items().Append(chooseColorMenuItem);
newTabFlyout.Items().Append(renameTabMenuItem);
newTabFlyout.Items().Append(duplicateTabMenuItem);
newTabFlyout.Items().Append(menuSeparator);
newTabFlyout.Items().Append(_CreateCloseSubMenu());
newTabFlyout.Items().Append(closeTabMenuItem);
@@ -1182,4 +1221,5 @@ namespace winrt::TerminalApp::implementation
DEFINE_EVENT(TerminalTab, ColorSelected, _colorSelected, winrt::delegate<winrt::Windows::UI::Color>);
DEFINE_EVENT(TerminalTab, ColorCleared, _colorCleared, winrt::delegate<>);
DEFINE_EVENT(TerminalTab, TabRaiseVisualBell, _TabRaiseVisualBellHandlers, winrt::delegate<>);
DEFINE_EVENT(TerminalTab, DuplicateRequested, _DuplicateRequestedHandlers, winrt::delegate<>);
}

View File

@@ -89,6 +89,8 @@ namespace winrt::TerminalApp::implementation
DECLARE_EVENT(ColorSelected, _colorSelected, winrt::delegate<winrt::Windows::UI::Color>);
DECLARE_EVENT(ColorCleared, _colorCleared, winrt::delegate<>);
DECLARE_EVENT(TabRaiseVisualBell, _TabRaiseVisualBellHandlers, winrt::delegate<>);
DECLARE_EVENT(DuplicateRequested, _DuplicateRequestedHandlers, winrt::delegate<>);
FORWARDED_TYPED_EVENT(TabRenamerDeactivated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, (&_headerControl), RenameEnded);
private:
std::shared_ptr<Pane> _rootPane{ nullptr };
@@ -142,6 +144,8 @@ namespace winrt::TerminalApp::implementation
void _RecalculateAndApplyReadOnly();
void _DuplicateTab();
friend class ::TerminalAppLocalTests::TabTests;
};
}

View File

@@ -13,12 +13,12 @@ namespace winrt::TerminalApp::implementation
TerminalTabStatus() = default;
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
OBSERVABLE_GETSET_PROPERTY(bool, IsPaneZoomed, _PropertyChangedHandlers);
OBSERVABLE_GETSET_PROPERTY(bool, IsProgressRingActive, _PropertyChangedHandlers);
OBSERVABLE_GETSET_PROPERTY(bool, IsProgressRingIndeterminate, _PropertyChangedHandlers);
OBSERVABLE_GETSET_PROPERTY(bool, BellIndicator, _PropertyChangedHandlers);
OBSERVABLE_GETSET_PROPERTY(bool, IsReadOnlyActive, _PropertyChangedHandlers);
OBSERVABLE_GETSET_PROPERTY(uint32_t, ProgressValue, _PropertyChangedHandlers);
WINRT_OBSERVABLE_PROPERTY(bool, IsPaneZoomed, _PropertyChangedHandlers);
WINRT_OBSERVABLE_PROPERTY(bool, IsProgressRingActive, _PropertyChangedHandlers);
WINRT_OBSERVABLE_PROPERTY(bool, IsProgressRingIndeterminate, _PropertyChangedHandlers);
WINRT_OBSERVABLE_PROPERTY(bool, BellIndicator, _PropertyChangedHandlers);
WINRT_OBSERVABLE_PROPERTY(bool, IsReadOnlyActive, _PropertyChangedHandlers);
WINRT_OBSERVABLE_PROPERTY(uint32_t, ProgressValue, _PropertyChangedHandlers);
};
}

View File

@@ -15,7 +15,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
public:
CursorPositionEventArgs() = default;
GETSET_PROPERTY(Windows::Foundation::Point, CurrentPosition);
WINRT_PROPERTY(Windows::Foundation::Point, CurrentPosition);
};
struct FontInfoEventArgs :
@@ -24,11 +24,11 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
public:
FontInfoEventArgs() = default;
GETSET_PROPERTY(Windows::Foundation::Size, FontSize);
WINRT_PROPERTY(Windows::Foundation::Size, FontSize);
GETSET_PROPERTY(winrt::hstring, FontFace);
WINRT_PROPERTY(winrt::hstring, FontFace);
GETSET_PROPERTY(Windows::UI::Text::FontWeight, FontWeight);
WINRT_PROPERTY(Windows::UI::Text::FontWeight, FontWeight);
};
struct TSFInputControl : TSFInputControlT<TSFInputControl>

View File

@@ -79,6 +79,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
_blinkTimer{},
_lastMouseClickTimestamp{},
_lastMouseClickPos{},
_lastMouseClickPosNoSelection{},
_selectionNeedsToBeCopied{ false },
_searchBox{ nullptr }
{
@@ -87,6 +88,9 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
_terminal = std::make_unique<::Microsoft::Terminal::Core::Terminal>();
// GH#8969: pre-seed working directory to prevent potential races
_terminal->SetWorkingDirectory(_settings.StartingDirectory());
auto pfnWarningBell = std::bind(&TermControl::_TerminalWarningBell, this);
_terminal->SetWarningBellCallback(pfnWarningBell);
@@ -189,7 +193,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
_autoScrollTimer.Interval(AutoScrollUpdateInterval);
_autoScrollTimer.Tick({ this, &TermControl::_UpdateAutoScroll });
_ApplyUISettings();
_ApplyUISettings(_settings);
}
// Method Description:
@@ -225,6 +229,18 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
}
}
void TermControl::SearchMatch(const bool goForward)
{
if (!_searchBox)
{
CreateSearchBoxControl();
}
else
{
_Search(_searchBox->TextBox().Text(), goForward, false);
}
}
// Method Description:
// - Search text in text buffer. This is triggered if the user click
// search button or press enter.
@@ -234,7 +250,9 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// - caseSensitive: boolean that represents if the current search is case sensitive
// Return Value:
// - <none>
void TermControl::_Search(const winrt::hstring& text, const bool goForward, const bool caseSensitive)
void TermControl::_Search(const winrt::hstring& text,
const bool goForward,
const bool caseSensitive)
{
if (text.size() == 0 || _closing)
{
@@ -267,7 +285,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// - RoutedEventArgs: not used
// Return Value:
// - <none>
void TermControl::_CloseSearchBoxControl(const winrt::Windows::Foundation::IInspectable& /*sender*/, RoutedEventArgs const& /*args*/)
void TermControl::_CloseSearchBoxControl(const winrt::Windows::Foundation::IInspectable& /*sender*/,
RoutedEventArgs const& /*args*/)
{
_searchBox->Visibility(Visibility::Collapsed);
@@ -277,10 +296,72 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// Method Description:
// - Given new settings for this profile, applies the settings to the current terminal.
// - This method is separate from UpdateSettings because there is an apparent optimizer
// issue that causes one of our hstring -> wstring_view conversions to result in garbage,
// but only from a coroutine context. See GH#8723.
// - INVARIANT: This method must be called from the UI thread.
// Arguments:
// - <none>
// Return Value:
// - <none>
void TermControl::_UpdateSettingsOnUIThread()
{
if (_closing)
{
return;
}
auto lock = _terminal->LockForWriting();
// Update our control settings
_ApplyUISettings(_settings);
// Update the terminal core with its new Core settings
_terminal->UpdateSettings(_settings);
if (!_initializedTerminal)
{
// If we haven't initialized, there's no point in continuing.
// Initialization will handle the renderer settings.
return;
}
// Update DxEngine settings under the lock
_renderEngine->SetSelectionBackground(_settings.SelectionBackground());
_renderEngine->SetRetroTerminalEffect(_settings.RetroTerminalEffect());
_renderEngine->SetPixelShaderPath(_settings.PixelShaderPath());
_renderEngine->SetForceFullRepaintRendering(_settings.ForceFullRepaintRendering());
_renderEngine->SetSoftwareRendering(_settings.SoftwareRendering());
switch (_settings.AntialiasingMode())
{
case TextAntialiasingMode::Cleartype:
_renderEngine->SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE);
break;
case TextAntialiasingMode::Aliased:
_renderEngine->SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE_ALIASED);
break;
case TextAntialiasingMode::Grayscale:
default:
_renderEngine->SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE);
break;
}
// Refresh our font with the renderer
const auto actualFontOldSize = _actualFont.GetSize();
_UpdateFont();
const auto actualFontNewSize = _actualFont.GetSize();
if (actualFontNewSize != actualFontOldSize)
{
_RefreshSizeUnderLock();
}
}
// Method Description:
// - Given Settings having been updated, applies the settings to the current terminal.
// Return Value:
// - <none>
winrt::fire_and_forget TermControl::UpdateSettings()
{
auto weakThis{ get_weak() };
@@ -292,49 +373,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// If 'weakThis' is locked, then we can safely work with 'this'
if (auto control{ weakThis.get() })
{
if (_closing)
{
co_return;
}
// Update our control settings
_ApplyUISettings();
// Update the terminal core with its new Core settings
_terminal->UpdateSettings(_settings);
auto lock = _terminal->LockForWriting();
// Update DxEngine settings under the lock
_renderEngine->SetSelectionBackground(_settings.SelectionBackground());
_renderEngine->SetRetroTerminalEffect(_settings.RetroTerminalEffect());
_renderEngine->SetPixelShaderPath(_settings.PixelShaderPath());
_renderEngine->SetForceFullRepaintRendering(_settings.ForceFullRepaintRendering());
_renderEngine->SetSoftwareRendering(_settings.SoftwareRendering());
switch (_settings.AntialiasingMode())
{
case TextAntialiasingMode::Cleartype:
_renderEngine->SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE);
break;
case TextAntialiasingMode::Aliased:
_renderEngine->SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE_ALIASED);
break;
case TextAntialiasingMode::Grayscale:
default:
_renderEngine->SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE);
break;
}
// Refresh our font with the renderer
const auto actualFontOldSize = _actualFont.GetSize();
_UpdateFont();
const auto actualFontNewSize = _actualFont.GetSize();
if (actualFontNewSize != actualFontOldSize)
{
_RefreshSizeUnderLock();
}
_UpdateSettingsOnUIThread();
}
}
@@ -384,21 +423,21 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// - <none>
// Return Value:
// - <none>
void TermControl::_ApplyUISettings()
void TermControl::_ApplyUISettings(const IControlSettings& newSettings)
{
_InitializeBackgroundBrush();
COLORREF bg = _settings.DefaultBackground();
COLORREF bg = newSettings.DefaultBackground();
_BackgroundColorChanged(bg);
// Apply padding as swapChainPanel's margin
auto newMargin = _ParseThicknessFromPadding(_settings.Padding());
auto newMargin = _ParseThicknessFromPadding(newSettings.Padding());
SwapChainPanel().Margin(newMargin);
// Initialize our font information.
const auto fontFace = _settings.FontFace();
const short fontHeight = gsl::narrow_cast<short>(_settings.FontSize());
const auto fontWeight = _settings.FontWeight();
const auto fontFace = newSettings.FontFace();
const short fontHeight = gsl::narrow_cast<short>(newSettings.FontSize());
const auto fontWeight = newSettings.FontWeight();
// The font width doesn't terribly matter, we'll only be using the
// height to look it up
// The other params here also largely don't matter.
@@ -410,12 +449,12 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// set TSF Foreground
Media::SolidColorBrush foregroundBrush{};
foregroundBrush.Color(static_cast<til::color>(_settings.DefaultForeground()));
foregroundBrush.Color(static_cast<til::color>(newSettings.DefaultForeground()));
TSFInputControl().Foreground(foregroundBrush);
TSFInputControl().Margin(newMargin);
// Apply settings for scrollbar
if (_settings.ScrollState() == ScrollbarState::Hidden)
if (newSettings.ScrollState() == ScrollbarState::Hidden)
{
// In the scenario where the user has turned off the OS setting to automatically hide scrollbars, the
// Terminal scrollbar would still be visible; so, we need to set the control's visibility accordingly to
@@ -1098,7 +1137,13 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// modifier key. We'll wait for a real keystroke to dismiss the
// GH #7395 - don't dismiss selection when taking PrintScreen
// selection.
if (_terminal->IsSelectionActive() && !KeyEvent::IsModifierKey(vkey) && vkey != VK_SNAPSHOT)
// GH#8522, GH#3758 - Only dismiss the selection on key _down_. If we
// dismiss on key up, then there's chance that we'll immediately dismiss
// a selection created by an action bound to a keydown.
if (_terminal->IsSelectionActive() &&
!KeyEvent::IsModifierKey(vkey) &&
vkey != VK_SNAPSHOT &&
keyDown)
{
const CoreWindow window = CoreWindow::GetForCurrentThread();
const auto leftWinKeyState = window.GetKeyState(VirtualKey::LeftWindows);
@@ -1301,35 +1346,55 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
mode = ::Terminal::SelectionExpansionMode::Line;
}
// Update the selection appropriately
if (ctrlEnabled && multiClickMapper == 1 &&
!(_terminal->GetHyperlinkAtPosition(terminalPosition).empty()))
{
_HyperlinkHandler(_terminal->GetHyperlinkAtPosition(terminalPosition));
}
else if (shiftEnabled && _terminal->IsSelectionActive())
{
// Shift+Click: only set expand on the "end" selection point
_terminal->SetSelectionEnd(terminalPosition, mode);
_selectionNeedsToBeCopied = true;
}
else if (mode == ::Terminal::SelectionExpansionMode::Cell && !shiftEnabled)
{
// Single Click: reset the selection and begin a new one
_terminal->ClearSelection();
_singleClickTouchdownPos = cursorPosition;
_selectionNeedsToBeCopied = false; // there's no selection, so there's nothing to update
}
else
{
// Multi-Click Selection: expand both "start" and "end" selection points
_terminal->MultiClickSelection(terminalPosition, mode);
_selectionNeedsToBeCopied = true;
}
// Update the selection appropriately
_lastMouseClickTimestamp = point.Timestamp();
_lastMouseClickPos = cursorPosition;
_renderer->TriggerSelection();
// Capture the position of the first click when no selection is active
if (mode == ::Terminal::SelectionExpansionMode::Cell && !_terminal->IsSelectionActive())
{
_singleClickTouchdownPos = cursorPosition;
_lastMouseClickPosNoSelection = cursorPosition;
}
// We reset the active selection if one of the conditions apply:
// - shift is not held
// - GH#9384: the position is the same as of the first click starting the selection
// (we need to reset selection on double-click or triple-click, so it captures the word or the line,
// rather than extending the selection)
if (_terminal->IsSelectionActive() && (!shiftEnabled || _lastMouseClickPosNoSelection == cursorPosition))
{
// Reset the selection
_terminal->ClearSelection();
_selectionNeedsToBeCopied = false; // there's no selection, so there's nothing to update
}
if (shiftEnabled)
{
if (_terminal->IsSelectionActive())
{
// If there is a selection we extend it using the selection mode
// (expand the "end"selection point)
_terminal->SetSelectionEnd(terminalPosition, mode);
}
else
{
// If there is no selection we establish it using the selected mode
// (expand both "start" and "end" selection points)
_terminal->MultiClickSelection(terminalPosition, mode);
}
_selectionNeedsToBeCopied = true;
}
_lastMouseClickTimestamp = point.Timestamp();
_lastMouseClickPos = cursorPosition;
_renderer->TriggerSelection();
}
}
else if (point.Properties().IsRightButtonPressed())
{
@@ -1376,7 +1441,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
if (!_focused && _settings.FocusFollowMouse())
{
Focus(FocusState::Pointer);
_FocusFollowMouseRequestedHandlers(*this, nullptr);
}
if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse || ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Pen)
@@ -1435,48 +1500,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
}
}
if (terminalPosition != _lastHoveredCell)
{
const auto uri = _terminal->GetHyperlinkAtPosition(terminalPosition);
if (!uri.empty())
{
// Update the tooltip with the URI
HoveredUri().Text(uri);
// Set the border thickness so it covers the entire cell
const auto charSizeInPixels = CharacterDimensions();
const auto htInDips = charSizeInPixels.Height / SwapChainPanel().CompositionScaleY();
const auto wtInDips = charSizeInPixels.Width / SwapChainPanel().CompositionScaleX();
const Thickness newThickness{ wtInDips, htInDips, 0, 0 };
HyperlinkTooltipBorder().BorderThickness(newThickness);
// Compute the location of the top left corner of the cell in DIPS
const til::size marginsInDips{ til::math::rounding, GetPadding().Left, GetPadding().Top };
const til::point startPos{ terminalPosition.X, terminalPosition.Y };
const til::size fontSize{ _actualFont.GetSize() };
const til::point posInPixels{ startPos * fontSize };
const til::point posInDIPs{ posInPixels / SwapChainPanel().CompositionScaleX() };
const til::point locationInDIPs{ posInDIPs + marginsInDips };
// Move the border to the top left corner of the cell
OverlayCanvas().SetLeft(HyperlinkTooltipBorder(), (locationInDIPs.x() - SwapChainPanel().ActualOffset().x));
OverlayCanvas().SetTop(HyperlinkTooltipBorder(), (locationInDIPs.y() - SwapChainPanel().ActualOffset().y));
}
_lastHoveredCell = terminalPosition;
const auto newId = _terminal->GetHyperlinkIdAtPosition(terminalPosition);
const auto newInterval = _terminal->GetHyperlinkIntervalFromPosition(terminalPosition);
// If the hyperlink ID changed or the interval changed, trigger a redraw all
// (so this will happen both when we move onto a link and when we move off a link)
if (newId != _lastHoveredId || (newInterval != _lastHoveredInterval))
{
_lastHoveredId = newId;
_lastHoveredInterval = newInterval;
_renderEngine->UpdateHyperlinkHoveredId(newId);
_renderer->UpdateLastHoveredInterval(newInterval);
_renderer->TriggerRedrawAll();
}
}
_UpdateHoveredCell(terminalPosition);
}
else if (_focused && ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Touch && _touchAnchor)
{
@@ -3303,6 +3327,74 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
}
}
// Method description:
// - Updates last hovered cell, renders / removes rendering of hyper-link if required
// Arguments:
// - terminalPosition: The terminal position of the pointer
void TermControl::_UpdateHoveredCell(const std::optional<COORD>& terminalPosition)
{
if (terminalPosition == _lastHoveredCell)
{
return;
}
_lastHoveredCell = terminalPosition;
if (terminalPosition.has_value())
{
const auto uri = _terminal->GetHyperlinkAtPosition(*terminalPosition);
if (!uri.empty())
{
// Update the tooltip with the URI
HoveredUri().Text(uri);
// Set the border thickness so it covers the entire cell
const auto charSizeInPixels = CharacterDimensions();
const auto htInDips = charSizeInPixels.Height / SwapChainPanel().CompositionScaleY();
const auto wtInDips = charSizeInPixels.Width / SwapChainPanel().CompositionScaleX();
const Thickness newThickness{ wtInDips, htInDips, 0, 0 };
HyperlinkTooltipBorder().BorderThickness(newThickness);
// Compute the location of the top left corner of the cell in DIPS
const til::size marginsInDips{ til::math::rounding, GetPadding().Left, GetPadding().Top };
const til::point startPos{ terminalPosition->X, terminalPosition->Y };
const til::size fontSize{ _actualFont.GetSize() };
const til::point posInPixels{ startPos * fontSize };
const til::point posInDIPs{ posInPixels / SwapChainPanel().CompositionScaleX() };
const til::point locationInDIPs{ posInDIPs + marginsInDips };
// Move the border to the top left corner of the cell
OverlayCanvas().SetLeft(HyperlinkTooltipBorder(), (locationInDIPs.x() - SwapChainPanel().ActualOffset().x));
OverlayCanvas().SetTop(HyperlinkTooltipBorder(), (locationInDIPs.y() - SwapChainPanel().ActualOffset().y));
}
}
const uint16_t newId = terminalPosition.has_value() ? _terminal->GetHyperlinkIdAtPosition(*terminalPosition) : 0u;
const auto newInterval = terminalPosition.has_value() ? _terminal->GetHyperlinkIntervalFromPosition(*terminalPosition) : std::nullopt;
// If the hyperlink ID changed or the interval changed, trigger a redraw all
// (so this will happen both when we move onto a link and when we move off a link)
if (newId != _lastHoveredId || (newInterval != _lastHoveredInterval))
{
_lastHoveredId = newId;
_lastHoveredInterval = newInterval;
_renderEngine->UpdateHyperlinkHoveredId(newId);
_renderer->UpdateLastHoveredInterval(newInterval);
_renderer->TriggerRedrawAll();
}
}
// Method Description:
// - Handle a mouse exited event, specifically clearing last hovered cell
// and removing selection from hyper link if exists
// Arguments:
// - sender: not used
// - args: event data
void TermControl::_PointerExitedHandler(Windows::Foundation::IInspectable const& /*sender*/, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& /*e*/)
{
_UpdateHoveredCell(std::nullopt);
}
// -------------------------------- WinRT Events ---------------------------------
// Winrt events need a method for adding a callback to the event and removing the callback.
// These macros will define them both for you.

View File

@@ -133,6 +133,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
void CreateSearchBoxControl();
void SearchMatch(const bool goForward);
bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down);
bool OnMouseWheel(const Windows::Foundation::Point location, const int32_t delta, const bool leftButtonDown, const bool midButtonDown, const bool rightButtonDown);
@@ -186,6 +188,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
TYPED_EVENT(HidePointerCursor, IInspectable, IInspectable);
TYPED_EVENT(RestorePointerCursor, IInspectable, IInspectable);
TYPED_EVENT(ReadOnlyChanged, IInspectable, IInspectable);
TYPED_EVENT(FocusFollowMouseRequested, IInspectable, IInspectable);
// clang-format on
private:
@@ -244,7 +247,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
std::optional<winrt::Windows::Foundation::Point> _touchAnchor;
// Track the last cell we hovered over (used in pointerMovedHandler)
COORD _lastHoveredCell;
std::optional<COORD> _lastHoveredCell;
// Track the last hyperlink ID we hovered over
uint16_t _lastHoveredId;
@@ -258,6 +261,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
unsigned int _multiClickCounter;
Timestamp _lastMouseClickTimestamp;
std::optional<winrt::Windows::Foundation::Point> _lastMouseClickPos;
std::optional<winrt::Windows::Foundation::Point> _lastMouseClickPosNoSelection;
std::optional<winrt::Windows::Foundation::Point> _singleClickTouchdownPos;
// This field tracks whether the selection has changed meaningfully
// since it was last copied. It's generally used to prevent copyOnSelect
@@ -269,7 +273,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
bool _isReadOnly{ false };
void _ApplyUISettings();
void _ApplyUISettings(const IControlSettings&);
void _UpdateSettingsOnUIThread();
void _UpdateSystemParameterSettings() noexcept;
void _InitializeBackgroundBrush();
winrt::fire_and_forget _BackgroundColorChanged(const COLORREF color);
@@ -283,6 +288,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
void _PointerPressedHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e);
void _PointerMovedHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e);
void _PointerReleasedHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e);
void _PointerExitedHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e);
void _MouseWheelHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e);
void _ScrollbarChangeHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Controls::Primitives::RangeBaseValueChangedEventArgs const& e);
void _GotFocusHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e);
@@ -341,10 +347,11 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
void _CompositionCompleted(winrt::hstring text);
void _CurrentCursorPositionHandler(const IInspectable& sender, const CursorPositionEventArgs& eventArgs);
void _FontInfoHandler(const IInspectable& sender, const FontInfoEventArgs& eventArgs);
winrt::fire_and_forget _AsyncCloseConnection();
winrt::fire_and_forget _RaiseReadOnlyWarning();
void _UpdateHoveredCell(const std::optional<COORD>& terminalPosition);
};
}

View File

@@ -102,6 +102,8 @@ namespace Microsoft.Terminal.TerminalControl
void CreateSearchBoxControl();
void SearchMatch(Boolean goForward);
void AdjustFontSize(Int32 fontSizeDelta);
void ResetFontSize();
@@ -120,5 +122,6 @@ namespace Microsoft.Terminal.TerminalControl
Boolean ReadOnly { get; };
void ToggleReadOnly();
event Windows.Foundation.TypedEventHandler<Object, Object> ReadOnlyChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> FocusFollowMouseRequested;
}
}

View File

@@ -45,7 +45,8 @@ the MIT License. See LICENSE in the project root for license information. -->
Background="Transparent"
PointerPressed="_PointerPressedHandler"
PointerMoved="_PointerMovedHandler"
PointerReleased="_PointerReleasedHandler">
PointerReleased="_PointerReleasedHandler"
PointerExited="_PointerExitedHandler">
<SwapChainPanel x:Name="SwapChainPanel"
SizeChanged="_SwapChainSizeChanged"

View File

@@ -70,6 +70,9 @@ namespace Microsoft::Terminal::Core
virtual bool SetWorkingDirectory(std::wstring_view uri) noexcept = 0;
virtual std::wstring_view GetWorkingDirectory() noexcept = 0;
virtual bool PushGraphicsRendition(const ::Microsoft::Console::VirtualTerminal::VTParameters options) noexcept = 0;
virtual bool PopGraphicsRendition() noexcept = 0;
protected:
ITerminalApi() = default;
};

View File

@@ -6,6 +6,7 @@
#include <conattrs.hpp>
#include "../../buffer/out/textBuffer.hpp"
#include "../../types/inc/sgrStack.hpp"
#include "../../renderer/inc/BlinkingState.hpp"
#include "../../terminal/parser/StateMachine.hpp"
#include "../../terminal/input/terminalInput.hpp"
@@ -124,6 +125,10 @@ public:
bool SetTaskbarProgress(const size_t state, const size_t progress) noexcept override;
bool SetWorkingDirectory(std::wstring_view uri) noexcept override;
std::wstring_view GetWorkingDirectory() noexcept override;
bool PushGraphicsRendition(const ::Microsoft::Console::VirtualTerminal::VTParameters options) noexcept override;
bool PopGraphicsRendition() noexcept override;
#pragma endregion
#pragma region ITerminalInput
@@ -347,6 +352,8 @@ private:
COORD _ConvertToBufferCell(const COORD viewportPos) const;
#pragma endregion
Microsoft::Console::VirtualTerminal::SgrStack _sgrStack;
#ifdef UNIT_TESTING
friend class TerminalCoreUnitTests::TerminalBufferTests;
friend class TerminalCoreUnitTests::TerminalApiTest;

View File

@@ -640,3 +640,31 @@ std::wstring_view Terminal::GetWorkingDirectory() noexcept
{
return _workingDirectory;
}
// Method Description:
// - Saves the current text attributes to an internal stack.
// Arguments:
// - options, cOptions: if present, specify which portions of the current text attributes
// should be saved. Only a small subset of GraphicsOptions are actually supported;
// others are ignored. If no options are specified, all attributes are stored.
// Return Value:
// - true
bool Terminal::PushGraphicsRendition(const VTParameters options) noexcept
{
_sgrStack.Push(_buffer->GetCurrentAttributes(), options);
return true;
}
// Method Description:
// - Restores text attributes from the internal stack. If only portions of text attributes
// were saved, combines those with the current attributes.
// Arguments:
// - <none>
// Return Value:
// - true
bool Terminal::PopGraphicsRendition() noexcept
{
const TextAttribute current = _buffer->GetCurrentAttributes();
_buffer->SetCurrentAttributes(_sgrStack.Pop(current));
return true;
}

View File

@@ -18,6 +18,9 @@ public:
bool SetGraphicsRendition(const ::Microsoft::Console::VirtualTerminal::VTParameters options) noexcept override;
bool PushGraphicsRendition(const ::Microsoft::Console::VirtualTerminal::VTParameters options) noexcept override;
bool PopGraphicsRendition() noexcept override;
bool CursorPosition(const size_t line,
const size_t column) noexcept override; // CUP

View File

@@ -276,3 +276,13 @@ bool TerminalDispatch::SetGraphicsRendition(const VTParameters options) noexcept
_terminalApi.SetTextAttributes(attr);
return true;
}
bool TerminalDispatch::PushGraphicsRendition(const VTParameters options) noexcept
{
return _terminalApi.PushGraphicsRendition(options);
}
bool TerminalDispatch::PopGraphicsRendition() noexcept
{
return _terminalApi.PopGraphicsRendition();
}

View File

@@ -54,7 +54,7 @@ std::vector<SMALL_RECT> Terminal::_GetSelectionRects() const noexcept
try
{
return _buffer->GetTextRects(_selection->start, _selection->end, _blockSelection);
return _buffer->GetTextRects(_selection->start, _selection->end, _blockSelection, false);
}
CATCH_LOG();
return result;

View File

@@ -0,0 +1,61 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "Actions.h"
#include "Actions.g.cpp"
#include "ActionsPageNavigationState.g.cpp"
#include "EnumEntry.h"
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::System;
using namespace winrt::Windows::UI::Core;
using namespace winrt::Windows::UI::Xaml::Navigation;
using namespace winrt::Microsoft::Terminal::Settings::Model;
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
Actions::Actions()
{
InitializeComponent();
_filteredActions = winrt::single_threaded_observable_vector<winrt::Microsoft::Terminal::Settings::Model::Command>();
}
void Actions::OnNavigatedTo(const NavigationEventArgs& e)
{
_State = e.Parameter().as<Editor::ActionsPageNavigationState>();
for (const auto& [k, command] : _State.Settings().GlobalSettings().Commands())
{
// Filter out nested commands, and commands that aren't bound to a
// key. This page is currently just for displaying the actions that
// _are_ bound to keys.
if (command.HasNestedCommands() || command.KeyChordText().empty())
{
continue;
}
_filteredActions.Append(command);
}
}
Collections::IObservableVector<Command> Actions::FilteredActions()
{
return _filteredActions;
}
void Actions::_OpenSettingsClick(const IInspectable& /*sender*/,
const Windows::UI::Xaml::RoutedEventArgs& /*eventArgs*/)
{
const CoreWindow window = CoreWindow::GetForCurrentThread();
const auto rAltState = window.GetKeyState(VirtualKey::RightMenu);
const auto lAltState = window.GetKeyState(VirtualKey::LeftMenu);
const bool altPressed = WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) ||
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);
const auto target = altPressed ? SettingsTarget::DefaultsFile : SettingsTarget::SettingsFile;
_State.RequestOpenJson(target);
}
}

View File

@@ -0,0 +1,49 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include "Actions.g.h"
#include "ActionsPageNavigationState.g.h"
#include "Utils.h"
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
struct ActionsPageNavigationState : ActionsPageNavigationStateT<ActionsPageNavigationState>
{
public:
ActionsPageNavigationState(const Model::CascadiaSettings& settings) :
_Settings{ settings } {}
void RequestOpenJson(const Model::SettingsTarget target)
{
_OpenJsonHandlers(nullptr, target);
}
WINRT_PROPERTY(Model::CascadiaSettings, Settings, nullptr)
TYPED_EVENT(OpenJson, Windows::Foundation::IInspectable, Model::SettingsTarget);
};
struct Actions : ActionsT<Actions>
{
public:
Actions();
void OnNavigatedTo(const winrt::Windows::UI::Xaml::Navigation::NavigationEventArgs& e);
Windows::Foundation::Collections::IObservableVector<winrt::Microsoft::Terminal::Settings::Model::Command> FilteredActions();
WINRT_PROPERTY(Editor::ActionsPageNavigationState, State, nullptr);
private:
friend struct ActionsT<Actions>; // for Xaml to bind events
Windows::Foundation::Collections::IObservableVector<winrt::Microsoft::Terminal::Settings::Model::Command> _filteredActions{ nullptr };
void _OpenSettingsClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
};
}
namespace winrt::Microsoft::Terminal::Settings::Editor::factory_implementation
{
BASIC_FACTORY(Actions);
}

View File

@@ -0,0 +1,23 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import "EnumEntry.idl";
namespace Microsoft.Terminal.Settings.Editor
{
runtimeclass ActionsPageNavigationState
{
Microsoft.Terminal.Settings.Model.CascadiaSettings Settings;
void RequestOpenJson(Microsoft.Terminal.Settings.Model.SettingsTarget target);
event Windows.Foundation.TypedEventHandler<Object, Microsoft.Terminal.Settings.Model.SettingsTarget> OpenJson;
};
[default_interface] runtimeclass Actions : Windows.UI.Xaml.Controls.Page
{
Actions();
ActionsPageNavigationState State { get; };
IObservableVector<Microsoft.Terminal.Settings.Model.Command> FilteredActions { get; };
}
}

View File

@@ -0,0 +1,190 @@
<!-- Copyright (c) Microsoft Corporation. All rights reserved. Licensed under
the MIT License. See LICENSE in the project root for license information. -->
<Page
x:Class="Microsoft.Terminal.Settings.Editor.Actions"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Microsoft.Terminal.Settings.Editor"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
xmlns:SettingsModel="using:Microsoft.Terminal.Settings.Model"
mc:Ignorable="d">
<Page.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="CommonResources.xaml"/>
</ResourceDictionary.MergedDictionaries>
<local:StringIsEmptyConverter x:Key="CommandKeyChordVisibilityConverter"/>
<!-- Template for actions. This is _heavily_ copied from the command
palette, with modifications:
* We don't need to use a HighlightedTextControl, because we're
not filtering this list
* We don't need the chevron for nested commands
* We're not displaying the icon
* We're binding directly to a Command, not a FilteredCommand
If we wanted to reuse the command palette's list more directly,
that's theoretically possible, but then it would need to be
lifted out of TerminalApp and either moved into the
TerminalSettingsEditor or moved to it's own project consumed by
both TSE and TerminalApp.
-->
<DataTemplate x:Key="GeneralItemTemplate" x:DataType="SettingsModel:Command">
<!-- This HorizontalContentAlignment="Stretch" is important
to make sure it takes the entire width of the line -->
<ListViewItem HorizontalContentAlignment="Stretch"
AutomationProperties.Name="{x:Bind Name, Mode=OneWay}"
AutomationProperties.AcceleratorKey="{x:Bind KeyChordText, Mode=OneWay}">
<Grid HorizontalAlignment="Stretch" ColumnSpacing="8" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="16"/>
<!-- icon -->
<ColumnDefinition Width="Auto"/>
<!-- command label -->
<ColumnDefinition Width="*"/>
<!-- key chord -->
<ColumnDefinition Width="32"/>
<!-- gutter for scrollbar -->
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="1"
HorizontalAlignment="Left"
Text="{x:Bind Name, Mode=OneWay}"/>
<!-- The block for the key chord is only visible
when there's actual text set as the label. See
CommandKeyChordVisibilityConverter for details.
Inexplicably, we don't need to set the
AutomationProperties to Raw here, unlike in the
CommandPalette. We're not quite sure why.-->
<Border Grid.Column="2"
Visibility="{x:Bind KeyChordText,
Mode=OneWay,
Converter={StaticResource CommandKeyChordVisibilityConverter}}"
Style="{ThemeResource KeyChordBorderStyle}"
Padding="2,0,2,0"
HorizontalAlignment="Right"
VerticalAlignment="Center">
<TextBlock Style="{ThemeResource KeyChordTextBlockStyle}"
FontSize="12"
Text="{x:Bind KeyChordText, Mode=OneWay}" />
</Border>
</Grid>
</ListViewItem>
</DataTemplate>
<!-- These resources again, HEAVILY copied from the command palette -->
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Dark">
<!-- TextBox colors !-->
<SolidColorBrush x:Key="TextControlBackground" Color="#333333"/>
<SolidColorBrush x:Key="TextBoxPlaceholderTextThemeBrush" Color="#B5B5B5"/>
<SolidColorBrush x:Key="TextControlForeground" Color="#B5B5B5"/>
<SolidColorBrush x:Key="TextControlBorderBrush" Color="#404040"/>
<SolidColorBrush x:Key="TextControlButtonForeground" Color="#B5B5B5"/>
<SolidColorBrush x:Key="TextControlBackgroundPointerOver" Color="#404040"/>
<SolidColorBrush x:Key="TextControlForegroundPointerOver" Color="#FFFFFF"/>
<SolidColorBrush x:Key="TextControlBorderBrushPointerOver" Color="#404040"/>
<SolidColorBrush x:Key="TextControlButtonForegroundPointerOver" Color="#FF4343"/>
<SolidColorBrush x:Key="TextControlBackgroundFocused" Color="#333333"/>
<SolidColorBrush x:Key="TextControlForegroundFocused" Color="#FFFFFF"/>
<SolidColorBrush x:Key="TextControlBorderBrushFocused" Color="#404040"/>
<SolidColorBrush x:Key="TextControlButtonForegroundPressed" Color="#FFFFFF"/>
<SolidColorBrush x:Key="TextControlButtonBackgroundPressed" Color="#FF4343"/>
<!-- KeyChordText styles -->
<Style x:Key="KeyChordBorderStyle" TargetType="Border">
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="1" />
<Setter Property="Background" Value="{ThemeResource SystemAltMediumLowColor}" />
<Setter Property="BorderBrush" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
</Style>
<Style x:Key="KeyChordTextBlockStyle" TargetType="TextBlock">
<Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
</Style>
</ResourceDictionary>
<ResourceDictionary x:Key="Light">
<!-- TextBox colors !-->
<SolidColorBrush x:Key="TextControlBackground" Color="#CCCCCC"/>
<SolidColorBrush x:Key="TextBoxPlaceholderTextThemeBrush" Color="#636363"/>
<SolidColorBrush x:Key="TextControlBorderBrush" Color="#636363"/>
<SolidColorBrush x:Key="TextControlButtonForeground" Color="#636363"/>
<SolidColorBrush x:Key="TextControlBackgroundPointerOver" Color="#DADADA"/>
<SolidColorBrush x:Key="TextControlBorderBrushPointerOver" Color="#636363"/>
<SolidColorBrush x:Key="TextControlButtonForegroundPointerOver" Color="#FF4343"/>
<SolidColorBrush x:Key="TextControlBackgroundFocused" Color="#CCCCCC"/>
<SolidColorBrush x:Key="TextControlBorderBrushFocused" Color="#636363"/>
<SolidColorBrush x:Key="TextControlButtonForegroundPressed" Color="#FFFFFF"/>
<SolidColorBrush x:Key="TextControlButtonBackgroundPressed" Color="#FF4343"/>
<!-- KeyChordText styles -->
<Style x:Key="KeyChordBorderStyle" TargetType="Border">
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="1" />
<Setter Property="Background" Value="{ThemeResource SystemAltMediumLowColor}" />
<Setter Property="BorderBrush" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
</Style>
<Style x:Key="KeyChordTextBlockStyle" TargetType="TextBlock">
<Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
</Style>
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<!-- KeyChordText styles (use XAML defaults for High Contrast theme) -->
<Style x:Key="KeyChordBorderStyle" TargetType="Border"/>
<Style x:Key="KeyChordTextBlockStyle" TargetType="TextBlock"/>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Page.Resources>
<ScrollViewer>
<StackPanel Style="{StaticResource SettingsStackStyle}">
<TextBlock x:Uid="Globals_KeybindingsDisclaimer"
Style="{StaticResource DisclaimerStyle}"/>
<!-- The Nav_OpenJSON resource just so happens to have a .Content
and .Tooltip that are _exactly_ what we're looking for here. -->
<HyperlinkButton x:Uid="Nav_OpenJSON"
Click="_OpenSettingsClick" />
<!-- Keybindings -->
<!-- NOTE: Globals_Keybindings.Header is not defined, because that
would result in the page having "Keybindings" displayed twice, which
looks quite redundant -->
<ContentPresenter x:Uid="Globals_Keybindings" Margin="0">
<ListView HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
SelectionMode="None"
IsItemClickEnabled="False"
CanReorderItems="False"
AllowDrop="False"
ItemsSource="{x:Bind FilteredActions, Mode=OneWay}"
ItemTemplate="{StaticResource GeneralItemTemplate}">
</ListView>
</ContentPresenter>
</StackPanel>
</ScrollViewer>
</Page>

View File

@@ -20,6 +20,14 @@ using namespace winrt::Windows::Foundation::Collections;
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
// The first 8 entries of the color table are non-bright colors, whereas the rest are bright.
static constexpr uint8_t ColorTableDivider{ 8 };
static constexpr std::wstring_view ForegroundColorTag{ L"Foreground" };
static constexpr std::wstring_view BackgroundColorTag{ L"Background" };
static constexpr std::wstring_view CursorColorTag{ L"CursorColor" };
static constexpr std::wstring_view SelectionBackgroundColorTag{ L"SelectionBackground" };
static const std::array<hstring, 16> TableColorNames = {
RS_(L"ColorScheme_Black/Header"),
RS_(L"ColorScheme_Red/Header"),
@@ -53,9 +61,25 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
ColorSchemes::ColorSchemes() :
_ColorSchemeList{ single_threaded_observable_vector<Model::ColorScheme>() },
_CurrentColorTable{ single_threaded_observable_vector<Editor::ColorTableEntry>() }
_CurrentNonBrightColorTable{ single_threaded_observable_vector<Editor::ColorTableEntry>() },
_CurrentBrightColorTable{ single_threaded_observable_vector<Editor::ColorTableEntry>() }
{
InitializeComponent();
Automation::AutomationProperties::SetName(ColorSchemeComboBox(), RS_(L"ColorScheme_Name/Header"));
Automation::AutomationProperties::SetFullDescription(ColorSchemeComboBox(), RS_(L"ColorScheme_Name/[using:Windows.UI.Xaml.Controls]ToolTipService/ToolTip"));
ToolTipService::SetToolTip(ColorSchemeComboBox(), box_value(RS_(L"ColorScheme_Name/[using:Windows.UI.Xaml.Controls]ToolTipService/ToolTip")));
Automation::AutomationProperties::SetName(RenameButton(), RS_(L"Rename/[using:Windows.UI.Xaml.Controls]ToolTipService/ToolTip"));
Automation::AutomationProperties::SetName(NameBox(), RS_(L"ColorScheme_Name/Header"));
Automation::AutomationProperties::SetFullDescription(NameBox(), RS_(L"ColorScheme_Name/[using:Windows.UI.Xaml.Controls]ToolTipService/ToolTip"));
ToolTipService::SetToolTip(NameBox(), box_value(RS_(L"ColorScheme_Name/[using:Windows.UI.Xaml.Controls]ToolTipService/ToolTip")));
Automation::AutomationProperties::SetName(RenameAcceptButton(), RS_(L"RenameAccept/[using:Windows.UI.Xaml.Controls]ToolTipService/ToolTip"));
Automation::AutomationProperties::SetName(RenameCancelButton(), RS_(L"RenameCancel/[using:Windows.UI.Xaml.Controls]ToolTipService/ToolTip"));
Automation::AutomationProperties::SetName(AddNewButton(), RS_(L"ColorScheme_AddNewButton/Text"));
Automation::AutomationProperties::SetName(DeleteButton(), RS_(L"ColorScheme_DeleteButton/Text"));
}
void ColorSchemes::OnNavigatedTo(const NavigationEventArgs& e)
@@ -70,9 +94,20 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
// very accurately.
for (uint8_t i = 0; i < TableColorNames.size(); ++i)
{
auto entry = winrt::make<ColorTableEntry>(i, Windows::UI::Color{ 0, 0, 0, 0 });
_CurrentColorTable.Append(entry);
const auto& entry{ winrt::make<ColorTableEntry>(i, Windows::UI::Color{ 0, 0, 0, 0 }) };
if (i < ColorTableDivider)
{
_CurrentNonBrightColorTable.Append(entry);
}
else
{
_CurrentBrightColorTable.Append(entry);
}
}
_CurrentForegroundColor = winrt::make<ColorTableEntry>(ForegroundColorTag, Windows::UI::Color{ 0, 0, 0, 0 });
_CurrentBackgroundColor = winrt::make<ColorTableEntry>(BackgroundColorTag, Windows::UI::Color{ 0, 0, 0, 0 });
_CurrentCursorColor = winrt::make<ColorTableEntry>(CursorColorTag, Windows::UI::Color{ 0, 0, 0, 0 });
_CurrentSelectionBackgroundColor = winrt::make<ColorTableEntry>(SelectionBackgroundColorTag, Windows::UI::Color{ 0, 0, 0, 0 });
// Try to look up the scheme that was navigated to. If we find it, immediately select it.
const std::wstring lastNameFromNav{ _State.LastSelectedScheme() };
@@ -85,6 +120,41 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
auto scheme = *it;
ColorSchemeComboBox().SelectedItem(scheme);
}
// populate color table grid
const auto colorLabelStyle{ Resources().Lookup(winrt::box_value(L"ColorLabelStyle")).as<Windows::UI::Xaml::Style>() };
const auto colorControlStyle{ Resources().Lookup(winrt::box_value(L"ColorControlStyle")).as<Windows::UI::Xaml::Style>() };
const auto colorTableEntryTemplate{ Resources().Lookup(winrt::box_value(L"ColorTableEntryTemplate")).as<DataTemplate>() };
auto setupColorControl = [colorTableEntryTemplate, colorControlStyle, colorTableGrid{ ColorTableGrid() }](const auto&& colorRef, const uint32_t& row, const uint32_t& col) {
ContentControl colorControl{};
colorControl.ContentTemplate(colorTableEntryTemplate);
colorControl.Style(colorControlStyle);
Data::Binding binding{};
binding.Source(colorRef);
binding.Mode(Data::BindingMode::TwoWay);
colorControl.SetBinding(ContentControl::ContentProperty(), binding);
colorTableGrid.Children().Append(colorControl);
Grid::SetRow(colorControl, row);
Grid::SetColumn(colorControl, col);
};
for (uint32_t row = 0; row < ColorTableGrid().RowDefinitions().Size(); ++row)
{
// color label
TextBlock label{};
label.Text(TableColorNames[row]);
label.Style(colorLabelStyle);
ColorTableGrid().Children().Append(label);
Grid::SetRow(label, row);
Grid::SetColumn(label, 0);
// regular color
setupColorControl(_CurrentNonBrightColorTable.GetAt(row), row, 1);
// bright color
setupColorControl(_CurrentBrightColorTable.GetAt(row), row, 2);
}
}
// Function Description:
@@ -149,20 +219,52 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void ColorSchemes::ColorPickerChanged(IInspectable const& sender,
ColorChangedEventArgs const& args)
{
if (auto picker = sender.try_as<ColorPicker>())
if (const auto& picker{ sender.try_as<ColorPicker>() })
{
if (auto tag = picker.Tag())
if (const auto& tag{ picker.Tag() })
{
auto index = winrt::unbox_value<uint8_t>(tag);
CurrentColorScheme().SetColorTableEntry(index, args.NewColor());
_CurrentColorTable.GetAt(index).Color(args.NewColor());
if (const auto index{ tag.try_as<uint8_t>() })
{
CurrentColorScheme().SetColorTableEntry(*index, args.NewColor());
if (index < ColorTableDivider)
{
_CurrentNonBrightColorTable.GetAt(*index).Color(args.NewColor());
}
else
{
_CurrentBrightColorTable.GetAt(*index - ColorTableDivider).Color(args.NewColor());
}
}
else if (const auto stringTag{ tag.try_as<hstring>() })
{
if (stringTag == ForegroundColorTag)
{
CurrentColorScheme().Foreground(args.NewColor());
_CurrentForegroundColor.Color(args.NewColor());
}
else if (stringTag == BackgroundColorTag)
{
CurrentColorScheme().Background(args.NewColor());
_CurrentBackgroundColor.Color(args.NewColor());
}
else if (stringTag == CursorColorTag)
{
CurrentColorScheme().CursorColor(args.NewColor());
_CurrentCursorColor.Color(args.NewColor());
}
else if (stringTag == SelectionBackgroundColorTag)
{
CurrentColorScheme().SelectionBackground(args.NewColor());
_CurrentSelectionBackgroundColor.Color(args.NewColor());
}
}
}
}
}
bool ColorSchemes::CanDeleteCurrentScheme() const
{
if (const auto scheme{ CurrentColorScheme() })
if (const auto& scheme{ CurrentColorScheme() })
{
// Only allow this color scheme to be deleted if it's not provided in-box
const std::wstring myName{ scheme.Name() };
@@ -295,14 +397,32 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
for (uint8_t i = 0; i < TableColorNames.size(); ++i)
{
_CurrentColorTable.GetAt(i).Color(colorScheme.Table()[i]);
if (i < ColorTableDivider)
{
_CurrentNonBrightColorTable.GetAt(i).Color(colorScheme.Table()[i]);
}
else
{
_CurrentBrightColorTable.GetAt(i - ColorTableDivider).Color(colorScheme.Table()[i]);
}
}
_CurrentForegroundColor.Color(colorScheme.Foreground());
_CurrentBackgroundColor.Color(colorScheme.Background());
_CurrentCursorColor.Color(colorScheme.CursorColor());
_CurrentSelectionBackgroundColor.Color(colorScheme.SelectionBackground());
}
ColorTableEntry::ColorTableEntry(uint8_t index, Windows::UI::Color color)
{
Name(TableColorNames[index]);
Index(winrt::box_value<uint8_t>(index));
Tag(winrt::box_value<uint8_t>(index));
Color(color);
}
ColorTableEntry::ColorTableEntry(std::wstring_view tag, Windows::UI::Color color)
{
Name(LocalizedNameForEnumName(L"ColorScheme_", tag, L"Text"));
Tag(winrt::box_value(tag));
Color(color);
}
}

View File

@@ -16,8 +16,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
ColorSchemesPageNavigationState(const Model::CascadiaSettings& settings) :
_Settings{ settings } {}
GETSET_PROPERTY(Model::CascadiaSettings, Settings, nullptr);
GETSET_PROPERTY(winrt::hstring, LastSelectedScheme, L"");
WINRT_PROPERTY(Model::CascadiaSettings, Settings, nullptr);
WINRT_PROPERTY(winrt::hstring, LastSelectedScheme, L"");
};
struct ColorSchemes : ColorSchemesT<ColorSchemes>
@@ -38,13 +38,18 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
bool CanDeleteCurrentScheme() const;
void DeleteConfirmation_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
GETSET_PROPERTY(Editor::ColorSchemesPageNavigationState, State, nullptr);
GETSET_PROPERTY(Windows::Foundation::Collections::IObservableVector<winrt::Microsoft::Terminal::Settings::Editor::ColorTableEntry>, CurrentColorTable, nullptr);
GETSET_PROPERTY(Windows::Foundation::Collections::IObservableVector<Model::ColorScheme>, ColorSchemeList, nullptr);
WINRT_PROPERTY(Editor::ColorSchemesPageNavigationState, State, nullptr);
WINRT_PROPERTY(Model::ColorScheme, CurrentColorScheme, nullptr);
WINRT_PROPERTY(Windows::Foundation::Collections::IVector<Editor::ColorTableEntry>, CurrentNonBrightColorTable, nullptr);
WINRT_PROPERTY(Windows::Foundation::Collections::IVector<Editor::ColorTableEntry>, CurrentBrightColorTable, nullptr);
WINRT_PROPERTY(Windows::Foundation::Collections::IObservableVector<Model::ColorScheme>, ColorSchemeList, nullptr);
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
OBSERVABLE_GETSET_PROPERTY(winrt::Microsoft::Terminal::Settings::Model::ColorScheme, CurrentColorScheme, _PropertyChangedHandlers, nullptr);
OBSERVABLE_GETSET_PROPERTY(bool, IsRenaming, _PropertyChangedHandlers, nullptr);
WINRT_OBSERVABLE_PROPERTY(bool, IsRenaming, _PropertyChangedHandlers, nullptr);
WINRT_OBSERVABLE_PROPERTY(Editor::ColorTableEntry, CurrentForegroundColor, _PropertyChangedHandlers, nullptr);
WINRT_OBSERVABLE_PROPERTY(Editor::ColorTableEntry, CurrentBackgroundColor, _PropertyChangedHandlers, nullptr);
WINRT_OBSERVABLE_PROPERTY(Editor::ColorTableEntry, CurrentCursorColor, _PropertyChangedHandlers, nullptr);
WINRT_OBSERVABLE_PROPERTY(Editor::ColorTableEntry, CurrentSelectionBackgroundColor, _PropertyChangedHandlers, nullptr);
private:
void _UpdateColorTable(const winrt::Microsoft::Terminal::Settings::Model::ColorScheme& colorScheme);
@@ -56,11 +61,12 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
public:
ColorTableEntry(uint8_t index, Windows::UI::Color color);
ColorTableEntry(std::wstring_view tag, Windows::UI::Color color);
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, Name, _PropertyChangedHandlers);
OBSERVABLE_GETSET_PROPERTY(IInspectable, Index, _PropertyChangedHandlers);
OBSERVABLE_GETSET_PROPERTY(Windows::UI::Color, Color, _PropertyChangedHandlers);
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, Name, _PropertyChangedHandlers);
WINRT_OBSERVABLE_PROPERTY(IInspectable, Tag, _PropertyChangedHandlers);
WINRT_OBSERVABLE_PROPERTY(Windows::UI::Color, Color, _PropertyChangedHandlers);
};
}

View File

@@ -18,15 +18,24 @@ namespace Microsoft.Terminal.Settings.Editor
Boolean CanDeleteCurrentScheme { get; };
Boolean IsRenaming { get; };
Microsoft.Terminal.Settings.Model.ColorScheme CurrentColorScheme { get; };
Windows.Foundation.Collections.IObservableVector<ColorTableEntry> CurrentColorTable;
// Terminal Colors
Windows.Foundation.Collections.IVector<ColorTableEntry> CurrentNonBrightColorTable { get; };
Windows.Foundation.Collections.IVector<ColorTableEntry> CurrentBrightColorTable { get; };
// System Colors
ColorTableEntry CurrentForegroundColor;
ColorTableEntry CurrentBackgroundColor;
ColorTableEntry CurrentCursorColor;
ColorTableEntry CurrentSelectionBackgroundColor;
Windows.Foundation.Collections.IObservableVector<Microsoft.Terminal.Settings.Model.ColorScheme> ColorSchemeList { get; };
}
[default_interface] runtimeclass ColorTableEntry : Windows.UI.Xaml.Data.INotifyPropertyChanged
{
String Name { get; };
IInspectable Index;
IInspectable Tag;
Windows.UI.Color Color;
}
}

View File

@@ -17,18 +17,25 @@ the MIT License. See LICENSE in the project root for license information. -->
<ResourceDictionary Source="CommonResources.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Thickness x:Key="ColorSchemesStandardControlMargin">0,0,10,20</Thickness>
<Style x:Key="ColorStackPanelStyle" TargetType="StackPanel">
<Setter Property="Margin" Value="{StaticResource ColorSchemesStandardControlMargin}"/>
<Setter Property="Height" Value="59"/>
<Style x:Key="GroupHeaderStyle" TargetType="TextBlock" BasedOn="{StaticResource SubtitleTextBlockStyle}">
<Setter Property="Margin" Value="0,0,0,4"/>
</Style>
<Style x:Key="ColorHeaderStyle" TargetType="TextBlock">
<Setter Property="Margin" Value="0,0,0,5"/>
<Style x:Key="ColorLabelStyle" TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<Style TargetType="Button" BasedOn="{StaticResource BaseButtonStyle}">
<Style x:Key="ColorTableGridStyle" TargetType="Grid">
<Setter Property="RowSpacing" Value="10"/>
<Setter Property="ColumnSpacing" Value="10"/>
</Style>
<Style x:Key="ColorControlStyle" TargetType="ContentControl">
<Setter Property="IsTabStop" Value="False"/>
</Style>
<Style x:Key="ColorButtonStyle" TargetType="Button" BasedOn="{StaticResource BaseButtonStyle}">
<Setter Property="BorderBrush" Value="{StaticResource SystemBaseLowColor}"/>
<Setter Property="Height" Value="30"/>
<Setter Property="Width" Value="100"/>
@@ -42,6 +49,36 @@ the MIT License. See LICENSE in the project root for license information. -->
<Setter Property="IsAlphaTextInputVisible" Value="True"/>
</Style>
<DataTemplate x:Key="ColorTableEntryTemplate" x:DataType="local:ColorTableEntry">
<Button Background="{x:Bind Color, Converter={StaticResource ColorToBrushConverter}, Mode=OneWay}"
ToolTipService.ToolTip="{x:Bind Name}"
AutomationProperties.Name="{x:Bind Name}"
Style="{StaticResource ColorButtonStyle}">
<Button.Resources>
<!-- Resources to colorize hover/pressed states based on the color of the button -->
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="ButtonBackgroundPointerOver" Color="{x:Bind Color, Converter={StaticResource ColorLightenConverter}, Mode=OneWay}"/>
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<SolidColorBrush x:Key="ButtonBackgroundPointerOver" Color="{x:Bind Color, Converter={StaticResource ColorLightenConverter}, Mode=OneWay}"/>
</ResourceDictionary>
<!-- No High contrast dictionary, let's just leave that unchanged. -->
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Button.Resources>
<Button.Flyout>
<Flyout>
<ColorPicker Tag="{x:Bind Tag, Mode=OneWay}"
Color="{x:Bind Color, Mode=OneWay}"
ColorChanged="ColorPickerChanged"/>
</Flyout>
</Button.Flyout>
</Button>
</DataTemplate>
<local:ColorToBrushConverter x:Key="ColorToBrushConverter"/>
<local:ColorToHexConverter x:Key="ColorToHexConverter"/>
<local:InvertedBooleanToVisibilityConverter x:Key="InvertedBooleanToVisibilityConverter"/>
@@ -51,22 +88,27 @@ the MIT License. See LICENSE in the project root for license information. -->
</Page.Resources>
<ScrollViewer>
<Grid Margin="{StaticResource StandardIndentMargin}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" MaxWidth="480"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState>
<VisualState.StateTriggers>
<!--Official guidance states that 640 is the breakpoint between small and medium devices.
Since MinWindowWidth is an inclusive range, we need to add 1 to it.-->
<AdaptiveTrigger MinWindowWidth="641"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="ColorPanel.Orientation" Value="Horizontal"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<StackPanel Margin="{StaticResource StandardIndentMargin}"
Spacing="24">
<!--Select Color and Add New Button-->
<StackPanel Orientation="Horizontal"
Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="2">
<StackPanel Orientation="Horizontal">
<StackPanel Orientation="Horizontal"
Visibility="{x:Bind IsRenaming, Converter={StaticResource InvertedBooleanToVisibilityConverter}, Mode=OneWay}">
@@ -111,6 +153,7 @@ the MIT License. See LICENSE in the project root for license information. -->
<!--Accept rename button-->
<Button x:Uid="RenameAccept"
x:Name="RenameAcceptButton"
Style="{StaticResource AccentSmallButtonStyle}"
Click="RenameAccept_Click">
<StackPanel Orientation="Horizontal">
@@ -120,6 +163,7 @@ the MIT License. See LICENSE in the project root for license information. -->
</Button>
<!--Cancel rename button-->
<Button x:Uid="RenameCancel"
x:Name="RenameCancelButton"
Style="{StaticResource SmallButtonStyle}"
Click="RenameCancel_Click">
<StackPanel Orientation="Horizontal">
@@ -130,7 +174,8 @@ the MIT License. See LICENSE in the project root for license information. -->
</StackPanel>
<!--Add new button-->
<Button Click="AddNew_Click"
<Button x:Name="AddNewButton"
Click="AddNew_Click"
Style="{StaticResource BrowseButtonStyle}">
<StackPanel Orientation="Horizontal">
<FontIcon Glyph="&#xE710;"
@@ -141,181 +186,109 @@ the MIT License. See LICENSE in the project root for license information. -->
</Button>
</StackPanel>
<!-- Color Table (Left Column)-->
<ItemsControl x:Name="ColorTableControl"
ItemsSource="{x:Bind CurrentColorTable, Mode=OneWay}"
Grid.Row="1"
Grid.Column="0"
Style="{StaticResource ItemsControlStyle}">
<!-- Terminal Colors (Left Column)-->
<StackPanel x:Name="ColorPanel"
Spacing="48">
<StackPanel>
<TextBlock x:Uid="ColorScheme_TerminalColorsHeader"
Style="{StaticResource GroupHeaderStyle}"/>
<Grid x:Name="ColorTableGrid"
Style="{StaticResource ColorTableGridStyle}">
<Grid.ColumnDefinitions>
<!--Labels-->
<ColumnDefinition Width="Auto"/>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid Orientation="Horizontal"
MaximumRowsOrColumns="4"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="local:ColorTableEntry">
<StackPanel Style="{StaticResource ColorStackPanelStyle}">
<TextBlock Text="{x:Bind Name, Mode=OneWay}"
Style="{StaticResource ColorHeaderStyle}"/>
<Button Background="{x:Bind Color, Converter={StaticResource ColorToBrushConverter}, Mode=OneWay}">
<!--Regular Colors-->
<ColumnDefinition Width="Auto"/>
<!--Bright Colors-->
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Button.Resources>
<!-- Resources to colorize hover/pressed states based on the color of the button -->
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="ButtonBackgroundPointerOver" Color="{x:Bind Color, Converter={StaticResource ColorLightenConverter}, Mode=OneWay}"/>
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<SolidColorBrush x:Key="ButtonBackgroundPointerOver" Color="{x:Bind Color, Converter={StaticResource ColorLightenConverter}, Mode=OneWay}"/>
</ResourceDictionary>
<!-- No High contrast dictionary, let's just leave that unchanged. -->
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Button.Resources>
<Button.Flyout>
<Flyout>
<ColorPicker Tag="{x:Bind Index, Mode=OneWay}"
Color="{x:Bind Color, Mode=OneWay}"
ColorChanged="ColorPickerChanged"/>
</Flyout>
</Button.Flyout>
</Button>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!-- Additional Colors (Right Column) -->
<ItemsControl Grid.Row="1"
Grid.Column="1"
Style="{StaticResource ItemsControlStyle}">
<StackPanel Style="{StaticResource ColorStackPanelStyle}">
<TextBlock x:Uid="ColorScheme_Foreground"
Style="{StaticResource ColorHeaderStyle}"/>
<Button Background="{Binding Color, ElementName=ForegroundPicker, Converter={StaticResource ColorToBrushConverter}, Mode=OneWay}">
<Button.Resources>
<!-- Resources to colorize hover/pressed states based on the color of the button -->
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="ButtonBackgroundPointerOver" Color="{Binding Color, ElementName=ForegroundPicker, Converter={StaticResource ColorLightenConverter}, Mode=OneWay}"/>
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<SolidColorBrush x:Key="ButtonBackgroundPointerOver" Color="{Binding Color, ElementName=ForegroundPicker, Converter={StaticResource ColorLightenConverter}, Mode=OneWay}"/>
</ResourceDictionary>
<!-- No High contrast dictionary, let's just leave that unchanged. -->
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Button.Resources>
<Button.Flyout>
<Flyout>
<ColorPicker x:Name="ForegroundPicker" Color="{x:Bind CurrentColorScheme.Foreground, Mode=TwoWay}"/>
</Flyout>
</Button.Flyout>
</Button>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
</Grid>
</StackPanel>
<StackPanel Style="{StaticResource ColorStackPanelStyle}">
<TextBlock x:Uid="ColorScheme_Background"
Style="{StaticResource ColorHeaderStyle}"/>
<Button Background="{Binding Color, ElementName=BackgroundPicker, Converter={StaticResource ColorToBrushConverter}, Mode=OneWay}">
<!-- System Colors (Right Column) -->
<StackPanel>
<TextBlock x:Uid="ColorScheme_SystemColorsHeader"
Style="{StaticResource GroupHeaderStyle}"/>
<Grid Style="{StaticResource ColorTableGridStyle}">
<Button.Resources>
<!-- Resources to colorize hover/pressed states based on the color of the button -->
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="ButtonBackgroundPointerOver" Color="{Binding Color, ElementName=BackgroundPicker, Converter={StaticResource ColorLightenConverter}, Mode=OneWay}"/>
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<SolidColorBrush x:Key="ButtonBackgroundPointerOver" Color="{Binding Color, ElementName=BackgroundPicker, Converter={StaticResource ColorLightenConverter}, Mode=OneWay}"/>
</ResourceDictionary>
<!-- No High contrast dictionary, let's just leave that unchanged. -->
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Button.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Button.Flyout>
<Flyout>
<ColorPicker x:Name="BackgroundPicker"
Color="{x:Bind CurrentColorScheme.Background, Mode=TwoWay}"/>
</Flyout>
</Button.Flyout>
</Button>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!--Foreground-->
<TextBlock x:Uid="ColorScheme_Foreground"
Style="{StaticResource ColorLabelStyle}"
Grid.Row="0"
Grid.Column="0"/>
<ContentControl x:Name="ForegroundButton"
ContentTemplate="{StaticResource ColorTableEntryTemplate}"
Content="{x:Bind CurrentForegroundColor, Mode=TwoWay}"
Style="{StaticResource ColorControlStyle}"
Grid.Row="0"
Grid.Column="1"/>
<!--Background-->
<TextBlock x:Uid="ColorScheme_Background"
Style="{StaticResource ColorLabelStyle}"
Grid.Row="1"
Grid.Column="0"/>
<ContentControl x:Name="BackgroundButton"
ContentTemplate="{StaticResource ColorTableEntryTemplate}"
Content="{x:Bind CurrentBackgroundColor, Mode=TwoWay}"
Style="{StaticResource ColorControlStyle}"
Grid.Row="1"
Grid.Column="1"/>
<!--Cursor Color-->
<TextBlock x:Uid="ColorScheme_CursorColor"
Style="{StaticResource ColorLabelStyle}"
Grid.Row="2"
Grid.Column="0"/>
<ContentControl x:Name="CursorColorButton"
ContentTemplate="{StaticResource ColorTableEntryTemplate}"
Content="{x:Bind CurrentCursorColor, Mode=TwoWay}"
Style="{StaticResource ColorControlStyle}"
Grid.Row="2"
Grid.Column="1"/>
<!--Selection Background-->
<TextBlock x:Uid="ColorScheme_SelectionBackground"
Style="{StaticResource ColorLabelStyle}"
Grid.Row="3"
Grid.Column="0"/>
<ContentControl x:Name="SelectionBackgroundButton"
ContentTemplate="{StaticResource ColorTableEntryTemplate}"
Content="{x:Bind CurrentSelectionBackgroundColor, Mode=TwoWay}"
Style="{StaticResource ColorControlStyle}"
Grid.Row="3"
Grid.Column="1"/>
</Grid>
</StackPanel>
<StackPanel Style="{StaticResource ColorStackPanelStyle}">
<TextBlock x:Uid="ColorScheme_CursorColor"
Style="{StaticResource ColorHeaderStyle}"/>
<Button Background="{Binding Color, ElementName=CursorColorPicker, Converter={StaticResource ColorToBrushConverter}, Mode=OneWay}">
<Button.Resources>
<!-- Resources to colorize hover/pressed states based on the color of the button -->
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="ButtonBackgroundPointerOver" Color="{Binding Color, ElementName=CursorColorPicker, Converter={StaticResource ColorLightenConverter}, Mode=OneWay}"/>
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<SolidColorBrush x:Key="ButtonBackgroundPointerOver" Color="{Binding Color, ElementName=CursorColorPicker, Converter={StaticResource ColorLightenConverter}, Mode=OneWay}"/>
</ResourceDictionary>
<!-- No High contrast dictionary, let's just leave that unchanged. -->
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Button.Resources>
<Button.Flyout>
<Flyout>
<ColorPicker x:Name="CursorColorPicker"
Color="{x:Bind CurrentColorScheme.CursorColor, Mode=TwoWay}"/>
</Flyout>
</Button.Flyout>
</Button>
</StackPanel>
<StackPanel Style="{StaticResource ColorStackPanelStyle}">
<TextBlock x:Uid="ColorScheme_SelectionBackground"
Style="{StaticResource ColorHeaderStyle}"/>
<Button Background="{Binding Color, ElementName=SelectionBackgroundPicker, Converter={StaticResource ColorToBrushConverter}, Mode=OneWay}">
<Button.Resources>
<!-- Resources to colorize hover/pressed states based on the color of the button -->
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="ButtonBackgroundPointerOver" Color="{Binding Color, ElementName=SelectionBackgroundPicker, Converter={StaticResource ColorLightenConverter}, Mode=OneWay}"/>
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<SolidColorBrush x:Key="ButtonBackgroundPointerOver" Color="{Binding Color, ElementName=SelectionBackgroundPicker, Converter={StaticResource ColorLightenConverter}, Mode=OneWay}"/>
</ResourceDictionary>
<!-- No High contrast dictionary, let's just leave that unchanged. -->
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Button.Resources>
<Button.Flyout>
<Flyout>
<ColorPicker x:Name="SelectionBackgroundPicker"
Color="{x:Bind CurrentColorScheme.SelectionBackground, Mode=TwoWay}"/>
</Flyout>
</Button.Flyout>
</Button>
</StackPanel>
</ItemsControl>
</StackPanel>
<!--Delete Button-->
<StackPanel Grid.Row="2"
Grid.Column="0"
Grid.ColumnSpan="2"
Style="{StaticResource PivotStackStyle}">
<StackPanel Style="{StaticResource PivotStackStyle}">
<Button x:Name="DeleteButton"
IsEnabled="{x:Bind CanDeleteCurrentScheme, Mode=OneWay}"
Style="{StaticResource DeleteButtonStyle}">
@@ -371,6 +344,6 @@ the MIT License. See LICENSE in the project root for license information. -->
Style="{StaticResource DisclaimerStyle}"
VerticalAlignment="Center"/>
</StackPanel>
</Grid>
</StackPanel>
</ScrollViewer>
</Page>

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