Compare commits

...

363 Commits

Author SHA1 Message Date
Mike Griese
9da196da7d Well at least it seems like we don't have one output thread per control now. That's something 2023-03-15 17:06:41 -05:00
Mike Griese
d7637ff33b well, we've made some pregress but it is WACKY 2023-03-15 17:00:54 -05:00
Mike Griese
7189228deb absolutely just aesthetic changes 2023-03-15 14:10:32 -05:00
Mike Griese
0fdb523072 just to be sure, we can change the prompt manually 2023-03-15 13:52:06 -05:00
Mike Griese
19ccea53d5 Start by making a more minimal control I can use 2023-03-15 13:44:57 -05:00
Mike Griese
bc76f3ada6 scratch project building 2023-03-15 10:49:22 -05:00
Mike Griese
a04a51bbe4 Manually enable shell completions support for selfhost 2023-03-14 10:26:53 -05:00
Mike Griese
12dbcf96fd *shakes fist* VS 2023-03-14 05:53:19 -05:00
Mike Griese
5bb0818c0d Merge branch 'dev/cazamor/selfhost/1.18' of https://github.com/microsoft/terminal into dev/cazamor/selfhost/1.18 2023-03-14 05:22:04 -05:00
Mike Griese
edd9ab10bd [unchaining of melkor] Y'know, maybe this isn't a sensible default after all 2023-03-14 05:03:36 -05:00
Mike Griese
c18466ae7c [an unexpected party] This was a bad merge conflict that resulted in windows entirely failing to start 2023-03-14 05:02:44 -05:00
Mike Griese
fcb8b59e6f A hotfix for the selfhost build 2023-03-13 11:25:28 -05:00
Mike Griese
cf1bc0a5b6 PRE-MERGE #14821 Add an efficient text stream write function 2023-03-13 06:43:41 -05:00
Leonard Hecker
f9fbb8ab3d Fix compilation 2023-03-13 01:40:19 +01:00
Leonard Hecker
17e3ca5390 Fix AuditMode failures 2023-03-12 18:08:38 +01:00
Leonard Hecker
5040328a06 Remove debug helpers 2023-03-12 17:42:08 +01:00
Leonard Hecker
173e916bcd Add CopyRangeFrom to later implement TextBuffer::Reflow 2023-03-12 17:39:07 +01:00
Mike Griese
cedfd3f5bd PRE-MERGE #14775 Add support for a right-click context menu 2023-03-11 05:51:09 -06:00
Mike Griese
8b96836e1d PRE-MERGE #14807 Add the ability to select a whole command (or its output) 2023-03-11 05:49:07 -06:00
Mike Griese
cf77c1758b PRE-MERGE #14943 Add support for opening the Suggestions UI with recent commands 2023-03-11 05:47:25 -06:00
Mike Griese
dbe8e13c57 PRE-MERGE #14939 Add tooltips to the Suggestions Control 2023-03-10 17:21:03 -06:00
Mike Griese
bd873fa0a2 PRE-MERGE #14938 Add Suggestions UI & experimental shell completions support 2023-03-10 17:20:47 -06:00
Mike Griese
a8ab6a4a43 PRE-MERGE #14944 Add support for running the Terminal without _any_ windows 2023-03-10 17:03:16 -06:00
Mike Griese
c4597dec0d PRE-MERGE #14935 Enable tearing out tabs to create new windows 2023-03-10 17:03:07 -06:00
Mike Griese
492c894b79 Merge branch 'dev/migrie/oop/3/akallabeth' into dev/migrie/oop/3/an-unexpected-party 2023-03-10 16:50:44 -06:00
Mike Griese
e23980c62a Merge branch 'dev/migrie/oop/3/quenta-silmarillion' into dev/migrie/oop/3/akallabeth 2023-03-10 16:39:24 -06:00
Mike Griese
514556f018 Merge branch 'dev/migrie/oop/3/valaquenta' into dev/migrie/oop/3/quenta-silmarillion 2023-03-10 16:38:57 -06:00
Mike Griese
6a0b1cdbdc Merge branch 'dev/migrie/oop/3/ainulindale' into dev/migrie/oop/3/feanor-and-the-unchaining-of-melkor 2023-03-10 16:26:13 -06:00
Mike Griese
c8ce5b4dc6 Merge branch 'dev/migrie/oop/3/ainulindale' into dev/migrie/oop/3/valaquenta 2023-03-10 16:21:20 -06:00
Mike Griese
9957e5ce52 I think that's the last of the nits 2023-03-10 15:53:45 -06:00
Mike Griese
1138416dc9 hey let's make sure it still works 2023-03-10 15:23:28 -06:00
Mike Griese
6d043532fc yea we can fold this in 2023-03-10 14:58:10 -06:00
Mike Griese
22c94bf10d spell 2023-03-10 14:57:56 -06:00
Mike Griese
cd2db82515 Merge branch 'main' into dev/migrie/fhl-2023/pwsh-autocomplete-demo 2023-03-10 14:57:31 -06:00
Mike Griese
339972ecea tests build and pass again 2023-03-10 14:34:46 -06:00
Mike Griese
81140a530e Merge branch 'dev/migrie/oop/3/foreword' into dev/migrie/oop/3/ainulindale 2023-03-10 10:46:53 -06:00
Mike Griese
f70775aca0 I don't trust you phyllis 2023-03-10 10:04:54 -06:00
Mike Griese
ca511c9cc9 this is why I don't use VS 2023-03-10 09:04:05 -06:00
Mike Griese
073350e66f fix the tests too 2023-03-10 09:02:46 -06:00
Mike Griese
44b238e777 Redo how ownership of WindowProperties works. This is cleaner by a good amount 2023-03-09 16:55:05 -06:00
Mike Griese
39a94503fc to wit, also make sure that there is a tablayout 2023-03-09 13:51:43 -06:00
Mike Griese
1b59eb9238 save this for later, we should only need ot look it up once 2023-03-09 13:50:13 -06:00
Mike Griese
434abc2474 Remove the need for TerminalPage to know the number of open windows
This removes all the weirdness around the way that TerminalPage needs to track
  the number of open windows. Instead of TerminalPage persisting empty state
  when the last tab closes, it lets the AppHost know that the last tab was
  closed due to _closing the tab_ (as opposed to closing the window / quitting).
  This gives AppHost an opportunity to persist empty state for that case,
  because _it_ knows how many windows there are.

  This could basically be its own PR.

  Probably worth xlinking this commit to #9800
2023-03-09 12:52:28 -06:00
Mike Griese
6dead99341 PR nits 2023-03-09 12:29:23 -06:00
Mike Griese
77218139d8 pr nits 2023-03-09 10:13:50 -06:00
Mike Griese
6e4b2e1048 Guess what's LOAD BEARING 2023-03-09 09:27:48 -06:00
Leonard Hecker
99f19a9c0a Fix AuditMode failures 2023-03-09 15:17:40 +01:00
Leonard Hecker
3d04d37b3a Make spell-check happy 2023-03-09 15:16:39 +01:00
Leonard Hecker
f8e6884fac Address feedback, Simplify code 2023-03-09 14:50:08 +01:00
Mike Griese
1dc243641c weak refs for fun and profit 2023-03-08 16:13:19 -06:00
Mike Griese
6f6880c017 this is more idomatic locking cause it's actually locking 2023-03-08 15:18:27 -06:00
Mike Griese
daddcf171e the easy nits 2023-03-08 12:32:53 -06:00
Mike Griese
33b3eac3d6 Merge remote-tracking branch 'origin/main' into dev/migrie/f/3337-just-for-funsies 2023-03-08 11:32:02 -06:00
Mike Griese
547b2c9e6a you never get used either 2023-03-08 11:25:45 -06:00
Mike Griese
74af809b66 mmmm fresh cut code 2023-03-08 11:23:59 -06:00
Mike Griese
b0ca581b43 .detach so we don't std::explode() 2023-03-08 11:10:15 -06:00
Mike Griese
5fe3fa5ea8 some cleanup around expanding commands 2023-03-08 10:59:42 -06:00
Mike Griese
3fa101731b one fewer IReference, thank gods 2023-03-07 16:44:26 -06:00
Mike Griese
b24cf615ea some of the more minor cleanup elements 2023-03-07 16:28:38 -06:00
Mike Griese
0efa5a9209 Merge branch 'dev/migrie/oop/3/akallabeth' into dev/migrie/oop/3/an-unexpected-party 2023-03-06 13:32:49 -06:00
Mike Griese
230d68b0de Merge branch 'dev/migrie/oop/3/quenta-silmarillion' into dev/migrie/oop/3/akallabeth 2023-03-06 13:32:28 -06:00
Mike Griese
d545c4b4ca Merge branch 'dev/migrie/oop/3/valaquenta' into dev/migrie/oop/3/quenta-silmarillion 2023-03-06 13:31:46 -06:00
Mike Griese
11ef8efbad Merge branch 'dev/migrie/oop/3/ainulindale' into dev/migrie/oop/3/valaquenta 2023-03-06 13:31:11 -06:00
Mike Griese
9a9fa4392a [ainulindale] Prevent a race between the Emperor and the window logic when updating settings
(cherry picked from commit 35c7474abe)
2023-03-06 13:30:32 -06:00
Mike Griese
35c7474abe [ainulindale] Prevent a race between the Emperor and the window logic when updating settings 2023-03-06 13:25:45 -06:00
Leonard Hecker
a6c5e7a943 Add an efficient text stream write function 2023-03-06 18:15:25 +01:00
Carlos Zamora
9e3e336275 PRE-MERGE #14874 Merge the LineFeed functionality into AdaptDispatch 2023-03-03 12:51:41 -08:00
Carlos Zamora
ffc8b120bf PRE-MERGE #14936 Ensure that delayed EOL wrap is reset when necessary 2023-03-03 12:40:57 -08:00
Carlos Zamora
6a9254ee18 PRE-MERGE #14944 Add support for running the Terminal without _any_ windows 2023-03-03 12:40:43 -08:00
Carlos Zamora
dbdc672b56 PRE-MERGE #14935 Enable tearing out tabs to create new windows 2023-03-03 12:40:26 -08:00
Mike Griese
654baa736e most of the PR feedback 2023-03-03 06:48:20 -06:00
Mike Griese
c4f3011297 Merge branch 'main' into dev/migrie/f/4588-select-shell-output 2023-03-03 06:10:36 -06:00
Mike Griese
796a02e0b0 Merge branch 'dev/migrie/oop/3/akallabeth' into dev/migrie/oop/3/an-unexpected-party 2023-03-02 16:53:32 -06:00
Mike Griese
491fa62a5a I'm not dead yet 2023-03-02 16:53:06 -06:00
Mike Griese
41827424db oh this SO could have been one commit
(cherry picked from commit 8428f666bf)
2023-03-02 16:24:11 -06:00
Mike Griese
2d6fbc97bf Merge branch 'dev/migrie/oop/3/quenta-silmarillion' into dev/migrie/oop/3/akallabeth
# Conflicts:
#	src/cascadia/TerminalApp/TerminalPage.h
2023-03-02 16:24:01 -06:00
Mike Griese
9d5ef3a10b format and spell 2023-03-02 15:10:19 -06:00
Mike Griese
442413fc1e Final cleanup for review 2023-03-02 14:55:14 -06:00
Mike Griese
3aa083ba9a sure. It doesn't matter' 2023-03-02 14:00:09 -06:00
Mike Griese
236912abab Merge branch 'dev/migrie/fhl-2023/pwsh-autocomplete-demo' into dev/migrie/f/14779-sxnui-recent-commands 2023-03-02 13:46:03 -06:00
Mike Griese
8c5bfb5756 Merge branch 'dev/migrie/oop/3/akallabeth' into dev/migrie/oop/3/an-unexpected-party 2023-03-02 13:31:25 -06:00
Mike Griese
59d6ff29a8 Merge branch 'dev/migrie/oop/3/quenta-silmarillion' into dev/migrie/oop/3/akallabeth 2023-03-02 13:31:04 -06:00
Mike Griese
7591b0f404 Fix a UiaEngine race that would absolutely block self-host
(cherry picked from commit fda16631cc)
2023-03-02 13:24:03 -06:00
Mike Griese
2c15f97ee8 Merge branch 'dev/migrie/oop/3/valaquenta' into dev/migrie/oop/3/quenta-silmarillion 2023-03-02 13:23:51 -06:00
Mike Griese
48aa555428 Merge branch 'dev/migrie/oop/3/ainulindale' into dev/migrie/oop/3/valaquenta 2023-03-02 13:23:22 -06:00
Mike Griese
b983c69c8e Merge branch 'dev/migrie/oop/3/foreword' into dev/migrie/oop/3/ainulindale 2023-03-02 13:22:01 -06:00
Mike Griese
fda16631cc Fix a UiaEngine race that would absolutely block self-host 2023-03-02 13:20:58 -06:00
Mike Griese
88033246f1 spelling is hard 2023-03-02 11:18:02 -06:00
Mike Griese
08f6a53be5 This is gross but reduces the number of redundant copies of these brushes we need 2023-03-02 11:05:16 -06:00
Mike Griese
8e2c7a7bdd some cleanup 2023-03-02 10:20:59 -06:00
Mike Griese
1e331a037c Merge branch 'dev/migrie/oop/3/ainulindale' of https://github.com/microsoft/terminal into dev/migrie/oop/3/ainulindale 2023-03-02 09:54:09 -06:00
Mike Griese
5c852c3c4c some smaller nits 2023-03-02 09:54:04 -06:00
Mike Griese
7e2eb0d8e5 Apply suggestions from code review
Co-authored-by: Carlos Zamora <carlos.zamora@microsoft.com>
2023-03-02 09:53:51 -06:00
Mike Griese
110bb7da9c surprisingly load bearing 2023-03-01 13:28:11 -06:00
Mike Griese
de56d9e6c8 Merge branch 'dev/migrie/fhl-2023/pwsh-autocomplete-demo' into dev/migrie/f/3121-tooltips 2023-03-01 13:13:46 -06:00
Mike Griese
dcdb145500 Dismiss tooltip on close 2023-03-01 13:13:36 -06:00
Mike Griese
20f5651224 final cleanup for review 2023-03-01 11:07:24 -06:00
Mike Griese
a21c8155e7 Merge branch 'dev/migrie/fhl-2023/pwsh-autocomplete-demo' into dev/migrie/f/14779-sxnui-recent-commands 2023-03-01 08:36:11 -06:00
Mike Griese
e09379c64f Merge branch 'dev/migrie/fhl-2023/pwsh-autocomplete-demo' into dev/migrie/f/3121-tooltips 2023-03-01 08:35:35 -06:00
Mike Griese
173a830339 Finish detaching it from the popup
(cherry picked from commit a172f53597)
2023-03-01 08:23:39 -06:00
Mike Griese
e544871893 very confident we can get all the logic into the sxnui
(cherry picked from commit 57a53279ae)
2023-03-01 08:23:32 -06:00
Mike Griese
e2cc27889a POC: We can take it out of the Popup and have it still work
(cherry picked from commit 5defde545f)
2023-03-01 08:23:16 -06:00
Mike Griese
a172f53597 Finish detaching it from the popup 2023-03-01 08:19:07 -06:00
James Holderness
4eada04716 Extend CursorSaveRestore test to cover delayed wrap. 2023-02-28 22:56:43 +00:00
James Holderness
a94bf69c30 Add unit test for controls resetting delayed wrap. 2023-02-28 22:56:43 +00:00
James Holderness
a811441e9d Don't output EL in the delayed wrap state. 2023-02-28 22:56:43 +00:00
James Holderness
bdac748b8a Save and restore the delayed wrap flag with DECSC/DECRC. 2023-02-28 22:56:43 +00:00
James Holderness
83a323d7ea Horizontal tabs should not reset the delayed wrap flag. 2023-02-28 22:56:39 +00:00
James Holderness
841a064401 Reset the delayed wrap flag when required. 2023-02-28 22:54:28 +00:00
Mike Griese
17dbc7b5ce That's it. Ship the PR 2023-02-28 14:41:56 -06:00
Mike Griese
08acc90e5f I'm amazing. Say it. I'm amazing 2023-02-28 14:26:00 -06:00
Mike Griese
4f8d64ec6f non-client borders accounted for: [X] 2023-02-28 13:58:10 -06:00
Mike Griese
5164bffa78 hey that's a good point carlos 2023-02-28 12:04:24 -06:00
Mike Griese
ada3f427a0 well that is an underwhelming codeformat commit. Thanks VS 2023-02-28 11:56:37 -06:00
Mike Griese
5e23a72beb Merge branch 'dev/migrie/oop/3/foreword' into dev/migrie/oop/3/ainulindale 2023-02-28 11:55:17 -06:00
Mike Griese
0199aba286 the last nits, I think 2023-02-28 11:29:53 -06:00
Mike Griese
b589d092e9 Lots of PR nits 2023-02-28 11:22:29 -06:00
Mike Griese
74260ce6e1 some gross dead code 2023-02-28 10:09:46 -06:00
Mike Griese
01388c95b0 some todo notes 2023-02-28 10:05:29 -06:00
Mike Griese
fa1140dc07 better scaling 2023-02-27 17:08:46 -06:00
Mike Griese
e1472095e2 events aren't really scaled correctly, but it does work 2023-02-27 16:54:17 -06:00
Mike Griese
b9dc8c3f5b Really, I did it on the first try 2023-02-27 14:25:29 -06:00
Mike Griese
055cdfedd0 moveTab to a nonexistent window creates that window.
This was actually easy enough that I'm tempted to include it in the prior branches
2023-02-27 14:03:25 -06:00
Mike Griese
257fe33269 Merge branch 'dev/migrie/oop/3/quenta-silmarillion' into dev/migrie/oop/3/akallabeth 2023-02-27 10:22:43 -06:00
Mike Griese
34bc66f8bf Merge branch 'dev/migrie/oop/3/valaquenta' into dev/migrie/oop/3/quenta-silmarillion 2023-02-27 10:20:55 -06:00
Mike Griese
6934bc8d05 Merge branch 'dev/migrie/oop/3/ainulindale' into dev/migrie/oop/3/valaquenta 2023-02-27 10:19:35 -06:00
Mike Griese
cacb822cf6 Dustin wanted a setting to opt-in to isolated mode, and this seems to work 2023-02-27 10:16:22 -06:00
Mike Griese
b75fb24512 Merge branch 'dev/migrie/oop/3/foreword' into dev/migrie/oop/3/ainulindale 2023-02-27 09:34:59 -06:00
Mike Griese
c79f27c3da std::vectors are better 2023-02-27 08:32:18 -06:00
Mike Griese
57a53279ae very confident we can get all the logic into the sxnui 2023-02-24 16:43:02 -06:00
Mike Griese
5defde545f POC: We can take it out of the Popup and have it still work 2023-02-24 16:20:41 -06:00
Mike Griese
c9e7156a59 notes, cleanup, order the history correctly 2023-02-24 15:46:35 -06:00
Mike Griese
dc71f0c346 align to the cursor in bottoms up mode 2023-02-24 14:54:21 -06:00
Mike Griese
f4c310a154 make this actually work again 2023-02-24 14:11:29 -06:00
Mike Griese
f3796da794 mostly all the plumbing to get this back into a reasonable state 2023-02-24 13:32:54 -06:00
Mike Griese
c839bfa2ca Cleanup for... review? 2023-02-24 11:30:12 -06:00
Mike Griese
06874ee321 dirty, but now with tooltips 2023-02-24 10:48:36 -06:00
Mike Griese
36b5759604 [PARENT] lmao I deleted the line that sends the inpu :facepalm" 2023-02-24 10:43:30 -06:00
Mike Griese
273f2a84ef Merge remote-tracking branch 'origin/main' into dev/migrie/oop/3/foreword 2023-02-23 16:56:15 -06:00
Mike Griese
d2a5a4aab7 [silmarillion] Disable the UIA renderer before detaching
(cherry picked from commit b1a867db6c)
2023-02-23 16:53:59 -06:00
Mike Griese
9a4739659b [ainulindale] The Terminal shouldn't crash on a WM_SETTINGCHANGE
(cherry picked from commit 7649725150)
2023-02-23 16:53:07 -06:00
Mike Griese
7649725150 [ainulindale] The Terminal shouldn't crash on a WM_SETTINGCHANGE 2023-02-23 16:51:16 -06:00
Mike Griese
84a41b4504 cleanup _for review_? 2023-02-23 16:34:16 -06:00
Mike Griese
7988d89c08 code cleanup 2023-02-23 14:08:07 -06:00
Mike Griese
a3c4776355 clamp horizontally 2023-02-23 14:01:36 -06:00
Mike Griese
b1a867db6c [silmarillion] Disable the UIA renderer before detaching 2023-02-23 12:26:05 -06:00
Mike Griese
614ec58c31 spel? I think that's it? 2023-02-23 10:25:50 -06:00
Mike Griese
70450afe7e 0 TODOs left 2023-02-23 10:24:22 -06:00
Mike Griese
9ec9da3da2 ACTUALLY put the tab in the right place after dropping
1 TODOs left
2023-02-23 09:38:55 -06:00
Mike Griese
b5dbae9244 OMEGA BODGE 2023-02-23 09:36:57 -06:00
Mike Griese
d67815bf0c Try to drag/drop to the rigt place in the tab row, only to discover that we ALWAYS drag the 0th tab now. Peculiar. 2023-02-23 08:27:35 -06:00
Mike Griese
4516b4b1b3 Properly account for the position of panes when opening the menu 2023-02-22 14:51:47 -06:00
Mike Griese
6586c4a816 Use the correct tab when dragging ; check the PID too
4 TODOs left
2023-02-22 14:42:30 -06:00
Mike Griese
f5909d9baa Open in different directions based on available space 2023-02-22 13:56:01 -06:00
Mike Griese
0cf13d81b1 Notes, cleanup. 6 TODOs remain 2023-02-22 11:24:30 -06:00
Mike Griese
2537342ffb On drop, ask the sender to send content to us.
The incredibly dumb solution I considered.

    As previously outlined in https://github.com/microsoft/terminal/issues/5000#issuecomment-1435328038
2023-02-22 11:11:06 -06:00
Mike Griese
8feb9098b9 bottom-up mode is basically done 2023-02-21 15:52:39 -06:00
James Holderness
3cb7cd731f Rewrite line feed adapter test. 2023-02-21 21:51:28 +00:00
James Holderness
d7b3e494df Workaround for removed margin API in screen buffer tests. 2023-02-21 21:14:08 +00:00
James Holderness
90bd976c75 Rewrite margin adapter test. 2023-02-21 21:14:08 +00:00
James Holderness
1b9498563f Remove unneeded margin APIs. 2023-02-21 21:14:08 +00:00
James Holderness
dbd9ef32ed Remove unneeded VT code from AdjustCursorPosition. 2023-02-21 21:14:08 +00:00
James Holderness
5042d82b30 Removed unused LineFeed API. 2023-02-21 21:14:07 +00:00
James Holderness
aa93940da9 Fix unit tests. 2023-02-21 21:14:03 +00:00
Mike Griese
d94183b897 icons are _slick_ 2023-02-21 14:25:58 -06:00
Mike Griese
c6cce0fd6b Revert "Everything-is-awful.wav"
This reverts commit b41ae7115c.
2023-02-21 12:41:31 -06:00
Mike Griese
b41ae7115c Everything-is-awful.wav 2023-02-21 12:41:21 -06:00
Mike Griese
c2a2caf029 Fixes for a bad merge 2023-02-21 12:41:01 -06:00
Mike Griese
2c66c32f97 bottoms up bottoms up 2023-02-21 11:20:03 -06:00
Mike Griese
ff5eead9ea Very important that the backspaces are trimmed from the preview 2023-02-21 10:25:55 -06:00
Mike Griese
026f342bc5 Merge remote-tracking branch 'origin/dev/migrie/f/3121-wE-dOnT-hAvE-dEv-DaYs' into dev/migrie/fhl-2023/pwsh-autocomplete-demo 2023-02-21 10:00:20 -06:00
James Holderness
b8f8ef311a Make sure ED sequence handles buffer rotation correctly. 2023-02-18 11:10:18 +00:00
James Holderness
b6974f0de5 Allow for multiple buffer rotation cycles. 2023-02-18 11:10:18 +00:00
James Holderness
26ebc0a359 Add API for conhost/terminal specific behavior. 2023-02-18 11:10:18 +00:00
James Holderness
59f9c64ad6 Implement the line feed functionality in AdaptDispatch. 2023-02-18 11:10:18 +00:00
Mike Griese
951ece1f3e Merge branch 'dev/migrie/oop/3/quenta-silmarillion' into dev/migrie/oop/3/akallabeth 2023-02-17 11:23:22 -06:00
Mike Griese
1f8766e972 shockingly few spel 2023-02-17 11:06:13 -06:00
Mike Griese
091660e96d cleanup and comments before the PR 2023-02-17 11:02:53 -06:00
Mike Griese
0e0d8572a3 Select the right tab before spliting it; movePane with a single pane works again 2023-02-17 10:21:18 -06:00
Mike Griese
48e7348f5c small updates to the protocol 2023-02-16 13:04:13 -06:00
Mike Griese
e785bfc8bb Merge branch 'main' into dev/migrie/f/12861-preview-input 2023-02-16 13:02:40 -06:00
Mike Griese
9da1192993 Merge branch 'dev/migrie/oop/3/valaquenta' into dev/migrie/oop/3/quenta-silmarillion 2023-02-16 12:00:55 -06:00
Mike Griese
f9caf191a9 Merge branch 'dev/migrie/oop/3/ainulindale' into dev/migrie/oop/3/valaquenta 2023-02-16 12:00:38 -06:00
Mike Griese
13257dafa0 0 TODOs remain 2023-02-16 11:11:09 -06:00
Mike Griese
ecab57fba1 that was quite silly 2023-02-16 11:10:06 -06:00
Mike Griese
091f32c575 a comment to go with the parent commit 2023-02-15 15:43:20 -06:00
Mike Griese
62a4ee058b https://media.tenor.com/knFWHfkiwoUAAAAC/im-fine-hades.gif 2023-02-15 15:39:23 -06:00
Mike Griese
2bc578bc6c Revert "I'm committing everything just in case, but I'm gonna revert and just do the part that fixed it"
This reverts commit 667b6586d2.
2023-02-15 15:33:49 -06:00
Mike Griese
667b6586d2 I'm committing everything just in case, but I'm gonna revert and just do the part that fixed it 2023-02-15 15:33:36 -06:00
Mike Griese
bc80943fd9 the tests all...pass? 2023-02-14 17:07:59 -06:00
Mike Griese
aa8b0c564d Merge branch 'dev/migrie/oop/3/foreword' into dev/migrie/oop/3/ainulindale 2023-02-14 16:47:11 -06:00
Mike Griese
118bffa6cd fix the tests 2023-02-14 15:51:18 -06:00
Mike Griese
f10dfac20f some reliability when moving tabs that currently have output across the boundary 2023-02-14 12:45:35 -06:00
Mike Griese
d01cab370f Merge branch 'main' into dev/migrie/f/3337-just-for-funsies 2023-02-14 12:20:02 -06:00
Mike Griese
e5f98807e2 cleanup to get ready for review 2023-02-14 11:34:00 -06:00
Mike Griese
8cac90fc4d Merge branch 'dev/migrie/oop/3/valaquenta' into dev/migrie/oop/3/quenta-silmarillion 2023-02-14 09:49:54 -06:00
Mike Griese
d06ad8b5dc spel 2023-02-14 09:48:19 -06:00
Mike Griese
4b22963f9d notes 2023-02-14 09:18:48 -06:00
Mike Griese
855a79dbe0 Add auto_revokers to all TermControl ev handlers, but that wasn't what ended up fixing this bug.
All we actually needed was to do a _FontSizeChangedHandlers() to set up the font size and the markers in the TermControl.
2023-02-14 09:08:23 -06:00
Mike Griese
28735112d2 Don't add multiple event handlers for the core
Basically, because we were projecting the event straight trhough the control, from core->page, basically when we would do a PasteRequested, we'd call the handler registered on the first control, then also the handler added by the second.
2023-02-14 09:00:40 -06:00
Mike Griese
83da057a70 Merge branch 'dev/migrie/oop/3/quenta-silmarillion' into dev/migrie/oop/3/akallabeth 2023-02-14 08:48:32 -06:00
Mike Griese
3988a1c4e5 [silmarillion] Fix moving a tab with panes. Now, the non-first panes don't rebuild themselves on move
(cherry picked from commit 7d903dea4a)
2023-02-14 08:48:12 -06:00
Mike Griese
2262f5574e Merge branch 'dev/migrie/oop/3/valaquenta' into dev/migrie/oop/3/quenta-silmarillion 2023-02-14 08:47:57 -06:00
Mike Griese
77346000df [valaquenta] Lifetime manage controls
(cherry picked from commit 4eba01f919)
2023-02-14 08:39:25 -06:00
Mike Griese
7d903dea4a [silmarillion] Fix moving a tab with panes. Now, the non-first panes don't rebuild themselves on move 2023-02-13 17:13:59 -06:00
Mike Griese
4eba01f919 [valaquenta] Lifetime manage controls 2023-02-13 16:08:35 -06:00
Mike Griese
f0802618e8 Merge branch 'dev/migrie/oop/3/quenta-silmarillion' into dev/migrie/oop/3/akallabeth 2023-02-13 14:27:34 -06:00
Mike Griese
68fad338cc Merge branch 'dev/migrie/oop/3/valaquenta' into dev/migrie/oop/3/quenta-silmarillion 2023-02-13 14:27:15 -06:00
Mike Griese
0f0316f322 Merge branch 'dev/migrie/oop/3/ainulindale' into dev/migrie/oop/3/valaquenta 2023-02-13 14:25:56 -06:00
Mike Griese
3f9decaa82 Merge branch 'dev/migrie/oop/3/foreword' into dev/migrie/oop/3/ainulindale 2023-02-13 14:25:36 -06:00
Mike Griese
d9d4d2e62d get the tests to build, at least 2023-02-13 12:08:54 -06:00
Mike Griese
82224bc87a this was usnused 2023-02-13 11:48:32 -06:00
Mike Griese
a9ac218c4d runformat 2023-02-13 10:57:19 -06:00
Mike Griese
603a2ce53a spel 2023-02-13 10:56:31 -06:00
Mike Griese
2621519f20 cleanup for review 2023-02-13 10:46:37 -06:00
Mike Griese
1ec8c0de2e Merge branch 'dev/migrie/oop/3/foreword' into dev/migrie/oop/3/ainulindale 2023-02-13 09:30:23 -06:00
Mike Griese
a4f19a9dff spel 2023-02-13 09:28:13 -06:00
Mike Griese
33685d9e9d bwahahahahaha 2023-02-13 09:26:51 -06:00
Mike Griese
65084c84ec Merge branch 'dev/migrie/oop/3/quenta-silmarillion' into dev/migrie/oop/3/akallabeth 2023-02-13 09:23:25 -06:00
Mike Griese
0ce6309c11 Merge branch 'dev/migrie/oop/3/valaquenta' into dev/migrie/oop/3/quenta-silmarillion 2023-02-13 08:51:44 -06:00
Mike Griese
985fcdbdb6 better UX for typing 2023-02-12 15:37:36 -06:00
Mike Griese
b0fa972ec1 make the menu mode compact, and remove the search box 2023-02-12 11:07:00 -06:00
Mike Griese
c0658975b2 Merge branch 'dev/migrie/oop/3/ainulindale' into dev/migrie/oop/3/valaquenta 2023-02-11 07:05:46 -06:00
Mike Griese
a76993365e merges are hard some times 2023-02-10 17:21:55 -06:00
Mike Griese
dffb41601b Merge branch 'dev/migrie/oop/3/foreword' into dev/migrie/oop/3/ainulindale 2023-02-10 17:09:29 -06:00
Mike Griese
e40575b18b let's do it 2023-02-10 16:50:58 -06:00
Mike Griese
cfa61088fe Merge remote-tracking branch 'origin/main' into dev/migrie/oop/3/foreword 2023-02-10 13:24:49 -06:00
Mike Griese
0ad5b596bd I think it's ready for review 2023-02-10 13:24:15 -06:00
Mike Griese
7660937139 and with that, we're ready to cleanup for review.
2 TODOs left
2023-02-10 13:05:57 -06:00
Mike Griese
45487294a0 Some additional cleanup of the WindowManager code
3 TODOs left
2023-02-10 11:16:37 -06:00
Mike Griese
f904e5d22d I give up and am moving on to the next TODOs. 8 TODOs remain 2023-02-10 09:48:08 -06:00
Mike Griese
f361b6c879 lots of removal of dead code from the sxnui 2023-02-10 06:55:16 -06:00
Mike Griese
7404dc3d35 zhu li, do the thing 2023-02-10 06:02:03 -06:00
Mike Griese
0395dc40a3 Revert "This actually seems to make the lifetime management worse - we just dtor the WindowEmperor randomly now, which is no good"
This reverts commit 7e91bdb289.
2023-02-09 16:39:15 -06:00
Mike Griese
7e91bdb289 This actually seems to make the lifetime management worse - we just dtor the WindowEmperor randomly now, which is no good 2023-02-09 16:39:07 -06:00
Mike Griese
c69f0bc444 Quake logic seems to work again.
Down to just 10 TODOs left
2023-02-09 14:16:27 -06:00
Mike Griese
e214624e1f Re-add support for the notification icon.
14 TODOs left
2023-02-09 13:52:44 -06:00
Mike Griese
b0726c2057 Handle Quit actions
16 TODOs
2023-02-09 12:54:23 -06:00
Mike Griese
055da357b1 Readd ShouldImmediatelyHandoffToElevated
17 TODOs remain
2023-02-09 12:33:43 -06:00
Mike Griese
950ce6c4fc make pane brushes a member variable, which is good enough
(cherry picked from commit 7bad8c9642)
2023-02-09 12:27:19 -06:00
Mike Griese
0b79e81ae9 Summoning works, except for making new windows, but DANG 2023-02-09 11:03:10 -06:00
Mike Griese
a7379ca8e9 It so SO works 2023-02-09 10:51:50 -06:00
Mike Griese
c97ac66d5d resart with fresh plumbing 2023-02-09 06:31:44 -06:00
Mike Griese
4d5f6d27fb cleanup. 18 TODOs remain 2023-02-08 17:26:33 -06:00
Mike Griese
3fb8e8cac3 Hey this worked!
The trick is, that if you do this while debugging, the AppState will get persisted as you're stepping through, which can make debugging this really tricky
2023-02-08 17:13:15 -06:00
Mike Griese
6e6d14e0dc We're getting closer to loading up the previous state, but it doesn't always work as expected
20 TODOs left
2023-02-08 15:29:04 -06:00
Mike Griese
a5a9930354 Hey look I brought two windows back into existence!...
They weren't from the persisted JSON, but they did come back

  21 TODOs left
2023-02-08 13:25:41 -06:00
Mike Griese
0f4c4d8eef It persists, but it doesn't restore yet.
21 TODOs left
2023-02-08 12:56:22 -06:00
Mike Griese
8bb839113a move more window persist code around.
It doesn't save or restore, but it does seem to not crash.

  24 TODOs left
2023-02-08 12:42:12 -06:00
Mike Griese
dc1ae9a895 Starting to move these things to the right places 2023-02-08 12:08:28 -06:00
Mike Griese
84e228f1fe Start to move window restoration into the Emperor
I think I have a vision for it now. 25 TODOs left.
2023-02-08 11:05:43 -06:00
Mike Griese
3026922e59 Fix the window name not updating as it changed. Added some notes, so, 26 TODOs left 2023-02-08 09:57:45 -06:00
Mike Griese
64257d830a This straight up isn't even used in main anymore, 26 TODOs left 2023-02-08 09:31:58 -06:00
Mike Griese
4db381e2cb not totally happy with this, but I understand why. 29 TODOs left 2023-02-08 09:10:09 -06:00
Mike Griese
40fdbc1536 Hey this comment was TODOne, 29 TODOs left 2023-02-08 09:02:32 -06:00
Mike Griese
07ff4183e4 Plumb initial load result into the window on startup
30 TODOs left
2023-02-08 09:01:48 -06:00
Mike Griese
d5396d1104 UWP is dead
32 TODOs
2023-02-08 08:45:05 -06:00
Mike Griese
93e9dc505d I guess we need to just hardcode these sizes. Sad, but okay.
33 TODOs
2023-02-08 08:44:49 -06:00
Mike Griese
761bd6a6ab This one was dead code, 34 TODOs left 2023-02-08 08:07:38 -06:00
Mike Griese
23c4d4c0f8 Move the initialization of the showHide Throttler after we init the dispatcher.
35 TODOs left
2023-02-08 08:05:46 -06:00
Mike Griese
4e7da2ec13 I guess this is just dead now
36 TODOs
2023-02-08 06:51:44 -06:00
Mike Griese
2332f0c0e4 Don't try to snap on create if we're not prepared to snap
37 TODOs left
2023-02-08 06:51:29 -06:00
Mike Griese
337b7b5699 Merge branch 'dev/migrie/oop/3/quenta-silmarillion' into dev/migrie/oop/3/akallabeth 2023-02-07 17:07:31 -06:00
Mike Griese
a7353ac347 Merge branch 'dev/migrie/oop/3/valaquenta' into dev/migrie/oop/3/quenta-silmarillion 2023-02-07 17:07:09 -06:00
Mike Griese
f27db5997e Merge branch 'dev/migrie/oop/3/ainulindale' into dev/migrie/oop/3/valaquenta 2023-02-07 17:05:48 -06:00
Mike Griese
2822c36507 [ainulindale] Expand commands in the AppLogic, not on each page
TerminalPage is the thing that ends up expanding iterable Command. It does
  this largely with copies - it makes a new map, a new vector, copies the
  Commands over, and does the work there before setting up the cmdpal.

  Except, it's not making a copy of the Commands, it's making a copy of the
  vector, with winrt objects all pointing at the Command objects that are
  ultimately owned by CascadiaSettings.

  This doesn't matter if there's only one TerminalPage - we'll only ever do that once.

  If there's many, on different threads, then one tpage will end up expanding
  the subcommands of one Command while another tpage is ALSO iterating on those
  subcommands. Hence why I'm getting `hresult_changed_state`s

(cherry picked from commit 2122eec186)
2023-02-07 17:04:25 -06:00
Mike Griese
f06e484324 [ainulindale] This I'm confident fixes some of the x-thread errors, but this doesn't fix the crash
(cherry picked from commit 700aadcb15)
2023-02-07 17:03:41 -06:00
Mike Griese
9924e23dec [ainulindale] "fix" hot reload
Doesn't work with multiple windows open, but doesn't do _nothing_

(cherry picked from commit 427a4a51c5)
2023-02-07 17:02:25 -06:00
Mike Griese
47336c0982 [ainulindale] Clean tear down the App when the process exits
(cherry picked from commit 57d1dd4358)
2023-02-07 17:00:28 -06:00
Mike Griese
2122eec186 [ainulindale] Expand commands in the AppLogic, not on each page
TerminalPage is the thing that ends up expanding iterable Command. It does
  this largely with copies - it makes a new map, a new vector, copies the
  Commands over, and does the work there before setting up the cmdpal.

  Except, it's not making a copy of the Commands, it's making a copy of the
  vector, with winrt objects all pointing at the Command objects that are
  ultimately owned by CascadiaSettings.

  This doesn't matter if there's only one TerminalPage - we'll only ever do that once.

  If there's many, on different threads, then one tpage will end up expanding
  the subcommands of one Command while another tpage is ALSO iterating on those
  subcommands. Hence why I'm getting `hresult_changed_state`s
2023-02-07 16:57:23 -06:00
Mike Griese
81524ea71e Revert "This might all be wrong"
This reverts commit e3fa46ff94.
2023-02-07 15:07:15 -06:00
Mike Griese
e3fa46ff94 This might all be wrong 2023-02-07 15:07:03 -06:00
Mike Griese
38b28e5bdb [foreward]? This is literally never used. This can go in main. 2023-02-07 14:49:09 -06:00
Mike Griese
700aadcb15 [ainulindale] This I'm confident fixes some of the x-thread errors, but this doesn't fix the crash 2023-02-07 14:48:20 -06:00
Mike Griese
7bad8c9642 make pane brushes a member variable, which is good enough 2023-02-07 13:06:06 -06:00
Mike Griese
eea6a7bcca Add auto_revokers to all TermControl ev handlers, but that wasn't what ended up fixing this bug.
All we actually needed was to do a _FontSizeChangedHandlers() to set up the font size and the markers in the TermControl.
2023-02-07 12:55:44 -06:00
Mike Griese
1cef94e696 Don't add multiple event handlers for the core
Basically, because we were projecting the event straight trhough the control, from core->page, basically when we would do a PasteRequested, we'd call the handler registered on the first control, then also the handler added by the second.
2023-02-07 11:43:10 -06:00
Mike Griese
c9ed8dc042 When we detach a tab, Close()ing the control shouldn't close the content. 2023-02-07 11:20:43 -06:00
Mike Griese
e2882055ee Interestingly, the order matters 2023-02-07 11:19:55 -06:00
Mike Griese
77e4289dcb oh boy missed a pair 2023-02-07 08:12:53 -06:00
Mike Griese
2a6520dfef cleanup 2023-02-07 06:11:26 -06:00
Mike Griese
35b519a175 use resorces 2023-02-07 05:57:03 -06:00
Mike Griese
ee63aff320 this fixes the selection crash. This makes sense - the original TermControl was kept alive by listeners to events on the Core, who's now on another thread 2023-02-06 14:19:49 -06:00
Mike Griese
ad1f331154 Merge branch 'dev/migrie/oop/3/quenta-silmarillion' into dev/migrie/oop/3/akallabeth
# Conflicts:
#	src/cascadia/TerminalApp/TerminalPage.h
2023-02-06 14:04:34 -06:00
Mike Griese
c82051ffdd [silmarillion] Pull DetachTab into a method
(cherry picked from commit ba7b5aec79)
2023-02-06 14:03:56 -06:00
Mike Griese
11f6561153 [silmarillion] Detach on moveTab, but not on drag
It remains to be seen if this worked for movePane, but one step at a time

(cherry picked from commit c282797cac)
2023-02-06 14:02:55 -06:00
Mike Griese
380f0295ca [silmarillion] Update the validation for the moveTab action
(cherry picked from commit a20201fff2)
2023-02-06 14:02:37 -06:00
Mike Griese
828695ee8a Detaching during tearout too 2023-02-06 14:00:00 -06:00
Mike Griese
ba7b5aec79 [silmarillion] Pull DetachTab into a method 2023-02-06 13:59:47 -06:00
Mike Griese
c282797cac [silmarillion] Detach on moveTab, but not on drag
It remains to be seen if this worked for movePane, but one step at a time
2023-02-06 13:48:52 -06:00
Mike Griese
a20201fff2 [silmarillion] Update the validation for the moveTab action 2023-02-06 13:42:54 -06:00
Mike Griese
0e546a62e0 Merge remote-tracking branch 'origin/main' into dev/migrie/f/4588-select-shell-output 2023-02-05 16:34:55 -06:00
Mike Griese
a1d532901c Revert "okay maybe I have silly free time ideas too"
This reverts commit eaf1a920e1.
2023-02-05 15:30:32 -06:00
Mike Griese
564102b36f Revert "forwarded_event, string tests"
This reverts commit 7001e93550.
2023-02-05 15:30:15 -06:00
Mike Griese
9ff0abe834 Revert "practical use, look"
This reverts commit eb88cd3a2d.
2023-02-05 15:30:13 -06:00
Mike Griese
559ecdb358 Revert "I suppose this is what happens when I have four uninterrupted hours away from the kid"
This reverts commit b535a5f3f3.
2023-02-05 15:30:09 -06:00
Mike Griese
ab55a8df7b Revert "sad beeps"
This reverts commit 520c89f1fe.
2023-02-05 15:30:00 -06:00
Mike Griese
c77f959c05 PR nits 2023-02-05 15:29:52 -06:00
Mike Griese
40046c38dc one shot one opportunity
Cherry-picked from 4b4e057a7
2023-02-03 13:40:37 -06:00
Mike Griese
427a4a51c5 [ainulindale] "fix" hot reload
Doesn't work with multiple windows open, but doesn't do _nothing_
2023-02-03 13:28:30 -06:00
Mike Griese
17057d147d (maybe valaquenta) well, the keybindings stuff is... better? 2023-02-03 10:45:20 -06:00
Mike Griese
631f690e42 remove the old control when the content gets moved 2023-02-03 09:41:16 -06:00
Mike Griese
57d1dd4358 [ainulindale] Clean tear down the App when the process exits 2023-02-03 09:15:53 -06:00
Mike Griese
713ea71725 Revert "this is important for moving, yknow, the panes"
This reverts commit 0a6c21fed0.
2023-02-03 09:10:17 -06:00
Mike Griese
0f33913423 actually start drawing to the new control when we move it 2023-02-03 09:10:04 -06:00
Mike Griese
7aef3afef8 bommand 2023-02-03 05:33:49 -06:00
Mike Griese
d6fe520843 'cleanup 2023-02-02 17:39:08 -06:00
Mike Griese
08ae0ece68 cleanup for review 2023-02-02 16:02:27 -06:00
Mike Griese
0328b4f720 icons, and dismiss the menu when the action's done 2023-02-02 14:56:33 -06:00
Mike Griese
854335b8bb cleanup; don't duplicate selection entries each time; open in the right place 2023-02-02 14:22:40 -06:00
Mike Griese
1f56807fa0 move to int IDs is okay too 2023-02-02 12:28:46 -06:00
Mike Griese
0a6c21fed0 this is important for moving, yknow, the panes 2023-02-02 12:09:25 -06:00
Mike Griese
ebe5fd8163 can I get a whoop whoop (Adds suuport for moving tabs to another window)
cherry-picked from 4acbffb0e
2023-02-02 11:58:05 -06:00
Mike Griese
d143f312d3 Add comments
This is basically the comment from aaefdf4408. The other comment wasn't relevant anymore
2023-02-02 11:30:01 -06:00
Mike Griese
3bd6957a6d This works to open a new pane in the new window...
but it doesn't render, and the old pane keeps on choochin.

  And it only works with a window name, not and ID

  And the `Dispatcher` definitely needs to get re-wired for the new thread, cause dragging it across a DPI boundary (aka, resize) crashes the window
2023-02-02 11:22:36 -06:00
Mike Griese
6a2239d487 Pane officially opened via the serialized actions, via across the process boundary
cherry-picked from 1f2bb760e
2023-02-02 10:52:45 -06:00
Mike Griese
4dcc67b4d7 Serialize the pane to a Content string, instead of just the guid.
There's a lot of renaming, signature changes here. But StartupActions is a good mechanism for passing panes around, more or less.

cherry-picked from cfe879da9
2023-02-02 08:40:45 -06:00
Mike Griese
37a8fde29c Holy shit all the plumbing worked on the first try
cherry-picked from 9870eb1d39
2023-02-02 08:16:37 -06:00
Mike Griese
2ee4111ca0 adapt the previous action adder for the new menu 2023-02-02 04:47:29 -06:00
Mike Griese
1b4f1efb09 I literally think this is the whole thing 2023-02-01 17:08:37 -06:00
Mike Griese
e623299ac6 notes 2023-02-01 14:12:31 -06:00
Mike Griese
274d62d8a8 it's working 2023-02-01 13:56:40 -06:00
Mike Griese
f655296c1e This does successfully get a window on the screen, which is pretty impressive
It exits itself after 30s, but hey it worked
2023-02-01 13:15:44 -06:00
Mike Griese
7a3e2e098d Start rewriting WindowManager to facilitate the new process model
At this point, I determined that I would need to make some big changes to
  AppHost and decided that it was time to commit before moving on.
2023-02-01 11:48:43 -06:00
Mike Griese
2c4613a494 wow it just... worked 2023-02-01 08:07:29 -06:00
Mike Griese
2dd01fbb69 open at the mouse 2023-02-01 05:53:31 -06:00
Mike Griese
6a600533c6 a commandbarflyout intsead, with a slightly more idomatic implementation 2023-02-01 05:39:11 -06:00
Mike Griese
f5b030c28c it runs! 2023-01-31 15:21:29 -06:00
Mike Griese
d456210f37 Use the single AppLogic for all windows. Remove the _app references. It launches and crashes immediately. We'll keep shuffling code. 2023-01-31 12:59:30 -06:00
Mike Griese
af14c2b751 [TO PARENT] Move the page ctor call, so that it can happen after the XAML island is started.
I think this can work in the parent at least
2023-01-31 12:58:39 -06:00
Mike Griese
ef7e2edfa5 Merge branch 'dev/migrie/oop/3/foreword' into dev/migrie/oop/3/ainulindale
# Conflicts:
#	src/cascadia/WindowsTerminal/AppHost.h
2023-01-31 11:11:35 -06:00
Mike Griese
5116ca1e77 I think the todo's that are left, we can move on without them for now. 2023-01-31 10:47:46 -06:00
Mike Griese
2195515937 it launches 2023-01-31 10:44:38 -06:00
Mike Griese
99bc280207 It doesn't crash on launch. That's something. There's no startupActions though, so it immediately exits 2023-01-31 09:34:02 -06:00
Mike Griese
439b21f879 this is dangerously close to compiling 2023-01-30 17:06:31 -06:00
Mike Griese
936c01f948 Start splitting AppLogic into AppLogic and Window logic
We'll need this for #5000, for ainulindale. This refactoring will be annoying
  enough as it is so we may as well do it as a first, separate PR.
2023-01-30 14:52:19 -06:00
Mike Griese
e6220b7fe7 Revert "I don't think I want any of these"
This reverts commit a5255ba8ed.
2023-01-30 12:37:35 -06:00
Mike Griese
a5255ba8ed I don't think I want any of these 2023-01-30 12:37:26 -06:00
Mike Griese
581acd40d9 I definitely want all of this. But I started down a path for refactoring AppLogic that I hate so I'm gonna start over 2023-01-30 12:37:12 -06:00
Mike Griese
191bca8e57 Merge branch 'main' into dev/migrie/f/3337-just-for-funsies 2023-01-29 09:57:12 -06:00
Mike Griese
ce6a2e79cb you-donkey.png 2022-12-15 18:02:33 -06:00
Mike Griese
e76564eb9d this makes it _less_ painful 2022-12-15 14:08:42 -06:00
Mike Griese
d867a6179f Add support for going up and down 2022-12-15 13:55:28 -06:00
Mike Griese
dbb4db3cc8 this gets the selection region actually correct 2022-12-14 13:37:38 -06:00
Mike Griese
23f9527280 this actually just selects the command+1 character 2022-12-14 13:12:05 -06:00
Mike Griese
5398ea3aa7 mostly, the plumbing for this feature 2022-12-14 12:50:18 -06:00
Dustin L. Howett
61a9c8090a Migrate spelling-0.0.21 changes from main 2022-10-25 14:59:43 -05:00
Mike Griese
dc2ebaf806 use resource strings too. Last commit of stuff from before paternity leave 2022-10-25 14:59:43 -05:00
Mike Griese
99b84a60b7 Merge remote-tracking branch 'origin/main' into dev/migrie/f/3337-just-for-funsies 2022-09-20 13:18:42 -05:00
Mike Griese
451d8f3609 move this code to interactivity. Scaling of event is wrong now 2022-09-19 07:33:01 -05:00
Mike Griese
29d454ba4c 90% plumbing, 10% bad implementation of making sure we right-clicked then released 2022-08-28 10:21:08 -05:00
Mike Griese
e6b33c3740 yea that's everything 2022-08-14 12:47:42 -05:00
Mike Griese
eb7d2be2e8 I am incapable of not making 100 tiny commits, this was already way to friggin big. 2022-08-13 14:49:38 -05:00
Mike Griese
520c89f1fe sad beeps 2022-07-23 15:07:38 -07:00
Mike Griese
b535a5f3f3 I suppose this is what happens when I have four uninterrupted hours away from the kid 2022-07-23 14:31:58 -07:00
Mike Griese
eb88cd3a2d practical use, look 2022-07-23 14:12:39 -07:00
Mike Griese
7001e93550 forwarded_event, string tests 2022-07-23 13:57:26 -07:00
Mike Griese
eaf1a920e1 okay maybe I have silly free time ideas too 2022-07-23 11:58:54 -07:00
Dustin L. Howett
ccfc83443b Migrate spelling-0.0.21 changes from main 2022-04-29 06:55:15 -05:00
Mike Griese
0bda66fc2f a comment I missed 2022-04-29 06:55:15 -05:00
Mike Griese
d3b5533a1e fix remaining bugs 2022-04-29 06:22:48 -05:00
Mike Griese
1449088e80 bugfixes for the demo 2022-04-28 16:17:21 -05:00
Mike Griese
c96799c6e9 Preview the input via the TSF input control. This is awesome, and should go into main 2022-04-28 16:14:51 -05:00
165 changed files with 16630 additions and 4356 deletions

View File

@@ -93,6 +93,7 @@ slnt
Sos
ssh
stakeholders
sxn
timeline
timelines
timestamped

View File

@@ -160,6 +160,7 @@ rcx
REGCLS
RETURNCMD
rfind
RLO
ROOTOWNER
roundf
RSHIFT

View File

@@ -311,6 +311,7 @@ CPLINFO
cplusplus
CPPCORECHECK
cppcorecheckrules
cpprest
cpprestsdk
cppwinrt
CProc
@@ -1436,6 +1437,7 @@ PPEB
ppf
ppguid
ppidl
pplx
PPROC
PPROCESS
ppropvar
@@ -2113,6 +2115,7 @@ WDDMCONSOLECONTEXT
wdm
webpage
websites
websockets
wekyb
wex
wextest

View File

@@ -6,7 +6,7 @@
"C_Cpp.loggingLevel": "None",
"files.associations": {
"xstring": "cpp",
"*.idl": "cpp",
"*.idl": "midl3",
"array": "cpp",
"future": "cpp",
"istream": "cpp",
@@ -106,4 +106,4 @@
"**/packages/**": true,
"**/Generated Files/**": true
}
}
}

View File

@@ -1822,7 +1822,7 @@
"name": {
"type": "string",
"description": "The name of the theme. This will be displayed in the settings UI.",
"not": {
"not": {
"enum": [ "light", "dark", "system" ]
}
},
@@ -2092,6 +2092,11 @@
"description": "When set to true, the terminal will focus the pane on mouse hover.",
"type": "boolean"
},
"compatibility.isolatedMode": {
"default": false,
"description": "When set to true, Terminal windows will not be able to interact with each other (including global hotkeys, tab drag/drop, running commandlines in existing windows, etc.). This is a compatibility escape hatch for users who are running into certain windowing issues.",
"type": "boolean"
},
"copyFormatting": {
"default": true,
"description": "When set to `true`, the color and font formatting of selected text is also copied to your clipboard. When set to `false`, only plain text is copied to your clipboard. An array of specific formats can also be used. Supported array values include `html` and `rtf`. Plain text is always copied.",

View File

@@ -1,6 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<PropertyGroup Label="NuGet Dependencies">
<TerminalMUX>true</TerminalMUX>
</PropertyGroup>
<Import Project="$(OpenConsoleDir)src\wap-common.build.pre.props" />
<PropertyGroup Label="Configuration">
<!--
@@ -136,13 +141,14 @@
<!-- **END VC LIBS HACK** -->
<!-- This is required to get the package dependency in the AppXManifest. -->
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="$(OpenConsoleDir)src\common.nugetversions.targets" />
<!-- <Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets'))" />
</Target>
</Target> -->

View File

@@ -28,22 +28,60 @@ namespace winrt::SampleApp::implementation
{
auto settings = winrt::make_self<implementation::MySettings>();
auto connectionSettings{ TerminalConnection::ConptyConnection::CreateSettings(L"cmd.exe /k echo This TermControl is hosted in-proc...",
winrt::hstring{},
L"",
nullptr,
32,
80,
winrt::guid()) };
{
auto connectionSettings{ TerminalConnection::ConptyConnection::CreateSettings(L"cmd.exe /k echo This TermControl is hosted in-proc...",
winrt::hstring{},
L"",
nullptr,
32,
80,
winrt::guid()) };
// "Microsoft.Terminal.TerminalConnection.ConptyConnection"
winrt::hstring myClass{ winrt::name_of<TerminalConnection::ConptyConnection>() };
TerminalConnection::ConnectionInformation connectInfo{ myClass, connectionSettings };
// "Microsoft.Terminal.TerminalConnection.ConptyConnection"
winrt::hstring myClass{ winrt::name_of<TerminalConnection::ConptyConnection>() };
TerminalConnection::ConnectionInformation connectInfo{ myClass, connectionSettings };
TerminalConnection::ITerminalConnection conn{ TerminalConnection::ConnectionInformation::CreateConnection(connectInfo) };
Control::TermControl control{ *settings, *settings, conn };
TerminalConnection::ITerminalConnection conn{ TerminalConnection::ConnectionInformation::CreateConnection(connectInfo) };
Control::TermControl control{ *settings, *settings, conn };
InProcContent().Children().Append(control);
InProcContent().Children().Append(control);
}
{
settings->DefaultBackground(til::color{ 0x25, 0x25, 0x25 });
settings->AutoMarkPrompts(true);
auto envMap = winrt::single_threaded_map<winrt::hstring, winrt::hstring>();
envMap.Insert(L"PROMPT", L"$e]133;D$e\\$e]133;A$e\\$e]9;9;$P$e\\$P$G$e]133;B$e\\");
auto connectionSettings{ TerminalConnection::ConptyConnection::CreateSettings(L"cmd.exe /k echo This is a BlockControl...",
winrt::hstring{},
L"",
envMap.GetView(),
32,
80,
winrt::guid()) };
// "Microsoft.Terminal.TerminalConnection.ConptyConnection"
winrt::hstring myClass{ winrt::name_of<TerminalConnection::ConptyConnection>() };
TerminalConnection::ConnectionInformation connectInfo{ myClass, connectionSettings };
TerminalConnection::ITerminalConnection conn{ TerminalConnection::ConnectionInformation::CreateConnection(connectInfo) };
Control::BlockContent content{ *settings, conn };
Control::BlockControl control{ content };
control.NewBlock({ get_weak(), &MyPage::_newBlockHandler });
control.Height(256);
control.VerticalAlignment(WUX::VerticalAlignment::Top);
control.HorizontalAlignment(WUX::HorizontalAlignment::Stretch);
WUX::Controls::Grid wrapper{};
wrapper.VerticalAlignment(WUX::VerticalAlignment::Top);
wrapper.HorizontalAlignment(WUX::HorizontalAlignment::Stretch);
wrapper.CornerRadius(WUX::CornerRadiusHelper::FromRadii(6, 6, 6, 6));
wrapper.Margin(WUX::ThicknessHelper::FromLengths(0, 4, 0, 4));
wrapper.Children().Append(control);
OutOfProcContent().Children().Append(wrapper);
}
}
// Method Description:
@@ -58,4 +96,23 @@ namespace winrt::SampleApp::implementation
return { L"Sample Application" };
}
winrt::fire_and_forget MyPage::_newBlockHandler(IInspectable sender, Control::BlockContent content)
{
co_await winrt::resume_foreground(Dispatcher());
Control::BlockControl control{ content };
// control.NewBlock({ get_weak(), &MyPage::_newBlockHandler });
control.Height(256);
control.VerticalAlignment(WUX::VerticalAlignment::Top);
control.HorizontalAlignment(WUX::HorizontalAlignment::Stretch);
WUX::Controls::Grid wrapper{};
wrapper.VerticalAlignment(WUX::VerticalAlignment::Top);
wrapper.HorizontalAlignment(WUX::HorizontalAlignment::Stretch);
wrapper.CornerRadius(WUX::CornerRadiusHelper::FromRadii(6, 6, 6, 6));
wrapper.Margin(WUX::ThicknessHelper::FromLengths(0, 4, 0, 4));
wrapper.Children().Append(control);
OutOfProcContent().Children().Append(wrapper);
}
}

View File

@@ -19,6 +19,8 @@ namespace winrt::SampleApp::implementation
private:
friend struct MyPageT<MyPage>; // for Xaml to bind events
winrt::fire_and_forget _newBlockHandler(Windows::Foundation::IInspectable sender, Microsoft::Terminal::Control::BlockContent content);
};
}

View File

@@ -45,13 +45,18 @@
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="#ff0000" />
<ScrollViewer Grid.Column="1"
Padding="4"
Background="#fff00f">
<StackPanel x:Name="OutOfProcContent"
Padding="16"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="#9090ff"
CornerRadius="4"
Orientation="Vertical" />
<Grid x:Name="OutOfProcContent"
Grid.Column="1"
Padding="16"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="#0000ff" />
</ScrollViewer>

View File

@@ -20,6 +20,7 @@
<PropertyGroup Label="NuGet Dependencies">
<TerminalCppWinrt>true</TerminalCppWinrt>
<TerminalMUX>true</TerminalMUX>
</PropertyGroup>
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<Import Project="$(OpenConsoleDir)src\common.nugetversions.props" />
@@ -90,19 +91,26 @@
<SubType>Code</SubType>
</Midl>
</ItemGroup>
<!-- ========================= Misc Files ======================== -->
<ItemGroup>
<PRIResource Include="Resources\en-US\Resources.resw" />
<OCResourceDirectory Include="Resources" />
<None Include="packages.config" />
</ItemGroup>
<!-- ========================= Project References ======================== -->
<ItemGroup>
</ItemGroup>
<!-- ========================= Other properties ======================== -->
<PropertyGroup>
<!-- This is a hack to get the ARM64 CI build working. See
https://github.com/Microsoft/msbuild/issues/3746 - it looks like MsBuild
just has a bug in it.-->
<ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>Warning</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
</PropertyGroup>
<!-- ========================== Other References ========================= -->
<ItemGroup>
<!-- Manually add references to each of our dependent winmds. Mark them as
@@ -156,13 +164,13 @@
<!-- This -must- go after cppwinrt.build.post.props because that includes many VS-provided props including appcontainer.common.props, which stomps on what cppwinrt.targets did. -->
<Import Project="$(OpenConsoleDir)src\common.nugetversions.targets" />
<Import Project="$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" />
<!-- <Import Project="$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets'))" />
</Target>
</Target> -->
<!--
By default, the PRI file will contain resource paths beginning with the

View File

@@ -13,6 +13,7 @@
</PropertyGroup>
<PropertyGroup Label="NuGet Dependencies">
<TerminalCppWinrt>true</TerminalCppWinrt>
<TerminalMUX>true</TerminalMUX>
</PropertyGroup>
<Import Project="..\..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<Import Project="$(OpenConsoleDir)src\common.nugetversions.props" />
@@ -90,13 +91,13 @@
</Link>
</ItemDefinitionGroup>
<Import Project="$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" />
<!-- <Import Project="$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets'))" />
</Target>
</Target> -->
<ItemDefinitionGroup>
<ClCompile>

View File

@@ -20,6 +20,7 @@
<TerminalXamlApplicationToolkit>true</TerminalXamlApplicationToolkit>
<TerminalVCRTForwarders>true</TerminalVCRTForwarders>
<TerminalThemeHelpers>true</TerminalThemeHelpers>
<TerminalMUX>true</TerminalMUX>
</PropertyGroup>
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
@@ -144,13 +145,13 @@
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" />
<!-- <Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets'))" />
</Target>
</Target> -->
<!-- Override GetPackagingOutputs to roll up all our dependencies.
This ensures that when the WAP packaging project asks what files go into

View File

@@ -4,7 +4,10 @@
#include "precomp.h"
#include "Row.hpp"
#include <til/unicode.h>
#include "textBuffer.hpp"
#include "../../types/inc/GlyphWidth.hpp"
// The STL is missing a std::iota_n analogue for std::iota, so I made my own.
template<typename OutIt, typename Diff, typename T>
@@ -65,6 +68,65 @@ constexpr OutIt copy_n_small(InIt first, Diff count, OutIt dest)
return dest;
}
RowTextIterator::RowTextIterator(std::span<const wchar_t> chars, std::span<const uint16_t> charOffsets, uint16_t offset) noexcept :
_chars{ chars },
_charOffsets{ charOffsets },
_beg{ offset },
_end{ offset }
{
operator++();
}
bool RowTextIterator::operator==(const RowTextIterator& other) const noexcept
{
return _beg == other._beg;
}
RowTextIterator& RowTextIterator::operator++() noexcept
{
_beg = _end;
while (++_end < _charOffsets.size() && _uncheckedIsTrailer(_end))
{
}
return *this;
}
const RowTextIterator& RowTextIterator::operator*() const noexcept
{
return *this;
}
std::wstring_view RowTextIterator::Text() const noexcept
{
const auto beg = _uncheckedCharOffset(_beg);
const auto end = _uncheckedCharOffset(_end);
return { _chars.begin() + beg, _chars.begin() + gsl::narrow_cast<size_t>(end - beg) };
}
til::CoordType RowTextIterator::Cols() const noexcept
{
return _end - _beg;
}
DbcsAttribute RowTextIterator::DbcsAttr() const noexcept
{
return Cols() == 2 ? DbcsAttribute::Leading : DbcsAttribute::Single;
}
// Safety: col must be [0, _columnCount].
uint16_t RowTextIterator::_uncheckedCharOffset(size_t col) const noexcept
{
assert(col <= _charOffsets.size());
return til::at(_charOffsets, col) & CharOffsetsMask;
}
// Safety: col must be [0, _columnCount].
bool RowTextIterator::_uncheckedIsTrailer(size_t col) const noexcept
{
assert(col <= _charOffsets.size());
return WI_IsFlagSet(til::at(_charOffsets, col), CharOffsetsTrailer);
}
// Routine Description:
// - constructor
// Arguments:
@@ -128,6 +190,16 @@ LineRendition ROW::GetLineRendition() const noexcept
return _lineRendition;
}
RowTextIterator ROW::Begin() const noexcept
{
return { _chars, _charOffsets, 0 };
}
RowTextIterator ROW::End() const noexcept
{
return { _chars, _charOffsets, _columnCount };
}
// Routine Description:
// - Sets all properties of the ROW to default values
// Arguments:
@@ -229,6 +301,36 @@ void ROW::TransferAttributes(const til::small_rle<TextAttribute, uint16_t, 1>& a
_attr.resize_trailing_extent(gsl::narrow<uint16_t>(newWidth));
}
til::CoordType ROW::NavigateToPrevious(til::CoordType column) const noexcept
{
return _adjustBackward(_clampedColumn(column - 1));
}
til::CoordType ROW::NavigateToNext(til::CoordType column) const noexcept
{
return _adjustForward(_clampedColumn(column + 1));
}
uint16_t ROW::_adjustBackward(uint16_t column) const noexcept
{
// Safety: This is a little bit more dangerous. The first column is supposed
// to never be a trailer and so this loop should exit if column == 0.
for (; _uncheckedIsTrailer(column); --column)
{
}
return column;
}
uint16_t ROW::_adjustForward(uint16_t column) const noexcept
{
// Safety: This is a little bit more dangerous. The last column is supposed
// to never be a trailer and so this loop should exit if column == _columnCount.
for (; _uncheckedIsTrailer(column); ++column)
{
}
return column;
}
// Routine Description:
// - clears char data in column in row
// Arguments:
@@ -375,90 +477,228 @@ void ROW::ReplaceAttributes(const til::CoordType beginIndex, const til::CoordTyp
_attr.replace(_clampedColumnInclusive(beginIndex), _clampedColumnInclusive(endIndex), newAttr);
}
void ROW::ReplaceCharacters(til::CoordType columnBegin, til::CoordType width, const std::wstring_view& chars)
[[msvc::forceinline]] ROW::WriteHelper::WriteHelper(ROW& row, til::CoordType columnBegin, til::CoordType columnLimit, const std::wstring_view& chars) noexcept :
row{ row },
chars{ chars }
{
const auto colBeg = _clampedUint16(columnBegin);
const auto colEnd = _clampedUint16(columnBegin + width);
colBeg = row._clampedColumnInclusive(columnBegin);
colLimit = row._clampedColumnInclusive(columnLimit);
chExtBeg = row._uncheckedCharOffset(colBeg);
colExtBeg = row._adjustBackward(colBeg);
leadingSpaces = colBeg - colExtBeg;
chBeg = chExtBeg + leadingSpaces;
colEnd = colBeg;
colExtEnd = 0;
charsConsumed = 0;
}
if (colBeg >= colEnd || colEnd > _columnCount || chars.empty())
[[msvc::forceinline]] bool ROW::WriteHelper::IsValid() const noexcept
{
return colBeg < colLimit && !chars.empty();
}
void ROW::ReplaceCharacters(til::CoordType columnBegin, til::CoordType width, const std::wstring_view& chars)
try
{
WriteHelper h{ *this, columnBegin, _columnCount, chars };
if (!h.IsValid())
{
return;
}
h.ReplaceCharacters(width);
h.Finish();
}
catch (...)
{
// Due to this function writing _charOffsets first, then calling _resizeChars (which may throw) and only then finally
// filling in _chars, we might end up in a situation were _charOffsets contains offsets outside of the _chars array.
// --> Restore this row to a known "okay"-state.
Reset(TextAttribute{});
throw;
}
// Safety:
// * colBeg is now [0, _columnCount)
// * colEnd is now (colBeg, _columnCount]
[[msvc::forceinline]] void ROW::WriteHelper::ReplaceCharacters(til::CoordType width) noexcept
{
const auto colEndNew = gsl::narrow_cast<uint16_t>(colEnd + width);
if (colEndNew > colLimit)
{
colExtEnd = colLimit;
}
else
{
til::at(row._charOffsets, colEnd++) = chBeg;
for (; colEnd < colEndNew; ++colEnd)
{
til::at(row._charOffsets, colEnd) = gsl::narrow_cast<uint16_t>(chBeg | CharOffsetsTrailer);
}
// Algorithm explanation
//
// Task:
// Replace the characters in cells [colBeg, colEnd) with a single `width`-wide glyph consisting of `chars`.
//
// Problem:
// Imagine that we have the following ROW contents:
// "xxyyzz"
// xx, yy, zz are 2 cell wide glyphs. We want to insert a 2 cell wide glyph ww at colBeg 1:
// ^^
// ww
// An incorrect result would be:
// "xwwyzz"
// The half cut off x and y glyph wouldn't make much sense, so we need to fill them with whitespace:
// " ww zz"
//
// Solution:
// Given the range we want to replace [colBeg, colEnd), we "extend" it to encompass leading (preceding)
// and trailing wide glyphs we partially overwrite resulting in the range [colExtBeg, colExtEnd), where
// colExtBeg <= colBeg and colExtEnd >= colEnd. In other words, the to be replaced range has been "extended".
// The amount of leading whitespace we need to insert is thus colBeg - colExtBeg
// and the amount of trailing whitespace colExtEnd - colEnd.
colExtEnd = colEnd;
charsConsumed = chars.size();
}
}
// Extend range downwards (leading whitespace)
uint16_t colExtBeg = colBeg;
// Safety: colExtBeg is [0, _columnCount], because colBeg is.
const uint16_t chExtBeg = _uncheckedCharOffset(colExtBeg);
// Safety: colExtBeg remains [0, _columnCount] due to colExtBeg != 0.
for (; colExtBeg != 0 && _uncheckedIsTrailer(colExtBeg); --colExtBeg)
til::CoordType ROW::Write(til::CoordType columnBegin, til::CoordType columnLimit, std::wstring_view& chars)
try
{
WriteHelper h{ *this, columnBegin, columnLimit, chars };
if (!h.IsValid())
{
return h.colBeg;
}
h.Write();
h.Finish();
chars = chars.substr(h.charsConsumed);
return h.colExtEnd;
}
catch (...)
{
Reset(TextAttribute{});
throw;
}
[[msvc::forceinline]] void ROW::WriteHelper::Write() noexcept
{
size_t ch = chBeg;
for (const auto& s : til::utf16_iterator{ chars })
{
const auto wide = til::at(s, 0) < 0x80 ? false : IsGlyphFullWidth(s);
const auto colEndNew = gsl::narrow_cast<uint16_t>(colEnd + 1u + wide);
if (colEndNew > colLimit)
{
colExtEnd = colLimit;
break;
}
til::at(row._charOffsets, colEnd++) = gsl::narrow_cast<uint16_t>(ch);
if (wide)
{
til::at(row._charOffsets, colEnd++) = gsl::narrow_cast<uint16_t>(ch | CharOffsetsTrailer);
}
colExtEnd = colEnd;
ch += s.size();
}
charsConsumed = ch - chBeg;
}
til::CoordType ROW::CopyRangeFrom(til::CoordType columnBegin, til::CoordType columnLimit, const ROW& other, til::CoordType& otherBegin, til::CoordType otherLimit)
try
{
const auto otherColBeg = other._clampedColumnInclusive(otherBegin);
const auto otherColLimit = other._clampedColumnInclusive(otherLimit);
std::span<uint16_t> charOffsets;
std::wstring_view chars;
if (otherColBeg < otherColLimit)
{
charOffsets = other._charOffsets.subspan(otherColBeg, static_cast<size_t>(otherColLimit) - otherColBeg + 1);
const auto charsOffset = charOffsets.front() & CharOffsetsMask;
// _chars is a std::span (for performance and because it refers to raw, shared memory), whereas
// chars is a std::wstring_view, because that's what we use everywhere else for sharing strings.
// We can't trivially convert the two (C++ Committee be like: Having 2 span types is completely
// reasonable and normal. Also, they're not convertible. Because fu.) so we do pointer stuff.
#pragma warning(suppress : 26481) // Don't use pointer arithmetic. Use span instead (bounds.1).
chars = { other._chars.data() + charsOffset, other._chars.size() - charsOffset };
}
WriteHelper h{ *this, columnBegin, columnLimit, chars };
if (!h.IsValid())
{
return h.colBeg;
}
// Any valid charOffsets array is at least 2 elements long (the 1st element is the start offset and the 2nd
// element is the length of the first glyph) and begins/ends with a non-trailer offset. We don't really
// need to test for the end offset, since `WriteHelper::WriteWithOffsets` already takes care of that.
if (charOffsets.size() < 2 || WI_IsFlagSet(charOffsets.front(), CharOffsetsTrailer) || WI_IsFlagSet(charOffsets.back(), CharOffsetsTrailer))
{
assert(false);
otherBegin = other.size();
return h.colBeg;
}
h.CopyRangeFrom(charOffsets);
h.Finish();
otherBegin += h.colEnd - h.colBeg;
return h.colExtEnd;
}
catch (...)
{
Reset(TextAttribute{});
throw;
}
[[msvc::forceinline]] void ROW::WriteHelper::CopyRangeFrom(const std::span<const uint16_t>& charOffsets) noexcept
{
// Since our `charOffsets` input is already in columns (just like the `ROW::_charOffsets`),
// we can directly look up the end char-offset, but...
const auto colExtEndInput = std::min(gsl::narrow_cast<uint16_t>(colLimit - colBeg), gsl::narrow<uint16_t>(charOffsets.size() - 1));
// ...since the colLimit might intersect with a wide glyph in `charOffset`, we need to adjust our input-colEnd.
auto colEndInput = colExtEndInput;
for (; WI_IsFlagSet(til::at(charOffsets, colEndInput), CharOffsetsTrailer); --colEndInput)
{
}
// Extend range upwards (trailing whitespace)
uint16_t colExtEnd = colEnd;
// Safety: colExtEnd cannot be incremented past _columnCount, because the last
// _charOffset at index _columnCount will never get the CharOffsetsTrailer flag.
for (; _uncheckedIsTrailer(colExtEnd); ++colExtEnd)
{
}
// Safety: After the previous loop colExtEnd is [0, _columnCount].
const uint16_t chExtEnd = _uncheckedCharOffset(colExtEnd);
const auto baseOffset = til::at(charOffsets, 0);
const auto endOffset = til::at(charOffsets, colEndInput);
const auto inToOutOffset = gsl::narrow_cast<uint16_t>(chBeg - baseOffset);
// Now with the `colEndInput` figured out, we can easily copy the `charOffsets` into the `_charOffsets`.
// It's possible to use SIMD for this loop for extra perf gains. Something like this for SSE2 (~8x faster):
// const auto in = _mm_loadu_si128(...);
// const auto off = _mm_and_epi32(in, _mm_set1_epi16(CharOffsetsMask));
// const auto trailer = _mm_and_epi32(in, _mm_set1_epi16(CharOffsetsTrailer));
// const auto out = _mm_or_epi32(_mm_add_epi16(off, _mm_set1_epi16(inToOutOffset)), trailer);
// _mm_store_si128(..., out);
for (uint16_t i = 0; i < colEndInput; ++i, ++colEnd)
{
const auto ch = til::at(charOffsets, i);
const auto off = ch & CharOffsetsMask;
const auto trailer = ch & CharOffsetsTrailer;
til::at(row._charOffsets, colEnd) = gsl::narrow_cast<uint16_t>((off + inToOutOffset) | trailer);
}
colExtEnd = gsl::narrow_cast<uint16_t>(colExtEndInput + colBeg);
charsConsumed = endOffset - baseOffset;
}
[[msvc::forceinline]] void ROW::WriteHelper::Finish()
{
colExtEnd = row._adjustForward(colExtEnd);
const uint16_t leadingSpaces = colBeg - colExtBeg;
const uint16_t trailingSpaces = colExtEnd - colEnd;
const size_t chExtEndNew = chars.size() + leadingSpaces + trailingSpaces + chExtBeg;
const uint16_t chExtEndOld = row._uncheckedCharOffset(colExtEnd);
const size_t chExtEndNew = charsConsumed + leadingSpaces + trailingSpaces + chExtBeg;
if (chExtEndNew != chExtEnd)
if (chExtEndNew != chExtEndOld)
{
_resizeChars(colExtEnd, chExtBeg, chExtEnd, chExtEndNew);
row._resizeChars(colExtEnd, chExtBeg, chExtEndOld, chExtEndNew);
}
// Add leading/trailing whitespace and copy chars
{
auto it = _chars.begin() + chExtBeg;
auto it = row._chars.begin() + chExtBeg;
it = fill_n_small(it, leadingSpaces, L' ');
it = copy_n_small(chars.begin(), chars.size(), it);
it = std::copy_n(chars.begin(), charsConsumed, it);
it = fill_n_small(it, trailingSpaces, L' ');
iota_n(row._charOffsets.begin() + colExtBeg, leadingSpaces, chExtBeg);
iota_n(row._charOffsets.begin() + colEnd, trailingSpaces, gsl::narrow_cast<uint16_t>(chBeg + charsConsumed));
}
// Update char offsets with leading/trailing whitespace and the chars columns.
// This updates `_doubleBytePadded` whenever we write the last column in the row. `_doubleBytePadded` tells our text
// reflow algorithm whether it should ignore the last column. This is important when writing wide characters into
// the terminal: If the last wide character in a row only fits partially, we should render whitespace, but
// during text reflow pretend as if no whitespace exists. After all, the user didn't write any whitespace there.
//
// The way this is written, it'll set `_doubleBytePadded` to `true` no matter whether a wide character didn't fit,
// or if the last 2 columns contain a wide character and a narrow character got written into the left half of it.
// In both cases `trailingSpaces` is 1 and fills the last column and `_doubleBytePadded` will be `true`.
if (colExtEnd == row._columnCount)
{
auto chPos = chExtBeg;
auto it = _charOffsets.begin() + colExtBeg;
it = iota_n_mut(it, leadingSpaces, chPos);
*it++ = chPos;
it = fill_small(it, _charOffsets.begin() + colEnd, gsl::narrow_cast<uint16_t>(chPos | CharOffsetsTrailer));
chPos = gsl::narrow_cast<uint16_t>(chPos + chars.size());
it = iota_n_mut(it, trailingSpaces, chPos);
row.SetDoubleBytePadded(colEnd < row._columnCount);
}
}
@@ -466,15 +706,15 @@ void ROW::ReplaceCharacters(til::CoordType columnBegin, til::CoordType width, co
// as it reallocates the backing buffer and shifts the char offsets.
// The parameters are difficult to explain, but their names are identical to
// local variables in ReplaceCharacters() which I've attempted to document there.
void ROW::_resizeChars(uint16_t colExtEnd, uint16_t chExtBeg, uint16_t chExtEnd, size_t chExtEndNew)
void ROW::_resizeChars(uint16_t colExtEnd, uint16_t chExtBeg, uint16_t chExtEndOld, size_t chExtEndNew)
{
const auto diff = chExtEndNew - chExtEnd;
const auto diff = chExtEndNew - chExtEndOld;
const auto currentLength = _charSize();
const auto newLength = currentLength + diff;
if (newLength <= _chars.size())
{
std::copy_n(_chars.begin() + chExtEnd, currentLength - chExtEnd, _chars.begin() + chExtEndNew);
std::copy_n(_chars.begin() + chExtEndOld, currentLength - chExtEndOld, _chars.begin() + chExtEndNew);
}
else
{
@@ -485,7 +725,7 @@ void ROW::_resizeChars(uint16_t colExtEnd, uint16_t chExtBeg, uint16_t chExtEnd,
const std::span chars{ charsHeap.get(), newCapacity };
std::copy_n(_chars.begin(), chExtBeg, chars.begin());
std::copy_n(_chars.begin() + chExtEnd, currentLength - chExtEnd, chars.begin() + chExtEndNew);
std::copy_n(_chars.begin() + chExtEndOld, currentLength - chExtEndOld, chars.begin() + chExtEndNew);
_charsHeap = std::move(charsHeap);
_chars = chars;
@@ -499,6 +739,11 @@ void ROW::_resizeChars(uint16_t colExtEnd, uint16_t chExtBeg, uint16_t chExtEnd,
}
}
til::small_rle<TextAttribute, uint16_t, 1>& ROW::Attributes() noexcept
{
return _attr;
}
const til::small_rle<TextAttribute, uint16_t, 1>& ROW::Attributes() const noexcept
{
return _attr;
@@ -681,11 +926,13 @@ uint16_t ROW::_charSize() const noexcept
// Safety: col must be [0, _columnCount].
uint16_t ROW::_uncheckedCharOffset(size_t col) const noexcept
{
assert(col < _charOffsets.size());
return til::at(_charOffsets, col) & CharOffsetsMask;
}
// Safety: col must be [0, _columnCount].
bool ROW::_uncheckedIsTrailer(size_t col) const noexcept
{
assert(col < _charOffsets.size());
return WI_IsFlagSet(til::at(_charOffsets, col), CharOffsetsTrailer);
}

View File

@@ -20,8 +20,6 @@ Revision History:
#pragma once
#include <span>
#include <til/rle.h>
#include "LineRendition.hpp"
@@ -37,6 +35,34 @@ enum class DelimiterClass
RegularChar
};
struct RowTextIterator
{
RowTextIterator(std::span<const wchar_t> chars, std::span<const uint16_t> charOffsets, uint16_t offset) noexcept;
bool operator==(const RowTextIterator& other) const noexcept;
RowTextIterator& operator++() noexcept;
const RowTextIterator& operator*() const noexcept;
std::wstring_view Text() const noexcept;
til::CoordType Cols() const noexcept;
DbcsAttribute DbcsAttr() const noexcept;
private:
uint16_t _uncheckedCharOffset(size_t col) const noexcept;
bool _uncheckedIsTrailer(size_t col) const noexcept;
// To simplify the detection of wide glyphs, we don't just store the simple character offset as described
// for _charOffsets. Instead we use the most significant bit to indicate whether any column is the
// trailing half of a wide glyph. This simplifies many implementation details via _uncheckedIsTrailer.
static constexpr uint16_t CharOffsetsTrailer = 0x8000;
static constexpr uint16_t CharOffsetsMask = 0x7fff;
std::span<const wchar_t> _chars;
std::span<const uint16_t> _charOffsets;
uint16_t _beg;
uint16_t _end;
};
class ROW final
{
public:
@@ -57,17 +83,25 @@ public:
bool WasDoubleBytePadded() const noexcept;
void SetLineRendition(const LineRendition lineRendition) noexcept;
LineRendition GetLineRendition() const noexcept;
RowTextIterator Begin() const noexcept;
RowTextIterator End() const noexcept;
void Reset(const TextAttribute& attr);
void Resize(wchar_t* charsBuffer, uint16_t* charOffsetsBuffer, uint16_t rowWidth, const TextAttribute& fillAttribute);
void TransferAttributes(const til::small_rle<TextAttribute, uint16_t, 1>& attr, til::CoordType newWidth);
til::CoordType NavigateToPrevious(til::CoordType column) const noexcept;
til::CoordType NavigateToNext(til::CoordType column) const noexcept;
void ClearCell(til::CoordType column);
OutputCellIterator WriteCells(OutputCellIterator it, til::CoordType columnBegin, std::optional<bool> wrap = std::nullopt, std::optional<til::CoordType> limitRight = std::nullopt);
bool SetAttrToEnd(til::CoordType columnBegin, TextAttribute attr);
void ReplaceAttributes(til::CoordType beginIndex, til::CoordType endIndex, const TextAttribute& newAttr);
void ReplaceCharacters(til::CoordType columnBegin, til::CoordType width, const std::wstring_view& chars);
til::CoordType Write(til::CoordType columnBegin, til::CoordType columnLimit, std::wstring_view& chars);
til::CoordType CopyRangeFrom(til::CoordType columnBegin, til::CoordType columnLimit, const ROW& other, til::CoordType& otherBegin, til::CoordType otherLimit);
til::small_rle<TextAttribute, uint16_t, 1>& Attributes() noexcept;
const til::small_rle<TextAttribute, uint16_t, 1>& Attributes() const noexcept;
TextAttribute GetAttrByColumn(til::CoordType column) const;
std::vector<uint16_t> GetHyperlinks() const;
@@ -89,6 +123,30 @@ public:
#endif
private:
// WriteHelper exists because other forms of abstracting this functionality away (like templates with lambdas)
// where only very poorly optimized by MSVC as it failed to inline the templates.
struct WriteHelper
{
explicit WriteHelper(ROW& row, til::CoordType columnBegin, til::CoordType columnLimit, const std::wstring_view& chars) noexcept;
bool IsValid() const noexcept;
void ReplaceCharacters(til::CoordType width) noexcept;
void Write() noexcept;
void CopyRangeFrom(const std::span<const uint16_t>& charOffsets) noexcept;
void Finish();
ROW& row;
const std::wstring_view& chars;
uint16_t colBeg;
uint16_t colLimit;
uint16_t chExtBeg;
uint16_t colExtBeg;
uint16_t leadingSpaces;
uint16_t chBeg;
uint16_t colEnd;
uint16_t colExtEnd;
size_t charsConsumed;
};
// To simplify the detection of wide glyphs, we don't just store the simple character offset as described
// for _charOffsets. Instead we use the most significant bit to indicate whether any column is the
// trailing half of a wide glyph. This simplifies many implementation details via _uncheckedIsTrailer.
@@ -102,13 +160,16 @@ private:
template<typename T>
constexpr uint16_t _clampedColumnInclusive(T v) const noexcept;
uint16_t _adjustBackward(uint16_t column) const noexcept;
uint16_t _adjustForward(uint16_t column) const noexcept;
wchar_t _uncheckedChar(size_t off) const noexcept;
uint16_t _charSize() const noexcept;
uint16_t _uncheckedCharOffset(size_t col) const noexcept;
bool _uncheckedIsTrailer(size_t col) const noexcept;
void _init() noexcept;
void _resizeChars(uint16_t colExtEnd, uint16_t chExtBeg, uint16_t chExtEnd, size_t chExtEndNew);
void _resizeChars(uint16_t colExtEnd, uint16_t chExtBeg, uint16_t chExtEndOld, size_t chExtEndNew);
// These fields are a bit "wasteful", but it makes all this a bit more robust against
// programming errors during initial development (which is when this comment was written).

View File

@@ -376,6 +376,25 @@ bool TextBuffer::_PrepareForDoubleByteSequence(const DbcsAttribute dbcsAttribute
return fSuccess;
}
void TextBuffer::ConsumeGrapheme(std::wstring_view& chars) noexcept
{
// This function is supposed to mirror the behavior of ROW::Write, when it reads characters off of `chars`.
// (I know that a UTF-16 code point is not a grapheme, but that's what we're working towards.)
chars = til::utf16_pop(chars);
}
til::CoordType TextBuffer::Write(til::CoordType row, til::CoordType columnBegin, til::CoordType columnLimit, bool wrapAtEOL, const TextAttribute& attributes, std::wstring_view& chars)
{
auto& r = GetRowByOffset(row);
const auto columnEnd = r.Write(columnBegin, columnLimit, chars);
r.ReplaceAttributes(columnBegin, columnEnd, attributes);
r.SetWrapForced(wrapAtEOL && columnEnd >= r.size());
TriggerRedraw(Viewport::FromExclusive({ columnBegin, row, columnEnd, row + 1 }));
return columnEnd;
}
// Routine Description:
// - Writes cells to the output buffer. Writes at the cursor.
// Arguments:
@@ -901,6 +920,11 @@ void TextBuffer::SetCurrentLineRendition(const LineRendition lineRendition)
}
TriggerRedraw(Viewport::FromDimensions({ 0, rowIndex }, { GetSize().Width(), 1 }));
}
// There is some variation in how this was handled by the different DEC
// terminals, but the STD 070 reference (on page D-13) makes it clear that
// the delayed wrap (aka the Last Column Flag) was expected to be reset when
// line rendition controls were executed.
GetCursor().ResetDelayEOLWrap();
}
void TextBuffer::ResetLineRenditionRange(const til::CoordType startRow, const til::CoordType endRow) noexcept

View File

@@ -89,6 +89,9 @@ public:
TextBufferTextIterator GetTextDataAt(const til::point at, const Microsoft::Console::Types::Viewport limit) const;
// Text insertion functions
static void ConsumeGrapheme(std::wstring_view& chars) noexcept;
til::CoordType Write(til::CoordType row, til::CoordType columnBegin, til::CoordType columnLimit, bool wrapAtEOL, const TextAttribute& attributes, std::wstring_view& chars);
OutputCellIterator Write(const OutputCellIterator givenIt);
OutputCellIterator Write(const OutputCellIterator givenIt,

View File

@@ -98,6 +98,26 @@ namespace TerminalAppLocalTests
}
}
}
void _logCommands(winrt::Windows::Foundation::Collections::IVector<Command> commands, const int indentation = 1)
{
if (indentation == 1)
{
Log::Comment((commands.Size() == 0) ? L"Commands:\n <none>" : L"Commands:");
}
for (const auto& cmd : commands)
{
Log::Comment(fmt::format(L"{0:>{1}}* {2}",
L"",
indentation,
cmd.Name())
.c_str());
if (cmd.HasNestedCommands())
{
_logCommandNames(cmd.NestedCommands(), indentation + 2);
}
}
}
};
void SettingsTests::TestIterateCommands()
@@ -164,14 +184,15 @@ namespace TerminalAppLocalTests
VERIFY_ARE_EQUAL(L"${profile.name}", realArgs.TerminalArgs().Profile());
}
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(nameMap, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
_logCommandNames(expandedCommands.GetView());
const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() };
_logCommands(expandedCommands);
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
VERIFY_ARE_EQUAL(3u, expandedCommands.Size());
{
auto command = expandedCommands.Lookup(L"iterable command profile0");
auto command = expandedCommands.GetAt(0);
VERIFY_ARE_EQUAL(L"iterable command profile0", command.Name());
VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs);
@@ -189,7 +210,8 @@ namespace TerminalAppLocalTests
}
{
auto command = expandedCommands.Lookup(L"iterable command profile1");
auto command = expandedCommands.GetAt(1);
VERIFY_ARE_EQUAL(L"iterable command profile1", command.Name());
VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs);
@@ -207,7 +229,8 @@ namespace TerminalAppLocalTests
}
{
auto command = expandedCommands.Lookup(L"iterable command profile2");
auto command = expandedCommands.GetAt(2);
VERIFY_ARE_EQUAL(L"iterable command profile2", command.Name());
VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs);
@@ -287,14 +310,16 @@ namespace TerminalAppLocalTests
VERIFY_ARE_EQUAL(L"${profile.name}", realArgs.TerminalArgs().Profile());
}
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(nameMap, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
_logCommandNames(expandedCommands.GetView());
const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() };
_logCommands(expandedCommands);
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
VERIFY_ARE_EQUAL(3u, expandedCommands.Size());
{
auto command = expandedCommands.Lookup(L"Split pane, profile: profile0");
auto command = expandedCommands.GetAt(0);
VERIFY_ARE_EQUAL(L"Split pane, profile: profile0", command.Name());
VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs);
@@ -312,7 +337,9 @@ namespace TerminalAppLocalTests
}
{
auto command = expandedCommands.Lookup(L"Split pane, profile: profile1");
auto command = expandedCommands.GetAt(1);
VERIFY_ARE_EQUAL(L"Split pane, profile: profile1", command.Name());
VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs);
@@ -330,7 +357,9 @@ namespace TerminalAppLocalTests
}
{
auto command = expandedCommands.Lookup(L"Split pane, profile: profile2");
auto command = expandedCommands.GetAt(2);
VERIFY_ARE_EQUAL(L"Split pane, profile: profile2", command.Name());
VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs);
@@ -412,14 +441,16 @@ namespace TerminalAppLocalTests
VERIFY_ARE_EQUAL(L"${profile.name}", realArgs.TerminalArgs().Profile());
}
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(nameMap, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
_logCommandNames(expandedCommands.GetView());
const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() };
_logCommands(expandedCommands);
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
VERIFY_ARE_EQUAL(3u, expandedCommands.Size());
{
auto command = expandedCommands.Lookup(L"iterable command profile0");
auto command = expandedCommands.GetAt(0);
VERIFY_ARE_EQUAL(L"iterable command profile0", command.Name());
VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs);
@@ -437,7 +468,9 @@ namespace TerminalAppLocalTests
}
{
auto command = expandedCommands.Lookup(L"iterable command profile1\"");
auto command = expandedCommands.GetAt(1);
VERIFY_ARE_EQUAL(L"iterable command profile1\"", command.Name());
VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs);
@@ -455,7 +488,9 @@ namespace TerminalAppLocalTests
}
{
auto command = expandedCommands.Lookup(L"iterable command profile2");
auto command = expandedCommands.GetAt(2);
VERIFY_ARE_EQUAL(L"iterable command profile2", command.Name());
VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs);
@@ -527,14 +562,15 @@ namespace TerminalAppLocalTests
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(settings.ActionMap().NameMap(), settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
_logCommandNames(expandedCommands.GetView());
const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() };
_logCommands(expandedCommands);
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
VERIFY_ARE_EQUAL(1u, expandedCommands.Size());
auto rootCommand = expandedCommands.Lookup(L"Connect to ssh...");
auto rootCommand = expandedCommands.GetAt(0);
VERIFY_IS_NOT_NULL(rootCommand);
VERIFY_ARE_EQUAL(L"Connect to ssh...", rootCommand.Name());
auto rootActionAndArgs = rootCommand.ActionAndArgs();
VERIFY_IS_NOT_NULL(rootActionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::Invalid, rootActionAndArgs.Action());
@@ -621,14 +657,16 @@ namespace TerminalAppLocalTests
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(settings.ActionMap().NameMap(), settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
_logCommandNames(expandedCommands.GetView());
const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() };
_logCommands(expandedCommands);
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
VERIFY_ARE_EQUAL(1u, expandedCommands.Size());
auto grandparentCommand = expandedCommands.Lookup(L"grandparent");
auto grandparentCommand = expandedCommands.GetAt(0);
VERIFY_IS_NOT_NULL(grandparentCommand);
VERIFY_ARE_EQUAL(L"grandparent", grandparentCommand.Name());
auto grandparentActionAndArgs = grandparentCommand.ActionAndArgs();
VERIFY_IS_NOT_NULL(grandparentActionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::Invalid, grandparentActionAndArgs.Action());
@@ -744,17 +782,22 @@ namespace TerminalAppLocalTests
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(settings.ActionMap().NameMap(), settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
_logCommandNames(expandedCommands.GetView());
const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() };
_logCommands(expandedCommands);
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
VERIFY_ARE_EQUAL(3u, expandedCommands.Size());
for (auto name : std::vector<std::wstring>({ L"profile0", L"profile1", L"profile2" }))
const std::vector<std::wstring> profileNames{ L"profile0", L"profile1", L"profile2" };
for (auto i = 0u; i < profileNames.size(); i++)
{
winrt::hstring commandName{ name + L"..." };
auto command = expandedCommands.Lookup(commandName);
const auto& name{ profileNames[i] };
winrt::hstring commandName{ profileNames[i] + L"..." };
auto command = expandedCommands.GetAt(i);
VERIFY_ARE_EQUAL(commandName, command.Name());
VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs);
@@ -880,14 +923,16 @@ namespace TerminalAppLocalTests
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(settings.ActionMap().NameMap(), settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
_logCommandNames(expandedCommands.GetView());
const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() };
_logCommands(expandedCommands);
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
VERIFY_ARE_EQUAL(1u, expandedCommands.Size());
auto rootCommand = expandedCommands.Lookup(L"New Tab With Profile...");
auto rootCommand = expandedCommands.GetAt(0);
VERIFY_IS_NOT_NULL(rootCommand);
VERIFY_ARE_EQUAL(L"New Tab With Profile...", rootCommand.Name());
auto rootActionAndArgs = rootCommand.ActionAndArgs();
VERIFY_IS_NOT_NULL(rootActionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::Invalid, rootActionAndArgs.Action());
@@ -982,13 +1027,16 @@ namespace TerminalAppLocalTests
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(settings.ActionMap().NameMap(), settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
_logCommandNames(expandedCommands.GetView());
const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() };
_logCommands(expandedCommands);
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
VERIFY_ARE_EQUAL(1u, expandedCommands.Size());
auto rootCommand = expandedCommands.Lookup(L"New Pane...");
auto rootCommand = expandedCommands.GetAt(0);
VERIFY_IS_NOT_NULL(rootCommand);
VERIFY_ARE_EQUAL(L"New Pane...", rootCommand.Name());
VERIFY_IS_NOT_NULL(rootCommand);
auto rootActionAndArgs = rootCommand.ActionAndArgs();
VERIFY_IS_NOT_NULL(rootActionAndArgs);
@@ -1205,8 +1253,8 @@ namespace TerminalAppLocalTests
VERIFY_ARE_EQUAL(L"${scheme.name}", realArgs.TerminalArgs().Profile());
}
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(nameMap, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
_logCommandNames(expandedCommands.GetView());
const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() };
_logCommands(expandedCommands);
VERIFY_ARE_EQUAL(3u, expandedCommands.Size());
@@ -1215,7 +1263,9 @@ namespace TerminalAppLocalTests
// just easy tests to write.
{
auto command = expandedCommands.Lookup(L"iterable command Campbell");
auto command = expandedCommands.GetAt(0);
VERIFY_ARE_EQUAL(L"iterable command Campbell", command.Name());
VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs);
@@ -1233,7 +1283,9 @@ namespace TerminalAppLocalTests
}
{
auto command = expandedCommands.Lookup(L"iterable command Campbell PowerShell");
auto command = expandedCommands.GetAt(1);
VERIFY_ARE_EQUAL(L"iterable command Campbell PowerShell", command.Name());
VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs);
@@ -1251,7 +1303,9 @@ namespace TerminalAppLocalTests
}
{
auto command = expandedCommands.Lookup(L"iterable command Vintage");
auto command = expandedCommands.GetAt(2);
VERIFY_ARE_EQUAL(L"iterable command Vintage", command.Name());
VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs);

View File

@@ -4,6 +4,7 @@
#include "pch.h"
#include "../TerminalApp/TerminalPage.h"
#include "../TerminalApp/TerminalWindow.h"
#include "../TerminalApp/MinMaxCloseControl.h"
#include "../TerminalApp/TabRowControl.h"
#include "../TerminalApp/ShortcutActionDispatch.h"
@@ -110,6 +111,7 @@ namespace TerminalAppLocalTests
void _initializeTerminalPage(winrt::com_ptr<winrt::TerminalApp::implementation::TerminalPage>& page,
CascadiaSettings initialSettings);
winrt::com_ptr<winrt::TerminalApp::implementation::TerminalPage> _commonSetup();
winrt::com_ptr<winrt::TerminalApp::implementation::WindowProperties> _windowProperties;
};
template<typename TFunction>
@@ -194,8 +196,11 @@ namespace TerminalAppLocalTests
{
winrt::com_ptr<winrt::TerminalApp::implementation::TerminalPage> page{ nullptr };
auto result = RunOnUIThread([&page]() {
page = winrt::make_self<winrt::TerminalApp::implementation::TerminalPage>();
_windowProperties = winrt::make_self<winrt::TerminalApp::implementation::WindowProperties>();
winrt::TerminalApp::WindowProperties props = *_windowProperties;
auto result = RunOnUIThread([&page, props]() {
page = winrt::make_self<winrt::TerminalApp::implementation::TerminalPage>(props);
VERIFY_IS_NOT_NULL(page);
});
VERIFY_SUCCEEDED(result);
@@ -239,9 +244,11 @@ namespace TerminalAppLocalTests
// it's weird.
winrt::TerminalApp::TerminalPage projectedPage{ nullptr };
_windowProperties = winrt::make_self<winrt::TerminalApp::implementation::WindowProperties>();
winrt::TerminalApp::WindowProperties props = *_windowProperties;
Log::Comment(NoThrowString().Format(L"Construct the TerminalPage"));
auto result = RunOnUIThread([&projectedPage, &page, initialSettings]() {
projectedPage = winrt::TerminalApp::TerminalPage();
auto result = RunOnUIThread([&projectedPage, &page, initialSettings, props]() {
projectedPage = winrt::TerminalApp::TerminalPage(props);
page.copy_from(winrt::get_self<winrt::TerminalApp::implementation::TerminalPage>(projectedPage));
page->_settings = initialSettings;
});
@@ -1242,14 +1249,16 @@ namespace TerminalAppLocalTests
END_TEST_METHOD_PROPERTIES()
auto page = _commonSetup();
page->RenameWindowRequested([&page](auto&&, const winrt::TerminalApp::RenameWindowRequestedArgs args) {
page->RenameWindowRequested([&page, this](auto&&, const winrt::TerminalApp::RenameWindowRequestedArgs args) {
// In the real terminal, this would bounce up to the monarch and
// come back down. Instead, immediately call back and set the name.
page->WindowName(args.ProposedName());
//
// This replicates how TerminalWindow works
_windowProperties->WindowName(args.ProposedName());
});
auto windowNameChanged = false;
page->PropertyChanged([&page, &windowNameChanged](auto&&, const winrt::WUX::Data::PropertyChangedEventArgs& args) mutable {
_windowProperties->PropertyChanged([&page, &windowNameChanged](auto&&, const winrt::WUX::Data::PropertyChangedEventArgs& args) mutable {
if (args.PropertyName() == L"WindowNameForDisplay")
{
windowNameChanged = true;
@@ -1260,7 +1269,7 @@ namespace TerminalAppLocalTests
page->_RequestWindowRename(winrt::hstring{ L"Foo" });
});
TestOnUIThread([&]() {
VERIFY_ARE_EQUAL(L"Foo", page->_WindowName);
VERIFY_ARE_EQUAL(L"Foo", page->WindowProperties().WindowName());
VERIFY_IS_TRUE(windowNameChanged,
L"The window name should have changed, and we should have raised a notification that WindowNameForDisplay changed");
});

View File

@@ -10,6 +10,7 @@
#include "ProposeCommandlineResult.h"
#include "Monarch.g.cpp"
#include "WindowRequestedArgs.g.cpp"
#include "../../types/inc/utils.hpp"
using namespace winrt;
@@ -658,6 +659,13 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
if (targetWindow == WindowingBehaviorUseNone)
{
// In this case, the targetWindow was UseNone, which means that we
// want to make a message box, but otherwise not make a Terminal
// window.
return winrt::make<Remoting::implementation::ProposeCommandlineResult>(false);
}
// 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.
@@ -687,6 +695,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
case WindowingBehaviorUseName:
windowID = _lookupPeasantIdForName(targetWindowName);
break;
case WindowingBehaviorUseNone:
// This should be impossible. The if statement above should have
// prevented WindowingBehaviorUseNone from falling in here.
// Explode, because this is a programming error.
THROW_HR(E_UNEXPECTED);
default:
windowID = ::base::saturated_cast<uint64_t>(targetWindow);
break;
@@ -724,6 +737,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
result->WindowName(targetWindowName);
result->ShouldCreateWindow(true);
_RequestNewWindowHandlers(*this, *winrt::make_self<WindowRequestedArgs>(*result, args));
// If this fails, it'll be logged in the following
// TraceLoggingWrite statement, with succeeded=false
}
@@ -759,6 +774,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
auto result{ winrt::make_self<Remoting::implementation::ProposeCommandlineResult>(true) };
result->Id(windowID);
result->WindowName(targetWindowName);
_RequestNewWindowHandlers(*this, *winrt::make_self<WindowRequestedArgs>(*result, args));
return *result;
}
}
@@ -773,6 +791,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
// In this case, no usable ID was provided. Return { true, nullopt }
auto result = winrt::make_self<Remoting::implementation::ProposeCommandlineResult>(true);
result->WindowName(targetWindowName);
_RequestNewWindowHandlers(*this, *winrt::make_self<WindowRequestedArgs>(*result, args));
return *result;
}
@@ -1034,4 +1055,95 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
return winrt::single_threaded_vector(std::move(vec));
}
void Monarch::RequestMoveContent(winrt::hstring window,
winrt::hstring content,
uint32_t tabIndex,
Windows::Foundation::IReference<Windows::Foundation::Rect> windowBounds)
{
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_MoveContent_Requested",
TraceLoggingWideString(window.c_str(), "window", "The name of the window we tried to move to"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
uint64_t windowId = _lookupPeasantIdForName(window);
if (windowId == 0)
{
// Try the name as an integer ID
uint32_t temp;
if (!Utils::StringToUint(window.c_str(), temp))
{
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_MoveContent_FailedToParseId",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
else
{
windowId = temp;
}
}
if (auto targetPeasant{ _getPeasant(windowId) })
{
auto request = winrt::make_self<implementation::AttachRequest>(content, tabIndex);
targetPeasant.AttachContentToWindow(*request);
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_MoveContent_Completed",
TraceLoggingInt64(windowId, "windowId", "The ID of the peasant which we sent the content to"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
else
{
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_MoveContent_NoWindow",
TraceLoggingInt64(windowId, "windowId", "We could not find a peasant with this ID"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
// In the case where window couldn't be found, then create a window
// for that name / ID.
//
// Don't let the window literally be named "-1", because that's silly
auto request = winrt::make_self<implementation::WindowRequestedArgs>(window == L"-1" ? L"" : window,
content,
windowBounds);
_RequestNewWindowHandlers(*this, *request);
}
}
// Very similar to the above. Someone came and told us that they were the target of a drag/drop, and they know who started it.
// We will go tell the person who started it that they should send that target the content which was dragged.
void Monarch::RequestSendContent(Remoting::RequestReceiveContentArgs args)
{
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_SendContent_Requested",
TraceLoggingUInt64(args.SourceWindow(), "source", "The window which started the drag"),
TraceLoggingUInt64(args.TargetWindow(), "target", "The window which was the target of the drop"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
if (auto senderPeasant{ _getPeasant(args.SourceWindow()) })
{
senderPeasant.SendContent(args);
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_SendContent_Completed",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
else
{
// We couldn't find the peasant that started the drag. Well that
// sure is weird, but that would indicate that the sender closed
// after starting the drag. No matter. We can just do nothing.
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_SendContent_NoWindow",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
}
}

View File

@@ -6,6 +6,7 @@
#include "Monarch.g.h"
#include "Peasant.h"
#include "WindowActivatedArgs.h"
#include "WindowRequestedArgs.g.h"
#include <atomic>
// We sure different GUIDs here depending on whether we're running a Release,
@@ -38,6 +39,36 @@ namespace RemotingUnitTests
namespace winrt::Microsoft::Terminal::Remoting::implementation
{
struct WindowRequestedArgs : public WindowRequestedArgsT<WindowRequestedArgs>
{
public:
WindowRequestedArgs(const Remoting::ProposeCommandlineResult& windowInfo, const Remoting::CommandlineArgs& command) :
_Id{ windowInfo.Id() ? windowInfo.Id().Value() : 0 }, // We'll use 0 as a sentinel, since no window will ever get to have that ID
_WindowName{ windowInfo.WindowName() },
_args{ command.Commandline() },
_CurrentDirectory{ command.CurrentDirectory() } {};
WindowRequestedArgs(const winrt::hstring& window, const winrt::hstring& content, Windows::Foundation::IReference<Windows::Foundation::Rect> bounds) :
_Id{ 0u },
_WindowName{ window },
_args{},
_CurrentDirectory{},
_Content{ content },
_InitialBounds{ bounds } {};
void Commandline(const winrt::array_view<const winrt::hstring>& value) { _args = { value.begin(), value.end() }; };
winrt::com_array<winrt::hstring> Commandline() { return winrt::com_array<winrt::hstring>{ _args.begin(), _args.end() }; }
WINRT_PROPERTY(uint64_t, Id);
WINRT_PROPERTY(winrt::hstring, WindowName);
WINRT_PROPERTY(winrt::hstring, CurrentDirectory);
WINRT_PROPERTY(winrt::hstring, Content);
WINRT_PROPERTY(Windows::Foundation::IReference<Windows::Foundation::Rect>, InitialBounds);
private:
winrt::com_array<winrt::hstring> _args;
};
struct Monarch : public MonarchT<Monarch>
{
Monarch();
@@ -60,6 +91,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo> GetPeasantInfos();
Windows::Foundation::Collections::IVector<winrt::hstring> GetAllWindowLayouts();
void RequestMoveContent(winrt::hstring window, winrt::hstring content, uint32_t tabIndex, Windows::Foundation::IReference<Windows::Foundation::Rect> windowBounds);
void RequestSendContent(Remoting::RequestReceiveContentArgs args);
TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs);
TYPED_EVENT(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(HideNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
@@ -67,6 +101,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
TYPED_EVENT(WindowClosed, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::QuitAllRequestedArgs);
TYPED_EVENT(RequestNewWindow, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs);
private:
uint64_t _ourPID;
@@ -191,4 +227,5 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
namespace winrt::Microsoft::Terminal::Remoting::factory_implementation
{
BASIC_FACTORY(Monarch);
BASIC_FACTORY(WindowRequestedArgs);
}

View File

@@ -18,6 +18,19 @@ namespace Microsoft.Terminal.Remoting
Boolean ShouldCreateWindow { get; }; // If you name this `CreateWindow`, the compiler will explode
}
[default_interface] runtimeclass WindowRequestedArgs {
WindowRequestedArgs(ProposeCommandlineResult windowInfo, CommandlineArgs command);
UInt64 Id { get; };
String WindowName { get; };
String[] Commandline { get; };
String CurrentDirectory { get; };
String Content { get; };
Windows.Foundation.IReference<Windows.Foundation.Rect> InitialBounds { get; };
}
[default_interface] runtimeclass SummonWindowSelectionArgs {
SummonWindowSelectionArgs();
SummonWindowSelectionArgs(String windowName);
@@ -31,8 +44,7 @@ namespace Microsoft.Terminal.Remoting
Windows.Foundation.IReference<UInt64> WindowID;
}
[default_interface] runtimeclass QuitAllRequestedArgs
{
[default_interface] runtimeclass QuitAllRequestedArgs {
QuitAllRequestedArgs();
Windows.Foundation.IAsyncAction BeforeQuitAllAction;
}
@@ -60,12 +72,17 @@ namespace Microsoft.Terminal.Remoting
Windows.Foundation.Collections.IVectorView<PeasantInfo> GetPeasantInfos { get; };
Windows.Foundation.Collections.IVector<String> GetAllWindowLayouts();
void RequestMoveContent(String window, String content, UInt32 tabIndex, Windows.Foundation.IReference<Windows.Foundation.Rect> bounds);
void RequestSendContent(RequestReceiveContentArgs args);
event Windows.Foundation.TypedEventHandler<Object, FindTargetWindowArgs> FindTargetWindowRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> ShowNotificationIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> HideNotificationIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> WindowCreated;
event Windows.Foundation.TypedEventHandler<Object, Object> WindowClosed;
event Windows.Foundation.TypedEventHandler<Object, QuitAllRequestedArgs> QuitAllRequested;
event Windows.Foundation.TypedEventHandler<Object, WindowRequestedArgs> RequestNewWindow;
};
runtimeclass Monarch : [default] IMonarch

View File

@@ -8,6 +8,8 @@
#include "GetWindowLayoutArgs.h"
#include "Peasant.g.cpp"
#include "../../types/inc/utils.hpp"
#include "AttachRequest.g.cpp"
#include "RequestReceiveContentArgs.g.cpp"
using namespace winrt;
using namespace winrt::Microsoft::Terminal;
@@ -275,6 +277,22 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
void Peasant::AttachContentToWindow(Remoting::AttachRequest request)
{
try
{
_AttachRequestedHandlers(*this, request);
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
}
TraceLoggingWrite(g_hRemotingProvider,
"Peasant_AttachContentToWindow",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
void Peasant::Quit()
{
try
@@ -310,4 +328,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
}
return args->WindowLayoutJson();
}
void Peasant::SendContent(winrt::Microsoft::Terminal::Remoting::RequestReceiveContentArgs args)
{
_SendContentRequestedHandlers(*this, args);
}
}

View File

@@ -5,6 +5,8 @@
#include "Peasant.g.h"
#include "RenameRequestArgs.h"
#include "AttachRequest.g.h"
#include "RequestReceiveContentArgs.g.h"
namespace RemotingUnitTests
{
@@ -12,6 +14,31 @@ namespace RemotingUnitTests
};
namespace winrt::Microsoft::Terminal::Remoting::implementation
{
struct AttachRequest : public AttachRequestT<AttachRequest>
{
WINRT_PROPERTY(winrt::hstring, Content);
WINRT_PROPERTY(uint32_t, TabIndex);
public:
AttachRequest(winrt::hstring content,
uint32_t tabIndex) :
_Content{ content },
_TabIndex{ tabIndex } {};
};
struct RequestReceiveContentArgs : RequestReceiveContentArgsT<RequestReceiveContentArgs>
{
WINRT_PROPERTY(uint64_t, SourceWindow);
WINRT_PROPERTY(uint64_t, TargetWindow);
WINRT_PROPERTY(uint32_t, TabIndex);
public:
RequestReceiveContentArgs(const uint64_t src, const uint64_t tgt, const uint32_t tabIndex) :
_SourceWindow{ src },
_TargetWindow{ tgt },
_TabIndex{ tabIndex } {};
};
struct Peasant : public PeasantT<Peasant>
{
Peasant();
@@ -32,11 +59,14 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
void RequestQuitAll();
void Quit();
void AttachContentToWindow(Remoting::AttachRequest request);
winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs GetLastActivatedArgs();
winrt::Microsoft::Terminal::Remoting::CommandlineArgs InitialArgs();
winrt::hstring GetWindowLayout();
void SendContent(winrt::Microsoft::Terminal::Remoting::RequestReceiveContentArgs args);
WINRT_PROPERTY(winrt::hstring, WindowName);
WINRT_PROPERTY(winrt::hstring, ActiveTabTitle);
@@ -47,12 +77,16 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
TYPED_EVENT(DisplayWindowIdRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(RenameRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::RenameRequestArgs);
TYPED_EVENT(SummonRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::SummonWindowBehavior);
TYPED_EVENT(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(HideNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(QuitRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(GetWindowLayoutRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::GetWindowLayoutArgs);
TYPED_EVENT(AttachRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::AttachRequest);
TYPED_EVENT(SendContentRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::RequestReceiveContentArgs);
private:
Peasant(const uint64_t testPID);
uint64_t _ourPID;
@@ -69,4 +103,5 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
namespace winrt::Microsoft::Terminal::Remoting::factory_implementation
{
BASIC_FACTORY(Peasant);
BASIC_FACTORY(RequestReceiveContentArgs);
}

View File

@@ -43,7 +43,6 @@ namespace Microsoft.Terminal.Remoting
ToMouse,
};
[default_interface] runtimeclass SummonWindowBehavior {
SummonWindowBehavior();
Boolean MoveToCurrentDesktop;
@@ -52,6 +51,18 @@ namespace Microsoft.Terminal.Remoting
MonitorBehavior ToMonitor;
}
[default_interface] runtimeclass AttachRequest {
String Content { get; };
UInt32 TabIndex { get; };
}
[default_interface] runtimeclass RequestReceiveContentArgs {
RequestReceiveContentArgs(UInt64 src, UInt64 tgt, UInt32 tabIndex);
UInt64 SourceWindow { get; };
UInt64 TargetWindow { get; };
UInt32 TabIndex { get; };
};
interface IPeasant
{
CommandlineArgs InitialArgs { get; };
@@ -70,23 +81,31 @@ namespace Microsoft.Terminal.Remoting
void RequestIdentifyWindows(); // Tells us to raise a IdentifyWindowsRequested
void RequestRename(RenameRequestArgs args); // Tells us to raise a RenameRequested
void Summon(SummonWindowBehavior behavior);
void RequestShowNotificationIcon();
void RequestHideNotificationIcon();
void RequestQuitAll();
void Quit();
String GetWindowLayout();
void AttachContentToWindow(AttachRequest request);
void SendContent(RequestReceiveContentArgs args);
event Windows.Foundation.TypedEventHandler<Object, WindowActivatedArgs> WindowActivated;
event Windows.Foundation.TypedEventHandler<Object, CommandlineArgs> ExecuteCommandlineRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> IdentifyWindowsRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> DisplayWindowIdRequested;
event Windows.Foundation.TypedEventHandler<Object, RenameRequestArgs> RenameRequested;
event Windows.Foundation.TypedEventHandler<Object, SummonWindowBehavior> SummonRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> ShowNotificationIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> HideNotificationIconRequested;
event Windows.Foundation.TypedEventHandler<Object, GetWindowLayoutArgs> GetWindowLayoutRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> QuitAllRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> QuitRequested;
event Windows.Foundation.TypedEventHandler<Object, AttachRequest> AttachRequested;
event Windows.Foundation.TypedEventHandler<Object, RequestReceiveContentArgs> SendContentRequested;
};
[default_interface] runtimeclass Peasant : IPeasant

View File

@@ -2,10 +2,13 @@
// Licensed under the MIT license.
#include "pch.h"
#include "WindowManager.h"
#include "MonarchFactory.h"
#include "CommandlineArgs.h"
#include "../inc/WindowingBehavior.h"
#include "MonarchFactory.h"
#include "CommandlineArgs.h"
#include "FindTargetWindowArgs.h"
#include "ProposeCommandlineResult.h"
@@ -21,32 +24,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
{
WindowManager::WindowManager()
{
_monarchWaitInterrupt.create();
// Register with COM as a server for the Monarch class
_registerAsMonarch();
// Instantiate an instance of the Monarch. This may or may not be in-proc!
auto foundMonarch = false;
while (!foundMonarch)
{
try
{
_createMonarchAndCallbacks();
// _createMonarchAndCallbacks will initialize _isKing
foundMonarch = true;
}
catch (...)
{
// If we fail to find the monarch,
// stay in this jail until we do.
TraceLoggingWrite(g_hRemotingProvider,
"WindowManager_ExceptionInCtor",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
}
}
WindowManager::~WindowManager()
{
// IMPORTANT! Tear down the registration as soon as we exit. If we're not a
@@ -55,32 +33,178 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
// monarch!
CoRevokeClassObject(_registrationHostClass);
_registrationHostClass = 0;
SignalClose();
_monarchWaitInterrupt.SetEvent();
// A thread is joinable once it's been started. Basically this just
// makes sure that the thread isn't just default-constructed.
if (_electionThread.joinable())
{
_electionThread.join();
}
}
void WindowManager::SignalClose()
void WindowManager::_createMonarch()
{
// Heads up! This only works because we're using
// "metadata-based-marshalling" for our WinRT types. That means the OS is
// using the .winmd file we generate to figure out the proxy/stub
// definitions for our types automatically. This only works in the following
// cases:
//
// * If we're running unpackaged: the .winmd must be a sibling of the .exe
// * If we're running packaged: the .winmd must be in the package root
_monarch = try_create_instance<Remoting::IMonarch>(Monarch_clsid,
CLSCTX_LOCAL_SERVER);
}
// Check if we became the king, and if we are, wire up callbacks.
void WindowManager::_createCallbacks()
{
assert(_monarch);
// Here, we're the king!
//
// This is where you should do any additional setup that might need to be
// done when we become the king. This will be called both for the first
// window, and when the current monarch dies.
_monarch.WindowCreated({ get_weak(), &WindowManager::_WindowCreatedHandlers });
_monarch.WindowClosed({ get_weak(), &WindowManager::_WindowClosedHandlers });
_monarch.FindTargetWindowRequested({ this, &WindowManager::_raiseFindTargetWindowRequested });
_monarch.QuitAllRequested({ get_weak(), &WindowManager::_QuitAllRequestedHandlers });
_monarch.RequestNewWindow({ get_weak(), &WindowManager::_raiseRequestNewWindow });
}
void WindowManager::_registerAsMonarch()
{
winrt::check_hresult(CoRegisterClassObject(Monarch_clsid,
winrt::make<::MonarchFactory>().get(),
CLSCTX_LOCAL_SERVER,
REGCLS_MULTIPLEUSE,
&_registrationHostClass));
}
void WindowManager::_raiseFindTargetWindowRequested(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args)
{
_FindTargetWindowRequestedHandlers(sender, args);
}
void WindowManager::_raiseRequestNewWindow(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs& args)
{
_RequestNewWindowHandlers(sender, args);
}
Remoting::ProposeCommandlineResult WindowManager::ProposeCommandline(const Remoting::CommandlineArgs& args, const bool isolatedMode)
{
if (!isolatedMode)
{
// _createMonarch always attempts to connect an existing monarch. In
// isolated mode, we don't want to do that.
_createMonarch();
}
if (_monarch)
{
try
// We connected to a monarch instance, not us though. This won't hit
// in isolated mode.
// Send the commandline over to the monarch process
if (_proposeToMonarch(args))
{
_monarch.SignalClose(_peasant.GetID());
// If that succeeded, then we don't need to make a new window.
// Our job is done. Either the monarch is going to run the
// commandline in an existing window, or a new one, but either way,
// this process doesn't need to make a new window.
return winrt::make<ProposeCommandlineResult>(false);
}
// Otherwise, we'll try to handle this ourselves.
}
// Theoretically, this condition is always true here:
//
// if (_monarch == nullptr)
//
// If we do still have a _monarch at this point, then we must have
// successfully proposed to it in _proposeToMonarch, so we can't get
// here with a monarch.
{
// No preexisting instance.
// Raise an event, to ask how to handle this commandline. We can't ask
// the app ourselves - we exist isolated from that knowledge (and
// dependency hell). The WindowManager will raise this up to the app
// host, which will then ask the AppLogic, who will then parse the
// commandline and determine the provided ID of the window.
auto findWindowArgs{ winrt::make_self<Remoting::implementation::FindTargetWindowArgs>(args) };
// This is handled by some handler in-proc
_FindTargetWindowRequestedHandlers(*this, *findWindowArgs);
// After the event was handled, ResultTargetWindow() will be filled with
// the parsed result.
const auto targetWindow = findWindowArgs->ResultTargetWindow();
const auto targetWindowName = findWindowArgs->ResultTargetWindowName();
if (targetWindow == WindowingBehaviorUseNone)
{
// This commandline doesn't deserve a window. Don't make a monarch
// either.
return winrt::make<ProposeCommandlineResult>(false);
}
else
{
// This commandline _does_ want a window, which means we do want
// to create a window, and a monarch.
//
// Congrats! This is now THE PROCESS. It's the only one that's
// getting any windows.
// In isolated mode, we don't want to register as the monarch,
// we just want to make a local one. So we'll skip this step.
// The condition below it will handle making the unregistered
// local monarch.
if (!isolatedMode)
{
_registerAsMonarch();
_createMonarch();
}
else
{
TraceLoggingWrite(g_hRemotingProvider,
"WindowManager_IntentionallyIsolated",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
if (!_monarch)
{
// Something catastrophically bad happened here OR we were
// intentionally in isolated mode. We don't want to just
// exit immediately. Instead, we'll just instantiate a local
// Monarch instance, without registering it. We're firmly in
// the realm of undefined behavior, but better to have some
// window than not.
_monarch = winrt::make<Monarch>();
TraceLoggingWrite(g_hRemotingProvider,
"WindowManager_FailedToCoCreate",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
_createCallbacks();
// So, we wanted a new peasant. Cool!
//
// We need to fill in args.ResultTargetWindow,
// args.ResultTargetWindowName so that we can create the new
// window with those values. Otherwise, the very first window
// won't obey the given name / ID.
//
// So let's just ask the monarch (ourselves) to get those values.
return _monarch.ProposeCommandline(args);
}
CATCH_LOG()
}
}
void WindowManager::_proposeToMonarch(const Remoting::CommandlineArgs& args,
std::optional<uint64_t>& givenID,
winrt::hstring& givenName)
// Method Description:
// - Helper attempting to call to the monarch multiple times. If the monarch
// fails to respond, or we encounter any sort of error, we'll try again
// until we find one, or decisively determine there isn't one.
bool WindowManager::_proposeToMonarch(const Remoting::CommandlineArgs& args)
{
// these two errors are Win32 errors, convert them to HRESULTS so we can actually compare below.
static constexpr auto RPC_SERVER_UNAVAILABLE_HR = HRESULT_FROM_WIN32(RPC_S_SERVER_UNAVAILABLE);
@@ -114,10 +238,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
// dies between now and the inspection of
// `result.ShouldCreateWindow` below, we don't want to explode
// (since _proposeToMonarch is not try/caught).
auto outOfProcResult = _monarch.ProposeCommandline(args);
result = winrt::make<implementation::ProposeCommandlineResult>(outOfProcResult);
proposedCommandline = true;
_monarch.ProposeCommandline(args);
return true;
}
catch (...)
{
@@ -154,560 +277,75 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
_monarch = winrt::make<winrt::Microsoft::Terminal::Remoting::implementation::Monarch>();
_createCallbacks();
// Set the monarch to null, so that we'll create a new one
// (or just generally check if we need to even make a window
// for this commandline.)
_monarch = nullptr;
return false;
}
else
{
// We failed to ask the monarch. It must have died. Try and
// find the real monarch. Don't perform an election, that
// assumes we have a peasant, which we don't yet.
_createMonarchAndCallbacks();
// _createMonarchAndCallbacks will initialize _isKing
}
if (_isKing)
{
// We became the king. We don't need to ProposeCommandline to ourself, we're just
// going to do it.
//
// Return early, because there's nothing else for us to do here.
// find another monarch.
_createMonarch();
if (!_monarch)
{
// We failed to create a monarch. That means there
// aren't any other windows, and we can become the monarch.
return false;
}
// Go back around the loop.
TraceLoggingWrite(g_hRemotingProvider,
"WindowManager_proposeToMonarch_becameKing",
"WindowManager_proposeToMonarch_tryAgain",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
// In WindowManager::ProposeCommandline, had we been the
// king originally, we would have started by setting
// this to true. We became the monarch here, so set it
// here as well.
_shouldCreateWindow = true;
return;
}
// Here, we created the new monarch, it wasn't us, so we're
// gonna go through the while loop again and ask the new
// king.
TraceLoggingWrite(g_hRemotingProvider,
"WindowManager_proposeToMonarch_tryAgain",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
}
// Here, the monarch (not us) has replied to the message. Get the
// valuables out of the response:
_shouldCreateWindow = result.ShouldCreateWindow();
if (result.Id())
{
givenID = result.Id().Value();
}
givenName = result.WindowName();
// TraceLogging doesn't have a good solution for logging an
// optional. So we have to repeat the calls here:
if (givenID)
{
TraceLoggingWrite(g_hRemotingProvider,
"WindowManager_ProposeCommandline",
TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"),
TraceLoggingUInt64(givenID.value(), "Id", "The ID we should assign our peasant"),
TraceLoggingWideString(givenName.c_str(), "Name", "The name we should assign this window"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
else
{
TraceLoggingWrite(g_hRemotingProvider,
"WindowManager_ProposeCommandline",
TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"),
TraceLoggingPointer(nullptr, "Id", "No ID provided"),
TraceLoggingWideString(givenName.c_str(), "Name", "The name we should assign this window"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
}
void WindowManager::ProposeCommandline(const Remoting::CommandlineArgs& args)
{
// If we're the king, we _definitely_ want to process the arguments, we were
// launched with them!
//
// Otherwise, the King will tell us if we should make a new window
_shouldCreateWindow = _isKing;
std::optional<uint64_t> givenID;
winrt::hstring givenName{};
if (!_isKing)
{
_proposeToMonarch(args, givenID, givenName);
}
// During _proposeToMonarch, it's possible that we found that the king was dead, and we're the new king. Cool! Do this now.
if (_isKing)
{
// We're the monarch, we don't need to propose anything. We're just
// going to do it.
//
// However, we _do_ need to ask what our name should be. It's
// possible someone started the _first_ wt with something like `wt
// -w king` as the commandline - we want to make sure we set our
// name to "king".
//
// The FindTargetWindow event is the WindowManager's way of saying
// "I do not know how to figure out how to turn this list of args
// into a window ID/name. Whoever's listening to this event does, so
// I'll ask them". It's a convoluted way of hooking the
// WindowManager up to AppLogic without actually telling it anything
// about TerminalApp (or even WindowsTerminal)
auto findWindowArgs{ winrt::make_self<Remoting::implementation::FindTargetWindowArgs>(args) };
_raiseFindTargetWindowRequested(nullptr, *findWindowArgs);
const auto responseId = findWindowArgs->ResultTargetWindow();
if (responseId > 0)
{
givenID = ::base::saturated_cast<uint64_t>(responseId);
TraceLoggingWrite(g_hRemotingProvider,
"WindowManager_ProposeCommandline_AsMonarch",
TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"),
TraceLoggingUInt64(givenID.value(), "Id", "The ID we should assign our peasant"),
TraceLoggingWideString(givenName.c_str(), "Name", "The name we should assign this window"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
else if (responseId == WindowingBehaviorUseName)
{
givenName = findWindowArgs->ResultTargetWindowName();
TraceLoggingWrite(g_hRemotingProvider,
"WindowManager_ProposeCommandline_AsMonarch",
TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"),
TraceLoggingUInt64(0, "Id", "The ID we should assign our peasant"),
TraceLoggingWideString(givenName.c_str(), "Name", "The name we should assign this window"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
else
{
TraceLoggingWrite(g_hRemotingProvider,
"WindowManager_ProposeCommandline_AsMonarch",
TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"),
TraceLoggingUInt64(0, "Id", "The ID we should assign our peasant"),
TraceLoggingWideString(L"", "Name", "The name we should assign this window"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
}
if (_shouldCreateWindow)
{
// If we should create a new window, then instantiate our Peasant
// instance, and tell that peasant to handle that commandline.
_createOurPeasant({ givenID }, givenName);
// Spawn a thread to wait on the monarch, and handle the election
if (!_isKing)
{
_createPeasantThread();
}
// This right here will just tell us to stash the args away for the
// future. The AppHost hasn't yet set up the callbacks, and the rest
// of the app hasn't started at all. We'll note them and come back
// later.
_peasant.ExecuteCommandline(args);
}
// Otherwise, we'll do _nothing_.
// I don't think we can ever get here, but the compiler doesn't know
return false;
}
bool WindowManager::ShouldCreateWindow()
{
return _shouldCreateWindow;
}
void WindowManager::_registerAsMonarch()
{
winrt::check_hresult(CoRegisterClassObject(Monarch_clsid,
winrt::make<::MonarchFactory>().get(),
CLSCTX_LOCAL_SERVER,
REGCLS_MULTIPLEUSE,
&_registrationHostClass));
}
void WindowManager::_createMonarch()
{
// Heads up! This only works because we're using
// "metadata-based-marshalling" for our WinRT types. That means the OS is
// using the .winmd file we generate to figure out the proxy/stub
// definitions for our types automatically. This only works in the following
// cases:
//
// * If we're running unpackaged: the .winmd must be a sibling of the .exe
// * If we're running packaged: the .winmd must be in the package root
_monarch = create_instance<Remoting::IMonarch>(Monarch_clsid,
CLSCTX_LOCAL_SERVER);
}
// Tries to instantiate a monarch, tries again, and eventually either throws
// (so that the caller will try again) or falls back to the isolated
// monarch.
void WindowManager::_redundantCreateMonarch()
{
_createMonarch();
if (_monarch == nullptr)
{
// See MSFT:38540483, GH#12774 for details.
TraceLoggingWrite(g_hRemotingProvider,
"WindowManager_NullMonarchTryAgain",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
// Here we're gonna just give it a quick second try.Probably not
// definitive, but might help.
_createMonarch();
}
if (_monarch == nullptr)
{
// See MSFT:38540483, GH#12774 for details.
if constexpr (Feature_IsolatedMonarchMode::IsEnabled())
{
// Fall back to having a in proc monarch. Were now isolated from
// other windows. This is a pretty torn state, but at least we
// didn't just explode.
TraceLoggingWrite(g_hRemotingProvider,
"WindowManager_NullMonarchIsolateMode",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
_monarch = winrt::make<winrt::Microsoft::Terminal::Remoting::implementation::Monarch>();
}
else
{
// The monarch is null. We're hoping that we can find another,
// hopefully us. We're gonna go back around the loop again and
// see what happens. If this is really an infinite loop (where
// the OS won't even give us back US as the monarch), then I
// suppose we'll find out soon enough.
TraceLoggingWrite(g_hRemotingProvider,
"WindowManager_NullMonarchTryAgain",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
winrt::hresult_error(E_UNEXPECTED, L"Did not expect the Monarch to ever be null");
}
}
}
// NOTE: This can throw! Callers include:
// - the constructor, who performs this in a loop until it successfully
// find a a monarch
// - the performElection method, which is called in the waitOnMonarch
// thread. All the calls in that thread are wrapped in try/catch's
// already.
// - _createOurPeasant, who might do this in a loop to establish us with the
// monarch.
void WindowManager::_createMonarchAndCallbacks()
{
_redundantCreateMonarch();
// We're pretty confident that we have a Monarch here.
_createCallbacks();
}
// Check if we became the king, and if we are, wire up callbacks.
void WindowManager::_createCallbacks()
{
// Save the result of checking if we're the king. We want to avoid
// unnecessary calls back and forth if we can.
_isKing = _areWeTheKing();
TraceLoggingWrite(g_hRemotingProvider,
"WindowManager_ConnectedToMonarch",
TraceLoggingUInt64(_monarch.GetPID(), "monarchPID", "The PID of the new Monarch"),
TraceLoggingBoolean(_isKing, "isKing", "true if we are the new monarch"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
if (_peasant)
{
if (const auto& lastActivated{ _peasant.GetLastActivatedArgs() })
{
// Inform the monarch of the time we were last activated
_monarch.HandleActivatePeasant(lastActivated);
}
}
if (!_isKing)
{
return;
}
// Here, we're the king!
//
// This is where you should do any additional setup that might need to be
// done when we become the king. This will be called both for the first
// window, and when the current monarch dies.
_monarch.WindowCreated({ get_weak(), &WindowManager::_WindowCreatedHandlers });
_monarch.WindowClosed({ get_weak(), &WindowManager::_WindowClosedHandlers });
_monarch.FindTargetWindowRequested({ this, &WindowManager::_raiseFindTargetWindowRequested });
_monarch.ShowNotificationIconRequested([this](auto&&, auto&&) { _ShowNotificationIconRequestedHandlers(*this, nullptr); });
_monarch.HideNotificationIconRequested([this](auto&&, auto&&) { _HideNotificationIconRequestedHandlers(*this, nullptr); });
_monarch.QuitAllRequested({ get_weak(), &WindowManager::_QuitAllRequestedHandlers });
_BecameMonarchHandlers(*this, nullptr);
}
bool WindowManager::_areWeTheKing()
{
const auto ourPID{ GetCurrentProcessId() };
const auto kingPID{ _monarch.GetPID() };
return (ourPID == kingPID);
}
Remoting::IPeasant WindowManager::_createOurPeasant(std::optional<uint64_t> givenID,
const winrt::hstring& givenName)
Remoting::Peasant WindowManager::CreatePeasant(const Remoting::WindowRequestedArgs& args)
{
auto p = winrt::make_self<Remoting::implementation::Peasant>();
if (givenID)
// This will be false if the Id is 0, which is our sentinel for "no specific ID was requested"
if (const auto id = args.Id())
{
p->AssignID(givenID.value());
p->AssignID(id);
}
// If the name wasn't specified, this will be an empty string.
p->WindowName(givenName);
_peasant = *p;
p->WindowName(args.WindowName());
// Try to add us to the monarch. If that fails, try to find a monarch
// again, until we find one (we will eventually find us)
while (true)
{
try
{
_monarch.AddPeasant(_peasant);
break;
}
catch (...)
{
try
{
// Wrap this in its own try/catch, because this can throw.
_createMonarchAndCallbacks();
}
catch (...)
{
}
}
}
p->ExecuteCommandline(*winrt::make_self<CommandlineArgs>(args.Commandline(), args.CurrentDirectory()));
_peasant.GetWindowLayoutRequested({ get_weak(), &WindowManager::_GetWindowLayoutRequestedHandlers });
_monarch.AddPeasant(*p);
p->GetWindowLayoutRequested({ get_weak(), &WindowManager::_GetWindowLayoutRequestedHandlers });
TraceLoggingWrite(g_hRemotingProvider,
"WindowManager_CreateOurPeasant",
TraceLoggingUInt64(_peasant.GetID(), "peasantID", "The ID of our new peasant"),
TraceLoggingUInt64(p->GetID(), "peasantID", "The ID of our new peasant"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
// If the peasant asks us to quit we should not try to act in future elections.
_peasant.QuitRequested([weakThis{ get_weak() }](auto&&, auto&&) {
if (auto wm = weakThis.get())
{
wm->_monarchWaitInterrupt.SetEvent();
}
});
return _peasant;
return *p;
}
// Method Description:
// - Attempt to connect to the monarch process. This might be us!
// - For the new monarch, add us to their list of peasants.
// Arguments:
// - <none>
// Return Value:
// - true iff we're the new monarch process.
// NOTE: This can throw!
bool WindowManager::_performElection()
void WindowManager::SignalClose(const Remoting::Peasant& peasant)
{
_createMonarchAndCallbacks();
// Tell the new monarch who we are. We might be that monarch!
_monarch.AddPeasant(_peasant);
// This method is only called when a _new_ monarch is elected. So
// don't do anything here that needs to be done for all monarch
// windows. This should only be for work that's done when a window
// _becomes_ a monarch, after the death of the previous monarch.
return _isKing;
}
void WindowManager::_createPeasantThread()
{
// If we catch an exception trying to get at the monarch ever, we can
// set the _monarchWaitInterrupt, and use that to trigger a new
// election. Though, we wouldn't be able to retry the function that
// caused the exception in the first place...
_electionThread = std::thread([this] {
_waitOnMonarchThread();
});
}
void WindowManager::_waitOnMonarchThread()
{
// This is the array of HANDLEs that we're going to wait on in
// WaitForMultipleObjects below.
// * waits[0] will be the handle to the monarch process. It gets
// signalled when the process exits / dies.
// * waits[1] is the handle to our _monarchWaitInterrupt event. Another
// thread can use that to manually break this loop. We'll do that when
// we're getting torn down.
HANDLE waits[2];
waits[1] = _monarchWaitInterrupt.get();
const auto peasantID = _peasant.GetID(); // safe: _peasant is in-proc.
auto exitThreadRequested = false;
while (!exitThreadRequested)
if (_monarch)
{
// At any point in all this, the current monarch might die. If it
// does, we'll go straight to a new election, in the "jail"
// try/catch below. Worst case, eventually, we'll become the new
// monarch.
try
{
// This might fail to even ask the monarch for its PID.
wil::unique_handle hMonarch{ OpenProcess(PROCESS_ALL_ACCESS,
FALSE,
static_cast<DWORD>(_monarch.GetPID())) };
// If we fail to open the monarch, then they don't exist
// anymore! Go straight to an election.
if (hMonarch.get() == nullptr)
{
const auto gle = GetLastError();
TraceLoggingWrite(g_hRemotingProvider,
"WindowManager_FailedToOpenMonarch",
TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"),
TraceLoggingUInt64(gle, "lastError", "The result of GetLastError"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
exitThreadRequested = _performElection();
continue;
}
waits[0] = hMonarch.get();
auto waitResult = WaitForMultipleObjects(2, waits, FALSE, INFINITE);
switch (waitResult)
{
case WAIT_OBJECT_0 + 0: // waits[0] was signaled, the handle to the monarch process
TraceLoggingWrite(g_hRemotingProvider,
"WindowManager_MonarchDied",
TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
// Connect to the new monarch, which might be us!
// If we become the monarch, then we'll return true and exit this thread.
exitThreadRequested = _performElection();
break;
case WAIT_OBJECT_0 + 1: // waits[1] was signaled, our manual interrupt
TraceLoggingWrite(g_hRemotingProvider,
"WindowManager_MonarchWaitInterrupted",
TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
exitThreadRequested = true;
break;
case WAIT_TIMEOUT:
// This should be impossible.
TraceLoggingWrite(g_hRemotingProvider,
"WindowManager_MonarchWaitTimeout",
TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
exitThreadRequested = true;
break;
default:
{
// Returning any other value is invalid. Just die.
const auto gle = GetLastError();
TraceLoggingWrite(g_hRemotingProvider,
"WindowManager_WaitFailed",
TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"),
TraceLoggingUInt64(gle, "lastError", "The result of GetLastError"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
ExitProcess(0);
}
}
}
catch (...)
{
// Theoretically, if window[1] dies when we're trying to get
// its PID we'll get here. If we just try to do the election
// once here, it's possible we might elect window[2], but have
// it die before we add ourselves as a peasant. That
// _performElection call will throw, and we wouldn't catch it
// here, and we'd die.
// Instead, we're going to have a resilient election process.
// We're going to keep trying an election, until one _doesn't_
// throw an exception. That might mean burning through all the
// other dying monarchs until we find us as the monarch. But if
// this process is alive, then there's _someone_ in the line of
// succession.
TraceLoggingWrite(g_hRemotingProvider,
"WindowManager_ExceptionInWaitThread",
TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
auto foundNewMonarch = false;
while (!foundNewMonarch)
{
try
{
exitThreadRequested = _performElection();
// It doesn't matter if we're the monarch, or someone
// else is, but if we complete the election, then we've
// registered with a new one. We can escape this jail
// and re-enter society.
foundNewMonarch = true;
}
catch (...)
{
// If we fail to acknowledge the results of the election,
// stay in this jail until we do.
TraceLoggingWrite(g_hRemotingProvider,
"WindowManager_ExceptionInNestedWaitThread",
TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
}
_monarch.SignalClose(peasant.GetID());
}
CATCH_LOG()
}
}
Remoting::Peasant WindowManager::CurrentWindow()
{
return _peasant;
}
void WindowManager::_raiseFindTargetWindowRequested(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args)
{
_FindTargetWindowRequestedHandlers(sender, args);
}
bool WindowManager::IsMonarch()
{
return _isKing;
}
void WindowManager::SummonWindow(const Remoting::SummonWindowSelectionArgs& args)
{
// We should only ever get called when we are the monarch, because only
@@ -741,42 +379,16 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
return 0;
}
// Method Description:
// - Ask the monarch to show a notification icon.
// Arguments:
// - <none>
// Return Value:
// - <none>
winrt::fire_and_forget WindowManager::RequestShowNotificationIcon()
{
co_await winrt::resume_background();
_peasant.RequestShowNotificationIcon();
}
// Method Description:
// - Ask the monarch to hide its notification icon.
// Arguments:
// - <none>
// Return Value:
// - <none>
winrt::fire_and_forget WindowManager::RequestHideNotificationIcon()
{
auto strongThis{ get_strong() };
co_await winrt::resume_background();
_peasant.RequestHideNotificationIcon();
}
// Method Description:
// - Ask the monarch to quit all windows.
// Arguments:
// - <none>
// Return Value:
// - <none>
winrt::fire_and_forget WindowManager::RequestQuitAll()
winrt::fire_and_forget WindowManager::RequestQuitAll(Remoting::Peasant peasant)
{
auto strongThis{ get_strong() };
co_await winrt::resume_background();
_peasant.RequestQuitAll();
peasant.RequestQuitAll();
}
bool WindowManager::DoesQuakeWindowExist()
@@ -784,9 +396,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
return _monarch.DoesQuakeWindowExist();
}
void WindowManager::UpdateActiveTabTitle(winrt::hstring title)
void WindowManager::UpdateActiveTabTitle(const winrt::hstring& title, const Remoting::Peasant& peasant)
{
winrt::get_self<implementation::Peasant>(_peasant)->ActiveTabTitle(title);
winrt::get_self<implementation::Peasant>(peasant)->ActiveTabTitle(title);
}
Windows::Foundation::Collections::IVector<winrt::hstring> WindowManager::GetAllWindowLayouts()
@@ -801,4 +413,19 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
}
return nullptr;
}
winrt::fire_and_forget WindowManager::RequestMoveContent(winrt::hstring window,
winrt::hstring content,
uint32_t tabIndex,
Windows::Foundation::IReference<Windows::Foundation::Rect> windowBounds)
{
co_await winrt::resume_background();
_monarch.RequestMoveContent(window, content, tabIndex, windowBounds);
}
winrt::fire_and_forget WindowManager::RequestSendContent(Remoting::RequestReceiveContentArgs args)
{
co_await winrt::resume_background();
_monarch.RequestSendContent(args);
}
}

View File

@@ -1,10 +1,8 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Class Name:
- WindowManager.h
Abstract:
- The Window Manager takes care of coordinating the monarch and peasant for this
process.
@@ -16,9 +14,7 @@ Abstract:
- When the monarch needs to ask the TerminalApp about how to parse a
commandline, it'll ask by raising an event that we'll bubble up to the
AppHost.
--*/
#pragma once
#include "WindowManager.g.h"
@@ -29,65 +25,51 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
{
struct WindowManager : public WindowManagerT<WindowManager>
{
public:
WindowManager();
~WindowManager();
winrt::Microsoft::Terminal::Remoting::ProposeCommandlineResult ProposeCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args, const bool isolatedMode);
Remoting::Peasant CreatePeasant(const Remoting::WindowRequestedArgs& args);
void ProposeCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args);
bool ShouldCreateWindow();
winrt::Microsoft::Terminal::Remoting::Peasant CurrentWindow();
bool IsMonarch();
void SignalClose(const Remoting::Peasant& peasant);
void SummonWindow(const Remoting::SummonWindowSelectionArgs& args);
void SignalClose();
void SummonAllWindows();
uint64_t GetNumberOfPeasants();
Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo> GetPeasantInfos();
winrt::fire_and_forget RequestShowNotificationIcon();
winrt::fire_and_forget RequestHideNotificationIcon();
winrt::fire_and_forget RequestQuitAll();
bool DoesQuakeWindowExist();
void UpdateActiveTabTitle(winrt::hstring title);
uint64_t GetNumberOfPeasants();
static winrt::fire_and_forget RequestQuitAll(Remoting::Peasant peasant);
void UpdateActiveTabTitle(const winrt::hstring& title, const Remoting::Peasant& peasant);
Windows::Foundation::Collections::IVector<winrt::hstring> GetAllWindowLayouts();
bool DoesQuakeWindowExist();
winrt::fire_and_forget RequestMoveContent(winrt::hstring window, winrt::hstring content, uint32_t tabIndex, Windows::Foundation::IReference<Windows::Foundation::Rect> windowBounds);
winrt::fire_and_forget RequestSendContent(Remoting::RequestReceiveContentArgs args);
TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs);
TYPED_EVENT(BecameMonarch, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(WindowCreated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(WindowClosed, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(HideNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::QuitAllRequestedArgs);
TYPED_EVENT(GetWindowLayoutRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::GetWindowLayoutArgs);
TYPED_EVENT(RequestNewWindow, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs);
private:
bool _shouldCreateWindow{ false };
bool _isKing{ false };
DWORD _registrationHostClass{ 0 };
winrt::Microsoft::Terminal::Remoting::IMonarch _monarch{ nullptr };
winrt::Microsoft::Terminal::Remoting::Peasant _peasant{ nullptr };
wil::unique_event _monarchWaitInterrupt;
std::thread _electionThread;
void _registerAsMonarch();
void _createMonarch();
void _redundantCreateMonarch();
void _createMonarchAndCallbacks();
void _createCallbacks();
bool _areWeTheKing();
winrt::Microsoft::Terminal::Remoting::IPeasant _createOurPeasant(std::optional<uint64_t> givenID,
const winrt::hstring& givenName);
void _registerAsMonarch();
bool _performElection();
void _createPeasantThread();
void _waitOnMonarchThread();
bool _proposeToMonarch(const Remoting::CommandlineArgs& args);
void _createCallbacks();
void _raiseFindTargetWindowRequested(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args);
void _proposeToMonarch(const Remoting::CommandlineArgs& args,
std::optional<uint64_t>& givenID,
winrt::hstring& givenName);
void _raiseRequestNewWindow(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs& args);
};
}

View File

@@ -7,29 +7,36 @@ namespace Microsoft.Terminal.Remoting
[default_interface] runtimeclass WindowManager
{
WindowManager();
void ProposeCommandline(CommandlineArgs args);
void SignalClose();
Boolean ShouldCreateWindow { get; };
IPeasant CurrentWindow();
Boolean IsMonarch { get; };
ProposeCommandlineResult ProposeCommandline(CommandlineArgs args, Boolean isolatedMode);
Peasant CreatePeasant(WindowRequestedArgs args);
void SignalClose(Peasant p);
void UpdateActiveTabTitle(String title, Peasant p);
static void RequestQuitAll(Peasant p);
void SummonWindow(SummonWindowSelectionArgs args);
void SummonAllWindows();
void RequestShowNotificationIcon();
void RequestHideNotificationIcon();
Windows.Foundation.Collections.IVector<String> GetAllWindowLayouts();
Windows.Foundation.Collections.IVectorView<PeasantInfo> GetPeasantInfos();
UInt64 GetNumberOfPeasants();
void RequestQuitAll();
void UpdateActiveTabTitle(String title);
Boolean DoesQuakeWindowExist();
Windows.Foundation.Collections.IVectorView<PeasantInfo> GetPeasantInfos();
void RequestMoveContent(String window, String content, UInt32 tabIndex, Windows.Foundation.IReference<Windows.Foundation.Rect> bounds);
void RequestSendContent(RequestReceiveContentArgs args);
event Windows.Foundation.TypedEventHandler<Object, FindTargetWindowArgs> FindTargetWindowRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> BecameMonarch;
event Windows.Foundation.TypedEventHandler<Object, Object> WindowCreated;
event Windows.Foundation.TypedEventHandler<Object, Object> WindowClosed;
event Windows.Foundation.TypedEventHandler<Object, QuitAllRequestedArgs> QuitAllRequested;
event Windows.Foundation.TypedEventHandler<Object, GetWindowLayoutArgs> GetWindowLayoutRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> ShowNotificationIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> HideNotificationIconRequested;
event Windows.Foundation.TypedEventHandler<Object, WindowRequestedArgs> RequestNewWindow;
};
}

View File

@@ -24,27 +24,5 @@ namespace winrt::TerminalApp::implementation
Name(command.Name());
KeyChordText(command.KeyChordText());
Icon(command.IconPath());
_commandChangedRevoker = command.PropertyChanged(winrt::auto_revoke, [weakThis{ get_weak() }](auto& sender, auto& e) {
auto item{ weakThis.get() };
auto senderCommand{ sender.try_as<Microsoft::Terminal::Settings::Model::Command>() };
if (item && senderCommand)
{
auto changedProperty = e.PropertyName();
if (changedProperty == L"Name")
{
item->Name(senderCommand.Name());
}
else if (changedProperty == L"KeyChordText")
{
item->KeyChordText(senderCommand.KeyChordText());
}
else if (changedProperty == L"IconPath")
{
item->Icon(senderCommand.IconPath());
}
}
});
}
}

View File

@@ -50,6 +50,7 @@ namespace winrt::TerminalApp::implementation
{
case ShortcutAction::SetColorScheme:
case ShortcutAction::AdjustOpacity:
case ShortcutAction::SendInput:
{
_RunRestorePreviews();
break;
@@ -140,6 +141,22 @@ namespace winrt::TerminalApp::implementation
});
}
void TerminalPage::_PreviewSendInput(const Settings::Model::SendInputArgs& args)
{
const auto backup = _restorePreviewFuncs.empty();
_ApplyToActiveControls([&](const auto& control) {
control.PreviewInput(args.Input());
if (backup)
{
_restorePreviewFuncs.emplace_back([=]() {
// On dismiss:
control.PreviewInput(L"");
});
}
});
}
void TerminalPage::_PreviewAction(const Settings::Model::ActionAndArgs& args)
{
switch (args.Action())
@@ -150,6 +167,9 @@ namespace winrt::TerminalApp::implementation
case ShortcutAction::AdjustOpacity:
_PreviewAdjustOpacity(args.Args().try_as<AdjustOpacityArgs>());
break;
case ShortcutAction::SendInput:
_PreviewSendInput(args.Args().try_as<SendInputArgs>());
break;
}
// GH#9818 Other ideas for actions that could be preview-able:

View File

@@ -77,22 +77,7 @@ namespace winrt::TerminalApp::implementation
/// <param name="e">Details about the launch request and process.</param>
void App::OnLaunched(const LaunchActivatedEventArgs& /*e*/)
{
// if this is a UWP... it means its our problem to hook up the content to the window here.
if (_isUwp)
{
auto content = Window::Current().Content();
if (content == nullptr)
{
auto logic = Logic();
logic.RunAsUwp(); // Must set UWP status first, settings might change based on it.
logic.ReloadSettings();
logic.Create();
auto page = logic.GetRoot().as<TerminalPage>();
Window::Current().Content(page);
Window::Current().Activate();
}
}
// We used to support a pure UWP version of the Terminal. This method
// was only ever used to do UWP-specific setup of our App.
}
}

View File

@@ -209,7 +209,7 @@ namespace winrt::TerminalApp::implementation
}
else if (const auto& realArgs = args.ActionArgs().try_as<MovePaneArgs>())
{
auto moved = _MovePane(realArgs.TabIndex());
auto moved = _MovePane(realArgs);
args.Handled(moved);
}
}
@@ -789,17 +789,8 @@ namespace winrt::TerminalApp::implementation
{
if (const auto& realArgs = actionArgs.ActionArgs().try_as<MoveTabArgs>())
{
auto direction = realArgs.Direction();
if (direction != MoveTabDirection::None)
{
if (auto focusedTabIndex = _GetFocusedTabIndex())
{
auto currentTabIndex = focusedTabIndex.value();
auto delta = direction == MoveTabDirection::Forward ? 1 : -1;
_TryMoveTab(currentTabIndex, currentTabIndex + delta);
}
}
actionArgs.Handled(true);
auto moved = _MoveTab(realArgs);
actionArgs.Handled(moved);
}
}
@@ -1131,6 +1122,35 @@ namespace winrt::TerminalApp::implementation
}
}
void TerminalPage::_HandleSelectCommand(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (args)
{
if (const auto& realArgs = args.ActionArgs().try_as<SelectCommandArgs>())
{
const auto res = _ApplyToActiveControls([&](auto& control) {
control.SelectCommand(realArgs.Direction() == Settings::Model::SelectOutputDirection::Previous);
});
args.Handled(res);
}
}
}
void TerminalPage::_HandleSelectOutput(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (args)
{
if (const auto& realArgs = args.ActionArgs().try_as<SelectOutputArgs>())
{
const auto res = _ApplyToActiveControls([&](auto& control) {
control.SelectOutput(realArgs.Direction() == Settings::Model::SelectOutputDirection::Previous);
});
args.Handled(res);
}
}
}
void TerminalPage::_HandleMarkMode(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
@@ -1161,6 +1181,32 @@ namespace winrt::TerminalApp::implementation
}
}
void TerminalPage::_HandleSuggestions(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (args)
{
if (const auto& realArgs = args.ActionArgs().try_as<SuggestionsArgs>())
{
auto source = realArgs.Source();
switch (source)
{
case SuggestionsSource::CommandHistory:
{
if (const auto& control{ _GetActiveControl() })
{
const auto context = control.CommandHistory();
_OpenSuggestions(Command::HistoryToCommands(context.History(), context.CurrentCommandline(), false), SuggestionsMode::Palette);
}
args.Handled(true);
}
break;
}
}
}
}
void TerminalPage::_HandleColorSelection(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{

View File

@@ -340,7 +340,7 @@ void AppCommandlineArgs::_buildMovePaneParser()
if (_movePaneTabIndex >= 0)
{
movePaneAction.Action(ShortcutAction::MovePane);
MovePaneArgs args{ static_cast<unsigned int>(_movePaneTabIndex) };
MovePaneArgs args{ static_cast<unsigned int>(_movePaneTabIndex), L"" };
movePaneAction.Args(args);
_startupActions.push_back(movePaneAction);
}

File diff suppressed because it is too large Load Diff

View File

@@ -5,10 +5,12 @@
#include "AppLogic.g.h"
#include "FindTargetWindowResult.g.h"
#include "SystemMenuChangeArgs.g.h"
#include "Jumplist.h"
#include "LanguageProfileNotifier.h"
#include "TerminalPage.h"
#include "AppCommandlineArgs.h"
#include "TerminalWindow.h"
#include "ContentManager.h"
#include <inc/cppwinrt_utils.h>
#include <ThrottledFunc.h>
@@ -36,18 +38,7 @@ namespace winrt::TerminalApp::implementation
FindTargetWindowResult(id, L""){};
};
struct SystemMenuChangeArgs : SystemMenuChangeArgsT<SystemMenuChangeArgs>
{
WINRT_PROPERTY(winrt::hstring, Name, L"");
WINRT_PROPERTY(SystemMenuChangeAction, Action, SystemMenuChangeAction::Add);
WINRT_PROPERTY(SystemMenuItemHandler, Handler, nullptr);
public:
SystemMenuChangeArgs(const winrt::hstring& name, SystemMenuChangeAction action, SystemMenuItemHandler handler = nullptr) :
_Name{ name }, _Action{ action }, _Handler{ handler } {};
};
struct AppLogic : AppLogicT<AppLogic, IInitializeWithWindow>
struct AppLogic : AppLogicT<AppLogic>
{
public:
static AppLogic* Current() noexcept;
@@ -56,172 +47,71 @@ namespace winrt::TerminalApp::implementation
AppLogic();
~AppLogic() = default;
STDMETHODIMP Initialize(HWND hwnd);
void Create();
bool IsUwp() const noexcept;
void RunAsUwp();
bool IsElevated() const noexcept;
void ReloadSettings();
bool HasSettingsStartupActions() const noexcept;
bool ShouldUsePersistedLayout() const;
void SaveWindowLayoutJsons(const Windows::Foundation::Collections::IVector<hstring>& layouts);
[[nodiscard]] Microsoft::Terminal::Settings::Model::CascadiaSettings GetSettings() const noexcept;
void Quit();
bool HasCommandlineArguments() const noexcept;
bool HasSettingsStartupActions() const noexcept;
int32_t SetStartupCommandline(array_view<const winrt::hstring> actions);
int32_t ExecuteCommandline(array_view<const winrt::hstring> actions, const winrt::hstring& cwd);
TerminalApp::FindTargetWindowResult FindTargetWindow(array_view<const winrt::hstring> actions);
winrt::hstring ParseCommandlineMessage();
bool ShouldExitEarly();
bool FocusMode() const;
bool Fullscreen() const;
void Maximized(bool newMaximized);
bool AlwaysOnTop() const;
bool AutoHideWindow();
bool ShouldUsePersistedLayout();
bool ShouldImmediatelyHandoffToElevated();
void HandoffToElevated();
hstring GetWindowLayoutJson(Microsoft::Terminal::Settings::Model::LaunchPosition position);
void SaveWindowLayoutJsons(const Windows::Foundation::Collections::IVector<hstring>& layouts);
void IdentifyWindow();
void RenameFailed();
winrt::hstring WindowName();
void WindowName(const winrt::hstring& name);
uint64_t WindowId();
void WindowId(const uint64_t& id);
void SetPersistedLayoutIdx(const uint32_t idx);
void SetNumberOfOpenWindows(const uint64_t num);
bool IsQuakeWindow() const noexcept;
void RequestExitFullscreen();
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();
bool GetShowTabsInTitlebar();
bool GetInitialAlwaysOnTop();
float CalcSnappedDimension(const bool widthOrHeight, const float dimension) const;
Windows::UI::Xaml::UIElement GetRoot() noexcept;
void SetInboundListener();
hstring Title();
void TitlebarClicked();
bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down);
void CloseWindow(Microsoft::Terminal::Settings::Model::LaunchPosition position);
void WindowVisibilityChanged(const bool showOrHide);
winrt::TerminalApp::TaskbarState TaskbarState();
winrt::Windows::UI::Xaml::Media::Brush TitlebarBrush();
void WindowActivated(const bool activated);
bool GetMinimizeToNotificationArea();
bool GetAlwaysShowNotificationIcon();
bool GetShowTitleInTitlebar();
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> ShowDialog(winrt::Windows::UI::Xaml::Controls::ContentDialog dialog);
void DismissDialog();
Windows::Foundation::Collections::IMapView<Microsoft::Terminal::Control::KeyChord, Microsoft::Terminal::Settings::Model::Command> GlobalHotkeys();
Microsoft::Terminal::Settings::Model::Theme Theme();
bool IsolatedMode();
bool AllowHeadless();
bool RequestsTrayIcon();
// -------------------------------- WinRT Events ---------------------------------
// PropertyChanged is surprisingly not a typed event, so we'll define that one manually.
// Usually we'd just do
// WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
//
// But what we're doing here is exposing the Page's PropertyChanged _as
// our own event_. It's a FORWARDED_CALLBACK, essentially.
winrt::event_token PropertyChanged(Windows::UI::Xaml::Data::PropertyChangedEventHandler const& handler) { return _root->PropertyChanged(handler); }
void PropertyChanged(winrt::event_token const& token) { _root->PropertyChanged(token); }
TerminalApp::TerminalWindow CreateNewWindow();
TYPED_EVENT(RequestedThemeChanged, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Settings::Model::Theme);
TYPED_EVENT(SettingsChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(SystemMenuChangeRequested, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::SystemMenuChangeArgs);
winrt::TerminalApp::ContentManager ContentManager();
TerminalApp::ParseCommandlineResult GetParseCommandlineMessage(array_view<const winrt::hstring> args);
TYPED_EVENT(SettingsChanged, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::SettingsLoadEventArgs);
private:
bool _isUwp{ false };
bool _isElevated{ false };
// If you add controls here, but forget to null them either here or in
// the ctor, you're going to have a bad time. It'll mysteriously fail to
// activate the AppLogic.
// ALSO: If you add any UIElements as roots here, make sure they're
// updated in _ApplyTheme. The root currently is _root.
winrt::com_ptr<TerminalPage> _root{ nullptr };
Microsoft::Terminal::Settings::Model::CascadiaSettings _settings{ nullptr };
winrt::hstring _settingsLoadExceptionText;
HRESULT _settingsLoadedResult = S_OK;
bool _loadedInitialSettings = false;
uint64_t _numOpenWindows{ 0 };
std::shared_mutex _dialogLock;
winrt::Windows::UI::Xaml::Controls::ContentDialog _dialog;
::TerminalApp::AppCommandlineArgs _appArgs;
bool _hasSettingsStartupActions{ false };
::TerminalApp::AppCommandlineArgs _settingsAppArgs;
std::shared_ptr<ThrottledFuncTrailing<>> _reloadSettings;
til::throttled_func_trailing<> _reloadState;
std::vector<Microsoft::Terminal::Settings::Model::SettingsLoadWarnings> _warnings{};
// These fields invoke _reloadSettings and must be destroyed before _reloadSettings.
// (C++ destroys members in reverse-declaration-order.)
winrt::com_ptr<LanguageProfileNotifier> _languageProfileNotifier;
wil::unique_folder_change_reader_nothrow _reader;
TerminalApp::ContentManager _contentManager{ *winrt::make_self<implementation::ContentManager>() };
static TerminalApp::FindTargetWindowResult _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();
bool _IsKeyboardServiceEnabled();
void _ApplyLanguageSettingChange() noexcept;
void _RefreshThemeRoutine();
fire_and_forget _ApplyStartupTaskStateChange();
void _OnLoaded(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
[[nodiscard]] HRESULT _TryLoadSettings() noexcept;
void _ProcessLazySettingsChanges();
void _RegisterSettingsChange();
fire_and_forget _DispatchReloadSettings();
void _OpenSettingsUI();
bool _hasCommandLineArguments{ false };
bool _hasSettingsStartupActions{ false };
std::vector<Microsoft::Terminal::Settings::Model::SettingsLoadWarnings> _warnings;
// These are events that are handled by the TerminalPage, but are
// exposed through the AppLogic. This macro is used to forward the event
// directly to them.
FORWARDED_TYPED_EVENT(SetTitleBarContent, winrt::Windows::Foundation::IInspectable, winrt::Windows::UI::Xaml::UIElement, _root, SetTitleBarContent);
FORWARDED_TYPED_EVENT(TitleChanged, winrt::Windows::Foundation::IInspectable, winrt::hstring, _root, TitleChanged);
FORWARDED_TYPED_EVENT(LastTabClosed, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::LastTabClosedEventArgs, _root, LastTabClosed);
FORWARDED_TYPED_EVENT(FocusModeChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, FocusModeChanged);
FORWARDED_TYPED_EVENT(FullscreenChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, FullscreenChanged);
FORWARDED_TYPED_EVENT(ChangeMaximizeRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, ChangeMaximizeRequested);
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);
FORWARDED_TYPED_EVENT(IdentifyWindowsRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, IdentifyWindowsRequested);
FORWARDED_TYPED_EVENT(RenameWindowRequested, Windows::Foundation::IInspectable, winrt::TerminalApp::RenameWindowRequestedArgs, _root, RenameWindowRequested);
FORWARDED_TYPED_EVENT(IsQuakeWindowChanged, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, IsQuakeWindowChanged);
FORWARDED_TYPED_EVENT(SummonWindowRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, SummonWindowRequested);
FORWARDED_TYPED_EVENT(CloseRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, CloseRequested);
FORWARDED_TYPED_EVENT(OpenSystemMenu, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, OpenSystemMenu);
FORWARDED_TYPED_EVENT(QuitRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, QuitRequested);
FORWARDED_TYPED_EVENT(ShowWindowChanged, Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Control::ShowWindowArgs, _root, ShowWindowChanged);
#ifdef UNIT_TESTING
friend class TerminalAppLocalTests::CommandlineTest;

View File

@@ -1,41 +1,25 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import "TerminalPage.idl";
import "ShortcutActionDispatch.idl";
import "IDirectKeyListener.idl";
import "TerminalWindow.idl";
namespace TerminalApp
{
struct InitialPosition
{
Int64 X;
Int64 Y;
};
[default_interface] runtimeclass FindTargetWindowResult
{
Int32 WindowId { get; };
String WindowName { get; };
};
delegate void SystemMenuItemHandler();
enum SystemMenuChangeAction
struct ParseCommandlineResult
{
Add = 0,
Remove = 1
String Message;
Int32 ExitCode;
};
[default_interface] runtimeclass SystemMenuChangeArgs {
String Name { get; };
SystemMenuChangeAction Action { get; };
SystemMenuItemHandler Handler { get; };
};
// See IDialogPresenter and TerminalPage's DialogPresenter for more
// information.
[default_interface] runtimeclass AppLogic : IDirectKeyListener, IDialogPresenter, Windows.UI.Xaml.Data.INotifyPropertyChanged
[default_interface] runtimeclass AppLogic
{
AppLogic();
@@ -50,94 +34,30 @@ namespace TerminalApp
void RunAsUwp();
Boolean IsElevated();
Boolean HasCommandlineArguments();
ContentManager ContentManager { get; };
Boolean HasSettingsStartupActions();
Int32 SetStartupCommandline(String[] commands);
Int32 ExecuteCommandline(String[] commands, String cwd);
String ParseCommandlineMessage { get; };
Boolean ShouldExitEarly { get; };
void Quit();
void ReloadSettings();
Windows.UI.Xaml.UIElement GetRoot();
void SetInboundListener();
String Title { get; };
Boolean FocusMode { get; };
Boolean Fullscreen { get; };
void Maximized(Boolean newMaximized);
Boolean AlwaysOnTop { get; };
Boolean AutoHideWindow { get; };
void IdentifyWindow();
String WindowName;
UInt64 WindowId;
void SetPersistedLayoutIdx(UInt32 idx);
void SetNumberOfOpenWindows(UInt64 num);
void RenameFailed();
void RequestExitFullscreen();
Boolean IsQuakeWindow();
Windows.Foundation.Size GetLaunchDimensions(UInt32 dpi);
Boolean CenterOnLaunch { get; };
InitialPosition GetInitialPosition(Int64 defaultInitialX, Int64 defaultInitialY);
Windows.UI.Xaml.ElementTheme GetRequestedTheme();
Microsoft.Terminal.Settings.Model.LaunchMode GetLaunchMode();
Boolean GetShowTabsInTitlebar();
Boolean GetInitialAlwaysOnTop();
Single CalcSnappedDimension(Boolean widthOrHeight, Single dimension);
void TitlebarClicked();
void CloseWindow(Microsoft.Terminal.Settings.Model.LaunchPosition position);
void WindowVisibilityChanged(Boolean showOrHide);
TaskbarState TaskbarState{ get; };
Windows.UI.Xaml.Media.Brush TitlebarBrush { get; };
void WindowActivated(Boolean activated);
Boolean ShouldUsePersistedLayout();
Boolean ShouldImmediatelyHandoffToElevated();
void HandoffToElevated();
String GetWindowLayoutJson(Microsoft.Terminal.Settings.Model.LaunchPosition position);
void SaveWindowLayoutJsons(Windows.Foundation.Collections.IVector<String> layouts);
Boolean GetMinimizeToNotificationArea();
Boolean GetAlwaysShowNotificationIcon();
Boolean GetShowTitleInTitlebar();
void ReloadSettings();
// Selected settings to expose
Microsoft.Terminal.Settings.Model.Theme Theme { get; };
Boolean IsolatedMode { get; };
Boolean AllowHeadless { get; };
Boolean RequestsTrayIcon { get; };
FindTargetWindowResult FindTargetWindow(String[] args);
Windows.Foundation.Collections.IMapView<Microsoft.Terminal.Control.KeyChord, Microsoft.Terminal.Settings.Model.Command> GlobalHotkeys();
TerminalWindow CreateNewWindow();
// See IDialogPresenter and TerminalPage's DialogPresenter for more
// information.
Windows.Foundation.IAsyncOperation<Windows.UI.Xaml.Controls.ContentDialogResult> ShowDialog(Windows.UI.Xaml.Controls.ContentDialog dialog);
void DismissDialog();
ParseCommandlineResult GetParseCommandlineMessage(String[] args);
event Windows.Foundation.TypedEventHandler<Object, Windows.UI.Xaml.UIElement> SetTitleBarContent;
event Windows.Foundation.TypedEventHandler<Object, String> TitleChanged;
event Windows.Foundation.TypedEventHandler<Object, LastTabClosedEventArgs> LastTabClosed;
event Windows.Foundation.TypedEventHandler<Object, Microsoft.Terminal.Settings.Model.Theme> RequestedThemeChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> FocusModeChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> FullscreenChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> ChangeMaximizeRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> AlwaysOnTopChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> RaiseVisualBell;
event Windows.Foundation.TypedEventHandler<Object, Object> SetTaskbarProgress;
event Windows.Foundation.TypedEventHandler<Object, Object> IdentifyWindowsRequested;
event Windows.Foundation.TypedEventHandler<Object, RenameWindowRequestedArgs> RenameWindowRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> SettingsChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> IsQuakeWindowChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> SummonWindowRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> CloseRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> OpenSystemMenu;
event Windows.Foundation.TypedEventHandler<Object, Object> QuitRequested;
event Windows.Foundation.TypedEventHandler<Object, TerminalApp.SystemMenuChangeArgs> SystemMenuChangeRequested;
event Windows.Foundation.TypedEventHandler<Object, Microsoft.Terminal.Control.ShowWindowArgs> ShowWindowChanged;
IMapView<Microsoft.Terminal.Control.KeyChord, Microsoft.Terminal.Settings.Model.Command> GlobalHotkeys();
event Windows.Foundation.TypedEventHandler<Object, SettingsLoadEventArgs> SettingsChanged;
}
}

View File

@@ -1,9 +1,6 @@
#include "pch.h"
#include "ColorPickupFlyout.h"
#include "ColorPickupFlyout.g.cpp"
#include "winrt/Windows.UI.Xaml.Media.h"
#include "winrt/Windows.UI.Xaml.Shapes.h"
#include "winrt/Windows.UI.Xaml.Interop.h"
#include <LibraryResources.h>
namespace winrt::TerminalApp::implementation

View File

@@ -225,15 +225,24 @@ namespace winrt::TerminalApp::implementation
void CommandPalette::_selectedCommandChanged(const IInspectable& /*sender*/,
const Windows::UI::Xaml::RoutedEventArgs& /*args*/)
{
const auto currentlyVisible{ Visibility() == Visibility::Visible };
const auto selectedCommand = _filteredActionsView().SelectedItem();
const auto filteredCommand{ selectedCommand.try_as<winrt::TerminalApp::FilteredCommand>() };
if (_currentMode == CommandPaletteMode::TabSwitchMode)
{
_switchToTab(filteredCommand);
}
else if (_currentMode == CommandPaletteMode::ActionMode && filteredCommand != nullptr)
else if (_currentMode == CommandPaletteMode::ActionMode &&
currentlyVisible)
{
if (const auto actionPaletteItem{ filteredCommand.Item().try_as<winrt::TerminalApp::ActionPaletteItem>() })
// If we don't have a selected command, then end any previews we
// might currently be showing.
if (filteredCommand == nullptr)
{
_PreviewActionHandlers(*this, nullptr);
}
else if (const auto actionPaletteItem{ filteredCommand.Item().try_as<winrt::TerminalApp::ActionPaletteItem>() })
{
_PreviewActionHandlers(*this, actionPaletteItem.Command());
}
@@ -1083,7 +1092,9 @@ namespace winrt::TerminalApp::implementation
{
std::copy(begin(commandsToFilter), end(commandsToFilter), std::back_inserter(actions));
}
else if (_currentMode == CommandPaletteMode::TabSearchMode || _currentMode == CommandPaletteMode::ActionMode || _currentMode == CommandPaletteMode::CommandlineMode)
else if (_currentMode == CommandPaletteMode::TabSearchMode ||
_currentMode == CommandPaletteMode::ActionMode ||
_currentMode == CommandPaletteMode::CommandlineMode)
{
for (const auto& action : commandsToFilter)
{

View File

@@ -0,0 +1,75 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "ContentManager.h"
#include "ContentManager.g.cpp"
#include <wil/token_helpers.h>
#include "../../types/inc/utils.hpp"
using namespace winrt::Windows::ApplicationModel;
using namespace winrt::Windows::ApplicationModel::DataTransfer;
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Xaml::Controls;
using namespace winrt::Windows::UI::Core;
using namespace winrt::Windows::System;
using namespace winrt::Microsoft::Terminal;
using namespace winrt::Microsoft::Terminal::Control;
using namespace winrt::Microsoft::Terminal::Settings::Model;
namespace winrt
{
namespace MUX = Microsoft::UI::Xaml;
using IInspectable = Windows::Foundation::IInspectable;
}
namespace winrt::TerminalApp::implementation
{
ControlInteractivity ContentManager::CreateCore(Microsoft::Terminal::Control::IControlSettings settings,
IControlAppearance unfocusedAppearance,
TerminalConnection::ITerminalConnection connection)
{
auto content = ControlInteractivity{ settings, unfocusedAppearance, connection };
content.Closed({ this, &ContentManager::_closedHandler });
const auto& contentGuid{ content.Id() };
_content.Insert(contentGuid, content);
return content;
}
ControlInteractivity ContentManager::LookupCore(winrt::guid id)
{
return _content.TryLookup(id);
}
void ContentManager::Detach(const Microsoft::Terminal::Control::TermControl& control)
{
const auto contentGuid{ control.ContentGuid() };
if (const auto& content{ LookupCore(contentGuid) })
{
control.Detach();
content.Attached({ get_weak(), &ContentManager::_finalizeDetach });
_recentlyDetachedContent.Insert(contentGuid, content);
}
}
void ContentManager::_finalizeDetach(winrt::Windows::Foundation::IInspectable sender,
winrt::Windows::Foundation::IInspectable e)
{
if (const auto& content{ sender.try_as<winrt::Microsoft::Terminal::Control::ControlInteractivity>() })
{
_recentlyDetachedContent.TryRemove(content.Id());
}
}
void ContentManager::_closedHandler(winrt::Windows::Foundation::IInspectable sender,
winrt::Windows::Foundation::IInspectable e)
{
if (const auto& content{ sender.try_as<winrt::Microsoft::Terminal::Control::ControlInteractivity>() })
{
const auto& contentGuid{ content.Id() };
_content.TryRemove(contentGuid);
_recentlyDetachedContent.TryRemove(contentGuid);
}
}
}

View File

@@ -0,0 +1,37 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include "ContentManager.g.h"
#include <inc/cppwinrt_utils.h>
namespace winrt::TerminalApp::implementation
{
struct ContentManager : ContentManagerT<ContentManager>
{
public:
ContentManager() = default;
Microsoft::Terminal::Control::ControlInteractivity CreateCore(Microsoft::Terminal::Control::IControlSettings settings,
Microsoft::Terminal::Control::IControlAppearance unfocusedAppearance,
Microsoft::Terminal::TerminalConnection::ITerminalConnection connection);
Microsoft::Terminal::Control::ControlInteractivity LookupCore(winrt::guid id);
void Detach(const Microsoft::Terminal::Control::TermControl& control);
private:
Windows::Foundation::Collections::IMap<winrt::guid, Microsoft::Terminal::Control::ControlInteractivity> _content{
winrt::multi_threaded_map<winrt::guid, Microsoft::Terminal::Control::ControlInteractivity>()
};
Windows::Foundation::Collections::IMap<winrt::guid, Microsoft::Terminal::Control::ControlInteractivity> _recentlyDetachedContent{
winrt::multi_threaded_map<winrt::guid, Microsoft::Terminal::Control::ControlInteractivity>()
};
void _finalizeDetach(winrt::Windows::Foundation::IInspectable sender,
winrt::Windows::Foundation::IInspectable e);
void _closedHandler(winrt::Windows::Foundation::IInspectable sender,
winrt::Windows::Foundation::IInspectable e);
};
}

View File

@@ -33,9 +33,6 @@ static const int CombinedPaneBorderSize = 2 * PaneBorderSize;
static const int AnimationDurationInMilliseconds = 200;
static const Duration AnimationDuration = DurationHelper::FromTimeSpan(winrt::Windows::Foundation::TimeSpan(std::chrono::milliseconds(AnimationDurationInMilliseconds)));
winrt::Windows::UI::Xaml::Media::SolidColorBrush Pane::s_focusedBorderBrush = { nullptr };
winrt::Windows::UI::Xaml::Media::SolidColorBrush Pane::s_unfocusedBorderBrush = { nullptr };
Pane::Pane(const Profile& profile, const TermControl& control, const bool lastFocused) :
_control{ control },
_lastActive{ lastFocused },
@@ -83,7 +80,7 @@ Pane::Pane(std::shared_ptr<Pane> first,
// Use the unfocused border color as the pane background, so an actual color
// appears behind panes as we animate them sliding in.
_root.Background(s_unfocusedBorderBrush);
_root.Background(_themeResources.unfocusedBorderBrush);
_root.Children().Append(_borderFirst);
_root.Children().Append(_borderSecond);
@@ -111,10 +108,12 @@ Pane::Pane(std::shared_ptr<Pane> first,
// - Extract the terminal settings from the current (leaf) pane's control
// to be used to create an equivalent control
// Arguments:
// - <none>
// - asContent: when true, we're trying to serialize this pane for moving across
// windows. In that case, we'll need to fill in the content guid for our new
// terminal args.
// Return Value:
// - Arguments appropriate for a SplitPane or NewTab action
NewTerminalArgs Pane::GetTerminalArgsForPane() const
NewTerminalArgs Pane::GetTerminalArgsForPane(const bool asContent) const
{
// Leaves are the only things that have controls
assert(_IsLeaf());
@@ -159,6 +158,12 @@ NewTerminalArgs Pane::GetTerminalArgsForPane() const
// object. That would work for schemes set by the Terminal, but not ones set
// by VT, but that seems good enough.
// Only fill in the ContentGuid if absolutely needed.
if (asContent)
{
args.ContentGuid(_control.ContentGuid());
}
return args;
}
@@ -170,36 +175,62 @@ NewTerminalArgs Pane::GetTerminalArgsForPane() const
// Arguments:
// - currentId: the id to use for the current/first pane
// - nextId: the id to use for a new pane if we split
// - asContent: We're serializing this set of actions as content actions for
// moving to other windows, so we need to make sure to include ContentGuid's
// in the final actions.
// - asMovePane: only used with asContent. When this is true, we're building
// these actions as a part of moving the pane to another window, but without
// the context of the hosting tab. In that case, we'll want to build a
// splitPane action even if we're just a single leaf, because there's no other
// parent to try and build an action for us.
// Return Value:
// - The state from building the startup actions, includes a vector of commands,
// the original root pane, the id of the focused pane, and the number of panes
// created.
Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId, uint32_t nextId)
Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId,
uint32_t nextId,
const bool asContent,
const bool asMovePane)
{
// if we are a leaf then all there is to do is defer to the parent.
if (_IsLeaf())
// Normally, if we're a leaf, return an empt set of actions, because the
// parent pane will build the SplitPane action for us. If we're building
// actions for a movePane action though, we'll still need to include
// ourselves.
if (!asMovePane && _IsLeaf())
{
if (_lastActive)
{
return { {}, shared_from_this(), currentId, 0 };
// empty args, this is the first pane, currentId is
return { .args = {}, .firstPane = shared_from_this(), .focusedPaneId = currentId, .panesCreated = 0 };
}
return { {}, shared_from_this(), std::nullopt, 0 };
return { .args = {}, .firstPane = shared_from_this(), .focusedPaneId = std::nullopt, .panesCreated = 0 };
}
auto buildSplitPane = [&](auto newPane) {
ActionAndArgs actionAndArgs;
actionAndArgs.Action(ShortcutAction::SplitPane);
const auto terminalArgs{ newPane->GetTerminalArgsForPane() };
const auto terminalArgs{ newPane->GetTerminalArgsForPane(asContent) };
// When creating a pane the split size is the size of the new pane
// and not position.
const auto splitDirection = _splitState == SplitState::Horizontal ? SplitDirection::Down : SplitDirection::Right;
SplitPaneArgs args{ SplitType::Manual, splitDirection, 1. - _desiredSplitPosition, terminalArgs };
const auto splitSize = (asContent && _IsLeaf() ? .5 : 1. - _desiredSplitPosition);
SplitPaneArgs args{ SplitType::Manual, splitDirection, splitSize, terminalArgs };
actionAndArgs.Args(args);
return actionAndArgs;
};
if (asContent && _IsLeaf())
{
return {
.args = { buildSplitPane(shared_from_this()) },
.firstPane = shared_from_this(),
.focusedPaneId = currentId,
.panesCreated = 1
};
}
auto buildMoveFocus = [](auto direction) {
MoveFocusArgs args{ direction };
@@ -226,7 +257,12 @@ Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId, uint32_t n
focusedPaneId = nextId;
}
return { { actionAndArgs }, _firstChild, focusedPaneId, 1 };
return {
.args = { actionAndArgs },
.firstPane = _firstChild,
.focusedPaneId = focusedPaneId,
.panesCreated = 1
};
}
// We now need to execute the commands for each side of the tree
@@ -263,7 +299,12 @@ Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId, uint32_t n
// mutually exclusive.
const auto focusedPaneId = firstState.focusedPaneId.has_value() ? firstState.focusedPaneId : secondState.focusedPaneId;
return { actions, firstState.firstPane, focusedPaneId, firstState.panesCreated + secondState.panesCreated + 1 };
return {
.args = { actions },
.firstPane = firstState.firstPane,
.focusedPaneId = focusedPaneId,
.panesCreated = firstState.panesCreated + secondState.panesCreated + 1
};
}
// Method Description:
@@ -1396,8 +1437,8 @@ void Pane::UpdateVisuals()
{
_UpdateBorders();
}
_borderFirst.BorderBrush(_lastActive ? s_focusedBorderBrush : s_unfocusedBorderBrush);
_borderSecond.BorderBrush(_lastActive ? s_focusedBorderBrush : s_unfocusedBorderBrush);
_borderFirst.BorderBrush(_lastActive ? _themeResources.focusedBorderBrush : _themeResources.unfocusedBorderBrush);
_borderSecond.BorderBrush(_lastActive ? _themeResources.focusedBorderBrush : _themeResources.unfocusedBorderBrush);
}
// Method Description:
@@ -1849,7 +1890,7 @@ winrt::fire_and_forget Pane::_CloseChildRoutine(const bool closeFirst)
Controls::Grid dummyGrid;
// GH#603 - we can safely add a BG here, as the control is gone right
// away, to fill the space as the rest of the pane expands.
dummyGrid.Background(s_unfocusedBorderBrush);
dummyGrid.Background(_themeResources.unfocusedBorderBrush);
// It should be the size of the closed pane.
dummyGrid.Width(removedOriginalSize.Width);
dummyGrid.Height(removedOriginalSize.Height);
@@ -2127,7 +2168,7 @@ void Pane::_SetupEntranceAnimation()
// * If we give the parent (us) root BG a color, then a transparent pane
// will flash opaque during the animation, then back to transparent, which
// looks bad.
_secondChild->_root.Background(s_unfocusedBorderBrush);
_secondChild->_root.Background(_themeResources.unfocusedBorderBrush);
const auto [firstSize, secondSize] = _CalcChildrenSizes(::base::saturated_cast<float>(totalSize));
@@ -3092,51 +3133,20 @@ float Pane::_ClampSplitPosition(const bool widthOrHeight, const float requestedV
return std::clamp(requestedValue, minSplitPosition, maxSplitPosition);
}
// Function Description:
// - Attempts to load some XAML resources that the Pane will need. This includes:
// * The Color we'll use for active Panes's borders - SystemAccentColor
// * The Brush we'll use for inactive Panes - TabViewBackground (to match the
// color of the titlebar)
// Arguments:
// - requestedTheme: this should be the currently active Theme for the app
// Return Value:
// - <none>
void Pane::SetupResources(const winrt::Windows::UI::Xaml::ElementTheme& requestedTheme)
// Method Description:
// - Update our stored brushes for the current theme. This will also recursively
// update all our children.
// - TerminalPage creates these brushes and ultimately owns them. Effectively,
// we're just storing a smart pointer to the page's brushes.
void Pane::UpdateResources(const PaneResources& resources)
{
const auto res = Application::Current().Resources();
const auto accentColorKey = winrt::box_value(L"SystemAccentColor");
if (res.HasKey(accentColorKey))
{
const auto colorFromResources = ThemeLookup(res, requestedTheme, accentColorKey);
// If SystemAccentColor is _not_ a Color for some reason, use
// Transparent as the color, so we don't do this process again on
// the next pane (by leaving s_focusedBorderBrush nullptr)
auto actualColor = winrt::unbox_value_or<Color>(colorFromResources, Colors::Black());
s_focusedBorderBrush = SolidColorBrush(actualColor);
}
else
{
// DON'T use Transparent here - if it's "Transparent", then it won't
// be able to hittest for clicks, and then clicking on the border
// will eat focus.
s_focusedBorderBrush = SolidColorBrush{ Colors::Black() };
}
_themeResources = resources;
UpdateVisuals();
const auto unfocusedBorderBrushKey = winrt::box_value(L"UnfocusedBorderBrush");
if (res.HasKey(unfocusedBorderBrushKey))
if (!_IsLeaf())
{
// MAKE SURE TO USE ThemeLookup, so that we get the correct resource for
// the requestedTheme, not just the value from the resources (which
// might not respect the settings' requested theme)
auto obj = ThemeLookup(res, requestedTheme, unfocusedBorderBrushKey);
s_unfocusedBorderBrush = obj.try_as<winrt::Windows::UI::Xaml::Media::SolidColorBrush>();
}
else
{
// DON'T use Transparent here - if it's "Transparent", then it won't
// be able to hittest for clicks, and then clicking on the border
// will eat focus.
s_unfocusedBorderBrush = SolidColorBrush{ Colors::Black() };
_firstChild->UpdateResources(resources);
_secondChild->UpdateResources(resources);
}
}

View File

@@ -51,6 +51,12 @@ enum class SplitState : int
Vertical = 2
};
struct PaneResources
{
winrt::Windows::UI::Xaml::Media::SolidColorBrush focusedBorderBrush{ nullptr };
winrt::Windows::UI::Xaml::Media::SolidColorBrush unfocusedBorderBrush{ nullptr };
};
class Pane : public std::enable_shared_from_this<Pane>
{
public:
@@ -91,8 +97,8 @@ public:
std::optional<uint32_t> focusedPaneId;
uint32_t panesCreated;
};
BuildStartupState BuildStartupActions(uint32_t currentId, uint32_t nextId);
winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetTerminalArgsForPane() const;
BuildStartupState BuildStartupActions(uint32_t currentId, uint32_t nextId, const bool asContent = false, const bool asMovePane = false);
winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetTerminalArgsForPane(const bool asContent = false) const;
void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings,
const winrt::Microsoft::Terminal::Settings::Model::Profile& profile);
@@ -136,7 +142,7 @@ public:
bool ContainsReadOnly() const;
static void SetupResources(const winrt::Windows::UI::Xaml::ElementTheme& requestedTheme);
void UpdateResources(const PaneResources& resources);
// Method Description:
// - A helper method for ad-hoc recursion on a pane tree. Walks the pane
@@ -217,8 +223,8 @@ private:
winrt::Windows::UI::Xaml::Controls::Grid _root{};
winrt::Windows::UI::Xaml::Controls::Border _borderFirst{};
winrt::Windows::UI::Xaml::Controls::Border _borderSecond{};
static winrt::Windows::UI::Xaml::Media::SolidColorBrush s_focusedBorderBrush;
static winrt::Windows::UI::Xaml::Media::SolidColorBrush s_unfocusedBorderBrush;
PaneResources _themeResources;
#pragma region Properties that need to be transferred between child / parent panes upon splitting / closing
std::shared_ptr<Pane> _firstChild{ nullptr };

View File

@@ -205,6 +205,15 @@
<data name="TabClose" xml:space="preserve">
<value>Close Tab</value>
</data>
<data name="PaneClose" xml:space="preserve">
<value>Close Pane</value>
</data>
<data name="SplitTabText" xml:space="preserve">
<value>Split Tab</value>
</data>
<data name="SplitPaneText" xml:space="preserve">
<value>Split Pane</value>
</data>
<data name="TabColorChoose" xml:space="preserve">
<value>Color...</value>
</data>
@@ -708,9 +717,6 @@
<data name="DropPathTabRun.Text" xml:space="preserve">
<value>Open a new tab in given starting directory</value>
</data>
<data name="SplitTabText" xml:space="preserve">
<value>Split Tab</value>
</data>
<data name="DropPathTabNewWindow.Text" xml:space="preserve">
<value>Open a new window with given starting directory</value>
</data>
@@ -795,4 +801,4 @@
<data name="NewTabMenuFolderEmpty" xml:space="preserve">
<value>Empty...</value>
</data>
</root>
</root>

View File

@@ -0,0 +1,30 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include "SettingsLoadEventArgs.g.h"
#include <inc/cppwinrt_utils.h>
namespace winrt::TerminalApp::implementation
{
struct SettingsLoadEventArgs : SettingsLoadEventArgsT<SettingsLoadEventArgs>
{
WINRT_PROPERTY(bool, Reload, false);
WINRT_PROPERTY(uint64_t, Result, S_OK);
WINRT_PROPERTY(winrt::hstring, ExceptionText, L"");
WINRT_PROPERTY(winrt::Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::SettingsLoadWarnings>, Warnings, nullptr);
WINRT_PROPERTY(Microsoft::Terminal::Settings::Model::CascadiaSettings, NewSettings, nullptr);
public:
SettingsLoadEventArgs(bool reload,
uint64_t result,
winrt::hstring exceptionText,
winrt::Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::SettingsLoadWarnings> warnings,
Microsoft::Terminal::Settings::Model::CascadiaSettings newSettings) :
_Reload{ reload },
_Result{ result },
_ExceptionText{ std::move(exceptionText) },
_Warnings{ std::move(warnings) },
_NewSettings{ std::move(newSettings) } {};
};
}

View File

@@ -50,7 +50,7 @@ namespace winrt::TerminalApp::implementation
// - <none>
// Return Value:
// - The list of actions.
std::vector<ActionAndArgs> SettingsTab::BuildStartupActions() const
std::vector<ActionAndArgs> SettingsTab::BuildStartupActions(const bool /*asContent*/) const
{
ActionAndArgs action;
action.Action(ShortcutAction::OpenSettings);

View File

@@ -30,7 +30,7 @@ namespace winrt::TerminalApp::implementation
void UpdateSettings(Microsoft::Terminal::Settings::Model::CascadiaSettings settings);
void Focus(winrt::Windows::UI::Xaml::FocusState focusState) override;
std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> BuildStartupActions() const override;
std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> BuildStartupActions(const bool asContent = false) const override;
private:
winrt::Windows::UI::Xaml::ElementTheme _requestedTheme;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,137 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include "FilteredCommand.h"
#include "SuggestionsControl.g.h"
#include "AppCommandlineArgs.h"
// fwdecl unittest classes
namespace TerminalAppLocalTests
{
class TabTests;
};
namespace winrt::TerminalApp::implementation
{
struct SuggestionsControl : SuggestionsControlT<SuggestionsControl>
{
SuggestionsControl();
Windows::Foundation::Collections::IObservableVector<winrt::TerminalApp::FilteredCommand> FilteredActions();
void SetCommands(const Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::Command>& actions);
void SetActionMap(const Microsoft::Terminal::Settings::Model::IActionMapView& actionMap);
bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down);
void SelectNextItem(const bool moveDown);
void ScrollPageUp();
void ScrollPageDown();
void ScrollToTop();
void ScrollToBottom();
Windows::UI::Xaml::FrameworkElement SelectedItem();
TerminalApp::SuggestionsMode Mode() const;
void Mode(TerminalApp::SuggestionsMode mode);
void Anchor(Windows::Foundation::Point anchor, Windows::Foundation::Size space, float characterHeight);
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, NoMatchesText, _PropertyChangedHandlers);
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, SearchBoxPlaceholderText, _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::SuggestionsControl, winrt::TerminalApp::TabBase);
TYPED_EVENT(CommandLineExecutionRequested, winrt::TerminalApp::SuggestionsControl, winrt::hstring);
TYPED_EVENT(DispatchCommandRequested, winrt::TerminalApp::SuggestionsControl, Microsoft::Terminal::Settings::Model::Command);
TYPED_EVENT(PreviewAction, Windows::Foundation::IInspectable, Microsoft::Terminal::Settings::Model::Command);
private:
friend struct SuggestionsControlT<SuggestionsControl>; // for Xaml to bind events
Windows::Foundation::Collections::IVector<winrt::TerminalApp::FilteredCommand> _allCommands{ nullptr };
Windows::Foundation::Collections::IVector<winrt::TerminalApp::FilteredCommand> _currentNestedCommands{ nullptr };
Windows::Foundation::Collections::IObservableVector<winrt::TerminalApp::FilteredCommand> _filteredActions{ nullptr };
Windows::Foundation::Collections::IVector<winrt::TerminalApp::FilteredCommand> _nestedActionStack{ nullptr };
Windows::Foundation::Collections::IVector<winrt::TerminalApp::FilteredCommand> _commandsToFilter();
TerminalApp::SuggestionsMode _mode{ TerminalApp::SuggestionsMode::Palette };
TerminalApp::SuggestionsDirection _direction{ TerminalApp::SuggestionsDirection::TopDown };
bool _lastFilterTextWasEmpty{ true };
Windows::Foundation::Point _anchor;
Windows::Foundation::Size _space;
void _filterTextChanged(const Windows::Foundation::IInspectable& sender,
const Windows::UI::Xaml::RoutedEventArgs& args);
void _previewKeyDownHandler(const Windows::Foundation::IInspectable& sender,
const Windows::UI::Xaml::Input::KeyRoutedEventArgs& e);
void _keyUpHandler(const Windows::Foundation::IInspectable& sender,
const Windows::UI::Xaml::Input::KeyRoutedEventArgs& e);
winrt::fire_and_forget _selectedCommandChanged(const Windows::Foundation::IInspectable& sender,
const Windows::UI::Xaml::RoutedEventArgs& args);
void _updateUIForStackChange();
void _rootPointerPressed(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e);
void _lostFocusHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args);
void _backdropPointerPressed(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e);
void _listItemClicked(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Controls::ItemClickEventArgs& e);
void _listItemSelectionChanged(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Controls::SelectionChangedEventArgs& e);
void _moveBackButtonClicked(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs&);
void _updateFilteredActions();
void _updateCurrentNestedCommands(const winrt::Microsoft::Terminal::Settings::Model::Command& parentCommand);
std::vector<winrt::TerminalApp::FilteredCommand> _collectFilteredActions();
void _close();
void _switchToMode();
void _setDirection(TerminalApp::SuggestionsDirection direction);
std::wstring _getTrimmedInput();
Microsoft::Terminal::Settings::Model::IActionMapView _actionMap{ nullptr };
winrt::Windows::UI::Xaml::Controls::ListView::SizeChanged_revoker _sizeChangedRevoker;
void _dispatchCommand(const winrt::TerminalApp::FilteredCommand& command);
static std::optional<winrt::TerminalApp::FilteredCommand> _buildCommandLineCommand(const winrt::hstring& commandLine);
void _dismissPalette();
void _scrollToIndex(uint32_t index);
uint32_t _getNumVisibleItems();
winrt::fire_and_forget _openTooltip(Microsoft::Terminal::Settings::Model::Command cmd);
void _choosingItemContainer(const Windows::UI::Xaml::Controls::ListViewBase& sender, const Windows::UI::Xaml::Controls::ChoosingItemContainerEventArgs& args);
void _containerContentChanging(const Windows::UI::Xaml::Controls::ListViewBase& sender, const Windows::UI::Xaml::Controls::ContainerContentChangingEventArgs& args);
winrt::TerminalApp::PaletteItemTemplateSelector _itemTemplateSelector{ nullptr };
std::unordered_map<Windows::UI::Xaml::DataTemplate, std::unordered_set<Windows::UI::Xaml::Controls::Primitives::SelectorItem>> _listViewItemsCache;
Windows::UI::Xaml::DataTemplate _listItemTemplate;
friend class TerminalAppLocalTests::TabTests;
};
}
namespace winrt::TerminalApp::factory_implementation
{
BASIC_FACTORY(SuggestionsControl);
}

View File

@@ -0,0 +1,49 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import "TabBase.idl";
import "IDirectKeyListener.idl";
import "HighlightedTextControl.idl";
import "FilteredCommand.idl";
namespace TerminalApp
{
enum SuggestionsMode
{
Palette = 0,
Menu,
// Inline,
};
enum SuggestionsDirection {
TopDown,
BottomUp
};
[default_interface] runtimeclass SuggestionsControl : Windows.UI.Xaml.Controls.UserControl, Windows.UI.Xaml.Data.INotifyPropertyChanged, IDirectKeyListener
{
SuggestionsControl();
String NoMatchesText { get; };
String SearchBoxPlaceholderText { get; };
String ControlName { get; };
String ParentCommandName { get; };
String ParsedCommandLineText { get; };
Windows.UI.Xaml.FrameworkElement SelectedItem { get; };
Windows.Foundation.Collections.IObservableVector<FilteredCommand> FilteredActions { get; };
SuggestionsMode Mode { get; set; };
void SetCommands(Windows.Foundation.Collections.IVector<Microsoft.Terminal.Settings.Model.Command> actions);
void SetActionMap(Microsoft.Terminal.Settings.Model.IActionMapView actionMap);
void SelectNextItem(Boolean moveDown);
void Anchor(Windows.Foundation.Point anchor, Windows.Foundation.Size space, Single characterHeight);
event Windows.Foundation.TypedEventHandler<SuggestionsControl, Microsoft.Terminal.Settings.Model.Command> DispatchCommandRequested;
event Windows.Foundation.TypedEventHandler<Object, Microsoft.Terminal.Settings.Model.Command> PreviewAction;
}
}

View File

@@ -0,0 +1,339 @@
<!--
Copyright (c) Microsoft Corporation. All rights reserved. Licensed under
the MIT License. See LICENSE in the project root for license information.
-->
<UserControl x:Class="TerminalApp.SuggestionsControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:SettingsModel="using:Microsoft.Terminal.Settings.Model"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:TerminalApp"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:model="using:Microsoft.Terminal.Settings.Model"
xmlns:mux="using:Microsoft.UI.Xaml.Controls"
AllowFocusOnInteraction="True"
AutomationProperties.Name="{x:Bind ControlName, Mode=OneWay}"
IsTabStop="True"
LostFocus="_lostFocusHandler"
PointerPressed="_rootPointerPressed"
PreviewKeyDown="_previewKeyDownHandler"
PreviewKeyUp="_keyUpHandler"
TabNavigation="Cycle"
mc:Ignorable="d">
<UserControl.Resources>
<ResourceDictionary>
<!-- This creates an instance of our CommandKeyChordVisibilityConverter we can reference below -->
<local:EmptyStringVisibilityConverter x:Key="CommandKeyChordVisibilityConverter" />
<local:EmptyStringVisibilityConverter x:Key="ParsedCommandLineTextVisibilityConverter" />
<local:EmptyStringVisibilityConverter x:Key="ParentCommandVisibilityConverter" />
<model:IconPathConverter x:Key="IconSourceConverter" />
<DataTemplate x:Key="ListItemTemplate"
x:DataType="local:FilteredCommand">
<ListViewItem Height="32"
MinHeight="0"
Padding="16,0,12,0"
HorizontalContentAlignment="Stretch"
AutomationProperties.AcceleratorKey="{x:Bind Item.KeyChordText, Mode=OneWay}"
AutomationProperties.Name="{x:Bind Item.Name, Mode=OneWay}"
FontSize="12" />
</DataTemplate>
<DataTemplate x:Key="GeneralItemTemplate"
x:DataType="local:FilteredCommand">
<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>
<ContentPresenter Grid.Column="0"
Width="16"
Height="16"
Content="{x:Bind Item.ResolvedIcon, Mode=OneWay}" />
<local:HighlightedTextControl Grid.Column="1"
HorizontalAlignment="Left"
Text="{x:Bind HighlightedName, Mode=OneWay}" />
</Grid>
</DataTemplate>
<DataTemplate x:Key="NestedItemTemplate"
x:DataType="local:FilteredCommand">
<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>
<ContentPresenter Grid.Column="0"
Width="16"
Height="16"
Content="{x:Bind Item.ResolvedIcon, Mode=OneWay}" />
<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"
Padding="2,0,2,0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
AutomationProperties.AccessibilityView="Raw"
Style="{ThemeResource KeyChordBorderStyle}"
Visibility="{x:Bind Item.KeyChordText, Mode=OneWay, Converter={StaticResource CommandKeyChordVisibilityConverter}}">
<TextBlock AutomationProperties.AccessibilityView="Raw"
FontSize="12"
Style="{ThemeResource KeyChordTextBlockStyle}"
Text="{x:Bind Item.KeyChordText, Mode=OneWay}" />
</Border>
<FontIcon Grid.Column="2"
HorizontalAlignment="Right"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
FontSize="12"
Glyph="&#xE76C;" />
</Grid>
</DataTemplate>
<local:PaletteItemTemplateSelector x:Key="PaletteItemTemplateSelector"
GeneralItemTemplate="{StaticResource GeneralItemTemplate}"
NestedItemTemplate="{StaticResource NestedItemTemplate}" />
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Dark">
<!-- KeyChordText styles -->
<Style x:Key="KeyChordBorderStyle"
TargetType="Border">
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="2" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderBrush" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
</Style>
<Style x:Key="KeyChordTextBlockStyle"
TargetType="TextBlock">
<Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
</Style>
<!-- ParsedCommandLineText styles -->
<Style x:Key="ParsedCommandLineBorderStyle"
TargetType="Border">
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}" />
<Setter Property="Background" Value="{ThemeResource CardBackgroundFillColorDefaultBrush}" />
<Setter Property="BorderBrush" Value="{ThemeResource CardStrokeColorDefaultBrush}" />
</Style>
<Style x:Key="ParsedCommandLineTextBlockStyle"
TargetType="TextBlock">
<Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
</Style>
</ResourceDictionary>
<ResourceDictionary x:Key="Light">
<!-- KeyChordText styles -->
<Style x:Key="KeyChordBorderStyle"
TargetType="Border">
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="2" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderBrush" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
</Style>
<Style x:Key="KeyChordTextBlockStyle"
TargetType="TextBlock">
<Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
</Style>
<!-- ParsedCommandLineText styles -->
<Style x:Key="ParsedCommandLineBorderStyle"
TargetType="Border">
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}" />
<Setter Property="Background" Value="{ThemeResource CardBackgroundFillColorDefaultBrush}" />
<Setter Property="BorderBrush" Value="{ThemeResource CardStrokeColorDefaultBrush}" />
</Style>
<Style x:Key="ParsedCommandLineTextBlockStyle"
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" />
<!-- ParsedCommandLineText styles (use XAML defaults for High Contrast theme) -->
<Style x:Key="ParsedCommandLineBorderStyle"
TargetType="Border" />
<Style x:Key="ParsedCommandLineTextBlockStyle"
TargetType="TextBlock" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="6*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="8*" />
<RowDefinition Height="2*" />
</Grid.RowDefinitions>
<Grid x:Name="_backdrop"
Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="0"
Grid.ColumnSpan="3"
MaxWidth="300"
MaxHeight="300"
Margin="0"
Padding="0,8,0,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="{ThemeResource FlyoutPresenterBackground}"
BorderBrush="{ThemeResource FlyoutBorderThemeBrush}"
BorderThickness="{ThemeResource FlyoutBorderThemeThickness}"
CornerRadius="{ThemeResource OverlayCornerRadius}"
PointerPressed="_backdropPointerPressed"
Shadow="{StaticResource SharedShadow}"
Translation="0,0,32">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<!-- Top-down _searchBox -->
<RowDefinition Height="Auto" />
<!-- Top-down ParentCommandName -->
<RowDefinition Height="Auto" />
<!-- Top-down UNUSED???????? -->
<RowDefinition Height="*" />
<!-- _filteredActionsView -->
<RowDefinition Height="Auto" />
<!-- bottom-up _searchBox -->
</Grid.RowDefinitions>
<TextBox x:Name="_searchBox"
Grid.Row="0"
Margin="8,0,8,8"
Padding="18,8,8,8"
IsSpellCheckEnabled="False"
PlaceholderText="{x:Bind SearchBoxPlaceholderText, Mode=OneWay}"
Text=""
TextChanged="_filterTextChanged"
Visibility="Collapsed" />
<StackPanel Grid.Row="1"
Margin="8,0,8,8"
Orientation="Horizontal"
Visibility="{x:Bind ParentCommandName, Mode=OneWay, Converter={StaticResource ParentCommandVisibilityConverter}}">
<Button x:Name="_parentCommandBackButton"
x:Uid="ParentCommandBackButton"
VerticalAlignment="Center"
Click="_moveBackButtonClicked"
ClickMode="Press">
<FontIcon FontFamily="{ThemeResource SymbolThemeFontFamily}"
FontSize="11"
Glyph="&#xE76b;" />
</Button>
<TextBlock x:Name="_parentCommandText"
Padding="16,4"
VerticalAlignment="Center"
FontStyle="Italic"
Text="{x:Bind ParentCommandName, Mode=OneWay}" />
</StackPanel>
<Border Grid.Row="1"
Margin="8,0,8,8"
Padding="16,12"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Style="{ThemeResource ParsedCommandLineBorderStyle}"
Visibility="{x:Bind ParsedCommandLineText, Mode=OneWay, Converter={StaticResource ParsedCommandLineTextVisibilityConverter}}">
<ScrollViewer MaxHeight="200"
VerticalScrollBarVisibility="Auto">
<TextBlock FontStyle="Italic"
Text="{x:Bind ParsedCommandLineText, Mode=OneWay}"
TextWrapping="Wrap" />
</ScrollViewer>
</Border>
<Border x:Name="_noMatchesText"
Grid.Row="3"
Height="36"
Margin="8,0,8,8"
Visibility="Collapsed">
<TextBlock Padding="12,0"
VerticalAlignment="Center"
FontStyle="Italic"
Text="{x:Bind NoMatchesText, Mode=OneWay}" />
</Border>
<ListView x:Name="_filteredActionsView"
Grid.Row="3"
Padding="4,-2,4,6"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
AllowDrop="False"
CanReorderItems="False"
ChoosingItemContainer="_choosingItemContainer"
ContainerContentChanging="_containerContentChanging"
IsItemClickEnabled="True"
ItemClick="_listItemClicked"
ItemsSource="{x:Bind FilteredActions}"
SelectionChanged="_listItemSelectionChanged"
SelectionMode="Single" />
<mux:TeachingTip x:Name="DescriptionTip"
Title=""
IsOpen="False"
PreferredPlacement="Right"
ShouldConstrainToRootBounds="False"
Subtitle="">
<ScrollViewer HorizontalScrollBarVisibility="Hidden"
HorizontalScrollMode="Enabled">
<TextBlock x:Name="_toolTipContent" />
</ScrollViewer>
</mux:TeachingTip>
</Grid>
</Grid>
</UserControl>

View File

@@ -264,6 +264,19 @@ namespace winrt::TerminalApp::implementation
tab->_RequestFocusActiveControlHandlers();
}
});
// BODGY: When the tab is drag/dropped, the TabView gets a
// TabDragStarting. However, the way it is implemented[^1], the
// TabViewItem needs either an Item or a Content for the event to
// include the correct TabViewItem. Otherwise, it will just return the
// first TabViewItem in the TabView with the same Content as the dragged
// tab (which, if the Content is null, will be the _first_ tab).
//
// So here, we'll stick an empty border in, just so that every tab has a
// Content which is not equal to the others.
//
// [^1]: microsoft-ui-xaml/blob/92fbfcd55f05c92ac65569f5d284c5b36492091e/dev/TabView/TabView.cpp#L751-L758
TabViewItem().Content(winrt::WUX::Controls::Border{});
}
std::optional<winrt::Windows::UI::Color> TabBase::GetTabColor()

View File

@@ -23,7 +23,7 @@ namespace winrt::TerminalApp::implementation
void UpdateTabViewIndex(const uint32_t idx, const uint32_t numTabs);
void SetActionMap(const Microsoft::Terminal::Settings::Model::IActionMapView& actionMap);
virtual std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> BuildStartupActions() const = 0;
virtual std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> BuildStartupActions(const bool asContent = false) const = 0;
virtual std::optional<winrt::Windows::UI::Color> GetTabColor();
void ThemeColor(const winrt::Microsoft::Terminal::Settings::Model::ThemeColor& focused,

View File

@@ -512,6 +512,11 @@ namespace winrt::TerminalApp::implementation
_mruTabs.RemoveAt(mruIndex);
}
if (_stashedDraggedTab && *_stashedDraggedTab == tab)
{
_stashedDraggedTab = nullptr;
}
_tabs.RemoveAt(tabIndex);
_tabView.TabItems().RemoveAt(tabIndex);
_UpdateTabIndices();
@@ -523,13 +528,7 @@ namespace winrt::TerminalApp::implementation
// if the user manually closed all tabs.
// Do this only if we are the last window; the monarch will notice
// we are missing and remove us that way otherwise.
if (!_maintainStateOnTabClose && ShouldUsePersistedLayout(_settings) && _numOpenWindows == 1)
{
auto state = ApplicationState::SharedInstance();
state.PersistedWindowLayouts(nullptr);
}
_LastTabClosedHandlers(*this, nullptr);
_LastTabClosedHandlers(*this, winrt::make<LastTabClosedEventArgs>(!_maintainStateOnTabClose));
}
else if (focusedTabIndex.has_value() && focusedTabIndex.value() == gsl::narrow_cast<uint32_t>(tabIndex))
{
@@ -709,7 +708,8 @@ namespace winrt::TerminalApp::implementation
winrt::TerminalApp::TabBase TerminalPage::_GetTabByTabViewItem(const Microsoft::UI::Xaml::Controls::TabViewItem& tabViewItem) const noexcept
{
uint32_t tabIndexFromControl{};
if (_tabView.TabItems().IndexOf(tabViewItem, tabIndexFromControl))
const auto items{ _tabView.TabItems() };
if (items.IndexOf(tabViewItem, tabIndexFromControl))
{
// If IndexOf returns true, we've actually got an index
return _tabs.GetAt(tabIndexFromControl);

View File

@@ -60,6 +60,9 @@
<Page Include="CommandPalette.xaml">
<SubType>Designer</SubType>
</Page>
<Page Include="SuggestionsControl.xaml">
<SubType>Designer</SubType>
</Page>
</ItemGroup>
<!-- ========================= Headers ======================== -->
<ItemGroup>
@@ -138,7 +141,19 @@
<ClInclude Include="AppLogic.h">
<DependentUpon>AppLogic.idl</DependentUpon>
</ClInclude>
<ClInclude Include="ContentManager.h">
<DependentUpon>TerminalPage.idl</DependentUpon>
</ClInclude>
<ClInclude Include="TerminalWindow.h">
<DependentUpon>TerminalWindow.idl</DependentUpon>
</ClInclude>
<ClInclude Include="SettingsLoadEventArgs.h">
<DependentUpon>TerminalWindow.idl</DependentUpon>
</ClInclude>
<ClInclude Include="Toast.h" />
<ClInclude Include="SuggestionsControl.h">
<DependentUpon>SuggestionsControl.xaml</DependentUpon>
</ClInclude>
</ItemGroup>
<!-- ========================= Cpp Files ======================== -->
<ItemGroup>
@@ -231,8 +246,17 @@
<ClCompile Include="AppLogic.cpp">
<DependentUpon>AppLogic.idl</DependentUpon>
</ClCompile>
<ClCompile Include="ContentManager.cpp">
<DependentUpon>TerminalPage.idl</DependentUpon>
</ClCompile>
<ClCompile Include="TerminalWindow.cpp">
<DependentUpon>TerminalWindow.idl</DependentUpon>
</ClCompile>
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
<ClCompile Include="Toast.cpp" />
<ClCompile Include="SuggestionsControl.cpp">
<DependentUpon>SuggestionsControl.xaml</DependentUpon>
</ClCompile>
</ItemGroup>
<!-- ========================= idl Files ======================== -->
<ItemGroup>
@@ -252,6 +276,7 @@
<Midl Include="ShortcutActionDispatch.idl" />
<Midl Include="AppKeyBindings.idl" />
<Midl Include="AppLogic.idl" />
<Midl Include="TerminalWindow.idl" />
<Midl Include="MinMaxCloseControl.idl">
<DependentUpon>MinMaxCloseControl.xaml</DependentUpon>
<SubType>Code</SubType>
@@ -292,6 +317,10 @@
<DependentUpon>CommandPalette.xaml</DependentUpon>
<SubType>Code</SubType>
</Midl>
<Midl Include="SuggestionsControl.idl">
<DependentUpon>SuggestionsControl.xaml</DependentUpon>
<SubType>Code</SubType>
</Midl>
<Midl Include="FilteredCommand.idl" />
<Midl Include="EmptyStringVisibilityConverter.idl" />
</ItemGroup>

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,10 @@
#include "TerminalTab.h"
#include "AppKeyBindings.h"
#include "AppCommandlineArgs.h"
#include "LastTabClosedEventArgs.g.h"
#include "RenameWindowRequestedArgs.g.h"
#include "RequestMoveContentArgs.g.h"
#include "RequestReceiveContentArgs.g.h"
#include "Toast.h"
#define DECLARE_ACTION_HANDLER(action) void _Handle##action(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args);
@@ -41,6 +44,15 @@ namespace winrt::TerminalApp::implementation
ScrollDown = 1
};
struct LastTabClosedEventArgs : LastTabClosedEventArgsT<LastTabClosedEventArgs>
{
WINRT_PROPERTY(bool, ClearPersistedState);
public:
LastTabClosedEventArgs(const bool& shouldClear) :
_ClearPersistedState{ shouldClear } {};
};
struct RenameWindowRequestedArgs : RenameWindowRequestedArgsT<RenameWindowRequestedArgs>
{
WINRT_PROPERTY(winrt::hstring, ProposedName);
@@ -50,10 +62,37 @@ namespace winrt::TerminalApp::implementation
_ProposedName{ name } {};
};
struct RequestMoveContentArgs : RequestMoveContentArgsT<RequestMoveContentArgs>
{
WINRT_PROPERTY(winrt::hstring, Window);
WINRT_PROPERTY(winrt::hstring, Content);
WINRT_PROPERTY(uint32_t, TabIndex);
WINRT_PROPERTY(Windows::Foundation::IReference<Windows::Foundation::Point>, WindowPosition);
public:
RequestMoveContentArgs(const winrt::hstring window, const winrt::hstring content, uint32_t tabIndex) :
_Window{ window },
_Content{ content },
_TabIndex{ tabIndex } {};
};
struct RequestReceiveContentArgs : RequestReceiveContentArgsT<RequestReceiveContentArgs>
{
WINRT_PROPERTY(uint64_t, SourceWindow);
WINRT_PROPERTY(uint64_t, TargetWindow);
WINRT_PROPERTY(uint32_t, TabIndex);
public:
RequestReceiveContentArgs(const uint64_t src, const uint64_t tgt, const uint32_t tabIndex) :
_SourceWindow{ src },
_TargetWindow{ tgt },
_TabIndex{ tabIndex } {};
};
struct TerminalPage : TerminalPageT<TerminalPage>
{
public:
TerminalPage();
TerminalPage(TerminalApp::WindowProperties properties, const TerminalApp::ContentManager& manager);
// This implements shobjidl's IInitializeWithWindow, but due to a XAML Compiler bug we cannot
// put it in our inheritance graph. https://github.com/microsoft/microsoft-ui-xaml/issues/3331
@@ -63,11 +102,8 @@ namespace winrt::TerminalApp::implementation
void Create();
bool ShouldUsePersistedLayout(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const;
bool ShouldImmediatelyHandoffToElevated(const Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const;
void HandoffToElevated(const Microsoft::Terminal::Settings::Model::CascadiaSettings& settings);
std::optional<uint32_t> LoadPersistedLayoutIdx(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const;
winrt::Microsoft::Terminal::Settings::Model::WindowLayout LoadPersistedLayout(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const;
Microsoft::Terminal::Settings::Model::WindowLayout GetWindowLayout();
winrt::fire_and_forget NewTerminalByDrop(winrt::Windows::UI::Xaml::DragEventArgs& e);
@@ -117,20 +153,8 @@ namespace winrt::TerminalApp::implementation
const bool initial,
const winrt::hstring cwd = L"");
// Normally, WindowName and WindowId would be
// WINRT_OBSERVABLE_PROPERTY's, but we want them to raise
// WindowNameForDisplay and WindowIdForDisplay instead
winrt::hstring WindowName() const noexcept;
winrt::fire_and_forget WindowName(const winrt::hstring& value);
uint64_t WindowId() const noexcept;
void WindowId(const uint64_t& value);
TerminalApp::WindowProperties WindowProperties() const noexcept { return _WindowProperties; };
void SetNumberOfOpenWindows(const uint64_t value);
void SetPersistedLayoutIdx(const uint32_t value);
winrt::hstring WindowIdForDisplay() const noexcept;
winrt::hstring WindowNameForDisplay() const noexcept;
bool IsQuakeWindow() const noexcept;
bool IsElevated() const noexcept;
void OpenSettingsUI();
@@ -138,6 +162,9 @@ namespace winrt::TerminalApp::implementation
bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down);
winrt::fire_and_forget AttachContent(winrt::hstring content, uint32_t tabIndex);
winrt::fire_and_forget SendContentToOther(winrt::TerminalApp::RequestReceiveContentArgs args);
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
// -------------------------------- WinRT Events ---------------------------------
@@ -153,13 +180,16 @@ namespace winrt::TerminalApp::implementation
TYPED_EVENT(Initialized, IInspectable, winrt::Windows::UI::Xaml::RoutedEventArgs);
TYPED_EVENT(IdentifyWindowsRequested, IInspectable, IInspectable);
TYPED_EVENT(RenameWindowRequested, Windows::Foundation::IInspectable, winrt::TerminalApp::RenameWindowRequestedArgs);
TYPED_EVENT(IsQuakeWindowChanged, IInspectable, IInspectable);
TYPED_EVENT(SummonWindowRequested, IInspectable, IInspectable);
TYPED_EVENT(CloseRequested, IInspectable, IInspectable);
TYPED_EVENT(OpenSystemMenu, IInspectable, IInspectable);
TYPED_EVENT(QuitRequested, IInspectable, IInspectable);
TYPED_EVENT(ShowWindowChanged, IInspectable, winrt::Microsoft::Terminal::Control::ShowWindowArgs)
TYPED_EVENT(RequestMoveContent, Windows::Foundation::IInspectable, winrt::TerminalApp::RequestMoveContentArgs);
TYPED_EVENT(RequestReceiveContent, Windows::Foundation::IInspectable, winrt::TerminalApp::RequestReceiveContentArgs);
WINRT_OBSERVABLE_PROPERTY(winrt::Windows::UI::Xaml::Media::Brush, TitlebarBrush, _PropertyChangedHandlers, nullptr);
private:
@@ -192,10 +222,8 @@ namespace winrt::TerminalApp::implementation
bool _isFullscreen{ false };
bool _isMaximized{ false };
bool _isAlwaysOnTop{ false };
winrt::hstring _WindowName{};
uint64_t _WindowId{ 0 };
std::optional<uint32_t> _loadFromPersistedLayoutIdx{};
uint64_t _numOpenWindows{ 0 };
bool _maintainStateOnTabClose{ false };
bool _rearranging{ false };
@@ -230,7 +258,17 @@ namespace winrt::TerminalApp::implementation
int _renamerLayoutCount{ 0 };
bool _renamerPressedEnter{ false };
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> _ShowDialogHelper(const std::wstring_view& name);
TerminalApp::WindowProperties _WindowProperties{ nullptr };
PaneResources _paneResources;
TerminalApp::ContentManager _manager{ nullptr };
winrt::com_ptr<winrt::TerminalApp::implementation::TabBase> _stashedDraggedTab{ nullptr };
til::point _dragOffset{ 0, 0 };
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult>
_ShowDialogHelper(const std::wstring_view& name);
void _ShowAboutDialog();
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> _ShowQuitDialog();
@@ -273,10 +311,6 @@ namespace winrt::TerminalApp::implementation
void _UpdateCommandsForPalette();
void _SetBackgroundImage(const winrt::Microsoft::Terminal::Settings::Model::IAppearanceConfig& newAppearance);
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, Microsoft::Terminal::Settings::Model::Command> _ExpandCommands(Windows::Foundation::Collections::IMapView<winrt::hstring, Microsoft::Terminal::Settings::Model::Command> commandsToExpand,
Windows::Foundation::Collections::IVectorView<Microsoft::Terminal::Settings::Model::Profile> profiles,
Windows::Foundation::Collections::IMapView<winrt::hstring, Microsoft::Terminal::Settings::Model::ColorScheme> schemes);
void _DuplicateFocusedTab();
void _DuplicateTab(const TerminalTab& tab);
@@ -301,7 +335,8 @@ namespace winrt::TerminalApp::implementation
bool _SelectTab(uint32_t tabIndex);
bool _MoveFocus(const Microsoft::Terminal::Settings::Model::FocusDirection& direction);
bool _SwapPane(const Microsoft::Terminal::Settings::Model::FocusDirection& direction);
bool _MovePane(const uint32_t tabIdx);
bool _MovePane(const Microsoft::Terminal::Settings::Model::MovePaneArgs args);
bool _MoveTab(const Microsoft::Terminal::Settings::Model::MoveTabArgs args);
template<typename F>
bool _ApplyToActiveControls(F f)
@@ -392,6 +427,8 @@ namespace winrt::TerminalApp::implementation
winrt::Microsoft::Terminal::Control::TermControl _InitControl(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings,
const winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection& connection);
winrt::Microsoft::Terminal::Control::TermControl _InitControl(const winrt::Microsoft::Terminal::Control::TermControl& term);
winrt::Microsoft::Terminal::Control::TermControl _InitControlFromContent(const winrt::guid& contentGuid);
std::shared_ptr<Pane> _MakePane(const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs = nullptr,
const winrt::TerminalApp::TabBase& sourceTab = nullptr,
@@ -429,6 +466,8 @@ namespace winrt::TerminalApp::implementation
void _RunRestorePreviews();
void _PreviewColorScheme(const Microsoft::Terminal::Settings::Model::SetColorSchemeArgs& args);
void _PreviewAdjustOpacity(const Microsoft::Terminal::Settings::Model::AdjustOpacityArgs& args);
void _PreviewSendInput(const Microsoft::Terminal::Settings::Model::SendInputArgs& args);
winrt::Microsoft::Terminal::Settings::Model::ActionAndArgs _lastPreviewedAction{ nullptr };
std::vector<std::function<void()>> _restorePreviewFuncs{};
@@ -459,8 +498,29 @@ namespace winrt::TerminalApp::implementation
void _updateThemeColors();
void _updateTabCloseButton(const winrt::Microsoft::UI::Xaml::Controls::TabViewItem& tabViewItem);
void _updatePaneResources(const winrt::Windows::UI::Xaml::ElementTheme& requestedTheme);
winrt::fire_and_forget _ControlMenuChangedHandler(const winrt::Windows::Foundation::IInspectable sender, const winrt::Microsoft::Terminal::Control::MenuChangedEventArgs args);
winrt::fire_and_forget _OpenSuggestions(Windows::Foundation::Collections::IVector<winrt::Microsoft::Terminal::Settings::Model::Command> commandsCollection, winrt::TerminalApp::SuggestionsMode mode);
winrt::fire_and_forget _ShowWindowChangedHandler(const IInspectable sender, const winrt::Microsoft::Terminal::Control::ShowWindowArgs args);
winrt::fire_and_forget _windowPropertyChanged(const IInspectable& sender, const winrt::Windows::UI::Xaml::Data::PropertyChangedEventArgs& args);
void _onTabDragStarting(winrt::Microsoft::UI::Xaml::Controls::TabView sender, winrt::Microsoft::UI::Xaml::Controls::TabViewTabDragStartingEventArgs e);
void _onTabStripDragOver(winrt::Windows::Foundation::IInspectable sender, winrt::Windows::UI::Xaml::DragEventArgs e);
winrt::fire_and_forget _onTabStripDrop(winrt::Windows::Foundation::IInspectable sender, winrt::Windows::UI::Xaml::DragEventArgs e);
winrt::fire_and_forget _onTabDroppedOutside(winrt::Windows::Foundation::IInspectable sender, winrt::Microsoft::UI::Xaml::Controls::TabViewTabDroppedOutsideEventArgs e);
void _DetachPaneFromWindow(std::shared_ptr<Pane> pane);
void _DetachTabFromWindow(const winrt::com_ptr<TabBase>& terminalTab);
void _MoveContent(std::vector<winrt::Microsoft::Terminal::Settings::Model::ActionAndArgs>& actions,
const winrt::hstring& windowName,
const uint32_t tabIndex,
Windows::Foundation::IReference<Windows::Foundation::Point> dragPoint = nullptr);
void _ContextMenuOpened(const IInspectable& sender, const IInspectable& args);
void _SelectionMenuOpened(const IInspectable& sender, const IInspectable& args);
void _PopulateContextMenu(const IInspectable& sender, const bool withSelection);
#pragma region ActionHandlers
// These are all defined in AppActionHandlers.cpp
@@ -477,4 +537,5 @@ namespace winrt::TerminalApp::implementation
namespace winrt::TerminalApp::factory_implementation
{
BASIC_FACTORY(TerminalPage);
BASIC_FACTORY(RequestReceiveContentArgs);
}

View File

@@ -5,21 +5,60 @@ import "IDirectKeyListener.idl";
namespace TerminalApp
{
delegate void LastTabClosedEventArgs();
// See IDialogPresenter and TerminalPage's DialogPresenter for more
// information.
[default_interface] runtimeclass ContentManager
{
Microsoft.Terminal.Control.ControlInteractivity CreateCore(Microsoft.Terminal.Control.IControlSettings settings,
Microsoft.Terminal.Control.IControlAppearance unfocusedAppearance,
Microsoft.Terminal.TerminalConnection.ITerminalConnection connection);
Microsoft.Terminal.Control.ControlInteractivity LookupCore(Guid id);
void Detach(Microsoft.Terminal.Control.TermControl control);
}
[default_interface] runtimeclass LastTabClosedEventArgs
{
Boolean ClearPersistedState { get; };
};
[default_interface] runtimeclass RenameWindowRequestedArgs
{
String ProposedName { get; };
};
[default_interface] runtimeclass RequestMoveContentArgs
{
String Window { get; };
String Content { get; };
UInt32 TabIndex { get; };
Windows.Foundation.IReference<Windows.Foundation.Point> WindowPosition { get; };
};
[default_interface] runtimeclass RequestReceiveContentArgs {
RequestReceiveContentArgs(UInt64 src, UInt64 tgt, UInt32 tabIndex);
UInt64 SourceWindow { get; };
UInt64 TargetWindow { get; };
UInt32 TabIndex { get; };
};
interface IDialogPresenter
{
Windows.Foundation.IAsyncOperation<Windows.UI.Xaml.Controls.ContentDialogResult> ShowDialog(Windows.UI.Xaml.Controls.ContentDialog dialog);
};
[default_interface] runtimeclass WindowProperties : Windows.UI.Xaml.Data.INotifyPropertyChanged
{
String WindowName { get; };
UInt64 WindowId { get; };
String WindowNameForDisplay { get; };
String WindowIdForDisplay { get; };
Boolean IsQuakeWindow();
};
[default_interface] runtimeclass TerminalPage : Windows.UI.Xaml.Controls.Page, Windows.UI.Xaml.Data.INotifyPropertyChanged, IDirectKeyListener
{
TerminalPage();
TerminalPage(WindowProperties properties, ContentManager manager);
// XAML bound properties
String ApplicationDisplayName { get; };
@@ -29,13 +68,9 @@ namespace TerminalApp
Boolean Fullscreen { get; };
Boolean AlwaysOnTop { get; };
WindowProperties WindowProperties { get; };
void IdentifyWindow();
String WindowName;
UInt64 WindowId;
String WindowNameForDisplay { get; };
String WindowIdForDisplay { get; };
void RenameFailed();
Boolean IsQuakeWindow();
// We cannot use the default XAML APIs because we want to make sure
// that there's only one application-global dialog visible at a time,
@@ -45,9 +80,11 @@ namespace TerminalApp
String KeyboardServiceDisabledText { get; };
TaskbarState TaskbarState{ get; };
void AttachContent(String content, UInt32 tabIndex);
Windows.UI.Xaml.Media.Brush TitlebarBrush { get; };
void WindowActivated(Boolean activated);
void SendContentToOther(RequestReceiveContentArgs args);
event Windows.Foundation.TypedEventHandler<Object, String> TitleChanged;
event Windows.Foundation.TypedEventHandler<Object, LastTabClosedEventArgs> LastTabClosed;
@@ -59,10 +96,13 @@ namespace TerminalApp
event Windows.Foundation.TypedEventHandler<Object, Object> SetTaskbarProgress;
event Windows.Foundation.TypedEventHandler<Object, Object> IdentifyWindowsRequested;
event Windows.Foundation.TypedEventHandler<Object, RenameWindowRequestedArgs> RenameWindowRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> IsQuakeWindowChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> SummonWindowRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> CloseRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> OpenSystemMenu;
event Windows.Foundation.TypedEventHandler<Object, Microsoft.Terminal.Control.ShowWindowArgs> ShowWindowChanged;
event Windows.Foundation.TypedEventHandler<Object, RequestMoveContentArgs> RequestMoveContent;
event Windows.Foundation.TypedEventHandler<Object, RequestReceiveContentArgs> RequestReceiveContent;
}
}

View File

@@ -195,16 +195,23 @@
PreviewKeyDown="_KeyDownHandler"
Visibility="Collapsed" />
<local:SuggestionsControl x:Name="SuggestionsUI"
Grid.Row="2"
HorizontalAlignment="Left"
VerticalAlignment="Top"
PreviewKeyDown="_KeyDownHandler"
Visibility="Collapsed" />
<!--
A TeachingTip with IsLightDismissEnabled="True" will immediately
dismiss itself if the window is unfocused (In Xaml Islands). This is
tracked by MUX#4382
-->
<mux:TeachingTip x:Name="WindowIdToast"
Title="{x:Bind WindowIdForDisplay}"
Title="{x:Bind WindowProperties.WindowIdForDisplay}"
x:Load="False"
IsLightDismissEnabled="True"
Subtitle="{x:Bind WindowNameForDisplay, Mode=OneWay}" />
Subtitle="{x:Bind WindowProperties.WindowNameForDisplay, Mode=OneWay}" />
<mux:TeachingTip x:Name="RenameFailedToast"
x:Uid="RenameFailedToast"
@@ -213,7 +220,7 @@
<mux:TeachingTip x:Name="WindowRenamer"
x:Uid="WindowRenamer"
Title="{x:Bind WindowIdForDisplay}"
Title="{x:Bind WindowProperties.WindowIdForDisplay}"
x:Load="False"
ActionButtonClick="_WindowRenamerActionClick"
ActionButtonStyle="{ThemeResource AccentButtonStyle}"
@@ -222,7 +229,7 @@
<TextBox x:Name="WindowRenamerTextBox"
KeyDown="_WindowRenamerKeyDown"
KeyUp="_WindowRenamerKeyUp"
Text="{x:Bind WindowName, Mode=OneWay}" />
Text="{x:Bind WindowProperties.WindowName, Mode=OneWay}" />
</mux:TeachingTip.Content>
</mux:TeachingTip>
</Grid>

View File

@@ -438,16 +438,16 @@ namespace winrt::TerminalApp::implementation
// - <none>
// Return Value:
// - A vector of commands
std::vector<ActionAndArgs> TerminalTab::BuildStartupActions() const
std::vector<ActionAndArgs> TerminalTab::BuildStartupActions(const bool asContent) const
{
// Give initial ids (0 for the child created with this tab,
// 1 for the child after the first split.
auto state = _rootPane->BuildStartupActions(0, 1);
auto state = _rootPane->BuildStartupActions(0, 1, asContent);
{
ActionAndArgs newTabAction{};
newTabAction.Action(ShortcutAction::NewTab);
NewTabArgs newTabArgs{ state.firstPane->GetTerminalArgsForPane() };
NewTabArgs newTabArgs{ state.firstPane->GetTerminalArgsForPane(asContent) };
newTabAction.Args(newTabArgs);
state.args.emplace(state.args.begin(), std::move(newTabAction));
@@ -783,6 +783,8 @@ namespace winrt::TerminalApp::implementation
bool TerminalTab::FocusPane(const uint32_t id)
{
if (_rootPane == nullptr)
return false;
_changingActivePane = true;
const auto res = _rootPane->FocusPane(id);
_changingActivePane = false;

View File

@@ -82,7 +82,7 @@ namespace winrt::TerminalApp::implementation
void EnterZoom();
void ExitZoom();
std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> BuildStartupActions() const override;
std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> BuildStartupActions(const bool asContent = false) const override;
int GetLeafPaneCount() const noexcept;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,238 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include "TerminalWindow.g.h"
#include "SystemMenuChangeArgs.g.h"
#include "WindowProperties.g.h"
#include "SettingsLoadEventArgs.h"
#include "TerminalPage.h"
#include "SettingsLoadEventArgs.h"
#include <inc/cppwinrt_utils.h>
#include <ThrottledFunc.h>
#ifdef UNIT_TESTING
// fwdecl unittest classes
namespace TerminalAppLocalTests
{
class CommandlineTest;
};
#endif
namespace winrt::TerminalApp::implementation
{
struct SystemMenuChangeArgs : SystemMenuChangeArgsT<SystemMenuChangeArgs>
{
WINRT_PROPERTY(winrt::hstring, Name, L"");
WINRT_PROPERTY(SystemMenuChangeAction, Action, SystemMenuChangeAction::Add);
WINRT_PROPERTY(SystemMenuItemHandler, Handler, nullptr);
public:
SystemMenuChangeArgs(const winrt::hstring& name, SystemMenuChangeAction action, SystemMenuItemHandler handler = nullptr) :
_Name{ name }, _Action{ action }, _Handler{ handler } {};
};
struct WindowProperties : WindowPropertiesT<WindowProperties>
{
// Normally, WindowName and WindowId would be
// WINRT_OBSERVABLE_PROPERTY's, but we want them to raise
// WindowNameForDisplay and WindowIdForDisplay instead
winrt::hstring WindowName() const noexcept;
void WindowName(const winrt::hstring& value);
uint64_t WindowId() const noexcept;
void WindowId(const uint64_t& value);
winrt::hstring WindowIdForDisplay() const noexcept;
winrt::hstring WindowNameForDisplay() const noexcept;
bool IsQuakeWindow() const noexcept;
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
private:
winrt::hstring _WindowName{};
uint64_t _WindowId{ 0 };
};
struct TerminalWindow : TerminalWindowT<TerminalWindow, IInitializeWithWindow>
{
public:
TerminalWindow(const TerminalApp::SettingsLoadEventArgs& settingsLoadedResult, const TerminalApp::ContentManager& manager);
~TerminalWindow() = default;
STDMETHODIMP Initialize(HWND hwnd);
void Create();
bool IsUwp() const noexcept;
bool IsElevated() const noexcept;
void Quit();
winrt::fire_and_forget UpdateSettings(winrt::TerminalApp::SettingsLoadEventArgs args);
bool HasCommandlineArguments() const noexcept;
int32_t SetStartupCommandline(array_view<const winrt::hstring> actions);
void SetStartupContent(winrt::hstring content, Windows::Foundation::IReference<Windows::Foundation::Rect> contentBounds);
int32_t ExecuteCommandline(array_view<const winrt::hstring> actions, const winrt::hstring& cwd);
void SetSettingsStartupArgs(const std::vector<winrt::Microsoft::Terminal::Settings::Model::ActionAndArgs>& actions);
winrt::hstring ParseCommandlineMessage();
bool ShouldExitEarly();
bool ShouldImmediatelyHandoffToElevated();
void HandoffToElevated();
bool FocusMode() const;
bool Fullscreen() const;
void Maximized(bool newMaximized);
bool AlwaysOnTop() const;
bool AutoHideWindow();
hstring GetWindowLayoutJson(Microsoft::Terminal::Settings::Model::LaunchPosition position);
void IdentifyWindow();
void RenameFailed();
std::optional<uint32_t> LoadPersistedLayoutIdx() const;
winrt::Microsoft::Terminal::Settings::Model::WindowLayout LoadPersistedLayout();
void SetPersistedLayoutIdx(const uint32_t idx);
void SetNumberOfOpenWindows(const uint64_t num);
bool ShouldUsePersistedLayout() const;
void ClearPersistedWindowState();
void RequestExitFullscreen();
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();
bool GetShowTabsInTitlebar();
bool GetInitialAlwaysOnTop();
float CalcSnappedDimension(const bool widthOrHeight, const float dimension) const;
Windows::UI::Xaml::UIElement GetRoot() noexcept;
hstring Title();
void TitlebarClicked();
bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down);
void CloseWindow(Microsoft::Terminal::Settings::Model::LaunchPosition position, const bool isLastWindow);
void WindowVisibilityChanged(const bool showOrHide);
winrt::TerminalApp::TaskbarState TaskbarState();
winrt::Windows::UI::Xaml::Media::Brush TitlebarBrush();
void WindowActivated(const bool activated);
bool GetMinimizeToNotificationArea();
bool GetAlwaysShowNotificationIcon();
bool GetShowTitleInTitlebar();
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> ShowDialog(winrt::Windows::UI::Xaml::Controls::ContentDialog dialog);
void DismissDialog();
Microsoft::Terminal::Settings::Model::Theme Theme();
void UpdateSettingsHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::TerminalApp::SettingsLoadEventArgs& arg);
void WindowName(const winrt::hstring& value);
void WindowId(const uint64_t& value);
bool IsQuakeWindow() const noexcept { return _WindowProperties->IsQuakeWindow(); }
TerminalApp::WindowProperties WindowProperties() { return *_WindowProperties; }
void AttachContent(winrt::hstring content, uint32_t tabIndex);
void SendContentToOther(winrt::TerminalApp::RequestReceiveContentArgs args);
// -------------------------------- WinRT Events ---------------------------------
// PropertyChanged is surprisingly not a typed event, so we'll define that one manually.
// Usually we'd just do
// WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
//
// But what we're doing here is exposing the Page's PropertyChanged _as
// our own event_. It's a FORWARDED_CALLBACK, essentially.
winrt::event_token PropertyChanged(Windows::UI::Xaml::Data::PropertyChangedEventHandler const& handler) { return _root->PropertyChanged(handler); }
void PropertyChanged(winrt::event_token const& token) { _root->PropertyChanged(token); }
TYPED_EVENT(RequestedThemeChanged, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Settings::Model::Theme);
private:
// If you add controls here, but forget to null them either here or in
// the ctor, you're going to have a bad time. It'll mysteriously fail to
// activate the AppLogic.
// ALSO: If you add any UIElements as roots here, make sure they're
// updated in _ApplyTheme. The root currently is _root.
winrt::com_ptr<TerminalPage> _root{ nullptr };
winrt::Windows::UI::Xaml::Controls::ContentDialog _dialog{ nullptr };
std::shared_mutex _dialogLock;
bool _hasCommandLineArguments{ false };
::TerminalApp::AppCommandlineArgs _appArgs;
bool _gotSettingsStartupActions{ false };
std::vector<winrt::Microsoft::Terminal::Settings::Model::ActionAndArgs> _settingsStartupArgs{};
Windows::Foundation::IReference<Windows::Foundation::Rect> _contentBounds{ nullptr };
winrt::com_ptr<TerminalApp::implementation::WindowProperties> _WindowProperties{ nullptr };
std::optional<uint32_t> _loadFromPersistedLayoutIdx{};
std::optional<winrt::Microsoft::Terminal::Settings::Model::WindowLayout> _cachedLayout{ std::nullopt };
Microsoft::Terminal::Settings::Model::CascadiaSettings _settings{ nullptr };
TerminalApp::SettingsLoadEventArgs _initialLoadResult{ nullptr };
TerminalApp::ContentManager _manager{ nullptr };
std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> _initialContentArgs;
void _ShowLoadErrorsDialog(const winrt::hstring& titleKey,
const winrt::hstring& contentKey,
HRESULT settingsLoadedResult,
const winrt::hstring& exceptionText);
void _ShowLoadWarningsDialog(const Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::SettingsLoadWarnings>& warnings);
bool _IsKeyboardServiceEnabled();
void _RefreshThemeRoutine();
void _OnLoaded(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
void _OpenSettingsUI();
// These are events that are handled by the TerminalPage, but are
// exposed through the AppLogic. This macro is used to forward the event
// directly to them.
FORWARDED_TYPED_EVENT(SetTitleBarContent, winrt::Windows::Foundation::IInspectable, winrt::Windows::UI::Xaml::UIElement, _root, SetTitleBarContent);
FORWARDED_TYPED_EVENT(TitleChanged, winrt::Windows::Foundation::IInspectable, winrt::hstring, _root, TitleChanged);
FORWARDED_TYPED_EVENT(LastTabClosed, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::LastTabClosedEventArgs, _root, LastTabClosed);
FORWARDED_TYPED_EVENT(FocusModeChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, FocusModeChanged);
FORWARDED_TYPED_EVENT(FullscreenChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, FullscreenChanged);
FORWARDED_TYPED_EVENT(ChangeMaximizeRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, ChangeMaximizeRequested);
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);
FORWARDED_TYPED_EVENT(IdentifyWindowsRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, IdentifyWindowsRequested);
FORWARDED_TYPED_EVENT(RenameWindowRequested, Windows::Foundation::IInspectable, winrt::TerminalApp::RenameWindowRequestedArgs, _root, RenameWindowRequested);
FORWARDED_TYPED_EVENT(SummonWindowRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, SummonWindowRequested);
FORWARDED_TYPED_EVENT(CloseRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, CloseRequested);
FORWARDED_TYPED_EVENT(OpenSystemMenu, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, OpenSystemMenu);
FORWARDED_TYPED_EVENT(QuitRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, QuitRequested);
FORWARDED_TYPED_EVENT(ShowWindowChanged, Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Control::ShowWindowArgs, _root, ShowWindowChanged);
TYPED_EVENT(IsQuakeWindowChanged, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable);
TYPED_EVENT(SystemMenuChangeRequested, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::SystemMenuChangeArgs);
TYPED_EVENT(SettingsChanged, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::SettingsLoadEventArgs);
FORWARDED_TYPED_EVENT(RequestMoveContent, Windows::Foundation::IInspectable, winrt::TerminalApp::RequestMoveContentArgs, _root, RequestMoveContent);
FORWARDED_TYPED_EVENT(RequestReceiveContent, Windows::Foundation::IInspectable, winrt::TerminalApp::RequestReceiveContentArgs, _root, RequestReceiveContent);
#ifdef UNIT_TESTING
friend class TerminalAppLocalTests::CommandlineTest;
#endif
};
}
namespace winrt::TerminalApp::factory_implementation
{
BASIC_FACTORY(TerminalWindow);
}

View File

@@ -0,0 +1,147 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import "TerminalPage.idl";
import "ShortcutActionDispatch.idl";
import "IDirectKeyListener.idl";
namespace TerminalApp
{
struct InitialPosition
{
Int64 X;
Int64 Y;
};
delegate void SystemMenuItemHandler();
enum SystemMenuChangeAction
{
Add = 0,
Remove = 1
};
[default_interface] runtimeclass SystemMenuChangeArgs {
String Name { get; };
SystemMenuChangeAction Action { get; };
SystemMenuItemHandler Handler { get; };
};
[default_interface] runtimeclass SettingsLoadEventArgs
{
Boolean Reload { get; };
UInt64 Result { get; };
IVector<Microsoft.Terminal.Settings.Model.SettingsLoadWarnings> Warnings { get; };
String ExceptionText { get; };
Microsoft.Terminal.Settings.Model.CascadiaSettings NewSettings { get; };
};
// See IDialogPresenter and TerminalPage's DialogPresenter for more
// information.
[default_interface] runtimeclass TerminalWindow : IDirectKeyListener, IDialogPresenter, Windows.UI.Xaml.Data.INotifyPropertyChanged
{
TerminalWindow(SettingsLoadEventArgs result, ContentManager manager);
// For your own sanity, it's better to do setup outside the ctor.
// If you do any setup in the ctor that ends up throwing an exception,
// then it might look like TermApp just failed to activate, which will
// cause you to chase down the rabbit hole of "why is TermApp not
// registered?" when it definitely is.
void Create();
Boolean IsElevated();
Boolean HasCommandlineArguments();
Int32 SetStartupCommandline(String[] commands);
void SetStartupContent(String json, Windows.Foundation.IReference<Windows.Foundation.Rect> bounds);
Int32 ExecuteCommandline(String[] commands, String cwd);
String ParseCommandlineMessage { get; };
Boolean ShouldExitEarly { get; };
Boolean ShouldImmediatelyHandoffToElevated();
void HandoffToElevated();
void Quit();
Windows.UI.Xaml.UIElement GetRoot();
String Title { get; };
Boolean FocusMode { get; };
Boolean Fullscreen { get; };
void Maximized(Boolean newMaximized);
Boolean AlwaysOnTop { get; };
Boolean AutoHideWindow { get; };
void IdentifyWindow();
void SetPersistedLayoutIdx(UInt32 idx);
void ClearPersistedWindowState();
void RenameFailed();
void RequestExitFullscreen();
Windows.Foundation.Size GetLaunchDimensions(UInt32 dpi);
Boolean CenterOnLaunch { get; };
InitialPosition GetInitialPosition(Int64 defaultInitialX, Int64 defaultInitialY);
Windows.UI.Xaml.ElementTheme GetRequestedTheme();
Microsoft.Terminal.Settings.Model.LaunchMode GetLaunchMode();
Boolean GetShowTabsInTitlebar();
Boolean GetInitialAlwaysOnTop();
Single CalcSnappedDimension(Boolean widthOrHeight, Single dimension);
void TitlebarClicked();
void CloseWindow(Microsoft.Terminal.Settings.Model.LaunchPosition position, Boolean isLastWindow);
void WindowVisibilityChanged(Boolean showOrHide);
TaskbarState TaskbarState{ get; };
Windows.UI.Xaml.Media.Brush TitlebarBrush { get; };
void WindowActivated(Boolean activated);
String GetWindowLayoutJson(Microsoft.Terminal.Settings.Model.LaunchPosition position);
Boolean GetMinimizeToNotificationArea();
Boolean GetAlwaysShowNotificationIcon();
Boolean GetShowTitleInTitlebar();
// These already have accessors as a part of IWindowProperties, but we
// also want to be able to set them.
WindowProperties WindowProperties { get; };
void WindowName(String name);
void WindowId(UInt64 id);
Boolean IsQuakeWindow();
// See IDialogPresenter and TerminalPage's DialogPresenter for more
// information.
void DismissDialog();
event Windows.Foundation.TypedEventHandler<Object, Windows.UI.Xaml.UIElement> SetTitleBarContent;
event Windows.Foundation.TypedEventHandler<Object, String> TitleChanged;
event Windows.Foundation.TypedEventHandler<Object, LastTabClosedEventArgs> LastTabClosed;
event Windows.Foundation.TypedEventHandler<Object, Microsoft.Terminal.Settings.Model.Theme> RequestedThemeChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> FocusModeChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> FullscreenChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> ChangeMaximizeRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> AlwaysOnTopChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> RaiseVisualBell;
event Windows.Foundation.TypedEventHandler<Object, Object> SetTaskbarProgress;
event Windows.Foundation.TypedEventHandler<Object, Object> IdentifyWindowsRequested;
event Windows.Foundation.TypedEventHandler<Object, RenameWindowRequestedArgs> RenameWindowRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> IsQuakeWindowChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> SummonWindowRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> CloseRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> OpenSystemMenu;
event Windows.Foundation.TypedEventHandler<Object, Object> QuitRequested;
event Windows.Foundation.TypedEventHandler<Object, TerminalApp.SystemMenuChangeArgs> SystemMenuChangeRequested;
event Windows.Foundation.TypedEventHandler<Object, Microsoft.Terminal.Control.ShowWindowArgs> ShowWindowChanged;
event Windows.Foundation.TypedEventHandler<Object, SettingsLoadEventArgs> SettingsChanged;
event Windows.Foundation.TypedEventHandler<Object, RequestMoveContentArgs> RequestMoveContent;
event Windows.Foundation.TypedEventHandler<Object, RequestReceiveContentArgs> RequestReceiveContent;
void AttachContent(String content, UInt32 tabIndex);
void SendContentToOther(RequestReceiveContentArgs args);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,379 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
//
// Module Name:
// - BlockContent.h
//
// Abstract:
// - This encapsulates a `Terminal` instance, a `DxEngine` and `Renderer`, and
// an `ITerminalConnection`. This is intended to be everything that someone
// might need to stand up a terminal instance in a control, but without any
// regard for how the UX works.
//
// Author:
// - Mike Griese (zadjii-msft) 01-Apr-2021
#pragma once
#include "BlockContent.g.h"
#include "ControlSettings.h"
#include "../../audio/midi/MidiAudio.hpp"
#include "../../renderer/base/Renderer.hpp"
#include "../../cascadia/TerminalCore/Terminal.hpp"
#include "../buffer/out/search.h"
#include "../buffer/out/TextColor.h"
#include <til/ticket_lock.h>
namespace ControlUnitTests
{
// class BlockContentTests;
class ControlInteractivityTests;
};
namespace winrt::Microsoft::Terminal::Control::implementation
{
struct BlockContent : BlockContentT<BlockContent>
{
public:
BlockContent(Control::IControlSettings settings,
TerminalConnection::ITerminalConnection connection);
~BlockContent();
bool Initialize(const double actualWidth,
const double actualHeight,
const double compositionScale);
void EnablePainting();
Control::IControlSettings Settings() { return *_settings; };
Control::IControlAppearance FocusedAppearance() const { return *_settings->FocusedAppearance(); };
Control::IControlAppearance UnfocusedAppearance() const { return *_settings->UnfocusedAppearance(); };
// bool HasUnfocusedAppearance() const;
// winrt::Microsoft::Terminal::Core::Scheme ColorScheme() const noexcept;
// void ColorScheme(const winrt::Microsoft::Terminal::Core::Scheme& scheme);
void UpdateSettings(const IControlSettings& settings /*, const IControlAppearance& newAppearance*/);
uint64_t SwapChainHandle() const;
// void Reparent(const Microsoft::Terminal::Control::IKeyBindings& keyBindings);
void SizeChanged(const double width, const double height);
void ScaleChanged(const double scale);
void SizeOrScaleChanged(const double width, const double height, const double scale);
// void AdjustFontSize(float fontSizeDelta);
// void ResetFontSize();
// FontInfo GetFont() const;
// winrt::Windows::Foundation::Size FontSizeInDips() const;
winrt::Windows::Foundation::Size FontSize() const noexcept;
// winrt::hstring FontFaceName() const noexcept;
// uint16_t FontWeight() const noexcept;
til::color BackgroundColor() const;
// void SendInput(const winrt::hstring& wstr);
// void PasteText(const winrt::hstring& hstr);
// bool CopySelectionToClipboard(bool singleLine, const Windows::Foundation::IReference<CopyFormat>& formats);
// void SelectAll();
// void ClearSelection();
// bool ToggleBlockSelection();
// void ToggleMarkMode();
// Control::SelectionInteractionMode SelectionMode() const;
// bool SwitchSelectionEndpoint();
// bool ExpandSelectionToWord();
// bool TryMarkModeKeybinding(const WORD vkey,
// const ::Microsoft::Terminal::Core::ControlKeyStates modifiers);
// void GotFocus();
// void LostFocus();
// void ToggleShaderEffects();
// void AdjustOpacity(const double adjustment);
// void ResumeRendering();
// void SetHoveredCell(Core::Point terminalPosition);
// void ClearHoveredCell();
// winrt::hstring GetHyperlink(const Core::Point position) const;
// winrt::hstring HoveredUriText() const;
// Windows::Foundation::IReference<Core::Point> HoveredCell() const;
// ::Microsoft::Console::Render::IRenderData* GetRenderData() const;
// void ColorSelection(const Control::SelectionColor& fg, const Control::SelectionColor& bg, Core::MatchMode matchMode);
void Close();
// #pragma region ICoreState
// const size_t TaskbarState() const noexcept;
// const size_t TaskbarProgress() const noexcept;
// hstring Title();
// Windows::Foundation::IReference<winrt::Windows::UI::Color> TabColor() noexcept;
// hstring WorkingDirectory() const;
TerminalConnection::ConnectionState ConnectionState() const;
int ScrollOffset();
int ViewHeight() const;
int BufferHeight() const;
// bool BracketedPasteEnabled() const noexcept;
// Windows::Foundation::Collections::IVector<Control::ScrollMark> ScrollMarks() const;
// void AddMark(const Control::ScrollMark& mark);
// void ClearMark();
// void ClearAllMarks();
// void ScrollToMark(const Control::ScrollToMarkDirection& direction);
// void SelectCommand(const bool goUp);
// void SelectOutput(const bool goUp);
// #pragma endregion
// #pragma region ITerminalInput
bool TrySendKeyEvent(const WORD vkey,
const WORD scanCode,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const bool keyDown);
bool SendCharEvent(const wchar_t ch,
const WORD scanCode,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers);
// bool SendMouseEvent(const til::point viewportPos,
// const unsigned int uiButton,
// const ::Microsoft::Terminal::Core::ControlKeyStates states,
// const short wheelDelta,
// const ::Microsoft::Console::VirtualTerminal::TerminalInput::MouseButtonState state);
// void UserScrollViewport(const int viewTop);
// void ClearBuffer(Control::ClearBufferType clearType);
// #pragma endregion
// void BlinkAttributeTick();
// void BlinkCursor();
bool CursorOn() const;
void CursorOn(const bool isCursorOn);
// bool IsVtMouseModeEnabled() const;
// bool ShouldSendAlternateScroll(const unsigned int uiButton, const int32_t delta) const;
// Core::Point CursorPosition() const;
// bool HasSelection() const;
// bool CopyOnSelect() const;
// Windows::Foundation::Collections::IVector<winrt::hstring> SelectedText(bool trimTrailingWhitespace) const;
// Control::SelectionData SelectionInfo() const;
// void SetSelectionAnchor(const til::point position);
// void SetEndSelectionPoint(const til::point position);
// void Search(const winrt::hstring& text,
// const bool goForward,
// const bool caseSensitive);
// void LeftClickOnTerminal(const til::point terminalPosition,
// const int numberOfClicks,
// const bool altEnabled,
// const bool shiftEnabled,
// const bool isOnOriginalPosition,
// bool& selectionNeedsToBeCopied);
// void AttachUiaEngine(::Microsoft::Console::Render::IRenderEngine* const pEngine);
// void DetachUiaEngine(::Microsoft::Console::Render::IRenderEngine* const pEngine);
// bool IsInReadOnlyMode() const;
// void ToggleReadOnlyMode();
// hstring ReadEntireBuffer() const;
// Control::CommandHistoryContext CommandHistory() const;
// static bool IsVintageOpacityAvailable() noexcept;
// void AdjustOpacity(const double opacity, const bool relative);
// void WindowVisibilityChanged(const bool showOrHide);
// uint64_t OwningHwnd();
// void OwningHwnd(uint64_t owner);
// RUNTIME_SETTING(double, Opacity, _settings->Opacity());
// RUNTIME_SETTING(bool, UseAcrylic, _settings->UseAcrylic());
// -------------------------------- WinRT Events ---------------------------------
// clang-format off
// WINRT_CALLBACK(FontSizeChanged, Control::FontSizeChangedEventArgs);
// TYPED_EVENT(CopyToClipboard, IInspectable, Control::CopyToClipboardEventArgs);
// TYPED_EVENT(TitleChanged, IInspectable, Control::TitleChangedEventArgs);
// TYPED_EVENT(WarningBell, IInspectable, IInspectable);
// TYPED_EVENT(TabColorChanged, IInspectable, IInspectable);
// TYPED_EVENT(BackgroundColorChanged, IInspectable, IInspectable);
TYPED_EVENT(ScrollPositionChanged, IInspectable, Control::ScrollPositionChangedArgs);
// TYPED_EVENT(CursorPositionChanged, IInspectable, IInspectable);
// TYPED_EVENT(TaskbarProgressChanged, IInspectable, IInspectable);
TYPED_EVENT(ConnectionStateChanged, IInspectable, IInspectable);
// TYPED_EVENT(HoveredHyperlinkChanged, IInspectable, IInspectable);
// TYPED_EVENT(RendererEnteredErrorState, IInspectable, IInspectable);
TYPED_EVENT(SwapChainChanged, IInspectable, IInspectable);
// TYPED_EVENT(RendererWarning, IInspectable, Control::RendererWarningArgs);
// TYPED_EVENT(RaiseNotice, IInspectable, Control::NoticeEventArgs);
// TYPED_EVENT(TransparencyChanged, IInspectable, Control::TransparencyChangedEventArgs);
// TYPED_EVENT(ReceivedOutput, IInspectable, IInspectable);
// TYPED_EVENT(FoundMatch, IInspectable, Control::FoundResultsArgs);
// TYPED_EVENT(ShowWindowChanged, IInspectable, Control::ShowWindowArgs);
// TYPED_EVENT(UpdateSelectionMarkers, IInspectable, Control::UpdateSelectionMarkersEventArgs);
// TYPED_EVENT(OpenHyperlink, IInspectable, Control::OpenHyperlinkEventArgs);
// TYPED_EVENT(MenuChanged, IInspectable, Control::MenuChangedEventArgs);
TYPED_EVENT(CloseTerminalRequested, IInspectable, IInspectable);
// TYPED_EVENT(Attached, IInspectable, IInspectable);
// clang-format on
private:
bool _initializedTerminal{ false };
bool _closing{ false };
TerminalConnection::ITerminalConnection _connection{ nullptr };
event_token _connectionOutputEventToken;
TerminalConnection::ITerminalConnection::StateChanged_revoker _connectionStateChangedRevoker;
winrt::com_ptr<ControlSettings> _settings{ nullptr };
std::shared_ptr<::Microsoft::Terminal::Core::Terminal> _terminal{ nullptr };
// NOTE: _renderEngine must be ordered before _renderer.
//
// As _renderer has a dependency on _renderEngine (through a raw pointer)
// we must ensure the _renderer is deallocated first.
// (C++ class members are destroyed in reverse order.)
std::unique_ptr<::Microsoft::Console::Render::IRenderEngine> _renderEngine{ nullptr };
std::unique_ptr<::Microsoft::Console::Render::Renderer> _renderer{ nullptr };
FontInfoDesired _desiredFont;
FontInfo _actualFont;
winrt::hstring _actualFontFaceName;
CSSLengthPercentage _cellWidth;
CSSLengthPercentage _cellHeight;
// storage location for the leading surrogate of a utf-16 surrogate pair
std::optional<wchar_t> _leadingSurrogate{ std::nullopt };
// std::optional<til::point> _lastHoveredCell{ std::nullopt };
// // Track the last hyperlink ID we hovered over
// uint16_t _lastHoveredId{ 0 };
// bool _isReadOnly{ false };
// std::optional<interval_tree::IntervalTree<til::point, size_t>::interval> _lastHoveredInterval{ std::nullopt };
// These members represent the size of the surface that we should be
// rendering to.
double _panelWidth{ 0 };
double _panelHeight{ 0 };
double _compositionScale{ 0 };
uint64_t _owningHwnd{ 0 };
winrt::Windows::System::DispatcherQueue _dispatcher{ nullptr };
// std::shared_ptr<ThrottledFuncTrailing<>> _tsfTryRedrawCanvas;
// std::unique_ptr<til::throttled_func_trailing<>> _updatePatternLocations;
std::shared_ptr<ThrottledFuncTrailing<Control::ScrollPositionChangedArgs>> _updateScrollBar;
////////////////////////////////////////////////////////////////////////
// NEW STUFF HERE //
////////////////////////////////////////////////////////////////////////
bool _isRootBlock{ false };
bool _gotFirstPrompt{ false };
// mark?
winrt::com_ptr<BlockContent> _first{ this->get_strong() };
winrt::com_ptr<BlockContent> _next{ nullptr };
winrt::com_ptr<BlockContent> _last{ this->get_strong() };
winrt::fire_and_forget _newPromptHandler(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::ScrollMark& mark);
winrt::event_token _newPromptRevoker;
winrt::com_ptr<BlockContent> _fork();
public:
BlockContent(TerminalConnection::ITerminalConnection connection,
winrt::com_ptr<ControlSettings> settings,
std::shared_ptr<::Microsoft::Terminal::Core::Terminal> terminal);
TYPED_EVENT(NewBlock, IInspectable, Control::BlockContent);
private:
////////////////////////////////////////////////////////////////////////
void _setupDispatcherAndCallbacks();
bool _setFontSizeUnderLock(float fontSize);
void _updateFont(const bool initialUpdate = false);
void _refreshSizeUnderLock();
// void _updateSelectionUI();
// bool _shouldTryUpdateSelection(const WORD vkey);
void _handleControlC();
void _sendInputToConnection(std::wstring_view wstr);
// #pragma region TerminalCoreCallbacks
// void _terminalCopyToClipboard(std::wstring_view wstr);
// void _terminalWarningBell();
// void _terminalTitleChanged(std::wstring_view wstr);
// void _terminalScrollPositionChanged(const int viewTop,
// const int viewHeight,
// const int bufferSize);
// void _terminalCursorPositionChanged();
// void _terminalTaskbarProgressChanged();
// void _terminalShowWindowChanged(bool showOrHide);
// void _terminalPlayMidiNote(const int noteNumber,
// const int velocity,
// const std::chrono::microseconds duration);
// void _terminalMenuChanged(std::wstring_view menuJson, int32_t replaceLength);
// #pragma endregion
// MidiAudio _midiAudio;
// winrt::Windows::System::DispatcherQueueTimer _midiAudioSkipTimer{ nullptr };
// #pragma region RendererCallbacks
// void _rendererWarning(const HRESULT hr);
void _renderEngineSwapChainChanged(const HANDLE handle);
// void _rendererBackgroundColorChanged();
// void _rendererTabColorChanged();
// #pragma endregion
// void _raiseReadOnlyWarning();
void _updateAntiAliasingMode();
void _connectionOutputHandler(const hstring& hstr);
// void _updateHoveredCell(const std::optional<til::point> terminalPosition);
// void _setOpacity(const double opacity);
// bool _isBackgroundTransparent();
// void _focusChanged(bool focused);
inline bool _IsClosing() const noexcept
{
#ifndef NDEBUG
if (_dispatcher)
{
// _closing isn't atomic and may only be accessed from the main thread.
//
// Though, the unit tests don't actually run in TAEF's main
// thread, so we don't care when we're running in tests.
assert(_inUnitTests || _dispatcher.HasThreadAccess());
}
#endif
return _closing;
}
// friend class ControlUnitTests::BlockContentTests;
friend class ControlUnitTests::ControlInteractivityTests;
bool _inUnitTests{ false };
};
}
namespace winrt::Microsoft::Terminal::Control::factory_implementation
{
BASIC_FACTORY(BlockContent);
// BASIC_FACTORY(SelectionColor);
}

View File

@@ -0,0 +1,207 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import "ICoreState.idl";
import "ControlCore.idl";
import "IControlSettings.idl";
import "EventArgs.idl";
namespace Microsoft.Terminal.Control
{
// // This is a mirror of
// // ::Microsoft::Console::VirtualTerminal::TerminalInput::MouseButtonState,
// // but projectable.
// // !! LOAD BEARING !! If you make this a struct with Booleans (like they
// // make the most sense as), then the app will crash trying to toss one of
// // these across the process boundary. I haven't the damndest idea why.
// [flags] enum MouseButtonState {
// IsLeftButtonDown = 0x1,
// IsMiddleButtonDown = 0x2,
// IsRightButtonDown = 0x4
// };
// enum ClearBufferType
// {
// Screen,
// Scrollback,
// All
// };
// enum SelectionInteractionMode
// {
// None,
// Mouse,
// Keyboard,
// Mark
// };
// [flags] enum SelectionEndpointTarget {
// Start = 0x1,
// End = 0x2
// };
// struct SelectionData
// {
// Microsoft.Terminal.Core.Point StartPos;
// Microsoft.Terminal.Core.Point EndPos;
// SelectionEndpointTarget Endpoint;
// Boolean StartAtLeftBoundary;
// Boolean EndAtRightBoundary;
// };
// [default_interface] runtimeclass SelectionColor
// {
// SelectionColor();
// Microsoft.Terminal.Core.Color Color;
// // If true, color.R is a value between 0 and 15, indicating an indexed color.
// // This mirrors how TextColor works internally, which is the primary target of this interface.
// Boolean IsIndex16;
// };
// [default_interface] runtimeclass CommandHistoryContext
// {
// IVector<String> History;
// String CurrentCommandline;
// };
[default_interface] runtimeclass BlockContent
{
BlockContent(IControlSettings settings,
Microsoft.Terminal.TerminalConnection.ITerminalConnection connection);
Boolean Initialize(Double actualWidth,
Double actualHeight,
Double compositionScale);
UInt64 SwapChainHandle { get; };
// [default_interface] runtimeclass ControlCore : ICoreState
// {
// ControlCore(IControlSettings settings,
// IControlAppearance unfocusedAppearance,
// Microsoft.Terminal.TerminalConnection.ITerminalConnection connection);
// Boolean Initialize(Double actualWidth,
// Double actualHeight,
// Double compositionScale);
// void UpdateSettings(IControlSettings settings, IControlAppearance appearance);
void UpdateSettings(IControlSettings settings);
// void ApplyAppearance(Boolean focused);
IControlSettings Settings { get; };
IControlAppearance FocusedAppearance { get; };
IControlAppearance UnfocusedAppearance { get; };
// Boolean HasUnfocusedAppearance();
// UInt64 SwapChainHandle { get; };
Windows.Foundation.Size FontSize { get; };
// String FontFaceName { get; };
// UInt16 FontWeight { get; };
// Double Opacity { get; };
// Boolean UseAcrylic { get; };
// Boolean TryMarkModeKeybinding(Int16 vkey,
// Microsoft.Terminal.Core.ControlKeyStates modifiers);
Boolean TrySendKeyEvent(Int16 vkey,
Int16 scanCode,
Microsoft.Terminal.Core.ControlKeyStates modifiers,
Boolean keyDown);
Boolean SendCharEvent(Char ch,
Int16 scanCode,
Microsoft.Terminal.Core.ControlKeyStates modifiers);
// void SendInput(String text);
// void PasteText(String text);
// void SelectAll();
// void ClearSelection();
// Boolean ToggleBlockSelection();
// void ToggleMarkMode();
// Boolean SwitchSelectionEndpoint();
// Boolean ExpandSelectionToWord();
// void ClearBuffer(ClearBufferType clearType);
// void SetHoveredCell(Microsoft.Terminal.Core.Point terminalPosition);
// void ClearHoveredCell();
// void ResetFontSize();
// void AdjustFontSize(Single fontSizeDelta);
void SizeChanged(Double width, Double height);
void ScaleChanged(Double scale);
void SizeOrScaleChanged(Double width, Double height, Double scale);
// void ToggleShaderEffects();
// void ToggleReadOnlyMode();
// Microsoft.Terminal.Core.Point CursorPosition { get; };
// void ResumeRendering();
// void BlinkAttributeTick();
// void Search(String text, Boolean goForward, Boolean caseSensitive);
Microsoft.Terminal.Core.Color BackgroundColor { get; };
// Boolean HasSelection { get; };
// IVector<String> SelectedText(Boolean trimTrailingWhitespace);
// SelectionData SelectionInfo { get; };
// SelectionInteractionMode SelectionMode();
// String HoveredUriText { get; };
// Windows.Foundation.IReference<Microsoft.Terminal.Core.Point> HoveredCell { get; };
// void BlinkCursor();
// Boolean IsInReadOnlyMode { get; };
Boolean CursorOn;
void EnablePainting();
// String ReadEntireBuffer();
// CommandHistoryContext CommandHistory();
// void AdjustOpacity(Double Opacity, Boolean relative);
// void WindowVisibilityChanged(Boolean showOrHide);
// void ColorSelection(SelectionColor fg, SelectionColor bg, Microsoft.Terminal.Core.MatchMode matchMode);
// event FontSizeChangedEventArgs FontSizeChanged;
// event Windows.Foundation.TypedEventHandler<Object, CopyToClipboardEventArgs> CopyToClipboard;
// event Windows.Foundation.TypedEventHandler<Object, TitleChangedEventArgs> TitleChanged;
// event Windows.Foundation.TypedEventHandler<Object, Object> WarningBell;
// event Windows.Foundation.TypedEventHandler<Object, Object> TabColorChanged;
// event Windows.Foundation.TypedEventHandler<Object, Object> BackgroundColorChanged;
event Windows.Foundation.TypedEventHandler<Object, ScrollPositionChangedArgs> ScrollPositionChanged;
// event Windows.Foundation.TypedEventHandler<Object, Object> CursorPositionChanged;
// event Windows.Foundation.TypedEventHandler<Object, Object> TaskbarProgressChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> ConnectionStateChanged;
// event Windows.Foundation.TypedEventHandler<Object, Object> HoveredHyperlinkChanged;
// event Windows.Foundation.TypedEventHandler<Object, Object> RendererEnteredErrorState;
event Windows.Foundation.TypedEventHandler<Object, Object> SwapChainChanged;
// event Windows.Foundation.TypedEventHandler<Object, RendererWarningArgs> RendererWarning;
// event Windows.Foundation.TypedEventHandler<Object, NoticeEventArgs> RaiseNotice;
// event Windows.Foundation.TypedEventHandler<Object, TransparencyChangedEventArgs> TransparencyChanged;
// event Windows.Foundation.TypedEventHandler<Object, Object> ReceivedOutput;
// event Windows.Foundation.TypedEventHandler<Object, FoundResultsArgs> FoundMatch;
// event Windows.Foundation.TypedEventHandler<Object, ShowWindowArgs> ShowWindowChanged;
// event Windows.Foundation.TypedEventHandler<Object, UpdateSelectionMarkersEventArgs> UpdateSelectionMarkers;
// event Windows.Foundation.TypedEventHandler<Object, OpenHyperlinkEventArgs> OpenHyperlink;
event Windows.Foundation.TypedEventHandler<Object, Object> CloseTerminalRequested;
// event Windows.Foundation.TypedEventHandler<Object, MenuChangedEventArgs> MenuChanged;
// event Windows.Foundation.TypedEventHandler<Object, Object> Attached;
// ICoreState
Microsoft.Terminal.TerminalConnection.ConnectionState ConnectionState { get; };
Int32 ScrollOffset { get; };
Int32 ViewHeight { get; };
Int32 BufferHeight { get; };
////////////////////////////////////////////////////////////////////////
event Windows.Foundation.TypedEventHandler<Object, BlockContent> NewBlock;
};
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,393 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include "BlockControl.g.h"
#include "XamlLights.h"
#include "EventArgs.h"
#include "../../renderer/base/Renderer.hpp"
#include "../../renderer/dx/DxRenderer.hpp"
#include "../../renderer/uia/UiaRenderer.hpp"
#include "../../cascadia/TerminalCore/Terminal.hpp"
#include "../buffer/out/search.h"
#include "SearchBoxControl.h"
#include "ControlInteractivity.h"
#include "ControlSettings.h"
namespace Microsoft::Console::VirtualTerminal
{
struct MouseButtonState;
}
namespace winrt::Microsoft::Terminal::Control::implementation
{
struct BlockControl : BlockControlT<BlockControl>
{
BlockControl(Control::BlockContent content);
// BlockControl(IControlSettings settings, Control::IControlAppearance unfocusedAppearance, TerminalConnection::ITerminalConnection connection);
// static Control::BlockControl AttachContent(Control::ControlInteractivity content, const Microsoft::Terminal::Control::IKeyBindings& keyBindings);
winrt::fire_and_forget UpdateControlSettings(Control::IControlSettings settings);
winrt::fire_and_forget UpdateControlSettings(Control::IControlSettings settings, Control::IControlAppearance unfocusedAppearance);
IControlSettings Settings() const;
// winrt::guid ContentGuid() const;
hstring GetProfileName() const;
bool CopySelectionToClipboard(bool singleLine, const Windows::Foundation::IReference<CopyFormat>& formats);
void PasteTextFromClipboard();
// void SelectAll();
// bool ToggleBlockSelection();
// void ToggleMarkMode();
// bool SwitchSelectionEndpoint();
// bool ExpandSelectionToWord();
void Close();
Windows::Foundation::Size CharacterDimensions() const;
// Windows::Foundation::Size MinimumSize();
// float SnapDimensionToGrid(const bool widthOrHeight, const float dimension);
// void PreviewInput(const winrt::hstring& text);
// Microsoft::Terminal::Core::Point CursorPositionInDips();
void WindowVisibilityChanged(const bool showOrHide);
// void ColorSelection(Control::SelectionColor fg, Control::SelectionColor bg, Core::MatchMode matchMode);
// #pragma region ICoreState
// const uint64_t TaskbarState() const noexcept;
// const uint64_t TaskbarProgress() const noexcept;
// hstring Title();
// Windows::Foundation::IReference<winrt::Windows::UI::Color> TabColor() noexcept;
// hstring WorkingDirectory() const;
TerminalConnection::ConnectionState ConnectionState() const;
// int ScrollOffset() const;
// int ViewHeight() const;
// int BufferHeight() const;
// bool BracketedPasteEnabled() const noexcept;
// double BackgroundOpacity() const;
// uint64_t OwningHwnd();
// void OwningHwnd(uint64_t owner);
// Windows::Foundation::Collections::IVector<Control::ScrollMark> ScrollMarks() const;
// void AddMark(const Control::ScrollMark& mark);
// void ClearMark();
// void ClearAllMarks();
// void ScrollToMark(const Control::ScrollToMarkDirection& direction);
// void SelectCommand(const bool goUp);
// void SelectOutput(const bool goUp);
// #pragma endregion
// void ScrollViewport(int viewTop);
// void AdjustFontSize(float fontSizeDelta);
// void ResetFontSize();
// til::point GetFontSize() const;
// void SendInput(const winrt::hstring& input);
// void ClearBuffer(Control::ClearBufferType clearType);
// void ToggleShaderEffects();
winrt::fire_and_forget RenderEngineSwapChainChanged(IInspectable sender, IInspectable args);
void _AttachDxgiSwapChainToXaml(HANDLE swapChainHandle);
winrt::fire_and_forget _RendererEnteredErrorState(IInspectable sender, IInspectable args);
// void _RenderRetryButton_Click(const IInspectable& button, const IInspectable& args);
// winrt::fire_and_forget _RendererWarning(IInspectable sender,
// Control::RendererWarningArgs args);
// void CreateSearchBoxControl();
// void SearchMatch(const bool goForward);
// bool SearchBoxEditInFocus() const;
// 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);
~BlockControl();
// Windows::UI::Xaml::Automation::Peers::AutomationPeer OnCreateAutomationPeer();
const Windows::UI::Xaml::Thickness GetPadding();
// static Windows::Foundation::Size GetProposedDimensions(const IControlSettings& settings,
// const uint32_t dpi,
// int32_t commandlineCols,
// int32_t commandlineRows);
// static Windows::Foundation::Size GetProposedDimensions(const IControlSettings& settings, const uint32_t dpi, const winrt::Windows::Foundation::Size& initialSizeInChars);
// void BellLightOn();
// bool ReadOnly() const noexcept;
// void ToggleReadOnly();
static Control::MouseButtonState GetPressedMouseButtons(const winrt::Windows::UI::Input::PointerPoint point);
static unsigned int GetPointerUpdateKind(const winrt::Windows::UI::Input::PointerPoint point);
static Windows::UI::Xaml::Thickness ParseThicknessFromPadding(const hstring padding);
// hstring ReadEntireBuffer() const;
// Control::CommandHistoryContext CommandHistory() const;
// winrt::Microsoft::Terminal::Core::Scheme ColorScheme() const noexcept;
// void ColorScheme(const winrt::Microsoft::Terminal::Core::Scheme& scheme) const noexcept;
// void AdjustOpacity(const double opacity, const bool relative);
// void Detach();
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
// -------------------------------- WinRT Events ---------------------------------
// clang-format off
// WINRT_CALLBACK(FontSizeChanged, Control::FontSizeChangedEventArgs);
// UNDER NO CIRCUMSTANCES SHOULD YOU ADD A (PROJECTED_)FORWARDED_TYPED_EVENT HERE
// BUBBLED_FORWARDED_TYPED_EVENT(CopyToClipboard, IInspectable, Control::CopyToClipboardEventArgs);
// BUBBLED_FORWARDED_TYPED_EVENT(TitleChanged, IInspectable, Control::TitleChangedEventArgs);
// BUBBLED_FORWARDED_TYPED_EVENT(TabColorChanged, IInspectable, IInspectable);
// BUBBLED_FORWARDED_TYPED_EVENT(SetTaskbarProgress, IInspectable, IInspectable);
BUBBLED_FORWARDED_TYPED_EVENT(ConnectionStateChanged, IInspectable, IInspectable);
// BUBBLED_FORWARDED_TYPED_EVENT(ShowWindowChanged, IInspectable, Control::ShowWindowArgs);
BUBBLED_FORWARDED_TYPED_EVENT(CloseTerminalRequested, IInspectable, IInspectable);
// BUBBLED_FORWARDED_TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs);
// BUBBLED_FORWARDED_TYPED_EVENT(MenuChanged, IInspectable, Control::MenuChangedEventArgs);
// TYPED_EVENT(OpenHyperlink, IInspectable, Control::OpenHyperlinkEventArgs);
// TYPED_EVENT(RaiseNotice, IInspectable, Control::NoticeEventArgs);
// TYPED_EVENT(HidePointerCursor, IInspectable, IInspectable);
// TYPED_EVENT(RestorePointerCursor, IInspectable, IInspectable);
// TYPED_EVENT(ReadOnlyChanged, IInspectable, IInspectable);
// TYPED_EVENT(FocusFollowMouseRequested, IInspectable, IInspectable);
TYPED_EVENT(Initialized, Control::BlockControl, Windows::UI::Xaml::RoutedEventArgs);
// TYPED_EVENT(WarningBell, IInspectable, IInspectable);
// clang-format on
WINRT_OBSERVABLE_PROPERTY(winrt::Windows::UI::Xaml::Media::Brush, BackgroundBrush, _PropertyChangedHandlers, nullptr);
private:
friend struct BlockControlT<BlockControl>; // friend our parent so it can bind private event handlers
// NOTE: _uiaEngine must be ordered before _core.
//
// ControlCore::AttachUiaEngine receives a IRenderEngine as a raw pointer, which we own.
// We must ensure that we first destroy the ControlCore before the UiaEngine instance
// in order to safely resolve this unsafe pointer dependency. Otherwise a deallocated
// IRenderEngine is accessed when ControlCore calls Renderer::TriggerTeardown.
// (C++ class members are destroyed in reverse order.)
// Further, the BlockControlAutomationPeer must be destructed after _uiaEngine!
// Control::BlockControlAutomationPeer _automationPeer{ nullptr };
// Control::ControlInteractivity _interactivity{ nullptr };
// Control::ControlCore _core{ nullptr };
Control::BlockContent _content{ nullptr };
winrt::com_ptr<SearchBoxControl> _searchBox;
bool _closing{ false };
bool _focused{ false };
bool _initializedTerminal{ false };
std::shared_ptr<ThrottledFuncLeading> _playWarningBell;
struct ScrollBarUpdate
{
std::optional<double> newValue;
double newMaximum;
double newMinimum;
double newViewportSize;
};
std::shared_ptr<ThrottledFuncTrailing<ScrollBarUpdate>> _updateScrollBar;
bool _isInternalScrollBarUpdate;
// Auto scroll occurs when user, while selecting, drags cursor outside
// viewport. View is then scrolled to 'follow' the cursor.
double _autoScrollVelocity;
std::optional<Windows::UI::Input::PointerPoint> _autoScrollingPointerPoint;
Windows::UI::Xaml::DispatcherTimer _autoScrollTimer;
std::optional<std::chrono::high_resolution_clock::time_point> _lastAutoScrollUpdateTime;
bool _pointerPressedInBounds{ false };
winrt::Windows::UI::Composition::ScalarKeyFrameAnimation _bellLightAnimation{ nullptr };
winrt::Windows::UI::Composition::ScalarKeyFrameAnimation _bellDarkAnimation{ nullptr };
Windows::UI::Xaml::DispatcherTimer _bellLightTimer{ nullptr };
std::optional<Windows::UI::Xaml::DispatcherTimer> _cursorTimer;
std::optional<Windows::UI::Xaml::DispatcherTimer> _blinkTimer;
winrt::Windows::UI::Xaml::Controls::SwapChainPanel::LayoutUpdated_revoker _layoutUpdatedRevoker;
bool _showMarksInScrollbar{ false };
bool _useRightClickContextMenu{ false };
bool _rightClickPressed{ false };
bool _isBackgroundLight{ false };
bool _detached{ false };
// winrt::Windows::Foundation::Collections::IObservableVector<winrt::Windows::UI::Xaml::Controls::ICommandBarElement> _originalPrimaryElements{ nullptr };
// winrt::Windows::Foundation::Collections::IObservableVector<winrt::Windows::UI::Xaml::Controls::ICommandBarElement> _originalSecondaryElements{ nullptr };
// winrt::Windows::Foundation::Collections::IObservableVector<winrt::Windows::UI::Xaml::Controls::ICommandBarElement> _originalSelectedPrimaryElements{ nullptr };
// winrt::Windows::Foundation::Collections::IObservableVector<winrt::Windows::UI::Xaml::Controls::ICommandBarElement> _originalSelectedSecondaryElements{ nullptr };
inline bool _IsClosing() const noexcept
{
#ifndef NDEBUG
// _closing isn't atomic and may only be accessed from the main thread.
if (const auto dispatcher = Dispatcher())
{
assert(dispatcher.HasThreadAccess());
}
#endif
return _closing;
}
void _UpdateSettingsFromUIThread();
void _UpdateAppearanceFromUIThread(Control::IControlAppearance newAppearance);
void _ApplyUISettings();
winrt::fire_and_forget UpdateAppearance(Control::IControlAppearance newAppearance);
void _SetBackgroundImage(const IControlAppearance& newAppearance);
void _InitializeBackgroundBrush();
winrt::fire_and_forget _coreBackgroundColorChanged(const IInspectable& sender, const IInspectable& args);
void _changeBackgroundColor(til::color bg);
static bool _isColorLight(til::color bg) noexcept;
void _changeBackgroundOpacity();
bool _InitializeTerminal(const bool reattach);
void _SetFontSize(int fontSize);
void _TappedHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::TappedRoutedEventArgs& e);
void _KeyDownHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::KeyRoutedEventArgs& e);
void _KeyUpHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::KeyRoutedEventArgs& e);
void _CharacterHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::CharacterReceivedRoutedEventArgs& e);
void _PointerPressedHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e);
void _PointerMovedHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e);
void _PointerReleasedHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e);
void _PointerExitedHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e);
void _MouseWheelHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e);
void _ScrollbarChangeHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Controls::Primitives::RangeBaseValueChangedEventArgs& e);
void _GotFocusHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e);
void _LostFocusHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e);
winrt::fire_and_forget _DragDropHandler(Windows::Foundation::IInspectable sender, Windows::UI::Xaml::DragEventArgs e);
void _DragOverHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::DragEventArgs& e);
winrt::fire_and_forget _HyperlinkHandler(Windows::Foundation::IInspectable sender, Control::OpenHyperlinkEventArgs e);
void _CursorTimerTick(const Windows::Foundation::IInspectable& sender, const Windows::Foundation::IInspectable& e);
void _BlinkTimerTick(const Windows::Foundation::IInspectable& sender, const Windows::Foundation::IInspectable& e);
void _BellLightOff(const Windows::Foundation::IInspectable& sender, const Windows::Foundation::IInspectable& e);
void _SetEndSelectionPointAtCursor(const Windows::Foundation::Point& cursorPosition);
void _SwapChainSizeChanged(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::SizeChangedEventArgs& e);
void _SwapChainScaleChanged(const Windows::UI::Xaml::Controls::SwapChainPanel& sender, const Windows::Foundation::IInspectable& args);
void _TerminalTabColorChanged(const std::optional<til::color> color);
void _ScrollPositionChanged(const IInspectable& sender, const Control::ScrollPositionChangedArgs& args);
winrt::fire_and_forget _CursorPositionChanged(const IInspectable& sender, const IInspectable& args);
bool _CapturePointer(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e);
bool _ReleasePointerCapture(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e);
void _TryStartAutoScroll(const Windows::UI::Input::PointerPoint& pointerPoint, const double scrollVelocity);
void _TryStopAutoScroll(const uint32_t pointerId);
void _UpdateAutoScroll(const Windows::Foundation::IInspectable& sender, const Windows::Foundation::IInspectable& e);
void _KeyHandler(const Windows::UI::Xaml::Input::KeyRoutedEventArgs& e, const bool keyDown);
static ::Microsoft::Terminal::Core::ControlKeyStates _GetPressedModifierKeys() noexcept;
bool _TryHandleKeyBinding(const WORD vkey, const WORD scanCode, ::Microsoft::Terminal::Core::ControlKeyStates modifiers) const;
static void _ClearKeyboardState(const WORD vkey, const WORD scanCode) noexcept;
bool _TrySendKeyEvent(const WORD vkey, const WORD scanCode, ::Microsoft::Terminal::Core::ControlKeyStates modifiers, const bool keyDown);
const til::point _toTerminalOrigin(winrt::Windows::Foundation::Point cursorPosition);
double _GetAutoScrollSpeed(double cursorDistanceFromBorder) const;
void _Search(const winrt::hstring& text, const bool goForward, const bool caseSensitive);
void _CloseSearchBoxControl(const winrt::Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args);
// // TSFInputControl Handlers
// 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 _hoveredHyperlinkChanged(IInspectable sender, IInspectable args);
// winrt::fire_and_forget _updateSelectionMarkers(IInspectable sender, Control::UpdateSelectionMarkersEventArgs args);
void _coreFontSizeChanged(const int fontWidth,
const int fontHeight,
const bool isInitialChange);
winrt::fire_and_forget _coreTransparencyChanged(IInspectable sender, Control::TransparencyChangedEventArgs args);
void _coreRaisedNotice(const IInspectable& s, const Control::NoticeEventArgs& args);
void _coreWarningBell(const IInspectable& sender, const IInspectable& args);
void _coreFoundMatch(const IInspectable& sender, const Control::FoundResultsArgs& args);
til::point _toPosInDips(const Core::Point terminalCellPos);
void _throttledUpdateScrollbar(const ScrollBarUpdate& update);
// void _contextMenuHandler(IInspectable sender, Control::ContextMenuRequestedEventArgs args);
// void _PasteCommandHandler(const IInspectable& sender, const IInspectable& args);
// void _CopyCommandHandler(const IInspectable& sender, const IInspectable& args);
// void _SearchCommandHandler(const IInspectable& sender, const IInspectable& args);
////////////////////////////////////////////////////////////////////////
BUBBLED_FORWARDED_TYPED_EVENT(NewBlock, IInspectable, Control::BlockContent);
////////////////////////////////////////////////////////////////////////
struct Revokers
{
Control::BlockContent::ScrollPositionChanged_revoker scrollPositionChanged;
// Control::ControlCore::WarningBell_revoker WarningBell;
// Control::ControlCore::CursorPositionChanged_revoker CursorPositionChanged;
// Control::ControlCore::RendererEnteredErrorState_revoker RendererEnteredErrorState;
// Control::ControlCore::BackgroundColorChanged_revoker BackgroundColorChanged;
// Control::ControlCore::FontSizeChanged_revoker FontSizeChanged;
// Control::ControlCore::TransparencyChanged_revoker TransparencyChanged;
// Control::ControlCore::RaiseNotice_revoker RaiseNotice;
// Control::ControlCore::HoveredHyperlinkChanged_revoker HoveredHyperlinkChanged;
// Control::ControlCore::FoundMatch_revoker FoundMatch;
// Control::ControlCore::UpdateSelectionMarkers_revoker UpdateSelectionMarkers;
// Control::ControlCore::OpenHyperlink_revoker coreOpenHyperlink;
// Control::ControlCore::CopyToClipboard_revoker CopyToClipboard;
// Control::ControlCore::TitleChanged_revoker TitleChanged;
// Control::ControlCore::TabColorChanged_revoker TabColorChanged;
// Control::ControlCore::TaskbarProgressChanged_revoker TaskbarProgressChanged;
Control::BlockContent::ConnectionStateChanged_revoker ConnectionStateChanged;
// Control::ControlCore::ShowWindowChanged_revoker ShowWindowChanged;
// Control::ControlCore::CloseTerminalRequested_revoker CloseTerminalRequested;
// Control::ControlCore::MenuChanged_revoker MenuChanged;
Control::BlockContent::NewBlock_revoker NewBlock;
// // These are set up in _InitializeTerminal
// Control::ControlCore::RendererWarning_revoker RendererWarning;
Control::BlockContent::SwapChainChanged_revoker SwapChainChanged;
// Control::ControlInteractivity::OpenHyperlink_revoker interactivityOpenHyperlink;
// Control::ControlInteractivity::ScrollPositionChanged_revoker interactivityScrollPositionChanged;
// Control::ControlInteractivity::PasteFromClipboard_revoker PasteFromClipboard;
} _revokers{};
};
}
namespace winrt::Microsoft::Terminal::Control::factory_implementation
{
BASIC_FACTORY(BlockControl);
}

View File

@@ -0,0 +1,127 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import "IMouseWheelListener.idl";
import "IControlSettings.idl";
import "ControlInteractivity.idl";
import "IDirectKeyListener.idl";
import "EventArgs.idl";
import "ICoreState.idl";
import "BlockContent.idl";
namespace Microsoft.Terminal.Control
{
[default_interface] runtimeclass BlockControl : Windows.UI.Xaml.Controls.UserControl,
// IDirectKeyListener,
// IMouseWheelListener,
// ICoreState,
Windows.UI.Xaml.Data.INotifyPropertyChanged
{
BlockControl(BlockContent content);
// BlockControl(IControlSettings settings,
// IControlAppearance unfocusedAppearance,
// Microsoft.Terminal.TerminalConnection.ITerminalConnection connection);
// static TermControl AttachContent(ControlInteractivity content, Microsoft.Terminal.Control.IKeyBindings keyBindings);
// static Windows.Foundation.Size GetProposedDimensions(IControlSettings settings,
// UInt32 dpi,
// Int32 commandlineCols,
// Int32 commandlineRows);
// void UpdateControlSettings(IControlSettings settings);
// void UpdateControlSettings(IControlSettings settings, IControlAppearance unfocusedAppearance);
// Guid ContentGuid{ get; };
// Microsoft.Terminal.Control.IControlSettings Settings { get; };
// event FontSizeChangedEventArgs FontSizeChanged;
// event Windows.Foundation.TypedEventHandler<Object, TitleChangedEventArgs> TitleChanged;
// event Windows.Foundation.TypedEventHandler<Object, CopyToClipboardEventArgs> CopyToClipboard;
// event Windows.Foundation.TypedEventHandler<Object, PasteFromClipboardEventArgs> PasteFromClipboard;
// event Windows.Foundation.TypedEventHandler<Object, OpenHyperlinkEventArgs> OpenHyperlink;
// event Windows.Foundation.TypedEventHandler<Object, Object> SetTaskbarProgress;
// event Windows.Foundation.TypedEventHandler<Object, NoticeEventArgs> RaiseNotice;
// event Windows.Foundation.TypedEventHandler<Object, Object> WarningBell;
// event Windows.Foundation.TypedEventHandler<Object, Object> HidePointerCursor;
// event Windows.Foundation.TypedEventHandler<Object, Object> RestorePointerCursor;
// event Windows.Foundation.TypedEventHandler<Object, Object> TabColorChanged;
// event Windows.Foundation.TypedEventHandler<Object, Object> ReadOnlyChanged;
// event Windows.Foundation.TypedEventHandler<Object, Object> FocusFollowMouseRequested;
// event Windows.Foundation.TypedEventHandler<Object, MenuChangedEventArgs> MenuChanged;
// Windows.UI.Xaml.Controls.CommandBarFlyout ContextMenu { get; };
// Windows.UI.Xaml.Controls.CommandBarFlyout SelectionContextMenu { get; };
// event Windows.Foundation.TypedEventHandler<TermControl, Windows.UI.Xaml.RoutedEventArgs> Initialized;
// // This is an event handler forwarder for the underlying connection.
// // We expose this and ConnectionState here so that it might eventually be data bound.
// event Windows.Foundation.TypedEventHandler<Object, IInspectable> ConnectionStateChanged;
// event Windows.Foundation.TypedEventHandler<Object, ShowWindowArgs> ShowWindowChanged;
// event Windows.Foundation.TypedEventHandler<Object, Object> CloseTerminalRequested;
// Boolean CopySelectionToClipboard(Boolean singleLine, Windows.Foundation.IReference<CopyFormat> formats);
// void PasteTextFromClipboard();
// void SelectAll();
// Boolean ToggleBlockSelection();
// void ToggleMarkMode();
// Boolean SwitchSelectionEndpoint();
// Boolean ExpandSelectionToWord();
// void ClearBuffer(ClearBufferType clearType);
// void Close();
// Windows.Foundation.Size CharacterDimensions { get; };
// Windows.Foundation.Size MinimumSize { get; };
// Single SnapDimensionToGrid(Boolean widthOrHeight, Single dimension);
// void WindowVisibilityChanged(Boolean showOrHide);
// void ScrollViewport(Int32 viewTop);
// void CreateSearchBoxControl();
// Boolean SearchBoxEditInFocus();
// void SearchMatch(Boolean goForward);
// void AdjustFontSize(Single fontSizeDelta);
// void ResetFontSize();
// void ToggleShaderEffects();
// void SendInput(String input);
// void BellLightOn();
// Boolean ReadOnly { get; };
// void ToggleReadOnly();
// String ReadEntireBuffer();
// CommandHistoryContext CommandHistory();
// void AdjustOpacity(Double Opacity, Boolean relative);
// // You'd think this should just be "Opacity", but UIElement already
// // defines an "Opacity", which we're actually not setting at all. We're
// // not overriding or changing _that_ value. Callers that want the
// // opacity set by the settings should call this instead.
// Double BackgroundOpacity { get; };
// void PreviewInput(String text);
// Windows.UI.Xaml.Media.Brush BackgroundBrush { get; };
// void ColorSelection(SelectionColor fg, SelectionColor bg, Microsoft.Terminal.Core.MatchMode matchMode);
// void Detach();
// Microsoft.Terminal.Core.Point CursorPositionInDips { get; };
////////////////////////////////////////////////////////////////////////
event Windows.Foundation.TypedEventHandler<Object, BlockContent> NewBlock;
}
}

View File

@@ -0,0 +1,200 @@
<!--
Copyright (c) Microsoft Corporation. All rights reserved. Licensed under
the MIT License. See LICENSE in the project root for license information.
-->
<UserControl x:Class="Microsoft.Terminal.Control.BlockControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:contract7NotPresent="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractNotPresent(Windows.Foundation.UniversalApiContract,7)"
xmlns:contract7Present="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract,7)"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:Microsoft.Terminal.Control"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
d:DesignHeight="768"
d:DesignWidth="1024"
AllowDrop="True"
AllowFocusOnInteraction="True"
Background="Transparent"
CharacterReceived="_CharacterHandler"
DragOver="_DragOverHandler"
Drop="_DragDropHandler"
GotFocus="_GotFocusHandler"
IsTabStop="True"
KeyUp="_KeyUpHandler"
LostFocus="_LostFocusHandler"
PointerWheelChanged="_MouseWheelHandler"
PreviewKeyDown="_KeyDownHandler"
TabNavigation="Cycle"
Tapped="_TappedHandler"
mc:Ignorable="d">
<UserControl.Resources>
<!--
BODGY: ControlsV2 changed the size of the scrollbars from 16dips to
12dips. This is harder for folks to hit with the mouse, and isn't
consistent with the rest of the scrollbars on the platform (as much
as they can be).
To work around this, we have to entirely copy the template for the
ScrollBar into our XAML file. We're then also re-defining
ScrollBarSize here to 16, so that the new template will pick up on
the new value.
This is kinda a pain, and we have to be careful to be sure to ingest
an updated version of the template any time we update MUX. The
latest ControlsV2 version of the template can be found at:
https://github.com/microsoft/microsoft-ui-xaml/blob/main/dev/CommonStyles/ScrollBar_themeresources.xaml#L218
We're also removing the corner radius, cause that should be flush
with the top of the window above the TermControl.
We're also planning on making this adjustable in the future
(GH#9218), where we might need this anyways.
-->
<x:Double x:Key="ScrollBarSize">16</x:Double>
</UserControl.Resources>
<!--
TODO GH#4031: We've investigated whether we should be using KeyDown or PreviewKeyDown
but not necessarily come to a consensus. It's possible that moving input to the SwapChainPanel
will solve a bunch of the search input issues we found last time we switched.
-->
<Grid x:Name="RootGrid">
<Grid.Lights>
<local:VisualBellLight x:Name="BellLight" />
</Grid.Lights>
<Image x:Name="BackgroundImage"
AutomationProperties.AccessibilityView="Raw" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0"
Background="Transparent"
PointerExited="_PointerExitedHandler"
PointerMoved="_PointerMovedHandler"
PointerPressed="_PointerPressedHandler"
PointerReleased="_PointerReleasedHandler"
Visibility="Visible">
<SwapChainPanel x:Name="SwapChainPanel"
CompositionScaleChanged="_SwapChainScaleChanged"
SizeChanged="_SwapChainSizeChanged">
<Canvas x:Name="OverlayCanvas"
Visibility="Visible">
<Border x:Name="HyperlinkTooltipBorder"
BorderBrush="Transparent">
<ToolTipService.ToolTip>
<ToolTip x:Name="LinkTip"
Placement="Mouse">
<TextBlock IsTextSelectionEnabled="True"
TextWrapping="Wrap">
<Run x:Name="HoveredUri" /> <LineBreak />
<Run x:Uid="HowToOpenRun"
FontStyle="Italic" />
</TextBlock>
</ToolTip>
</ToolTipService.ToolTip>
</Border>
</Canvas>
<Canvas x:Name="SelectionCanvas"
Visibility="Visible">
<Path Name="SelectionStartMarker"
Data="M 0 0 L 4 5.5996094 L 4 14 L 5 14 L 5 7 L 5 4.1992188 L 5 0 L 0 0 z "
Visibility="Collapsed" />
<Path Name="SelectionEndMarker"
Data="M 0 0 L 0 4.1992188 L 0 7 L 0 14 L 1 14 L 1 5.5996094 L 5 0 L 0 0 z "
Visibility="Collapsed" />
</Canvas>
</SwapChainPanel>
<!--
Putting this in a grid w/ the SwapChainPanel
ensures that it's always aligned w/ the scrollbar
-->
<local:SearchBoxControl x:Name="SearchBox"
HorizontalAlignment="Right"
VerticalAlignment="Top"
x:Load="False"
Closed="_CloseSearchBoxControl"
Search="_Search"
Visibility="Collapsed" />
</Grid>
<ScrollBar x:Name="ScrollBar"
Grid.Column="1"
HorizontalAlignment="Right"
VerticalAlignment="Stretch"
IndicatorMode="MouseIndicator"
IsTabStop="False"
LargeChange="4"
Maximum="1"
Orientation="Vertical"
PointerPressed="_CapturePointer"
PointerReleased="_ReleasePointerCapture"
SmallChange="1"
ValueChanged="_ScrollbarChangeHandler"
ViewportSize="10" />
<Grid x:Name="ScrollMarksGrid"
Grid.Column="1"
Width="{StaticResource ScrollBarSize}"
HorizontalAlignment="Right"
VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border Grid.Row="0"
Height="{StaticResource ScrollBarSize}" />
<Canvas x:Name="ScrollBarCanvas"
Grid.Row="1"
Width="{StaticResource ScrollBarSize}"
HorizontalAlignment="Right"
VerticalAlignment="Stretch" />
<Border Grid.Row="2"
Height="{StaticResource ScrollBarSize}" />
</Grid>
</Grid>
<!-- <local:TSFInputControl x:Name="TSFInputControl"
CompositionCompleted="_CompositionCompleted"
CurrentCursorPosition="_CurrentCursorPositionHandler"
CurrentFontInfo="_FontInfoHandler" />-->
<Grid x:Name="RendererFailedNotice"
HorizontalAlignment="Center"
VerticalAlignment="Center"
x:Load="False">
<Border Margin="8,8,8,8"
Padding="8,8,8,8"
Background="{ThemeResource SystemControlBackgroundAltHighBrush}"
BorderBrush="{ThemeResource SystemAccentColor}"
BorderThickness="2,2,2,2"
CornerRadius="{ThemeResource OverlayCornerRadius}">
<StackPanel>
<TextBlock x:Uid="TermControl_RendererFailedTextBlock"
HorizontalAlignment="Center"
TextWrapping="WrapWholeWords" />
<Button x:Uid="TermControl_RendererRetryButton"
HorizontalAlignment="Right" />
</StackPanel>
</Border>
</Grid>
</Grid>
</UserControl>

View File

@@ -125,6 +125,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
auto pfnPlayMidiNote = std::bind(&ControlCore::_terminalPlayMidiNote, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
_terminal->SetPlayMidiNoteCallback(pfnPlayMidiNote);
auto pfnMenuChanged = std::bind(&ControlCore::_terminalMenuChanged, this, std::placeholders::_1, std::placeholders::_2);
_terminal->MenuChangedCallback(pfnMenuChanged);
// MSFT 33353327: Initialize the renderer in the ctor instead of Initialize().
// We need the renderer to be ready to accept new engines before the SwapChainPanel is ready to go.
// If we wait, a screen reader may try to get the AutomationPeer (aka the UIA Engine), and we won't be able to attach
@@ -154,7 +157,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation
THROW_IF_FAILED(localPointerToThread->Initialize(_renderer.get()));
}
_setupDispatcherAndCallbacks();
UpdateSettings(settings, unfocusedAppearance);
}
void ControlCore::_setupDispatcherAndCallbacks()
{
// Get our dispatcher. If we're hosted in-proc with XAML, this will get
// us the same dispatcher as TermControl::Dispatcher(). If we're out of
// proc, this'll return null. We'll need to instead make a new
@@ -211,8 +220,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
core->_ScrollPositionChangedHandlers(*core, update);
}
});
UpdateSettings(settings, unfocusedAppearance);
}
ControlCore::~ControlCore()
@@ -225,6 +232,30 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
}
void ControlCore::Detach()
{
// Disable the renderer, so that it doesn't try to start any new frames
// for our engines while we're not attached to anything.
_renderer->WaitForPaintCompletionAndDisable(INFINITE);
_tsfTryRedrawCanvas.reset();
_updatePatternLocations.reset();
_updateScrollBar.reset();
}
void ControlCore::Reparent(const Microsoft::Terminal::Control::IKeyBindings& keyBindings)
{
_settings->KeyBindings(keyBindings);
_setupDispatcherAndCallbacks();
const auto actualNewSize = _actualFont.GetSize();
// Bubble this up, so our new control knows how big we want the font.
_FontSizeChangedHandlers(actualNewSize.width, actualNewSize.height, true);
// Turn the rendering back on now that we're ready to go.
_renderer->EnablePainting();
_AttachedHandlers(*this, nullptr);
}
bool ControlCore::Initialize(const double actualWidth,
const double actualHeight,
const double compositionScale)
@@ -574,7 +605,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// itself - it was initiated by the mouse wheel, or the scrollbar.
_terminal->UserScrollViewport(viewTop);
(*_updatePatternLocations)();
if (_updatePatternLocations)
{
(*_updatePatternLocations)();
}
}
void ControlCore::AdjustOpacity(const double adjustment)
@@ -971,18 +1005,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ControlCore::SizeChanged(const double width,
const double height)
{
// _refreshSizeUnderLock redraws the entire terminal.
// Don't call it if we don't have to.
if (_panelWidth == width && _panelHeight == height)
{
return;
}
_panelWidth = width;
_panelHeight = height;
auto lock = _terminal->LockForWriting();
_refreshSizeUnderLock();
SizeOrScaleChanged(width, height, _compositionScale);
}
void ControlCore::ScaleChanged(const double scale)
@@ -991,19 +1014,31 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
return;
}
SizeOrScaleChanged(_panelWidth, _panelHeight, scale);
}
void ControlCore::SizeOrScaleChanged(const double width,
const double height,
const double scale)
{
// _refreshSizeUnderLock redraws the entire terminal.
// Don't call it if we don't have to.
if (_compositionScale == scale)
if (_panelWidth == width && _panelHeight == height && _compositionScale == scale)
{
return;
}
const auto oldScale = _compositionScale;
_panelWidth = width;
_panelHeight = height;
_compositionScale = scale;
auto lock = _terminal->LockForWriting();
// _updateFont relies on the new _compositionScale set above
_updateFont();
if (oldScale != scale)
{
// _updateFont relies on the new _compositionScale set above
_updateFont();
}
_refreshSizeUnderLock();
}
@@ -1364,7 +1399,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
auto update{ winrt::make<ScrollPositionChangedArgs>(viewTop,
viewHeight,
bufferSize) };
if (!_inUnitTests)
if (!_inUnitTests && _updateScrollBar)
{
_updateScrollBar->Run(update);
}
@@ -1374,14 +1409,21 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
// Additionally, start the throttled update of where our links are.
(*_updatePatternLocations)();
if (_updatePatternLocations)
{
(*_updatePatternLocations)();
}
}
void ControlCore::_terminalCursorPositionChanged()
{
// When the buffer's cursor moves, start the throttled func to
// eventually dispatch a CursorPositionChanged event.
_tsfTryRedrawCanvas->Run();
if (_tsfTryRedrawCanvas)
{
_tsfTryRedrawCanvas->Run();
}
}
void ControlCore::_terminalTaskbarProgressChanged()
@@ -1409,7 +1451,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// The UI thread might try to acquire the console lock from time to time.
// --> Unlock it, so the UI doesn't hang while we're busy.
const auto suspension = _terminal->SuspendLock();
// This call will block for the duration, unless shutdown early.
_midiAudio.PlayNote(reinterpret_cast<HWND>(_owningHwnd), noteNumber, velocity, std::chrono::duration_cast<std::chrono::milliseconds>(duration));
}
@@ -1660,6 +1701,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// _renderer will always exist since it's introduced in the ctor
_renderer->AddRenderEngine(pEngine);
}
void ControlCore::DetachUiaEngine(::Microsoft::Console::Render::IRenderEngine* const pEngine)
{
_renderer->RemoveRenderEngine(pEngine);
}
bool ControlCore::IsInReadOnlyMode() const
{
@@ -1683,7 +1728,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_terminal->Write(hstr);
// Start the throttled update of where our hyperlinks are.
(*_updatePatternLocations)();
if (_updatePatternLocations)
{
(*_updatePatternLocations)();
}
}
catch (...)
{
@@ -1692,6 +1740,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
}
uint64_t ControlCore::SwapChainHandle() const
{
// This is called by:
// * TermControl::RenderEngineSwapChainChanged, who is only registered
// after Core::Initialize() is called.
// * TermControl::_InitializeTerminal, after the call to Initialize, for
// _AttachDxgiSwapChainToXaml.
// In both cases, we'll have a _renderEngine by then.
return _renderEngine ? reinterpret_cast<uint64_t>(_renderEngine->GetSwapChainHandle()) : 0u;
}
// Method Description:
// - Clear the contents of the buffer. The region cleared is given by
// clearType:
@@ -1750,6 +1809,41 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return hstring{ str };
}
// Get all of our recent commands. This will only really work if the user has enabled shell integration.
Control::CommandHistoryContext ControlCore::CommandHistory() const
{
auto terminalLock = _terminal->LockForWriting();
auto context = winrt::make_self<CommandHistoryContext>();
const auto& textBuffer = _terminal->GetTextBuffer();
for (const auto& mark : _terminal->GetScrollMarks())
{
// The command text is between the `end` (which denotes the end of
// the prompt) and the `commandEnd`.
bool markHasCommand = mark.commandEnd.has_value() &&
mark.commandEnd != mark.end;
if (!markHasCommand)
continue;
// Get the text of the command
const auto line = mark.end.y;
const auto& row = textBuffer.GetRowByOffset(line);
const auto rowText = row.GetText();
const auto commandText = rowText.substr(mark.end.x, mark.commandEnd->x);
// Trim off trailing spaces. In my experimentation, especially with
// cmd.exe, there was a lot of unexpected trailing whitespace.
// Probably from using autoMarkPrompts to mark the end of the
// commands
const auto strEnd = commandText.find_last_not_of(UNICODE_SPACE);
if (strEnd != std::string::npos)
{
const auto trimmed = commandText.substr(0, strEnd + 1);
context->History().Append(winrt::hstring{ trimmed });
}
}
return *context;
}
Core::Scheme ControlCore::ColorScheme() const noexcept
{
Core::Scheme s;
@@ -2097,6 +2191,120 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
}
void ControlCore::_terminalMenuChanged(std::wstring_view menuJson,
int32_t replaceLength)
{
auto args = winrt::make_self<MenuChangedEventArgs>(winrt::hstring{ menuJson },
replaceLength);
_MenuChangedHandlers(*this, *args);
}
void ControlCore::SelectCommand(const bool goUp)
{
const til::point start = HasSelection() ? (goUp ? _terminal->GetSelectionAnchor() : _terminal->GetSelectionEnd()) :
_terminal->GetTextBuffer().GetCursor().GetPosition();
std::optional<DispatchTypes::ScrollMark> nearest{ std::nullopt };
const auto& marks{ _terminal->GetScrollMarks() };
auto it = marks.rbegin();
const auto end = marks.rend();
for (; it != end; ++it)
{
const auto& m = *it;
// If this mark doesn't know anything about the position of its
// command, OR it does but thinks that it was empty, then just skip
// it.
if (!m.HasCommand())
{
continue;
}
// If this mark is before/after the start of our search in the
// buffer, ...
const auto inTheRightDirection = (goUp && (m.commandEnd < start)) || // prev
(!goUp && (m.end > start)); // next
// (If we're going down, we need to compare the end, not the
// commandEnd, to actually find the next one. Otherwise we'll just
// find the mark of the current selection again.
if (inTheRightDirection)
{
// ... and we either haven't found a match, or the current nearest
// is after/before this mark in the buffer
if (!nearest.has_value() ||
((goUp && (*m.commandEnd > *nearest->commandEnd)) || // prev
(!goUp && (m.end < *nearest->commandEnd)))) // next
{
// stash this as the new match
nearest = m;
}
}
}
if (nearest.has_value())
{
const auto start = nearest->end;
auto end = *nearest->commandEnd;
const auto bufferSize{ _terminal->GetTextBuffer().GetSize() };
bufferSize.DecrementInBounds(end);
auto lock = _terminal->LockForWriting();
_terminal->SelectNewRegion(start, end);
_renderer->TriggerSelection();
}
}
void ControlCore::SelectOutput(const bool goUp)
{
const til::point start = HasSelection() ? (goUp ? _terminal->GetSelectionAnchor() : _terminal->GetSelectionEnd()) :
_terminal->GetTextBuffer().GetCursor().GetPosition();
std::optional<DispatchTypes::ScrollMark> nearest{ std::nullopt };
const auto& marks{ _terminal->GetScrollMarks() };
auto it = marks.rbegin();
const auto end = marks.rend();
for (; it != end; ++it)
{
const auto& m = *it;
// If this mark doesn't know anything about the position of its
// output, OR it does but thinks that it was empty, then just skip
// it.
if (!m.HasOutput())
{
continue;
}
// If this mark is before/after the start of our search in the buffer, ...
const auto inTheRightDirection = (goUp && (m.outputEnd < start)) || // prev
(!goUp && (m.commandEnd > start)); // next
// (If we're going down, we need to compare the commandEnd, not the
// outputEnd, to actually find the next one. Otherwise we'll just
// find the mark of the current selection again.)
if (inTheRightDirection)
{
// .. and we either haven't found a match, or the current
// nearest is after this mark in the buffer
if (!nearest.has_value() ||
((goUp && (*m.outputEnd > *nearest->outputEnd)) || // prev
(!goUp && (*m.commandEnd < *nearest->outputEnd)))) // next
{
// stash this as the new match
nearest = m;
}
}
}
if (nearest.has_value())
{
const auto start = *nearest->commandEnd;
auto end = *nearest->outputEnd;
const auto bufferSize{ _terminal->GetTextBuffer().GetSize() };
bufferSize.DecrementInBounds(end);
auto lock = _terminal->LockForWriting();
_terminal->SelectNewRegion(start, end);
_renderer->TriggerSelection();
}
}
void ControlCore::ColorSelection(const Control::SelectionColor& fg, const Control::SelectionColor& bg, Core::MatchMode matchMode)
{
if (HasSelection())

View File

@@ -17,6 +17,7 @@
#include "ControlCore.g.h"
#include "SelectionColor.g.h"
#include "CommandHistoryContext.g.h"
#include "ControlSettings.h"
#include "../../audio/midi/MidiAudio.hpp"
#include "../../renderer/base/Renderer.hpp"
@@ -49,6 +50,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
WINRT_PROPERTY(til::color, Color);
WINRT_PROPERTY(bool, IsIndex16);
};
struct CommandHistoryContext : CommandHistoryContextT<CommandHistoryContext>
{
WINRT_PROPERTY(Windows::Foundation::Collections::IVector<winrt::hstring>, History, winrt::single_threaded_vector<winrt::hstring>());
WINRT_PROPERTY(winrt::hstring, CurrentCommandline);
};
struct ControlCore : ControlCoreT<ControlCore>
{
@@ -63,6 +69,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const double compositionScale);
void EnablePainting();
void Detach();
void UpdateSettings(const Control::IControlSettings& settings, const IControlAppearance& newAppearance);
void ApplyAppearance(const bool& focused);
Control::IControlSettings Settings() { return *_settings; };
@@ -73,8 +81,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
winrt::Microsoft::Terminal::Core::Scheme ColorScheme() const noexcept;
void ColorScheme(const winrt::Microsoft::Terminal::Core::Scheme& scheme);
uint64_t SwapChainHandle() const;
void Reparent(const Microsoft::Terminal::Control::IKeyBindings& keyBindings);
void SizeChanged(const double width, const double height);
void ScaleChanged(const double scale);
void SizeOrScaleChanged(const double width, const double height, const double scale);
void AdjustFontSize(float fontSizeDelta);
void ResetFontSize();
@@ -140,7 +152,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ClearMark();
void ClearAllMarks();
void ScrollToMark(const Control::ScrollToMarkDirection& direction);
void SelectCommand(const bool goUp);
void SelectOutput(const bool goUp);
#pragma endregion
#pragma region ITerminalInput
@@ -190,11 +203,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation
bool& selectionNeedsToBeCopied);
void AttachUiaEngine(::Microsoft::Console::Render::IRenderEngine* const pEngine);
void DetachUiaEngine(::Microsoft::Console::Render::IRenderEngine* const pEngine);
bool IsInReadOnlyMode() const;
void ToggleReadOnlyMode();
hstring ReadEntireBuffer() const;
Control::CommandHistoryContext CommandHistory() const;
static bool IsVintageOpacityAvailable() noexcept;
@@ -232,7 +247,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
TYPED_EVENT(ShowWindowChanged, IInspectable, Control::ShowWindowArgs);
TYPED_EVENT(UpdateSelectionMarkers, IInspectable, Control::UpdateSelectionMarkersEventArgs);
TYPED_EVENT(OpenHyperlink, IInspectable, Control::OpenHyperlinkEventArgs);
TYPED_EVENT(MenuChanged, IInspectable, Control::MenuChangedEventArgs);
TYPED_EVENT(CloseTerminalRequested, IInspectable, IInspectable);
TYPED_EVENT(Attached, IInspectable, IInspectable);
// clang-format on
private:
@@ -285,6 +304,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
std::unique_ptr<til::throttled_func_trailing<>> _updatePatternLocations;
std::shared_ptr<ThrottledFuncTrailing<Control::ScrollPositionChangedArgs>> _updateScrollBar;
void _setupDispatcherAndCallbacks();
bool _setFontSizeUnderLock(float fontSize);
void _updateFont(const bool initialUpdate = false);
void _refreshSizeUnderLock();
@@ -307,6 +328,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void _terminalPlayMidiNote(const int noteNumber,
const int velocity,
const std::chrono::microseconds duration);
void _terminalMenuChanged(std::wstring_view menuJson, int32_t replaceLength);
#pragma endregion
MidiAudio _midiAudio;

View File

@@ -58,6 +58,12 @@ namespace Microsoft.Terminal.Control
Boolean IsIndex16;
};
[default_interface] runtimeclass CommandHistoryContext
{
IVector<String> History;
String CurrentCommandline;
};
[default_interface] runtimeclass ControlCore : ICoreState
{
ControlCore(IControlSettings settings,
@@ -76,6 +82,8 @@ namespace Microsoft.Terminal.Control
IControlAppearance UnfocusedAppearance { get; };
Boolean HasUnfocusedAppearance();
UInt64 SwapChainHandle { get; };
Windows.Foundation.Size FontSize { get; };
String FontFaceName { get; };
UInt16 FontWeight { get; };
@@ -108,6 +116,7 @@ namespace Microsoft.Terminal.Control
void AdjustFontSize(Single fontSizeDelta);
void SizeChanged(Double width, Double height);
void ScaleChanged(Double scale);
void SizeOrScaleChanged(Double width, Double height, Double scale);
void ToggleShaderEffects();
void ToggleReadOnlyMode();
@@ -126,13 +135,13 @@ namespace Microsoft.Terminal.Control
String HoveredUriText { get; };
Windows.Foundation.IReference<Microsoft.Terminal.Core.Point> HoveredCell { get; };
void Close();
void BlinkCursor();
Boolean IsInReadOnlyMode { get; };
Boolean CursorOn;
void EnablePainting();
String ReadEntireBuffer();
CommandHistoryContext CommandHistory();
void AdjustOpacity(Double Opacity, Boolean relative);
void WindowVisibilityChanged(Boolean showOrHide);
@@ -162,5 +171,9 @@ namespace Microsoft.Terminal.Control
event Windows.Foundation.TypedEventHandler<Object, UpdateSelectionMarkersEventArgs> UpdateSelectionMarkers;
event Windows.Foundation.TypedEventHandler<Object, OpenHyperlinkEventArgs> OpenHyperlink;
event Windows.Foundation.TypedEventHandler<Object, Object> CloseTerminalRequested;
event Windows.Foundation.TypedEventHandler<Object, MenuChangedEventArgs> MenuChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> Attached;
};
}

View File

@@ -44,7 +44,48 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_lastMouseClickPos{},
_selectionNeedsToBeCopied{ false }
{
_guid = ::Microsoft::Console::Utils::CreateGuid();
_core = winrt::make_self<ControlCore>(settings, unfocusedAppearance, connection);
_core->Attached([weakThis = get_weak()](auto&&, auto&&) {
if (auto self{ weakThis.get() })
{
self->_AttachedHandlers(*self, nullptr);
}
});
}
winrt::guid ControlInteractivity::Id()
{
return _guid;
}
void ControlInteractivity::Detach()
{
if (_uiaEngine)
{
// There's a potential race here where we've removed the TermControl
// from the UI tree, but the UIA engine is in the middle of a paint,
// and the UIA engine will try to dispatch to the
// TermControlAutomationPeer, which (is now)/(will very soon be) gone.
//
// To alleviate, make sure to disable the UIA engine and remove it,
// and ALSO disable the renderer. Core.Detach will take care of the
// WaitForPaintCompletionAndDisable (which will stop the renderer
// after all current engines are done painting).
//
// Simply disabling the UIA engine is not enough, because it's
// possible that it had already started presenting here.
LOG_IF_FAILED(_uiaEngine->Disable());
_core->DetachUiaEngine(_uiaEngine.get());
}
_core->Detach();
}
void ControlInteractivity::Reparent(const Microsoft::Terminal::Control::IKeyBindings& keyBindings)
{
_core->Reparent(keyBindings);
}
// Method Description:
@@ -73,6 +114,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return *_core;
}
void ControlInteractivity::Close()
{
_ClosedHandlers(*this, nullptr);
if (_core)
{
_core->Close();
}
}
// Method Description:
// - Returns the number of clicks that occurred (double and triple click support).
// Every call to this function registers a click.
@@ -205,7 +255,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// GH#9396: we prioritize hyper-link over VT mouse events
auto hyperlink = _core->GetHyperlink(terminalPosition.to_core_point());
if (WI_IsFlagSet(buttonState, MouseButtonState::IsLeftButtonDown) &&
ctrlEnabled && !hyperlink.empty())
ctrlEnabled &&
!hyperlink.empty())
{
const auto clickCount = _numberOfClicks(pixelPosition, timestamp);
// Handle hyper-link only on the first click to prevent multiple activations
@@ -255,14 +306,22 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
else if (WI_IsFlagSet(buttonState, MouseButtonState::IsRightButtonDown))
{
// Try to copy the text and clear the selection
const auto successfulCopy = CopySelectionToClipboard(shiftEnabled, nullptr);
_core->ClearSelection();
if (_core->CopyOnSelect() || !successfulCopy)
if (_core->Settings().RightClickContextMenu())
{
// CopyOnSelect: right click always pastes!
// Otherwise: no selection --> paste
RequestPasteTextFromClipboard();
auto contextArgs = winrt::make<ContextMenuRequestedEventArgs>(til::point{ pixelPosition }.to_winrt_point());
_ContextMenuRequestedHandlers(*this, contextArgs);
}
else
{
// Try to copy the text and clear the selection
const auto successfulCopy = CopySelectionToClipboard(shiftEnabled, nullptr);
_core->ClearSelection();
if (_core->CopyOnSelect() || !successfulCopy)
{
// CopyOnSelect: right click always pastes!
// Otherwise: no selection --> paste
RequestPasteTextFromClipboard();
}
}
}
}
@@ -649,7 +708,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
try
{
const auto autoPeer = winrt::make_self<implementation::InteractivityAutomationPeer>(this);
if (_uiaEngine)
{
_core->DetachUiaEngine(_uiaEngine.get());
}
_uiaEngine = std::make_unique<::Microsoft::Console::Render::UiaEngine>(autoPeer.get());
_core->AttachUiaEngine(_uiaEngine.get());
return *autoPeer;

View File

@@ -44,6 +44,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void Initialize();
Control::ControlCore Core();
void Close();
void Detach();
Control::InteractivityAutomationPeer OnCreateAutomationPeer();
::Microsoft::Console::Render::IRenderData* GetRenderData() const;
@@ -85,9 +88,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void SetEndSelectionPoint(const Core::Point pixelPosition);
bool ManglePathsForWsl();
winrt::guid Id();
void Reparent(const Microsoft::Terminal::Control::IKeyBindings& keyBindings);
TYPED_EVENT(OpenHyperlink, IInspectable, Control::OpenHyperlinkEventArgs);
TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs);
TYPED_EVENT(ScrollPositionChanged, IInspectable, Control::ScrollPositionChangedArgs);
TYPED_EVENT(ContextMenuRequested, IInspectable, Control::ContextMenuRequestedEventArgs);
TYPED_EVENT(Attached, IInspectable, IInspectable);
TYPED_EVENT(Closed, IInspectable, IInspectable);
private:
// NOTE: _uiaEngine must be ordered before _core.
@@ -129,6 +139,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
std::optional<interval_tree::IntervalTree<til::point, size_t>::interval> _lastHoveredInterval{ std::nullopt };
winrt::guid _guid;
unsigned int _numberOfClicks(Core::Point clickPos, Timestamp clickTime);
void _updateSystemParameterSettings() noexcept;

View File

@@ -23,6 +23,12 @@ namespace Microsoft.Terminal.Control
void GotFocus();
void LostFocus();
Guid Id { get; };
void Reparent(Microsoft.Terminal.Control.IKeyBindings keyBindings);
void Detach();
void Close();
InteractivityAutomationPeer OnCreateAutomationPeer();
Boolean CopySelectionToClipboard(Boolean singleLine, Windows.Foundation.IReference<CopyFormat> formats);
@@ -65,6 +71,11 @@ namespace Microsoft.Terminal.Control
event Windows.Foundation.TypedEventHandler<Object, ScrollPositionChangedArgs> ScrollPositionChanged;
event Windows.Foundation.TypedEventHandler<Object, PasteFromClipboardEventArgs> PasteFromClipboard;
// Used to communicate to the TermControl, but not necessarily higher up in the stack
event Windows.Foundation.TypedEventHandler<Object, ContextMenuRequestedEventArgs> ContextMenuRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> Closed;
event Windows.Foundation.TypedEventHandler<Object, Object> Attached;
};
}

View File

@@ -5,6 +5,7 @@
#include "EventArgs.h"
#include "TitleChangedEventArgs.g.cpp"
#include "CopyToClipboardEventArgs.g.cpp"
#include "ContextMenuRequestedEventArgs.g.cpp"
#include "PasteFromClipboardEventArgs.g.cpp"
#include "OpenHyperlinkEventArgs.g.cpp"
#include "NoticeEventArgs.g.cpp"
@@ -14,3 +15,4 @@
#include "FoundResultsArgs.g.cpp"
#include "ShowWindowArgs.g.cpp"
#include "UpdateSelectionMarkersEventArgs.g.cpp"
#include "MenuChangedEventArgs.g.cpp"

View File

@@ -5,6 +5,7 @@
#include "TitleChangedEventArgs.g.h"
#include "CopyToClipboardEventArgs.g.h"
#include "ContextMenuRequestedEventArgs.g.h"
#include "PasteFromClipboardEventArgs.g.h"
#include "OpenHyperlinkEventArgs.g.h"
#include "NoticeEventArgs.g.h"
@@ -14,6 +15,7 @@
#include "FoundResultsArgs.g.h"
#include "ShowWindowArgs.g.h"
#include "UpdateSelectionMarkersEventArgs.g.h"
#include "MenuChangedEventArgs.g.h"
namespace winrt::Microsoft::Terminal::Control::implementation
{
@@ -53,6 +55,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
Windows::Foundation::IReference<CopyFormat> _formats;
};
struct ContextMenuRequestedEventArgs : public ContextMenuRequestedEventArgsT<ContextMenuRequestedEventArgs>
{
public:
ContextMenuRequestedEventArgs(winrt::Windows::Foundation::Point point) :
_Point(point) {}
WINRT_PROPERTY(winrt::Windows::Foundation::Point, Point);
};
struct PasteFromClipboardEventArgs : public PasteFromClipboardEventArgsT<PasteFromClipboardEventArgs>
{
public:
@@ -169,4 +180,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation
WINRT_PROPERTY(bool, ClearMarkers, false);
};
struct MenuChangedEventArgs : public MenuChangedEventArgsT<MenuChangedEventArgs>
{
public:
MenuChangedEventArgs(const winrt::hstring menuJson, const int32_t replaceLength) :
_MenuJson(menuJson),
_ReplacementLength(replaceLength)
{
}
WINRT_PROPERTY(winrt::hstring, MenuJson, L"");
WINRT_PROPERTY(int32_t, ReplacementLength, 0);
};
}

View File

@@ -22,6 +22,11 @@ namespace Microsoft.Terminal.Control
Windows.Foundation.IReference<CopyFormat> Formats { get; };
}
runtimeclass ContextMenuRequestedEventArgs
{
Windows.Foundation.Point Point { get; };
}
runtimeclass TitleChangedEventArgs
{
String Title;
@@ -83,4 +88,10 @@ namespace Microsoft.Terminal.Control
{
Boolean ClearMarkers { get; };
}
runtimeclass MenuChangedEventArgs
{
String MenuJson { get; };
Int32 ReplacementLength { get; };
}
}

View File

@@ -61,5 +61,6 @@ namespace Microsoft.Terminal.Control
Boolean SoftwareRendering { get; };
Boolean ShowMarks { get; };
Boolean UseBackgroundImageForWindow { get; };
Boolean RightClickContextMenu { get; };
};
}

View File

@@ -57,6 +57,8 @@ namespace Microsoft.Terminal.Control
void ClearMark();
void ClearAllMarks();
void ScrollToMark(ScrollToMarkDirection direction);
void SelectCommand(Boolean goUp);
void SelectOutput(Boolean goUp);
IVector<ScrollMark> ScrollMarks { get; };
};

View File

@@ -208,4 +208,36 @@ Please either install the missing font or choose another one.</value>
<value>No results found</value>
<comment>Announced to a screen reader when the user searches for some text and there are no matches for that text in the terminal.</comment>
</data>
</root>
<data name="PasteCommandButton.Label" xml:space="preserve">
<value>Paste</value>
<comment>The label of a button for pasting the contents of the clipboard.</comment>
</data>
<data name="PasteCommandButton.ToolTipService.ToolTip" xml:space="preserve">
<value>Paste</value>
<comment>The tooltip for a paste button</comment>
</data>
<data name="CopyCommandButton.Label" xml:space="preserve">
<value>Copy</value>
<comment>The label of a button for copying the selected text to the clipboard.</comment>
</data>
<data name="CopyCommandButton.ToolTipService.ToolTip" xml:space="preserve">
<value>Copy</value>
<comment>The tooltip for a copy button</comment>
</data>
<data name="PasteWithSelectionCommandButton.Label" xml:space="preserve">
<value>Paste</value>
<comment>The label of a button for pasting the contents of the clipboard.</comment>
</data>
<data name="PasteWithSelectionCommandButton.ToolTipService.ToolTip" xml:space="preserve">
<value>Paste</value>
<comment>The tooltip for a paste button</comment>
</data>
<data name="SearchCommandButton.Label" xml:space="preserve">
<value>Find...</value>
<comment>The label of a button for searching for the selected text</comment>
</data>
<data name="SearchCommandButton.ToolTipService.ToolTip" xml:space="preserve">
<value>Find</value>
<comment>The tooltip for a button for searching for the selected text</comment>
</data>
</root>

View File

@@ -14,8 +14,6 @@ Author(s):
--*/
#pragma once
#include "winrt/Windows.UI.Xaml.h"
#include "winrt/Windows.UI.Xaml.Controls.h"
#include "SearchBoxControl.g.h"

View File

@@ -437,4 +437,26 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void TSFInputControl::_formatUpdatingHandler(CoreTextEditContext sender, const CoreTextFormatUpdatingEventArgs& /*args*/)
{
}
void TSFInputControl::ManuallyDisplayText(const winrt::hstring& text)
{
_focused = !text.empty();
Canvas().Visibility(text.empty() ? Visibility::Collapsed : Visibility::Visible);
_inputBuffer.clear();
_activeTextStart = 0;
_inComposition = false;
// HACK trim off leading DEL chars.
std::wstring_view view{ text.c_str() };
const auto strBegin = view.find_first_not_of(L"\x7f");
if (strBegin != std::wstring::npos)
{
view = view.substr(strBegin * 2);
}
TextBlock().Text(winrt::hstring{ view });
TextBlock().UpdateLayout();
TryRedrawCanvas();
}
}

View File

@@ -40,6 +40,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ClearBuffer();
void TryRedrawCanvas();
void ManuallyDisplayText(const winrt::hstring& text);
void Close();
// -------------------------------- WinRT Events ---------------------------------

View File

@@ -31,6 +31,9 @@ namespace Microsoft.Terminal.Control
void ClearBuffer();
void TryRedrawCanvas();
void ManuallyDisplayText(String text);
void Close();
}
}

View File

@@ -50,6 +50,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
TermControl::TermControl(IControlSettings settings,
Control::IControlAppearance unfocusedAppearance,
TerminalConnection::ITerminalConnection connection) :
TermControl{ winrt::make<implementation::ControlInteractivity>(settings, unfocusedAppearance, connection) }
{
}
TermControl::TermControl(Control::ControlInteractivity content) :
_interactivity{ content },
_isInternalScrollBarUpdate{ false },
_autoScrollVelocity{ 0 },
_autoScrollingPointerPoint{ std::nullopt },
@@ -61,32 +67,38 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
InitializeComponent();
_interactivity = winrt::make<implementation::ControlInteractivity>(settings, unfocusedAppearance, connection);
_core = _interactivity.Core();
// These events might all be triggered by the connection, but that
// should be drained and closed before we complete destruction. So these
// are safe.
_core.ScrollPositionChanged({ this, &TermControl::_ScrollPositionChanged });
_core.WarningBell({ this, &TermControl::_coreWarningBell });
_core.CursorPositionChanged({ this, &TermControl::_CursorPositionChanged });
// This event is specifically triggered by the renderer thread, a BG thread. Use a weak ref here.
_core.RendererEnteredErrorState({ get_weak(), &TermControl::_RendererEnteredErrorState });
_revokers.RendererEnteredErrorState = _core.RendererEnteredErrorState(winrt::auto_revoke, { get_weak(), &TermControl::_RendererEnteredErrorState });
// These callbacks can only really be triggered by UI interactions. So
// they don't need weak refs - they can't be triggered unless we're
// alive.
_core.BackgroundColorChanged({ this, &TermControl::_coreBackgroundColorChanged });
_core.FontSizeChanged({ this, &TermControl::_coreFontSizeChanged });
_core.TransparencyChanged({ this, &TermControl::_coreTransparencyChanged });
_core.RaiseNotice({ this, &TermControl::_coreRaisedNotice });
_core.HoveredHyperlinkChanged({ this, &TermControl::_hoveredHyperlinkChanged });
_core.FoundMatch({ this, &TermControl::_coreFoundMatch });
_core.UpdateSelectionMarkers({ this, &TermControl::_updateSelectionMarkers });
_core.OpenHyperlink({ this, &TermControl::_HyperlinkHandler });
_interactivity.OpenHyperlink({ this, &TermControl::_HyperlinkHandler });
_interactivity.ScrollPositionChanged({ this, &TermControl::_ScrollPositionChanged });
_revokers.BackgroundColorChanged = _core.BackgroundColorChanged(winrt::auto_revoke, { get_weak(), &TermControl::_coreBackgroundColorChanged });
_revokers.FontSizeChanged = _core.FontSizeChanged(winrt::auto_revoke, { get_weak(), &TermControl::_coreFontSizeChanged });
_revokers.TransparencyChanged = _core.TransparencyChanged(winrt::auto_revoke, { get_weak(), &TermControl::_coreTransparencyChanged });
_revokers.RaiseNotice = _core.RaiseNotice(winrt::auto_revoke, { get_weak(), &TermControl::_coreRaisedNotice });
_revokers.HoveredHyperlinkChanged = _core.HoveredHyperlinkChanged(winrt::auto_revoke, { get_weak(), &TermControl::_hoveredHyperlinkChanged });
_revokers.FoundMatch = _core.FoundMatch(winrt::auto_revoke, { get_weak(), &TermControl::_coreFoundMatch });
_revokers.UpdateSelectionMarkers = _core.UpdateSelectionMarkers(winrt::auto_revoke, { get_weak(), &TermControl::_updateSelectionMarkers });
_revokers.coreOpenHyperlink = _core.OpenHyperlink(winrt::auto_revoke, { get_weak(), &TermControl::_HyperlinkHandler });
_revokers.interactivityOpenHyperlink = _interactivity.OpenHyperlink(winrt::auto_revoke, { get_weak(), &TermControl::_HyperlinkHandler });
_revokers.interactivityScrollPositionChanged = _interactivity.ScrollPositionChanged(winrt::auto_revoke, { get_weak(), &TermControl::_ScrollPositionChanged });
// "Bubbled" events - ones we want to handle, by raising our own event.
_revokers.CopyToClipboard = _core.CopyToClipboard(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleCopyToClipboard });
_revokers.TitleChanged = _core.TitleChanged(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleTitleChanged });
_revokers.TabColorChanged = _core.TabColorChanged(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleTabColorChanged });
_revokers.TaskbarProgressChanged = _core.TaskbarProgressChanged(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleSetTaskbarProgress });
_revokers.ConnectionStateChanged = _core.ConnectionStateChanged(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleConnectionStateChanged });
_revokers.ShowWindowChanged = _core.ShowWindowChanged(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleShowWindowChanged });
_revokers.CloseTerminalRequested = _core.CloseTerminalRequested(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleCloseTerminalRequested });
_revokers.MenuChanged = _core.MenuChanged(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleMenuChanged });
_revokers.PasteFromClipboard = _interactivity.PasteFromClipboard(winrt::auto_revoke, { get_weak(), &TermControl::_bubblePasteFromClipboard });
_interactivity.ContextMenuRequested({ this, &TermControl::_contextMenuHandler });
// Initialize the terminal only once the swapchainpanel is loaded - that
// way, we'll be able to query the real pixel size it got on layout
@@ -95,8 +107,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// in any layout change chain. That gives us great flexibility in finding the right point
// at which to initialize our renderer (and our terminal).
// Any earlier than the last layout update and we may not know the terminal's starting size.
if (_InitializeTerminal())
if (_InitializeTerminal(false))
{
// Only let this succeed once.
_layoutUpdatedRevoker.revoke();
@@ -130,11 +141,101 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
});
// These events might all be triggered by the connection, but that
// should be drained and closed before we complete destruction. So these
// are safe.
//
// NOTE: _ScrollPositionChanged has to be registered after we set up the
// _updateScrollBar func. Otherwise, we could get a callback from an
// attached content before we set up the throttled func, and that'll A/V
_revokers.coreScrollPositionChanged = _core.ScrollPositionChanged(winrt::auto_revoke, { get_weak(), &TermControl::_ScrollPositionChanged });
_revokers.WarningBell = _core.WarningBell(winrt::auto_revoke, { get_weak(), &TermControl::_coreWarningBell });
_revokers.CursorPositionChanged = _core.CursorPositionChanged(winrt::auto_revoke, { get_weak(), &TermControl::_CursorPositionChanged });
static constexpr auto AutoScrollUpdateInterval = std::chrono::microseconds(static_cast<int>(1.0 / 30.0 * 1000000));
_autoScrollTimer.Interval(AutoScrollUpdateInterval);
_autoScrollTimer.Tick({ this, &TermControl::_UpdateAutoScroll });
_autoScrollTimer.Tick({ get_weak(), &TermControl::_UpdateAutoScroll });
_ApplyUISettings();
_originalPrimaryElements = winrt::single_threaded_observable_vector<Controls::ICommandBarElement>();
_originalSecondaryElements = winrt::single_threaded_observable_vector<Controls::ICommandBarElement>();
_originalSelectedPrimaryElements = winrt::single_threaded_observable_vector<Controls::ICommandBarElement>();
_originalSelectedSecondaryElements = winrt::single_threaded_observable_vector<Controls::ICommandBarElement>();
for (const auto& e : ContextMenu().PrimaryCommands())
{
_originalPrimaryElements.Append(e);
}
for (const auto& e : ContextMenu().SecondaryCommands())
{
_originalSecondaryElements.Append(e);
}
for (const auto& e : SelectionContextMenu().PrimaryCommands())
{
_originalSelectedPrimaryElements.Append(e);
}
for (const auto& e : SelectionContextMenu().SecondaryCommands())
{
_originalSelectedSecondaryElements.Append(e);
}
ContextMenu().Closed([weakThis = get_weak()](auto&&, auto&&) {
if (auto control{ weakThis.get() }; !control->_IsClosing())
{
control->ContextMenu().PrimaryCommands().Clear();
control->ContextMenu().SecondaryCommands().Clear();
for (const auto& e : control->_originalPrimaryElements)
{
control->ContextMenu().PrimaryCommands().Append(e);
}
for (const auto& e : control->_originalSecondaryElements)
{
control->ContextMenu().SecondaryCommands().Append(e);
}
}
});
SelectionContextMenu().Closed([weakThis = get_weak()](auto&&, auto&&) {
if (auto control{ weakThis.get() }; !control->_IsClosing())
{
control->SelectionContextMenu().PrimaryCommands().Clear();
control->SelectionContextMenu().SecondaryCommands().Clear();
for (const auto& e : control->_originalSelectedPrimaryElements)
{
control->SelectionContextMenu().PrimaryCommands().Append(e);
}
for (const auto& e : control->_originalSelectedSecondaryElements)
{
control->SelectionContextMenu().SecondaryCommands().Append(e);
}
}
});
}
Control::TermControl TermControl::AttachContent(Control::ControlInteractivity content, const Microsoft::Terminal::Control::IKeyBindings& keyBindings)
{
const auto term{ winrt::make_self<TermControl>(content) };
term->_AttachDxgiSwapChainToXaml(reinterpret_cast<HANDLE>(term->_core.SwapChainHandle()));
content.Reparent(keyBindings);
// Initialize the terminal only once the swapchainpanel is loaded - that
// way, we'll be able to query the real pixel size it got on layout
auto r = term->SwapChainPanel().LayoutUpdated(winrt::auto_revoke, [term](auto /*s*/, auto /*e*/) {
// Replace the normal initialize routine with one that will allow up
// to complete initialization even though the Core was already
// initialized.
if (term->_InitializeTerminal(true))
{
// Only let this succeed once.
term->_layoutUpdatedRevoker.revoke();
}
});
term->_layoutUpdatedRevoker.swap(r);
return *term;
}
winrt::guid TermControl::ContentGuid() const
{
return _interactivity.Id();
}
void TermControl::_throttledUpdateScrollbar(const ScrollBarUpdate& update)
@@ -292,7 +393,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - Given Settings having been updated, applies the settings to the current terminal.
// Return Value:
// - <none>
winrt::fire_and_forget TermControl::UpdateControlSettings(IControlSettings settings, IControlAppearance unfocusedAppearance)
winrt::fire_and_forget TermControl::UpdateControlSettings(IControlSettings settings,
IControlAppearance unfocusedAppearance)
{
auto weakThis{ get_weak() };
@@ -388,6 +490,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - <none>
void TermControl::SendInput(const winrt::hstring& wstr)
{
PreviewInput(L"");
_core.SendInput(wstr);
}
void TermControl::ClearBuffer(Control::ClearBufferType clearType)
@@ -419,6 +522,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// settings might be out-of-proc in the future
auto settings{ _core.Settings() };
_useRightClickContextMenu = settings.RightClickContextMenu();
// Apply padding as swapChainPanel's margin
const auto newMargin = ParseThicknessFromPadding(settings.Padding());
SwapChainPanel().Margin(newMargin);
@@ -808,7 +913,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
nativePanel->SetSwapChainHandle(swapChainHandle);
}
bool TermControl::_InitializeTerminal()
bool TermControl::_InitializeTerminal(const bool reattach)
{
if (_initializedTerminal)
{
@@ -832,18 +937,26 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// after Enable, then it'll be possible to paint the frame once
// _before_ the warning handler is set up, and then warnings from
// the first paint will be ignored!
_core.RendererWarning({ get_weak(), &TermControl::_RendererWarning });
_revokers.RendererWarning = _core.RendererWarning(winrt::auto_revoke, { get_weak(), &TermControl::_RendererWarning });
const auto coreInitialized = _core.Initialize(panelWidth,
panelHeight,
panelScaleX);
if (!coreInitialized)
// If we're re-attaching an existing content, then we want to proceed even though the Terminal was already initialized.
if (!reattach)
{
return false;
const auto coreInitialized = _core.Initialize(panelWidth,
panelHeight,
panelScaleX);
if (!coreInitialized)
{
return false;
}
_interactivity.Initialize();
}
else
{
_core.SizeOrScaleChanged(panelWidth, panelHeight, panelScaleX);
}
_interactivity.Initialize();
_core.SwapChainChanged({ get_weak(), &TermControl::RenderEngineSwapChainChanged });
_revokers.SwapChainChanged = _core.SwapChainChanged(winrt::auto_revoke, { get_weak(), &TermControl::RenderEngineSwapChainChanged });
_core.EnablePainting();
auto bufferHeight = _core.BufferHeight();
@@ -1144,12 +1257,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return true;
}
// TODO: GH#5000
// The Core owning the keybindings is weird. That's for sure. In the
// future, we may want to pass the keybindings into the control
// separately, so the control can have a pointer to an in-proc
// Keybindings object, rather than routing through the ControlCore.
// (see GH#5000)
auto bindings = _core.Settings().KeyBindings();
if (!bindings)
{
@@ -1998,13 +2105,24 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_RestorePointerCursorHandlers(*this, nullptr);
_revokers = {};
// Disconnect the TSF input control so it doesn't receive EditContext events.
TSFInputControl().Close();
_autoScrollTimer.Stop();
_core.Close();
if (!_detached)
{
_interactivity.Close();
}
}
}
void TermControl::Detach()
{
_revokers = {};
_interactivity.Detach();
_detached = true;
}
// Method Description:
// - Scrolls the viewport of the terminal and updates the scroll bar accordingly
@@ -2741,18 +2859,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
IControlSettings TermControl::Settings() const
{
// TODO: GH#5000
// We still need this in a couple places:
// - Pane.cpp uses this for parsing out the StartingTitle, Commandline,
// etc for Pane::GetTerminalArgsForPane.
// - TerminalTab::_CreateToolTipTitle uses the ProfileName for the
// tooltip for the tab.
//
// These both happen on the UI thread right now. In the future, when we
// have to hop across the process boundary to get at the core settings,
// it may make sense to cache these values inside the TermControl
// itself, so it can do the hop once when it's first setup, rather than
// when it's needed by the UI thread.
return _core.Settings();
}
@@ -2939,9 +3045,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// Ensure the marker is oriented properly
// (i.e. if start is at the beginning of the buffer, it should be flipped)
auto transform{ marker.RenderTransform().as<Windows::UI::Xaml::Media::ScaleTransform>() };
transform.ScaleX(std::abs(transform.ScaleX()) * (flipMarker ? -1.0 : 1.0));
marker.RenderTransform(transform);
//
// Note - This RenderTransform might not be a
// ScaleTransform, if we haven't had a _coreFontSizeChanged
// handled yet, because that's the first place we set the
// RenderTransform
if (const auto& transform{ marker.RenderTransform().try_as<Windows::UI::Xaml::Media::ScaleTransform>() })
{
transform.ScaleX(std::abs(transform.ScaleX()) * (flipMarker ? -1.0 : 1.0));
marker.RenderTransform(transform);
}
// Compute the location of the top left corner of the cell in DIPS
auto terminalPos{ targetEnd ? markerData.EndPos : markerData.StartPos };
@@ -3099,6 +3212,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
return _core.ReadEntireBuffer();
}
Control::CommandHistoryContext TermControl::CommandHistory() const
{
return _core.CommandHistory();
}
Core::Scheme TermControl::ColorScheme() const noexcept
{
@@ -3157,6 +3274,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return _core.OwningHwnd();
}
void TermControl::PreviewInput(const winrt::hstring& text)
{
TSFInputControl().ManuallyDisplayText(text);
}
void TermControl::AddMark(const Control::ScrollMark& mark)
{
_core.AddMark(mark);
@@ -3170,8 +3292,89 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return _core.ScrollMarks();
}
void TermControl::SelectCommand(const bool goUp)
{
_core.SelectCommand(goUp);
}
void TermControl::SelectOutput(const bool goUp)
{
_core.SelectOutput(goUp);
}
void TermControl::ColorSelection(Control::SelectionColor fg, Control::SelectionColor bg, Core::MatchMode matchMode)
{
_core.ColorSelection(fg, bg, matchMode);
}
// Returns the text cursor's position relative to our origin, in DIPs.
Microsoft::Terminal::Core::Point TermControl::CursorPositionInDips()
{
const til::point cursorPos{ _core.CursorPosition() };
const til::size fontSize{ til::math::flooring, CharacterDimensions() };
// Convert text buffer cursor position to client coordinate position
// within the window. This point is in _pixels_
const til::point clientCursorPos{ cursorPos * fontSize };
// Get scale factor for view
const double scaleFactor = DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel();
const til::point clientCursorInDips{ til::math::flooring, clientCursorPos.x / scaleFactor, clientCursorPos.y / scaleFactor };
auto padding{ GetPadding() };
til::point relativeToOrigin{ til::math::flooring,
clientCursorInDips.x + padding.Left,
clientCursorInDips.y + padding.Top };
return relativeToOrigin.to_core_point();
}
void TermControl::_contextMenuHandler(IInspectable /*sender*/,
Control::ContextMenuRequestedEventArgs args)
{
Controls::Primitives::FlyoutShowOptions myOption{};
myOption.ShowMode(Controls::Primitives::FlyoutShowMode::Standard);
myOption.Placement(Controls::Primitives::FlyoutPlacementMode::TopEdgeAlignedLeft);
// Position the menu where the pointer is. This was the best way I found how.
const til::point absolutePointerPos{ til::math::rounding, CoreWindow::GetForCurrentThread().PointerPosition() };
const til::point absoluteWindowOrigin{ til::math::rounding,
CoreWindow::GetForCurrentThread().Bounds().X,
CoreWindow::GetForCurrentThread().Bounds().Y };
// Get the offset (margin + tabs, etc..) of the control within the window
const til::point controlOrigin{ til::math::flooring,
this->TransformToVisual(nullptr).TransformPoint(Windows::Foundation::Point(0, 0)) };
const auto pos = (absolutePointerPos - absoluteWindowOrigin - controlOrigin).to_winrt_point();
myOption.Position(pos);
(_core.HasSelection() ? SelectionContextMenu() :
ContextMenu())
.ShowAt(*this, myOption);
}
void TermControl::_PasteCommandHandler(const IInspectable& /*sender*/,
const IInspectable& /*args*/)
{
_interactivity.RequestPasteTextFromClipboard();
ContextMenu().Hide();
SelectionContextMenu().Hide();
}
void TermControl::_CopyCommandHandler(const IInspectable& /*sender*/,
const IInspectable& /*args*/)
{
// formats = nullptr -> copy all formats
_interactivity.CopySelectionToClipboard(false, nullptr);
ContextMenu().Hide();
SelectionContextMenu().Hide();
}
void TermControl::_SearchCommandHandler(const IInspectable& /*sender*/,
const IInspectable& /*args*/)
{
ContextMenu().Hide();
SelectionContextMenu().Hide();
SearchMatch(false);
}
}

View File

@@ -25,14 +25,18 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
struct TermControl : TermControlT<TermControl>
{
TermControl(IControlSettings settings,
Control::IControlAppearance unfocusedAppearance,
TerminalConnection::ITerminalConnection connection);
TermControl(Control::ControlInteractivity content);
TermControl(IControlSettings settings, Control::IControlAppearance unfocusedAppearance, TerminalConnection::ITerminalConnection connection);
static Control::TermControl AttachContent(Control::ControlInteractivity content, const Microsoft::Terminal::Control::IKeyBindings& keyBindings);
winrt::fire_and_forget UpdateControlSettings(Control::IControlSettings settings);
winrt::fire_and_forget UpdateControlSettings(Control::IControlSettings settings, Control::IControlAppearance unfocusedAppearance);
IControlSettings Settings() const;
winrt::guid ContentGuid() const;
hstring GetProfileName() const;
bool CopySelectionToClipboard(bool singleLine, const Windows::Foundation::IReference<CopyFormat>& formats);
@@ -47,6 +51,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
Windows::Foundation::Size MinimumSize();
float SnapDimensionToGrid(const bool widthOrHeight, const float dimension);
void PreviewInput(const winrt::hstring& text);
Microsoft::Terminal::Core::Point CursorPositionInDips();
void WindowVisibilityChanged(const bool showOrHide);
void ColorSelection(Control::SelectionColor fg, Control::SelectionColor bg, Core::MatchMode matchMode);
@@ -77,6 +85,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ClearMark();
void ClearAllMarks();
void ScrollToMark(const Control::ScrollToMarkDirection& direction);
void SelectCommand(const bool goUp);
void SelectOutput(const bool goUp);
#pragma endregion
@@ -130,27 +140,32 @@ namespace winrt::Microsoft::Terminal::Control::implementation
static Windows::UI::Xaml::Thickness ParseThicknessFromPadding(const hstring padding);
hstring ReadEntireBuffer() const;
Control::CommandHistoryContext CommandHistory() const;
winrt::Microsoft::Terminal::Core::Scheme ColorScheme() const noexcept;
void ColorScheme(const winrt::Microsoft::Terminal::Core::Scheme& scheme) const noexcept;
void AdjustOpacity(const double opacity, const bool relative);
void Detach();
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
// -------------------------------- WinRT Events ---------------------------------
// clang-format off
WINRT_CALLBACK(FontSizeChanged, Control::FontSizeChangedEventArgs);
PROJECTED_FORWARDED_TYPED_EVENT(CopyToClipboard, IInspectable, Control::CopyToClipboardEventArgs, _core, CopyToClipboard);
PROJECTED_FORWARDED_TYPED_EVENT(TitleChanged, IInspectable, Control::TitleChangedEventArgs, _core, TitleChanged);
PROJECTED_FORWARDED_TYPED_EVENT(TabColorChanged, IInspectable, IInspectable, _core, TabColorChanged);
PROJECTED_FORWARDED_TYPED_EVENT(SetTaskbarProgress, IInspectable, IInspectable, _core, TaskbarProgressChanged);
PROJECTED_FORWARDED_TYPED_EVENT(ConnectionStateChanged, IInspectable, IInspectable, _core, ConnectionStateChanged);
PROJECTED_FORWARDED_TYPED_EVENT(ShowWindowChanged, IInspectable, Control::ShowWindowArgs, _core, ShowWindowChanged);
PROJECTED_FORWARDED_TYPED_EVENT(CloseTerminalRequested, IInspectable, IInspectable, _core, CloseTerminalRequested);
// UNDER NO CIRCUMSTANCES SHOULD YOU ADD A (PROJECTED_)FORWARDED_TYPED_EVENT HERE
BUBBLED_FORWARDED_TYPED_EVENT(CopyToClipboard, IInspectable, Control::CopyToClipboardEventArgs);
BUBBLED_FORWARDED_TYPED_EVENT(TitleChanged, IInspectable, Control::TitleChangedEventArgs);
BUBBLED_FORWARDED_TYPED_EVENT(TabColorChanged, IInspectable, IInspectable);
BUBBLED_FORWARDED_TYPED_EVENT(SetTaskbarProgress, IInspectable, IInspectable);
BUBBLED_FORWARDED_TYPED_EVENT(ConnectionStateChanged, IInspectable, IInspectable);
BUBBLED_FORWARDED_TYPED_EVENT(ShowWindowChanged, IInspectable, Control::ShowWindowArgs);
BUBBLED_FORWARDED_TYPED_EVENT(CloseTerminalRequested, IInspectable, IInspectable);
PROJECTED_FORWARDED_TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs, _interactivity, PasteFromClipboard);
BUBBLED_FORWARDED_TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs);
BUBBLED_FORWARDED_TYPED_EVENT(MenuChanged, IInspectable, Control::MenuChangedEventArgs);
TYPED_EVENT(OpenHyperlink, IInspectable, Control::OpenHyperlinkEventArgs);
TYPED_EVENT(RaiseNotice, IInspectable, Control::NoticeEventArgs);
@@ -160,6 +175,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
TYPED_EVENT(FocusFollowMouseRequested, IInspectable, IInspectable);
TYPED_EVENT(Initialized, Control::TermControl, Windows::UI::Xaml::RoutedEventArgs);
TYPED_EVENT(WarningBell, IInspectable, IInspectable);
// clang-format on
WINRT_OBSERVABLE_PROPERTY(winrt::Windows::UI::Xaml::Media::Brush, BackgroundBrush, _PropertyChangedHandlers, nullptr);
@@ -216,8 +232,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation
winrt::Windows::UI::Xaml::Controls::SwapChainPanel::LayoutUpdated_revoker _layoutUpdatedRevoker;
bool _showMarksInScrollbar{ false };
bool _useRightClickContextMenu{ false };
bool _rightClickPressed{ false };
bool _isBackgroundLight{ false };
bool _detached{ false };
winrt::Windows::Foundation::Collections::IObservableVector<winrt::Windows::UI::Xaml::Controls::ICommandBarElement> _originalPrimaryElements{ nullptr };
winrt::Windows::Foundation::Collections::IObservableVector<winrt::Windows::UI::Xaml::Controls::ICommandBarElement> _originalSecondaryElements{ nullptr };
winrt::Windows::Foundation::Collections::IObservableVector<winrt::Windows::UI::Xaml::Controls::ICommandBarElement> _originalSelectedPrimaryElements{ nullptr };
winrt::Windows::Foundation::Collections::IObservableVector<winrt::Windows::UI::Xaml::Controls::ICommandBarElement> _originalSelectedSecondaryElements{ nullptr };
inline bool _IsClosing() const noexcept
{
@@ -243,7 +267,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
static bool _isColorLight(til::color bg) noexcept;
void _changeBackgroundOpacity();
bool _InitializeTerminal();
bool _InitializeTerminal(const bool reattach);
void _SetFontSize(int fontSize);
void _TappedHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::TappedRoutedEventArgs& e);
void _KeyDownHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::KeyRoutedEventArgs& e);
@@ -315,6 +339,43 @@ namespace winrt::Microsoft::Terminal::Control::implementation
til::point _toPosInDips(const Core::Point terminalCellPos);
void _throttledUpdateScrollbar(const ScrollBarUpdate& update);
void _contextMenuHandler(IInspectable sender, Control::ContextMenuRequestedEventArgs args);
void _PasteCommandHandler(const IInspectable& sender, const IInspectable& args);
void _CopyCommandHandler(const IInspectable& sender, const IInspectable& args);
void _SearchCommandHandler(const IInspectable& sender, const IInspectable& args);
struct Revokers
{
Control::ControlCore::ScrollPositionChanged_revoker coreScrollPositionChanged;
Control::ControlCore::WarningBell_revoker WarningBell;
Control::ControlCore::CursorPositionChanged_revoker CursorPositionChanged;
Control::ControlCore::RendererEnteredErrorState_revoker RendererEnteredErrorState;
Control::ControlCore::BackgroundColorChanged_revoker BackgroundColorChanged;
Control::ControlCore::FontSizeChanged_revoker FontSizeChanged;
Control::ControlCore::TransparencyChanged_revoker TransparencyChanged;
Control::ControlCore::RaiseNotice_revoker RaiseNotice;
Control::ControlCore::HoveredHyperlinkChanged_revoker HoveredHyperlinkChanged;
Control::ControlCore::FoundMatch_revoker FoundMatch;
Control::ControlCore::UpdateSelectionMarkers_revoker UpdateSelectionMarkers;
Control::ControlCore::OpenHyperlink_revoker coreOpenHyperlink;
Control::ControlCore::CopyToClipboard_revoker CopyToClipboard;
Control::ControlCore::TitleChanged_revoker TitleChanged;
Control::ControlCore::TabColorChanged_revoker TabColorChanged;
Control::ControlCore::TaskbarProgressChanged_revoker TaskbarProgressChanged;
Control::ControlCore::ConnectionStateChanged_revoker ConnectionStateChanged;
Control::ControlCore::ShowWindowChanged_revoker ShowWindowChanged;
Control::ControlCore::CloseTerminalRequested_revoker CloseTerminalRequested;
Control::ControlCore::MenuChanged_revoker MenuChanged;
// These are set up in _InitializeTerminal
Control::ControlCore::RendererWarning_revoker RendererWarning;
Control::ControlCore::SwapChainChanged_revoker SwapChainChanged;
Control::ControlInteractivity::OpenHyperlink_revoker interactivityOpenHyperlink;
Control::ControlInteractivity::ScrollPositionChanged_revoker interactivityScrollPositionChanged;
Control::ControlInteractivity::PasteFromClipboard_revoker PasteFromClipboard;
} _revokers{};
};
}

View File

@@ -3,6 +3,7 @@
import "IMouseWheelListener.idl";
import "IControlSettings.idl";
import "ControlInteractivity.idl";
import "IDirectKeyListener.idl";
import "EventArgs.idl";
import "ICoreState.idl";
@@ -17,10 +18,14 @@ namespace Microsoft.Terminal.Control
ICoreState,
Windows.UI.Xaml.Data.INotifyPropertyChanged
{
TermControl(ControlInteractivity content);
TermControl(IControlSettings settings,
IControlAppearance unfocusedAppearance,
Microsoft.Terminal.TerminalConnection.ITerminalConnection connection);
static TermControl AttachContent(ControlInteractivity content, Microsoft.Terminal.Control.IKeyBindings keyBindings);
static Windows.Foundation.Size GetProposedDimensions(IControlSettings settings,
UInt32 dpi,
Int32 commandlineCols,
@@ -29,6 +34,8 @@ namespace Microsoft.Terminal.Control
void UpdateControlSettings(IControlSettings settings);
void UpdateControlSettings(IControlSettings settings, IControlAppearance unfocusedAppearance);
Guid ContentGuid{ get; };
Microsoft.Terminal.Control.IControlSettings Settings { get; };
event FontSizeChangedEventArgs FontSizeChanged;
@@ -44,6 +51,10 @@ namespace Microsoft.Terminal.Control
event Windows.Foundation.TypedEventHandler<Object, Object> TabColorChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> ReadOnlyChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> FocusFollowMouseRequested;
event Windows.Foundation.TypedEventHandler<Object, MenuChangedEventArgs> MenuChanged;
Windows.UI.Xaml.Controls.CommandBarFlyout ContextMenu { get; };
Windows.UI.Xaml.Controls.CommandBarFlyout SelectionContextMenu { get; };
event Windows.Foundation.TypedEventHandler<TermControl, Windows.UI.Xaml.RoutedEventArgs> Initialized;
// This is an event handler forwarder for the underlying connection.
@@ -88,6 +99,7 @@ namespace Microsoft.Terminal.Control
void ToggleReadOnly();
String ReadEntireBuffer();
CommandHistoryContext CommandHistory();
void AdjustOpacity(Double Opacity, Boolean relative);
@@ -97,8 +109,15 @@ namespace Microsoft.Terminal.Control
// opacity set by the settings should call this instead.
Double BackgroundOpacity { get; };
void PreviewInput(String text);
Windows.UI.Xaml.Media.Brush BackgroundBrush { get; };
void ColorSelection(SelectionColor fg, SelectionColor bg, Microsoft.Terminal.Core.MatchMode matchMode);
void Detach();
Microsoft.Terminal.Core.Point CursorPositionInDips { get; };
}
}

View File

@@ -31,6 +31,30 @@
mc:Ignorable="d">
<UserControl.Resources>
<CommandBarFlyout x:Name="ContextMenu">
<AppBarButton x:Name="PasteCommandButton"
x:Uid="PasteCommandButton"
Click="_PasteCommandHandler"
Icon="Paste" />
</CommandBarFlyout>
<CommandBarFlyout x:Name="SelectionContextMenu">
<AppBarButton x:Name="CopyCommandButton"
x:Uid="CopyCommandButton"
Click="_CopyCommandHandler"
Icon="Copy" />
<AppBarButton x:Name="PasteWithSelectionCommandButton"
x:Uid="PasteWithSelectionCommandButton"
Click="_PasteCommandHandler"
Icon="Paste" />
<CommandBarFlyout.SecondaryCommands>
<AppBarButton x:Name="SearchCommandButton"
x:Uid="SearchCommandButton"
Click="_SearchCommandHandler"
Icon="Find" />
</CommandBarFlyout.SecondaryCommands>
</CommandBarFlyout>
<!--
BODGY: ControlsV2 changed the size of the scrollbars from 16dips to
12dips. This is harder for folks to hit with the mouse, and isn't
@@ -1183,6 +1207,7 @@
<Grid.Lights>
<local:VisualBellLight x:Name="BellLight" />
</Grid.Lights>
<Image x:Name="BackgroundImage"
AutomationProperties.AccessibilityView="Raw" />
<Grid>
@@ -1202,7 +1227,6 @@
<SwapChainPanel x:Name="SwapChainPanel"
CompositionScaleChanged="_SwapChainScaleChanged"
SizeChanged="_SwapChainSizeChanged">
<Canvas x:Name="OverlayCanvas"
Visibility="Visible">
<Border x:Name="HyperlinkTooltipBorder"

View File

@@ -68,6 +68,14 @@
<DependentUpon>TSFInputControl.xaml</DependentUpon>
</ClInclude>
<ClInclude Include="XamlUiaTextRange.h" />
<ClInclude Include="BlockContent.h">
<DependentUpon>BlockContent.idl</DependentUpon>
</ClInclude>
<ClInclude Include="BlockControl.h">
<DependentUpon>BlockControl.xaml</DependentUpon>
</ClInclude>
</ItemGroup>
<!-- ========================= Cpp Files ======================== -->
<ItemGroup>
@@ -110,6 +118,14 @@
<DependentUpon>InteractivityAutomationPeer.idl</DependentUpon>
</ClCompile>
<ClCompile Include="XamlUiaTextRange.cpp" />
<ClCompile Include="BlockContent.cpp">
<DependentUpon>BlockContent.idl</DependentUpon>
</ClCompile>
<ClCompile Include="BlockControl.cpp">
<DependentUpon>BlockControl.xaml</DependentUpon>
</ClCompile>
</ItemGroup>
<!-- ========================= idl Files ======================== -->
<ItemGroup>
@@ -136,6 +152,12 @@
<Midl Include="TSFInputControl.idl">
<DependentUpon>TSFInputControl.xaml</DependentUpon>
</Midl>
<Midl Include="BlockContent.idl" />
<Midl Include="BlockControl.idl">
<DependentUpon>BlockControl.xaml</DependentUpon>
</Midl>
</ItemGroup>
<!-- ========================= XAML Files ======================== -->
<ItemGroup>
@@ -148,6 +170,11 @@
<Page Include="TSFInputControl.xaml">
<SubType>Designer</SubType>
</Page>
<Page Include="BlockControl.xaml">
<SubType>Designer</SubType>
</Page>
</ItemGroup>
<!-- ========================= Misc Files ======================== -->
<ItemGroup>

View File

@@ -490,9 +490,7 @@ void Terminal::Write(std::wstring_view stringView)
const til::point cursorPosAfter{ cursor.GetPosition() };
// Firing the CursorPositionChanged event is very expensive so we try not to
// do that when the cursor does not need to be redrawn. We don't do this
// inside _AdjustCursorPosition, only once we're done writing the whole run
// of output.
// do that when the cursor does not need to be redrawn.
if (cursorPosBefore != cursorPosAfter)
{
_NotifyTerminalCursorPositionChanged();
@@ -1078,146 +1076,16 @@ Viewport Terminal::_GetVisibleViewport() const noexcept
size);
}
void Terminal::_AdjustCursorPosition(const til::point proposedPosition)
void Terminal::_PreserveUserScrollOffset(const int viewportDelta) noexcept
{
#pragma warning(suppress : 26496) // cpp core checks wants this const but it's modified below.
auto proposedCursorPosition = proposedPosition;
auto& cursor = _activeBuffer().GetCursor();
const auto bufferSize = _activeBuffer().GetSize();
// If we're about to scroll past the bottom of the buffer, instead cycle the
// buffer.
til::CoordType rowsPushedOffTopOfBuffer = 0;
const auto newRows = std::max(0, proposedCursorPosition.y - bufferSize.Height() + 1);
if (proposedCursorPosition.y >= bufferSize.Height())
// When the mutable viewport is moved down, and there's an active selection,
// or the visible viewport isn't already at the bottom, then we want to keep
// the visible viewport where it is. To do this, we adjust the scroll offset
// by the same amount that we've just moved down.
if (viewportDelta > 0 && (IsSelectionActive() || _scrollOffset != 0))
{
for (auto dy = 0; dy < newRows; dy++)
{
_activeBuffer().IncrementCircularBuffer();
proposedCursorPosition.y--;
rowsPushedOffTopOfBuffer++;
// Update our selection too, so it doesn't move as the buffer is cycled
if (_selection)
{
// Stash this, so we can make sure to update the pivot to match later
const auto pivotWasStart = _selection->start == _selection->pivot;
// If the start of the selection is above 0, we can reduce both the start and end by 1
if (_selection->start.y > 0)
{
_selection->start.y -= 1;
_selection->end.y -= 1;
}
else
{
// The start of the selection is at 0, if the end is greater than 0, then only reduce the end
if (_selection->end.y > 0)
{
_selection->start.x = 0;
_selection->end.y -= 1;
}
else
{
// Both the start and end of the selection are at 0, clear the selection
_selection.reset();
}
}
// If we still have a selection, make sure to sync the pivot
// with whichever value is the right one.
//
// Failure to do this might lead to GH #14462
if (_selection.has_value())
{
_selection->pivot = pivotWasStart ? _selection->start : _selection->end;
}
}
}
// manually erase our pattern intervals since the locations have changed now
_patternIntervalTree = {};
}
// Update Cursor Position
cursor.SetPosition(proposedCursorPosition);
// Move the viewport down if the cursor moved below the viewport.
// Obviously, don't need to do this in the alt buffer.
if (!_inAltBuffer())
{
auto updatedViewport = false;
const auto scrollAmount = std::max(0, proposedCursorPosition.y - _mutableViewport.BottomInclusive());
if (scrollAmount > 0)
{
const auto newViewTop = std::max(0, proposedCursorPosition.y - (_mutableViewport.Height() - 1));
// In the alt buffer, we never need to adjust _mutableViewport, which is the viewport of the main buffer.
if (newViewTop != _mutableViewport.Top())
{
_mutableViewport = Viewport::FromDimensions({ 0, newViewTop },
_mutableViewport.Dimensions());
updatedViewport = true;
}
}
// If the viewport moved, or we circled the buffer, we might need to update
// our _scrollOffset
if (updatedViewport || newRows != 0)
{
const auto oldScrollOffset = _scrollOffset;
// scroll if...
// - no selection is active
// - viewport is already at the bottom
const auto scrollToOutput = !IsSelectionActive() && _scrollOffset == 0;
_scrollOffset = scrollToOutput ? 0 : _scrollOffset + scrollAmount + newRows;
// Clamp the range to make sure that we don't scroll way off the top of the buffer
_scrollOffset = std::clamp(_scrollOffset,
0,
_activeBuffer().GetSize().Height() - _mutableViewport.Height());
// If the new scroll offset is different, then we'll still want to raise a scroll event
updatedViewport = updatedViewport || (oldScrollOffset != _scrollOffset);
}
// If the viewport moved, then send a scrolling notification.
if (updatedViewport)
{
_NotifyScrollEvent();
}
}
if (rowsPushedOffTopOfBuffer != 0)
{
if (_scrollMarks.size() > 0)
{
for (auto& mark : _scrollMarks)
{
// Move the mark up
mark.start.y -= rowsPushedOffTopOfBuffer;
// If the mark had sub-regions, then move those pointers too
if (mark.commandEnd.has_value())
{
(*mark.commandEnd).y -= rowsPushedOffTopOfBuffer;
}
if (mark.outputEnd.has_value())
{
(*mark.outputEnd).y -= rowsPushedOffTopOfBuffer;
}
}
_scrollMarks.erase(std::remove_if(_scrollMarks.begin(),
_scrollMarks.end(),
[](const VirtualTerminal::DispatchTypes::ScrollMark& m) { return m.start.y < 0; }),
_scrollMarks.end());
}
// We have to report the delta here because we might have circled the text buffer.
// That didn't change the viewport and therefore the TriggerScroll(void)
// method can't detect the delta on its own.
const til::point delta{ 0, -rowsPushedOffTopOfBuffer };
_activeBuffer().TriggerScroll(delta);
const auto maxScrollOffset = _activeBuffer().GetSize().Height() - _mutableViewport.Height();
_scrollOffset = std::min(_scrollOffset + viewportDelta, maxScrollOffset);
}
}
@@ -1411,6 +1279,11 @@ const size_t Microsoft::Terminal::Core::Terminal::GetTaskbarProgress() const noe
return _taskbarProgress;
}
void Microsoft::Terminal::Core::Terminal::MenuChangedCallback(std::function<void(std::wstring_view, int32_t)> pfn) noexcept
{
_pfnMenuChanged.swap(pfn);
}
Scheme Terminal::GetColorScheme() const
{
Scheme s;

View File

@@ -17,6 +17,8 @@
#include <til/ticket_lock.h>
#include "../../inc/cppwinrt_utils.h"
static constexpr std::wstring_view linkPattern{ LR"(\b(https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|$!:,.;]*[A-Za-z0-9+&@#/%=~_|$])" };
static constexpr size_t TaskbarMinProgress{ 10 };
@@ -113,10 +115,8 @@ public:
void SetTextAttributes(const TextAttribute& attrs) noexcept override;
void SetAutoWrapMode(const bool wrapAtEOL) noexcept override;
bool GetAutoWrapMode() const noexcept override;
void SetScrollingRegion(const til::inclusive_rect& scrollMargins) noexcept override;
void WarningBell() override;
bool GetLineFeedMode() const noexcept override;
void LineFeed(const bool withReturn, const bool wrapForced) override;
void SetWindowTitle(const std::wstring_view title) override;
CursorType GetUserDefaultCursorStyle() const noexcept override;
bool ResizeWindow(const til::CoordType width, const til::CoordType height) noexcept override;
@@ -140,6 +140,11 @@ public:
bool IsConsolePty() const noexcept override;
bool IsVtInputEnabled() const noexcept override;
void NotifyAccessibilityChange(const til::rect& changedRect) noexcept override;
void NotifyBufferRotation(const int delta) override;
void InvokeMenu(std::wstring_view menuJson, int32_t replaceLength) override;
#pragma endregion
void ClearMark();
@@ -213,6 +218,7 @@ public:
void TaskbarProgressChangedCallback(std::function<void()> pfn) noexcept;
void SetShowWindowCallback(std::function<void(bool)> pfn) noexcept;
void SetPlayMidiNoteCallback(std::function<void(const int, const int, const std::chrono::microseconds)> pfn) noexcept;
void MenuChangedCallback(std::function<void(std::wstring_view, int32_t)> pfn) noexcept;
void SetCursorOn(const bool isOn);
bool IsCursorBlinkingAllowed() const noexcept;
@@ -310,6 +316,7 @@ private:
std::function<void()> _pfnTaskbarProgressChanged;
std::function<void(bool)> _pfnShowWindowChanged;
std::function<void(const int, const int, const std::chrono::microseconds)> _pfnPlayMidiNote;
std::function<void(std::wstring_view, int32_t)> _pfnMenuChanged;
RenderSettings _renderSettings;
std::unique_ptr<::Microsoft::Console::VirtualTerminal::StateMachine> _stateMachine;
@@ -333,7 +340,7 @@ private:
size_t _hyperlinkPatternId = 0;
std::wstring _workingDirectory;
std::wstring _workingDirectory{};
// This default fake font value is only used to check if the font is a raster font.
// Otherwise, the font is changed to a real value with the renderer via TriggerFontChange.
@@ -351,7 +358,7 @@ private:
};
std::optional<SelectionAnchors> _selection;
bool _blockSelection = false;
std::wstring _wordDelimiters;
std::wstring _wordDelimiters{};
SelectionExpansion _multiClickSelectionMode = SelectionExpansion::Char;
SelectionInteractionMode _selectionMode = SelectionInteractionMode::None;
bool _selectionIsTargetingUrl = false;
@@ -420,7 +427,7 @@ private:
Microsoft::Console::Types::Viewport _GetMutableViewport() const noexcept;
Microsoft::Console::Types::Viewport _GetVisibleViewport() const noexcept;
void _AdjustCursorPosition(const til::point proposedPosition);
void _PreserveUserScrollOffset(const int viewportDelta) noexcept;
void _NotifyScrollEvent() noexcept;
@@ -444,6 +451,8 @@ private:
void _MoveByBuffer(SelectionDirection direction, til::point& pos) noexcept;
#pragma endregion
WINRT_CALLBACK(NewPrompt, winrt::delegate<const Microsoft::Console::VirtualTerminal::DispatchTypes::ScrollMark&>);
#ifdef UNIT_TESTING
friend class TerminalCoreUnitTests::TerminalBufferTests;
friend class TerminalCoreUnitTests::TerminalApiTest;

View File

@@ -48,9 +48,11 @@ void Terminal::SetViewportPosition(const til::point position) noexcept
// The viewport is fixed at 0,0 for the alt buffer, so this is a no-op.
if (!_inAltBuffer())
{
const auto viewportDelta = position.y - _GetMutableViewport().Origin().y;
const auto dimensions = _GetMutableViewport().Dimensions();
_mutableViewport = Viewport::FromDimensions(position, dimensions);
Terminal::_NotifyScrollEvent();
_PreserveUserScrollOffset(viewportDelta);
_NotifyScrollEvent();
}
}
@@ -70,11 +72,6 @@ bool Terminal::GetAutoWrapMode() const noexcept
return true;
}
void Terminal::SetScrollingRegion(const til::inclusive_rect& /*scrollMargins*/) noexcept
{
// TODO: This will be needed to fully support DECSTBM.
}
void Terminal::WarningBell()
{
_pfnWarningBell();
@@ -86,22 +83,6 @@ bool Terminal::GetLineFeedMode() const noexcept
return false;
}
void Terminal::LineFeed(const bool withReturn, const bool wrapForced)
{
auto cursorPos = _activeBuffer().GetCursor().GetPosition();
// If the line was forced to wrap, set the wrap status.
// When explicitly moving down a row, clear the wrap status.
_activeBuffer().GetRowByOffset(cursorPos.y).SetWrapForced(wrapForced);
cursorPos.y++;
if (withReturn)
{
cursorPos.x = 0;
}
_AdjustCursorPosition(cursorPos);
}
void Terminal::SetWindowTitle(const std::wstring_view title)
{
if (!_suppressApplicationTitle)
@@ -349,6 +330,8 @@ void Terminal::MarkPrompt(const DispatchTypes::ScrollMark& mark)
{
_currentPromptState = PromptState::Prompt;
}
_NewPromptHandlers(mark);
}
void Terminal::MarkCommandStart()
@@ -467,3 +450,70 @@ void Terminal::NotifyAccessibilityChange(const til::rect& /*changedRect*/) noexc
{
// This is only needed in conhost. Terminal handles accessibility in another way.
}
void Terminal::NotifyBufferRotation(const int delta)
{
// Update our selection, so it doesn't move as the buffer is cycled
if (_selection)
{
// If the end of the selection will be out of range after the move, we just
// clear the selection. Otherwise we move both the start and end points up
// by the given delta and clamp to the first row.
if (_selection->end.y < delta)
{
_selection.reset();
}
else
{
// Stash this, so we can make sure to update the pivot to match later.
const auto pivotWasStart = _selection->start == _selection->pivot;
_selection->start.y = std::max(_selection->start.y - delta, 0);
_selection->end.y = std::max(_selection->end.y - delta, 0);
// Make sure to sync the pivot with whichever value is the right one.
_selection->pivot = pivotWasStart ? _selection->start : _selection->end;
}
}
// manually erase our pattern intervals since the locations have changed now
_patternIntervalTree = {};
const auto hasScrollMarks = _scrollMarks.size() > 0;
if (hasScrollMarks)
{
for (auto& mark : _scrollMarks)
{
// Move the mark up
mark.start.y -= delta;
// If the mark had sub-regions, then move those pointers too
if (mark.commandEnd.has_value())
{
(*mark.commandEnd).y -= delta;
}
if (mark.outputEnd.has_value())
{
(*mark.outputEnd).y -= delta;
}
}
_scrollMarks.erase(std::remove_if(_scrollMarks.begin(),
_scrollMarks.end(),
[](const auto& m) { return m.start.y < 0; }),
_scrollMarks.end());
}
const auto oldScrollOffset = _scrollOffset;
_PreserveUserScrollOffset(delta);
if (_scrollOffset != oldScrollOffset || hasScrollMarks)
{
_NotifyScrollEvent();
}
}
void Terminal::InvokeMenu(std::wstring_view menuJson, int32_t replaceLength)
{
if (_pfnMenuChanged)
{
_pfnMenuChanged(menuJson, replaceLength);
}
}

View File

@@ -7,6 +7,8 @@
// it after some of our C++/WinRT headers.
#define BLOCK_TIL
#include <LibraryIncludes.h>
#include <wil/cppwinrt.h>
#include "winrt/Windows.Foundation.h"
#include "winrt/Microsoft.Terminal.Core.h"

View File

@@ -52,6 +52,7 @@ static constexpr std::string_view SwitchToTabKey{ "switchToTab" };
static constexpr std::string_view TabSearchKey{ "tabSearch" };
static constexpr std::string_view ToggleAlwaysOnTopKey{ "toggleAlwaysOnTop" };
static constexpr std::string_view ToggleCommandPaletteKey{ "commandPalette" };
static constexpr std::string_view SuggestionsKey{ "suggestions" };
static constexpr std::string_view ToggleFocusModeKey{ "toggleFocusMode" };
static constexpr std::string_view SetFocusModeKey{ "setFocusMode" };
static constexpr std::string_view ToggleFullscreenKey{ "toggleFullscreen" };
@@ -81,6 +82,8 @@ static constexpr std::string_view QuitKey{ "quit" };
static constexpr std::string_view AdjustOpacityKey{ "adjustOpacity" };
static constexpr std::string_view RestoreLastClosedKey{ "restoreLastClosed" };
static constexpr std::string_view SelectAllKey{ "selectAll" };
static constexpr std::string_view SelectCommandKey{ "selectCommand" };
static constexpr std::string_view SelectOutputKey{ "selectOutput" };
static constexpr std::string_view MarkModeKey{ "markMode" };
static constexpr std::string_view ToggleBlockSelectionKey{ "toggleBlockSelection" };
static constexpr std::string_view SwitchSelectionEndpointKey{ "switchSelectionEndpoint" };
@@ -327,22 +330,24 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
winrt::hstring ActionAndArgs::GenerateName() const
{
// Sentinel used to indicate this command must ALWAYS be generated by GenerateName
static const winrt::hstring MustGenerate{ L"" };
// Use a magic static to initialize this map, because we won't be able
// to load the resources at _init_, only at runtime.
static const auto GeneratedActionNames = []() {
return std::unordered_map<ShortcutAction, winrt::hstring>{
{ ShortcutAction::AdjustFontSize, RS_(L"AdjustFontSizeCommandKey") },
{ ShortcutAction::CloseOtherPanes, RS_(L"CloseOtherPanesCommandKey") },
{ ShortcutAction::CloseOtherTabs, L"" }, // Intentionally omitted, must be generated by GenerateName
{ ShortcutAction::CloseOtherTabs, MustGenerate },
{ ShortcutAction::ClosePane, RS_(L"ClosePaneCommandKey") },
{ ShortcutAction::CloseTab, L"" }, // Intentionally omitted, must be generated by GenerateName
{ ShortcutAction::CloseTabsAfter, L"" }, // Intentionally omitted, must be generated by GenerateName
{ ShortcutAction::CloseTab, MustGenerate },
{ ShortcutAction::CloseTabsAfter, MustGenerate },
{ ShortcutAction::CloseWindow, RS_(L"CloseWindowCommandKey") },
{ ShortcutAction::CopyText, RS_(L"CopyTextCommandKey") },
{ ShortcutAction::DuplicateTab, RS_(L"DuplicateTabCommandKey") },
{ ShortcutAction::ExecuteCommandline, RS_(L"ExecuteCommandlineCommandKey") },
{ ShortcutAction::Find, RS_(L"FindCommandKey") },
{ ShortcutAction::Invalid, L"" },
{ ShortcutAction::Invalid, MustGenerate },
{ ShortcutAction::MoveFocus, RS_(L"MoveFocusCommandKey") },
{ ShortcutAction::MovePane, RS_(L"MovePaneCommandKey") },
{ ShortcutAction::SwapPane, RS_(L"SwapPaneCommandKey") },
@@ -367,46 +372,49 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{ ShortcutAction::AddMark, RS_(L"AddMarkCommandKey") },
{ ShortcutAction::ClearMark, RS_(L"ClearMarkCommandKey") },
{ ShortcutAction::ClearAllMarks, RS_(L"ClearAllMarksCommandKey") },
{ ShortcutAction::SendInput, L"" },
{ ShortcutAction::SetColorScheme, L"" },
{ ShortcutAction::SendInput, MustGenerate },
{ ShortcutAction::SetColorScheme, MustGenerate },
{ ShortcutAction::SetTabColor, RS_(L"ResetTabColorCommandKey") },
{ ShortcutAction::SplitPane, RS_(L"SplitPaneCommandKey") },
{ ShortcutAction::SwitchToTab, RS_(L"SwitchToTabCommandKey") },
{ ShortcutAction::TabSearch, RS_(L"TabSearchCommandKey") },
{ ShortcutAction::ToggleAlwaysOnTop, RS_(L"ToggleAlwaysOnTopCommandKey") },
{ ShortcutAction::ToggleCommandPalette, L"" },
{ ShortcutAction::ToggleCommandPalette, MustGenerate },
{ ShortcutAction::Suggestions, MustGenerate },
{ ShortcutAction::ToggleFocusMode, RS_(L"ToggleFocusModeCommandKey") },
{ ShortcutAction::SetFocusMode, L"" },
{ ShortcutAction::SetFocusMode, MustGenerate },
{ ShortcutAction::ToggleFullscreen, RS_(L"ToggleFullscreenCommandKey") },
{ ShortcutAction::SetFullScreen, L"" },
{ ShortcutAction::SetMaximized, L"" },
{ ShortcutAction::SetFullScreen, MustGenerate },
{ ShortcutAction::SetMaximized, MustGenerate },
{ ShortcutAction::TogglePaneZoom, RS_(L"TogglePaneZoomCommandKey") },
{ ShortcutAction::ToggleSplitOrientation, RS_(L"ToggleSplitOrientationCommandKey") },
{ ShortcutAction::ToggleShaderEffects, RS_(L"ToggleShaderEffectsCommandKey") },
{ ShortcutAction::MoveTab, L"" }, // Intentionally omitted, must be generated by GenerateName
{ ShortcutAction::MoveTab, MustGenerate },
{ ShortcutAction::BreakIntoDebugger, RS_(L"BreakIntoDebuggerCommandKey") },
{ ShortcutAction::FindMatch, L"" }, // Intentionally omitted, must be generated by GenerateName
{ ShortcutAction::FindMatch, MustGenerate },
{ ShortcutAction::TogglePaneReadOnly, RS_(L"TogglePaneReadOnlyCommandKey") },
{ ShortcutAction::NewWindow, RS_(L"NewWindowCommandKey") },
{ ShortcutAction::IdentifyWindow, RS_(L"IdentifyWindowCommandKey") },
{ ShortcutAction::IdentifyWindows, RS_(L"IdentifyWindowsCommandKey") },
{ ShortcutAction::RenameWindow, RS_(L"ResetWindowNameCommandKey") },
{ ShortcutAction::OpenWindowRenamer, RS_(L"OpenWindowRenamerCommandKey") },
{ ShortcutAction::GlobalSummon, L"" }, // Intentionally omitted, must be generated by GenerateName
{ ShortcutAction::GlobalSummon, MustGenerate },
{ ShortcutAction::QuakeMode, RS_(L"QuakeModeCommandKey") },
{ ShortcutAction::FocusPane, L"" }, // Intentionally omitted, must be generated by GenerateName
{ ShortcutAction::FocusPane, MustGenerate },
{ ShortcutAction::OpenSystemMenu, RS_(L"OpenSystemMenuCommandKey") },
{ ShortcutAction::ExportBuffer, L"" }, // Intentionally omitted, must be generated by GenerateName
{ ShortcutAction::ClearBuffer, L"" }, // Intentionally omitted, must be generated by GenerateName
{ ShortcutAction::MultipleActions, L"" }, // Intentionally omitted, must be generated by GenerateName
{ ShortcutAction::ExportBuffer, MustGenerate },
{ ShortcutAction::ClearBuffer, MustGenerate },
{ ShortcutAction::MultipleActions, MustGenerate },
{ ShortcutAction::Quit, RS_(L"QuitCommandKey") },
{ ShortcutAction::AdjustOpacity, L"" }, // Intentionally omitted, must be generated by GenerateName
{ ShortcutAction::AdjustOpacity, MustGenerate },
{ ShortcutAction::RestoreLastClosed, RS_(L"RestoreLastClosedCommandKey") },
{ ShortcutAction::SelectCommand, MustGenerate },
{ ShortcutAction::SelectOutput, MustGenerate },
{ ShortcutAction::SelectAll, RS_(L"SelectAllCommandKey") },
{ ShortcutAction::MarkMode, RS_(L"MarkModeCommandKey") },
{ ShortcutAction::ToggleBlockSelection, RS_(L"ToggleBlockSelectionCommandKey") },
{ ShortcutAction::SwitchSelectionEndpoint, RS_(L"SwitchSelectionEndpointCommandKey") },
{ ShortcutAction::ColorSelection, L"" }, // Intentionally omitted, must be generated by GenerateName
{ ShortcutAction::ColorSelection, MustGenerate },
{ ShortcutAction::ExpandSelectionToWord, RS_(L"ExpandSelectionToWordCommandKey") },
};
}();
@@ -423,4 +431,30 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
const auto found = GeneratedActionNames.find(_Action);
return found != GeneratedActionNames.end() ? found->second : L"";
}
winrt::hstring ActionAndArgs::Serialize(winrt::Windows::Foundation::Collections::IVector<Model::ActionAndArgs> args)
{
Json::Value json{ Json::objectValue };
JsonUtils::SetValueForKey(json, "actions", args);
Json::StreamWriterBuilder wbuilder;
auto str = Json::writeString(wbuilder, json);
return winrt::to_hstring(str);
}
winrt::Windows::Foundation::Collections::IVector<Model::ActionAndArgs> ActionAndArgs::Deserialize(winrt::hstring content)
{
auto data = winrt::to_string(content);
std::string errs;
std::unique_ptr<Json::CharReader> reader{ Json::CharReaderBuilder::CharReaderBuilder().newCharReader() };
Json::Value root;
if (!reader->parse(data.data(), data.data() + data.size(), &root, &errs))
{
throw winrt::hresult_error(WEB_E_INVALID_JSON_STRING, winrt::to_hstring(errs));
}
winrt::Windows::Foundation::Collections::IVector<Model::ActionAndArgs> result{ nullptr };
JsonUtils::GetValueForKey(root, "actions", result);
return result;
}
}

View File

@@ -16,6 +16,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
std::vector<SettingsLoadWarnings>& warnings);
static Json::Value ToJson(const Model::ActionAndArgs& val);
static winrt::hstring Serialize(winrt::Windows::Foundation::Collections::IVector<Model::ActionAndArgs> args);
static winrt::Windows::Foundation::Collections::IVector<Model::ActionAndArgs> Deserialize(winrt::hstring content);
ActionAndArgs() = default;
ActionAndArgs(ShortcutAction action);
ActionAndArgs(ShortcutAction action, IActionArgs args) :

View File

@@ -13,6 +13,7 @@
#include "ResizePaneArgs.g.cpp"
#include "MoveFocusArgs.g.cpp"
#include "MovePaneArgs.g.cpp"
#include "MoveTabArgs.g.cpp"
#include "SwapPaneArgs.g.cpp"
#include "AdjustFontSizeArgs.g.cpp"
#include "SendInputArgs.g.cpp"
@@ -28,11 +29,11 @@
#include "CloseOtherTabsArgs.g.cpp"
#include "CloseTabsAfterArgs.g.cpp"
#include "CloseTabArgs.g.cpp"
#include "MoveTabArgs.g.cpp"
#include "ScrollToMarkArgs.g.cpp"
#include "AddMarkArgs.g.cpp"
#include "FindMatchArgs.g.cpp"
#include "ToggleCommandPaletteArgs.g.cpp"
#include "SuggestionsArgs.g.cpp"
#include "NewWindowArgs.g.cpp"
#include "PrevTabArgs.g.cpp"
#include "NextTabArgs.g.cpp"
@@ -43,6 +44,8 @@
#include "ClearBufferArgs.g.cpp"
#include "MultipleActionsArgs.g.cpp"
#include "AdjustOpacityArgs.g.cpp"
#include "SelectCommandArgs.g.cpp"
#include "SelectOutputArgs.g.cpp"
#include "ColorSelectionArgs.g.cpp"
#include <LibraryResources.h>
@@ -245,6 +248,12 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
winrt::hstring MovePaneArgs::GenerateName() const
{
if (!Window().empty())
{
return winrt::hstring{
fmt::format(L"{}, window:{}, tab index:{}", RS_(L"MovePaneCommandKey"), Window(), TabIndex())
};
}
return winrt::hstring{
fmt::format(L"{}, tab index:{}", RS_(L"MovePaneCommandKey"), TabIndex())
};
@@ -648,6 +657,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
winrt::hstring MoveTabArgs::GenerateName() const
{
if (!Window().empty())
{
return winrt::hstring{
fmt::format(std::wstring_view(RS_(L"MoveTabToWindowCommandKey")),
Window())
};
}
winrt::hstring directionString;
switch (Direction())
{
@@ -673,6 +690,16 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
return RS_(L"ToggleCommandPaletteCommandKey");
}
winrt::hstring SuggestionsArgs::GenerateName() const
{
switch (Source())
{
case SuggestionsSource::CommandHistory:
return RS_(L"SuggestionsCommandHistoryCommandKey");
}
return RS_(L"SuggestionsCommandKey");
}
winrt::hstring FindMatchArgs::GenerateName() const
{
switch (Direction())
@@ -956,4 +983,27 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
};
}
}
winrt::hstring SelectOutputArgs::GenerateName() const
{
switch (Direction())
{
case SelectOutputDirection::Next:
return RS_(L"SelectOutputNextCommandKey");
case SelectOutputDirection::Previous:
return RS_(L"SelectOutputPreviousCommandKey");
}
return L"";
}
winrt::hstring SelectCommandArgs::GenerateName() const
{
switch (Direction())
{
case SelectOutputDirection::Next:
return RS_(L"SelectCommandNextCommandKey");
case SelectOutputDirection::Previous:
return RS_(L"SelectCommandPreviousCommandKey");
}
return L"";
}
}

View File

@@ -34,6 +34,7 @@
#include "AddMarkArgs.g.h"
#include "MoveTabArgs.g.h"
#include "ToggleCommandPaletteArgs.g.h"
#include "SuggestionsArgs.g.h"
#include "FindMatchArgs.g.h"
#include "NewWindowArgs.g.h"
#include "PrevTabArgs.g.h"
@@ -45,6 +46,8 @@
#include "ClearBufferArgs.g.h"
#include "MultipleActionsArgs.g.h"
#include "AdjustOpacityArgs.g.h"
#include "SelectCommandArgs.g.h"
#include "SelectOutputArgs.g.h"
#include "ColorSelectionArgs.g.h"
#include "JsonUtils.h"
@@ -96,8 +99,9 @@ private:
X(Windows::Foundation::IReference<Control::CopyFormat>, CopyFormatting, "copyFormatting", false, nullptr)
////////////////////////////////////////////////////////////////////////////////
#define MOVE_PANE_ARGS(X) \
X(uint32_t, TabIndex, "index", false, 0)
#define MOVE_PANE_ARGS(X) \
X(uint32_t, TabIndex, "index", false, 0) \
X(winrt::hstring, Window, "window", false, L"")
////////////////////////////////////////////////////////////////////////////////
#define SWITCH_TO_TAB_ARGS(X) \
@@ -172,8 +176,15 @@ private:
X(Windows::Foundation::IReference<uint32_t>, Index, "index", false, nullptr)
////////////////////////////////////////////////////////////////////////////////
#define MOVE_TAB_ARGS(X) \
X(MoveTabDirection, Direction, "direction", args->Direction() == MoveTabDirection::None, MoveTabDirection::None)
// Interestingly, the order MATTERS here. Window has to be BEFORE Direction,
// because otherwise we won't have parsed the Window yet when we validate the
// Direction.
#define MOVE_TAB_ARGS(X) \
X(winrt::hstring, Window, "window", false, L"") \
X(MoveTabDirection, Direction, "direction", (args->Direction() == MoveTabDirection::None) && (args->Window().empty()), MoveTabDirection::None)
// Other ideas:
// X(uint32_t, TabIndex, "index", false, 0) \ // target? source?
////////////////////////////////////////////////////////////////////////////////
#define SCROLL_UP_ARGS(X) \
@@ -195,6 +206,10 @@ private:
#define TOGGLE_COMMAND_PALETTE_ARGS(X) \
X(CommandPaletteLaunchMode, LaunchMode, "launchMode", false, CommandPaletteLaunchMode::Action)
////////////////////////////////////////////////////////////////////////////////
#define SUGGESTIONS_ARGS(X) \
X(SuggestionsSource, Source, "source", false, SuggestionsSource::Tasks)
////////////////////////////////////////////////////////////////////////////////
#define FIND_MATCH_ARGS(X) \
X(FindMatchDirection, Direction, "direction", args->Direction() == FindMatchDirection::None, FindMatchDirection::None)
@@ -236,6 +251,14 @@ private:
X(int32_t, Opacity, "opacity", false, 0) \
X(bool, Relative, "relative", false, true)
////////////////////////////////////////////////////////////////////////////////
#define SELECT_COMMAND_ARGS(X) \
X(SelectOutputDirection, Direction, "direction", false, SelectOutputDirection::Previous)
////////////////////////////////////////////////////////////////////////////////
#define SELECT_OUTPUT_ARGS(X) \
X(SelectOutputDirection, Direction, "direction", false, SelectOutputDirection::Previous)
////////////////////////////////////////////////////////////////////////////////
#define COLOR_SELECTION_ARGS(X) \
X(winrt::Microsoft::Terminal::Control::SelectionColor, Foreground, "foreground", false, nullptr) \
@@ -276,6 +299,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
ACTION_ARG(Windows::Foundation::IReference<bool>, SuppressApplicationTitle, nullptr);
ACTION_ARG(winrt::hstring, ColorScheme);
ACTION_ARG(Windows::Foundation::IReference<bool>, Elevate, nullptr);
ACTION_ARG(winrt::guid, ContentGuid);
static constexpr std::string_view CommandlineKey{ "commandline" };
static constexpr std::string_view StartingDirectoryKey{ "startingDirectory" };
@@ -286,6 +310,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
static constexpr std::string_view SuppressApplicationTitleKey{ "suppressApplicationTitle" };
static constexpr std::string_view ColorSchemeKey{ "colorScheme" };
static constexpr std::string_view ElevateKey{ "elevate" };
static constexpr std::string_view ContentKey{ "__content" };
public:
hstring GenerateName() const;
@@ -304,7 +329,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
otherAsUs->_Profile == _Profile &&
otherAsUs->_SuppressApplicationTitle == _SuppressApplicationTitle &&
otherAsUs->_ColorScheme == _ColorScheme &&
otherAsUs->_Elevate == _Elevate;
otherAsUs->_Elevate == _Elevate &&
otherAsUs->_ContentGuid == _ContentGuid;
}
return false;
};
@@ -321,6 +347,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
JsonUtils::GetValueForKey(json, SuppressApplicationTitleKey, args->_SuppressApplicationTitle);
JsonUtils::GetValueForKey(json, ColorSchemeKey, args->_ColorScheme);
JsonUtils::GetValueForKey(json, ElevateKey, args->_Elevate);
JsonUtils::GetValueForKey(json, ContentKey, args->_ContentGuid);
return *args;
}
static Json::Value ToJson(const Model::NewTerminalArgs& val)
@@ -340,6 +367,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
JsonUtils::SetValueForKey(json, SuppressApplicationTitleKey, args->_SuppressApplicationTitle);
JsonUtils::SetValueForKey(json, ColorSchemeKey, args->_ColorScheme);
JsonUtils::SetValueForKey(json, ElevateKey, args->_Elevate);
JsonUtils::SetValueForKey(json, ContentKey, args->_ContentGuid);
return json;
}
Model::NewTerminalArgs Copy() const
@@ -354,6 +382,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
copy->_SuppressApplicationTitle = _SuppressApplicationTitle;
copy->_ColorScheme = _ColorScheme;
copy->_Elevate = _Elevate;
copy->_ContentGuid = _ContentGuid;
return *copy;
}
size_t Hash() const
@@ -373,6 +402,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
h.write(SuppressApplicationTitle());
h.write(ColorScheme());
h.write(Elevate());
h.write(ContentGuid());
}
};
}
@@ -649,6 +679,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
ACTION_ARGS_STRUCT(ToggleCommandPaletteArgs, TOGGLE_COMMAND_PALETTE_ARGS);
ACTION_ARGS_STRUCT(SuggestionsArgs, SUGGESTIONS_ARGS);
ACTION_ARGS_STRUCT(FindMatchArgs, FIND_MATCH_ARGS);
ACTION_ARGS_STRUCT(PrevTabArgs, PREV_TAB_ARGS);
@@ -734,6 +766,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
ACTION_ARGS_STRUCT(AdjustOpacityArgs, ADJUST_OPACITY_ARGS);
ACTION_ARGS_STRUCT(SelectCommandArgs, SELECT_COMMAND_ARGS);
ACTION_ARGS_STRUCT(SelectOutputArgs, SELECT_OUTPUT_ARGS);
ACTION_ARGS_STRUCT(ColorSelectionArgs, COLOR_SELECTION_ARGS);
}
@@ -741,6 +776,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
{
BASIC_FACTORY(ActionEventArgs);
BASIC_FACTORY(CopyTextArgs);
BASIC_FACTORY(SwitchToTabArgs);
BASIC_FACTORY(NewTerminalArgs);
BASIC_FACTORY(NewTabArgs);
@@ -750,6 +786,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
BASIC_FACTORY(RenameTabArgs);
BASIC_FACTORY(SwapPaneArgs);
BASIC_FACTORY(SplitPaneArgs);
BASIC_FACTORY(SendInputArgs);
BASIC_FACTORY(SetFocusModeArgs);
BASIC_FACTORY(SetFullScreenArgs);
BASIC_FACTORY(SetMaximizedArgs);
@@ -770,4 +807,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
BASIC_FACTORY(ClearBufferArgs);
BASIC_FACTORY(MultipleActionsArgs);
BASIC_FACTORY(AdjustOpacityArgs);
BASIC_FACTORY(SuggestionsArgs);
BASIC_FACTORY(SelectCommandArgs);
BASIC_FACTORY(SelectOutputArgs);
}

View File

@@ -80,6 +80,12 @@ namespace Microsoft.Terminal.Settings.Model
Previous
};
enum SelectOutputDirection
{
Previous = 0,
Next,
};
enum CommandPaletteLaunchMode
{
Action = 0,
@@ -106,6 +112,12 @@ namespace Microsoft.Terminal.Settings.Model
ToCurrent,
ToMouse,
};
enum SuggestionsSource
{
Tasks,
CommandHistory,
DirectoryHistory,
};
[default_interface] runtimeclass NewTerminalArgs {
NewTerminalArgs();
@@ -132,6 +144,8 @@ namespace Microsoft.Terminal.Settings.Model
// not modify whatever the profile's value is (either true or false)
Windows.Foundation.IReference<Boolean> Elevate;
Guid ContentGuid{ get; set; };
Boolean Equals(NewTerminalArgs other);
String GenerateName();
String ToCommandline();
@@ -146,6 +160,7 @@ namespace Microsoft.Terminal.Settings.Model
[default_interface] runtimeclass CopyTextArgs : IActionArgs
{
CopyTextArgs();
Boolean SingleLine { get; };
Windows.Foundation.IReference<Microsoft.Terminal.Control.CopyFormat> CopyFormatting { get; };
};
@@ -158,8 +173,9 @@ namespace Microsoft.Terminal.Settings.Model
[default_interface] runtimeclass MovePaneArgs : IActionArgs
{
MovePaneArgs(UInt32 tabIndex);
MovePaneArgs(UInt32 tabIndex, String Window);
UInt32 TabIndex;
String Window;
};
[default_interface] runtimeclass SwitchToTabArgs : IActionArgs
@@ -192,6 +208,8 @@ namespace Microsoft.Terminal.Settings.Model
[default_interface] runtimeclass SendInputArgs : IActionArgs
{
SendInputArgs(String input);
String Input { get; };
};
@@ -276,8 +294,9 @@ namespace Microsoft.Terminal.Settings.Model
[default_interface] runtimeclass MoveTabArgs : IActionArgs
{
MoveTabArgs(MoveTabDirection direction);
MoveTabArgs(String window, MoveTabDirection direction);
MoveTabDirection Direction { get; };
String Window { get; };
};
[default_interface] runtimeclass ScrollUpArgs : IActionArgs
@@ -306,6 +325,13 @@ namespace Microsoft.Terminal.Settings.Model
CommandPaletteLaunchMode LaunchMode { get; };
};
[default_interface] runtimeclass SuggestionsArgs : IActionArgs
{
SuggestionsArgs();
SuggestionsArgs(SuggestionsSource source);
SuggestionsSource Source { get; };
};
[default_interface] runtimeclass FindMatchArgs : IActionArgs
{
FindMatchArgs(FindMatchDirection direction);
@@ -384,4 +410,17 @@ namespace Microsoft.Terminal.Settings.Model
Microsoft.Terminal.Control.SelectionColor Background;
Microsoft.Terminal.Core.MatchMode MatchMode { get; };
};
[default_interface] runtimeclass SelectCommandArgs : IActionArgs
{
SelectCommandArgs(SelectOutputDirection direction);
SelectOutputDirection Direction { get; };
}
[default_interface] runtimeclass SelectOutputArgs : IActionArgs
{
SelectOutputArgs(SelectOutputDirection direction);
SelectOutputDirection Direction { get; };
}
}

View File

@@ -4,12 +4,14 @@
#include "pch.h"
#include "AllShortcutActions.h"
#include "ActionMap.h"
#include "Command.h"
#include "AllShortcutActions.h"
#include "ActionMap.g.cpp"
using namespace winrt::Microsoft::Terminal::Settings::Model;
using namespace winrt::Microsoft::Terminal::Control;
using namespace winrt::Windows::Foundation::Collections;
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
@@ -118,7 +120,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
// Method Description:
// - Retrieves a map of actions that can be bound to a key
Windows::Foundation::Collections::IMapView<hstring, Model::ActionAndArgs> ActionMap::AvailableActions()
IMapView<hstring, Model::ActionAndArgs> ActionMap::AvailableActions()
{
if (!_AvailableActionsCache)
{
@@ -172,7 +174,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
// - Retrieves a map of command names to the commands themselves
// - These commands should not be modified directly because they may result in
// an invalid state for the `ActionMap`
Windows::Foundation::Collections::IMapView<hstring, Model::Command> ActionMap::NameMap()
IMapView<hstring, Model::Command> ActionMap::NameMap()
{
if (!_NameMapCache)
{
@@ -283,7 +285,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
return cumulativeActions;
}
Windows::Foundation::Collections::IMapView<Control::KeyChord, Model::Command> ActionMap::GlobalHotkeys()
IMapView<Control::KeyChord, Model::Command> ActionMap::GlobalHotkeys()
{
if (!_GlobalHotkeysCache)
{
@@ -292,7 +294,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
return _GlobalHotkeysCache.GetView();
}
Windows::Foundation::Collections::IMapView<Control::KeyChord, Model::Command> ActionMap::KeyBindings()
IMapView<Control::KeyChord, Model::Command> ActionMap::KeyBindings()
{
if (!_KeyBindingMapCache)
{
@@ -854,4 +856,79 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
cmd->ActionAndArgs(action);
AddAction(*cmd);
}
void ActionMap::_recursiveUpdateCommandKeybindingLabels()
{
const auto& commands{ _ExpandedCommandsCache };
for (const auto& command : commands)
{
if (command.HasNestedCommands())
{
_recursiveUpdateCommandKeybindingLabels();
}
else
{
// If there's a keybinding that's bound to exactly this command,
// then get the keychord and display it as a
// part of the command in the UI.
// We specifically need to do this for nested commands.
const auto keyChord{ GetKeyBindingForAction(command.ActionAndArgs().Action(),
command.ActionAndArgs().Args()) };
command.RegisterKey(keyChord);
}
}
}
// This is a helper to aid in sorting commands by their `Name`s, alphabetically.
static bool _compareSchemeNames(const ColorScheme& lhs, const ColorScheme& rhs)
{
std::wstring leftName{ lhs.Name() };
std::wstring rightName{ rhs.Name() };
return leftName.compare(rightName) < 0;
}
void ActionMap::ExpandCommands(const IVectorView<Model::Profile>& profiles,
const IMapView<winrt::hstring, Model::ColorScheme>& schemes)
{
// TODO in review - It's a little weird to stash the expanded commands
// into a separate map. Is it possible to just replace the name map with
// the post-expanded commands?
//
// WHILE also making sure that upon re-saving the commands, we don't
// actually serialize the results of the expansion. I don't think it is.
std::vector<Model::ColorScheme> sortedSchemes;
sortedSchemes.reserve(schemes.Size());
for (const auto& nameAndScheme : schemes)
{
sortedSchemes.push_back(nameAndScheme.Value());
}
std::sort(sortedSchemes.begin(),
sortedSchemes.end(),
_compareSchemeNames);
auto copyOfCommands = winrt::single_threaded_map<winrt::hstring, Model::Command>();
const auto& commandsToExpand{ NameMap() };
for (auto nameAndCommand : commandsToExpand)
{
copyOfCommands.Insert(nameAndCommand.Key(), nameAndCommand.Value());
}
implementation::Command::ExpandCommands(copyOfCommands,
profiles,
winrt::param::vector_view<Model::ColorScheme>{ sortedSchemes });
_ExpandedCommandsCache = winrt::single_threaded_vector<Model::Command>();
for (const auto& [_, command] : copyOfCommands)
{
_ExpandedCommandsCache.Append(command);
}
}
IVector<Model::Command> ActionMap::ExpandedCommands()
{
return _ExpandedCommandsCache;
}
}

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