Compare commits

...

132 Commits

Author SHA1 Message Date
Mike Griese
2e275b627d Merge branch 'dev/migrie/fhl/tasks-pane' into dev/migrie/fhl/md-pane 2024-03-22 06:54:48 -05:00
Mike Griese
37b4b7b6c0 fix builds 2024-03-22 06:54:29 -05:00
Mike Griese
6576f949c4 Merge branch 'dev/migrie/f/sui-panes' into dev/migrie/fhl/tasks-pane 2024-03-21 14:02:45 -05:00
Mike Griese
216cc3fa66 Merge branch 'dev/migrie/fhl/scratchpad-pane' into dev/migrie/f/sui-panes 2024-03-21 14:02:24 -05:00
Mike Griese
d41793470f Merge branch 'dev/migrie/fhl/non-terminal-panes-2023' into dev/migrie/fhl/scratchpad-pane 2024-03-21 13:51:52 -05:00
Mike Griese
df73d75541 derp 2024-03-21 13:51:34 -05:00
Mike Griese
4230878ae8 Merge branch 'dev/migrie/fhl/tasks-pane' into dev/migrie/fhl/md-pane 2024-03-21 13:22:15 -05:00
Mike Griese
66878d2ddc Merge branch 'dev/migrie/f/sui-panes' into dev/migrie/fhl/tasks-pane 2024-03-21 13:21:51 -05:00
Mike Griese
77022e92f1 Merge branch 'dev/migrie/fhl/scratchpad-pane' into dev/migrie/f/sui-panes 2024-03-21 13:17:20 -05:00
Mike Griese
f1ab16e7d5 Merge branch 'dev/migrie/fhl/non-terminal-panes-2023' into dev/migrie/fhl/scratchpad-pane 2024-03-21 13:16:08 -05:00
Mike Griese
2083b2ff9e Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/non-terminal-panes-2023 2024-03-21 13:14:32 -05:00
Mike Griese
a73dd7ad28 Merge branch 'dev/migrie/fhl/tasks-pane' into dev/migrie/fhl/md-pane 2024-03-20 09:41:06 -05:00
Mike Griese
7b84041ae1 Merge branch 'dev/migrie/f/sui-panes' into dev/migrie/fhl/tasks-pane 2024-03-20 09:38:28 -05:00
Mike Griese
bcceb85057 Merge branch 'dev/migrie/fhl/scratchpad-pane' into dev/migrie/f/sui-panes 2024-03-20 09:20:16 -05:00
Mike Griese
352e0a211a fix settings pane for merge
(cherry picked from commit 0c6a353967)
2024-03-20 09:19:03 -05:00
Mike Griese
e0bb8409b3 Fix scratch pane for merge
(cherry picked from commit 591080db39)
2024-03-20 09:17:55 -05:00
Mike Griese
04f90ecb5f Fix MD pane for merge 2024-03-20 09:15:14 -05:00
Mike Griese
8da2d7e287 fix tasks for merge 2024-03-20 09:14:47 -05:00
Mike Griese
0c6a353967 fix settings pane for merge 2024-03-20 09:14:28 -05:00
Mike Griese
591080db39 Fix scratch pane for merge 2024-03-20 09:13:54 -05:00
Mike Griese
e199fbe492 Merge branch 'dev/migrie/f/sui-panes' into dev/migrie/fhl/md-pane
# Conflicts:
#	src/cascadia/TerminalApp/TerminalAppLib.vcxproj
#	src/cascadia/TerminalApp/TerminalPaneContent.idl
#	src/cascadia/TerminalApp/TerminalTab.cpp
2024-03-20 09:13:26 -05:00
Mike Griese
8f2a129a1e Merge branch 'dev/migrie/f/sui-panes' into dev/migrie/fhl/tasks-pane 2024-03-20 06:50:28 -05:00
Mike Griese
c8d0c0aab7 Merge branch 'dev/migrie/fhl/scratchpad-pane' into dev/migrie/f/sui-panes 2024-03-20 06:40:43 -05:00
Mike Griese
2357653de5 Merge branch 'dev/migrie/fhl/non-terminal-panes-2023' into dev/migrie/fhl/scratchpad-pane 2024-03-20 06:40:04 -05:00
Mike Griese
52970ef854 RegisterBigTimeEncapsulationViolatingTerminalPaneContentEvents 2024-03-19 16:30:00 -05:00
Mike Griese
826fc087b0 hey there buddy, did you get lost? 2024-03-19 15:50:58 -05:00
Mike Griese
a7533faf45 eh these events are from pane content anyways! 2024-03-19 15:15:08 -05:00
Mike Griese
052dc78af5 more nits 2024-03-19 14:54:30 -05:00
Mike Griese
fd8b083a46 get rid of this file 2024-03-19 13:55:11 -05:00
Mike Griese
6789ec0765 some of the easier nits 2024-03-19 13:34:35 -05:00
Mike Griese
863840ee0f Merge branch 'dev/migrie/fhl/scratchpad-pane' into dev/migrie/f/sui-panes 2024-03-18 13:20:43 -05:00
Mike Griese
1951f30434 Merge branch 'dev/migrie/fhl/non-terminal-panes-2023' into dev/migrie/fhl/scratchpad-pane 2024-03-18 13:20:26 -05:00
Mike Griese
524d658699 GREAT-GREAT-GRANDPARENT: Hey when a pane wants to get closed, we should close it 2024-03-18 13:19:45 -05:00
Mike Griese
ef775a87c9 Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/non-terminal-panes-2023 2024-03-18 13:19:26 -05:00
Mike Griese
318256098c live editing of the file because ITS COOL 2024-03-15 14:12:53 -05:00
Mike Griese
79abf4cc00 GREAT-GREAT-GRANDPARENT: Hey when a pane wants to get closed, we should close it 2024-03-15 12:01:42 -05:00
Mike Griese
46786dd372 add support for relative paths 2024-03-15 10:19:32 -05:00
Mike Griese
a13e81d00b add a open command for fun and profit 2024-03-15 10:11:01 -05:00
Mike Griese
fe9e388aee remove this dead end 2024-03-15 09:38:35 -05:00
Mike Griese
8af1cbb6a6 yep, can't be done 2024-03-15 09:36:51 -05:00
Mike Griese
5025df3c30 a dead end 2024-03-15 09:19:21 -05:00
Mike Griese
0dbc9870df Selectable text, and plaintext support 2024-03-15 06:22:41 -05:00
Mike Griese
91d75e2c5b wire up the buttons to sendInput, and add dark theming. cause Sir, this is a Terminal 2024-03-14 16:58:57 -05:00
Mike Griese
ddd817847e add action to wire it all up 2024-03-14 16:13:22 -05:00
Mike Griese
f07b6a0d10 wholesale import of code blocks and markdown page as a pane. no idea if woks 2024-03-14 15:37:13 -05:00
Mike Griese
6a5d273c43 PARENT: fix build 2024-03-14 15:27:40 -05:00
Mike Griese
06cebd092d add dependency on md4c" 2024-03-14 14:24:39 -05:00
Mike Griese
5b4747f7b7 Merge remote-tracking branch 'origin/dev/migrie/f/sui-panes' into dev/migrie/fhl/tasks-pane 2024-03-14 14:10:21 -05:00
Mike Griese
978fd6e2ba Merge branch 'dev/migrie/fhl/scratchpad-pane' into dev/migrie/f/sui-panes 2024-03-08 10:36:17 -06:00
Mike Griese
35651bc92c Merge branch 'dev/migrie/fhl/non-terminal-panes-2023' into dev/migrie/fhl/scratchpad-pane 2024-03-08 10:33:31 -06:00
Mike Griese
a3fbc64384 Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/non-terminal-panes-2023 2024-03-08 10:33:00 -06:00
Mike Griese
b6254f8294 GREAT-GRANDPARENT: This fixes a crash in parent pane selection
(cherry picked from commit 91a0d0e26d)
2024-03-07 16:51:02 -06:00
Mike Griese
4d47cd5866 cleanup 2024-03-04 16:34:36 -06:00
Mike Griese
0a11643f1d sanely pass around a cache instead of... whatever that was. 2024-03-04 16:30:16 -06:00
Mike Griese
17075d6744 Merge branch 'dev/migrie/fhl/scratchpad-pane' into dev/migrie/f/sui-panes 2024-02-28 11:50:29 -06:00
Mike Griese
25a8851986 Merge branch 'dev/migrie/fhl/non-terminal-panes-2023' into dev/migrie/fhl/scratchpad-pane 2024-02-28 11:36:02 -06:00
Mike Griese
de5f7af25d Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/non-terminal-panes-2023 2024-02-28 11:32:40 -06:00
Mike Griese
14a00c8abf lots of cleanup 2024-02-21 12:14:47 -06:00
Mike Griese
02a5593f5b this is amazing, I'm amazing, everything is awesome 2024-02-21 12:07:45 -06:00
Mike Griese
2960476019 holy fuck this is better than I imagined a week ago 2024-02-21 11:39:58 -06:00
Mike Griese
9300647b58 turns out Visibility works exactly like you'd want for a TreeView 2024-02-21 11:20:33 -06:00
Mike Griese
b706a6d679 yes yes subclasses are hard in C 2024-02-21 11:01:36 -06:00
Mike Griese
17d70f5221 weird but works surprisingly well 2024-02-21 10:57:05 -06:00
Mike Griese
69cb54509c Blindly, what if TaskViewModel was a FilteredCommand? 2024-02-21 10:16:41 -06:00
Mike Griese
def66306a1 I wanted to start adding a filter box, but that got tricky 2024-02-21 09:32:50 -06:00
Mike Griese
d86bd57876 Merge branch 'dev/migrie/fhl/tasks-pane' of https://github.com/microsoft/terminal into dev/migrie/fhl/tasks-pane 2024-02-21 09:18:46 -06:00
Mike Griese
01ca03f433 Don't generate names with visualized SPC and BS for the sxnui
You know, this doesn't really help right now, but it should fix #16577,
and maybe help with #16578 (but not the command palette part)
2024-02-21 09:17:44 -06:00
Mike Griese
c94c00b760 more better styling 2024-02-21 09:11:47 -06:00
Mike Griese
afa7a2478b more better styling 2024-02-20 16:12:12 -06:00
Mike Griese
db427f2cd1 better styling 2024-02-20 16:11:34 -06:00
Mike Griese
2f3ecf1ea0 plumb the action through to the task pane 2024-02-20 15:41:10 -06:00
Mike Griese
d7a6b1899c re-add the visibility hack; add a play button that does nothing 2024-02-20 09:28:42 -06:00
Mike Griese
365c068da1 this fixes that random item reuse problem 2024-02-19 12:42:24 -06:00
Mike Griese
8418d6a1cb omg bind to the IsSelected 2024-02-16 15:54:21 -06:00
Mike Griese
0bb13f9eee real xaml 2024-02-16 09:23:50 -06:00
Mike Griese
e72b1bf835 nested commands are easy guys 2024-02-16 08:40:29 -06:00
Mike Griese
81b35ff143 this worked surprisingly quickly 2024-02-16 08:32:30 -06:00
Mike Griese
092b3558f3 Merge branch 'dev/migrie/fhl/scratchpad-pane' into dev/migrie/f/sui-panes 2024-02-08 09:47:33 -06:00
Mike Griese
c2446334e6 Merge branch 'dev/migrie/fhl/non-terminal-panes-2023' into dev/migrie/fhl/scratchpad-pane 2024-02-08 09:46:46 -06:00
Mike Griese
3982358188 Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/non-terminal-panes-2023 2024-02-08 09:40:19 -06:00
Mike Griese
0d528f84f2 Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/non-terminal-panes-2023 2024-01-19 16:25:31 -06:00
Mike Griese
6bc711de06 maybe I'm not that good at coding 2023-11-08 11:10:58 -06:00
Mike Griese
f622d80004 Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/non-terminal-panes-2023 2023-11-08 05:55:27 -06:00
Mike Griese
4cec7e9b4b try to remove a few of these but ultimately, eh 2023-11-06 06:01:55 -06:00
Mike Griese
cf920e7d58 Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/non-terminal-panes-2023 2023-11-02 06:13:22 -05:00
Mike Griese
389ba20a98 spel 2023-10-25 14:41:57 -05:00
Mike Griese
dd8606ff9b Merge branch 'dev/migrie/fhl/scratchpad-pane' into dev/migrie/f/sui-panes 2023-10-25 11:04:43 -05:00
Mike Griese
7bc1457d42 nits and such 2023-10-25 11:03:41 -05:00
Mike Griese
e9e04d4e70 Merge branch 'dev/migrie/fhl/non-terminal-panes-2023' into dev/migrie/fhl/scratchpad-pane 2023-10-25 09:37:37 -05:00
Mike Griese
58e8f3c11c mostly nits 2023-10-25 09:37:23 -05:00
Mike Griese
8df9523a77 Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/non-terminal-panes-2023 2023-10-25 09:04:35 -05:00
Mike Griese
fd0640997d annoying build break 2023-10-13 15:17:38 -05:00
Mike Griese
fb74fc8c6a dead code 2023-10-13 14:58:19 -05:00
Mike Griese
5f4087ff00 finish exorcising SettingsTab 2023-10-13 14:56:50 -05:00
Mike Griese
81889a685c derp 2023-10-13 14:08:49 -05:00
Mike Griese
e82c627ebe dead code removal 2023-10-13 12:09:08 -05:00
Mike Griese
d726165330 terrible, but it works 2023-10-13 12:06:59 -05:00
Mike Griese
57e1f26d14 Merge branch 'dev/migrie/fhl/scratchpad-pane' into dev/migrie/f/sui-panes 2023-10-13 11:36:27 -05:00
Mike Griese
b49997b4b4 Merge branch 'dev/migrie/fhl/non-terminal-panes-2023' into dev/migrie/fhl/scratchpad-pane 2023-10-13 11:12:24 -05:00
Mike Griese
2086e0f3af Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/non-terminal-panes-2023 2023-10-13 10:39:02 -05:00
Mike Griese
6107c3e551 Merge branch 'dev/migrie/fhl/scratchpad-pane' into dev/migrie/f/sui-panes 2023-09-11 05:43:06 -05:00
Mike Griese
46469aa5e3 Merge branch 'dev/migrie/fhl/non-terminal-panes-2023' into dev/migrie/fhl/scratchpad-pane 2023-09-11 05:24:30 -05:00
Mike Griese
c869b47e13 Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/non-terminal-panes-2023 2023-09-11 05:22:43 -05:00
Mike Griese
9531069538 background brush, done 2023-08-07 15:17:09 -05:00
Mike Griese
521e301541 update settings should work now 2023-08-03 13:50:11 -05:00
Mike Griese
842326daa5 icons for non-terminal pane content 2023-08-03 11:31:57 -05:00
Mike Griese
fb7c80938b derp 2023-08-01 11:37:10 -05:00
Mike Griese
29d0d57656 this works better than it has any right to 2023-07-27 16:29:26 -05:00
Mike Griese
cbd61b0a7d POC: yea, this works 2023-07-27 15:55:05 -05:00
Mike Griese
1cc9835454 feature flags too 2023-07-27 13:58:44 -05:00
Mike Griese
86914bdfc1 Merge branch 'dev/migrie/fhl/non-terminal-panes-2023' into dev/migrie/fhl/scratchpad-pane 2023-07-25 13:28:25 -05:00
Mike Griese
e0b003ad4d Merge branch 'main' into dev/migrie/fhl/non-terminal-panes-2023 2023-07-24 14:10:40 -05:00
Mike Griese
f89368c19b [PARENT] try to use GetActiveTerminalControl less in TerminalTab
(cherry picked from commit 262d95aae5)
2023-07-20 10:59:41 -05:00
Mike Griese
5582e1bcc8 [PARENT] You know what, I just went for it.
(cherry picked from commit 63ba8e19fd)
2023-07-20 10:59:28 -05:00
Mike Griese
a23c1a24dc keybindings too 2023-07-20 07:39:02 -05:00
Mike Griese
5f9add4000 Merge branch 'dev/migrie/fhl/non-terminal-panes-2023' into dev/migrie/fhl/scratchpad-pane 2023-07-20 07:04:10 -05:00
Mike Griese
11126f9b37 Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/non-terminal-panes-2023 2023-07-20 07:02:16 -05:00
Mike Griese
e31202b0b8 Merge commit '6a10ea5' into dev/migrie/fhl/non-terminal-panes-2023 2023-07-20 07:02:04 -05:00
Mike Griese
e6dc314c17 Merge commit 'b4042ea' into dev/migrie/fhl/non-terminal-panes-2023 2023-07-19 16:22:03 -05:00
Mike Griese
2d4030683a Let's just make it experimental 2023-07-18 13:47:58 -05:00
Mike Griese
262d95aae5 [PARENT] try to use GetActiveTerminalControl less in TerminalTab 2023-07-18 13:47:38 -05:00
Mike Griese
63ba8e19fd [PARENT] You know what, I just went for it. 2023-07-18 13:21:18 -05:00
Mike Griese
1b39db7ab0 Single commit that adds the whole scratchpad and action 2023-07-18 12:58:55 -05:00
Mike Griese
2dd8f409b2 [TO PARENT] dead code 2023-07-18 11:48:33 -05:00
Mike Griese
049c043279 some last cleanups 2023-07-18 10:26:32 -05:00
Mike Griese
a1da6c117e huge shuffling so that pane content can raise events instead of relying on termcontrol 2023-07-18 10:13:44 -05:00
Mike Griese
7c9ffb0e02 snapping now uses an interface, so that it's not TermControl-specific 2023-07-18 06:06:07 -05:00
Mike Griese
84df8197d4 close event 2023-07-17 14:22:12 -05:00
Mike Griese
5b3aa54b56 move GetNewTerminalArgs into IPaneContent 2023-07-17 12:42:43 -05:00
Mike Griese
ef6bb8a73c hey look, it builds now 2023-07-17 12:35:27 -05:00
Mike Griese
4e144425f0 Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/non-terminal-panes-2023 2023-07-17 10:53:56 -05:00
Mike Griese
f353323a23 I wanted to do this in one shot but _zelda_ 2023-05-12 13:32:12 -05:00
59 changed files with 10127 additions and 970 deletions

View File

@@ -324,6 +324,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalAzBridge", "src\cas
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fmt", "src\dep\fmt\fmt.vcxproj", "{6BAE5851-50D5-4934-8D5E-30361A8A40F3}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "md4c", "src\dep\md4c\md4c.vcxproj", "{7CAE5851-50D5-4934-8D5E-30361A8A40F3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WpfTerminalTestNetCore", "src\cascadia\WpfTerminalTestNetCore\WpfTerminalTestNetCore.csproj", "{1588FD7C-241E-4E7D-9113-43735F3E6BAD}"
ProjectSection(ProjectDependencies) = postProject
{CA5CAD1A-F542-4635-A069-7CAEFB930070} = {CA5CAD1A-F542-4635-A069-7CAEFB930070}
@@ -1825,6 +1827,36 @@ Global
{6BAE5851-50D5-4934-8D5E-30361A8A40F3}.Release|x64.Build.0 = Release|x64
{6BAE5851-50D5-4934-8D5E-30361A8A40F3}.Release|x86.ActiveCfg = Release|Win32
{6BAE5851-50D5-4934-8D5E-30361A8A40F3}.Release|x86.Build.0 = Release|Win32
{7CAE5851-50D5-4934-8D5E-30361A8A40F3}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32
{7CAE5851-50D5-4934-8D5E-30361A8A40F3}.AuditMode|ARM.ActiveCfg = AuditMode|Win32
{7CAE5851-50D5-4934-8D5E-30361A8A40F3}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64
{7CAE5851-50D5-4934-8D5E-30361A8A40F3}.AuditMode|ARM64.Build.0 = AuditMode|ARM64
{7CAE5851-50D5-4934-8D5E-30361A8A40F3}.AuditMode|x64.ActiveCfg = AuditMode|x64
{7CAE5851-50D5-4934-8D5E-30361A8A40F3}.AuditMode|x64.Build.0 = AuditMode|x64
{7CAE5851-50D5-4934-8D5E-30361A8A40F3}.AuditMode|x86.ActiveCfg = AuditMode|Win32
{7CAE5851-50D5-4934-8D5E-30361A8A40F3}.AuditMode|x86.Build.0 = AuditMode|Win32
{7CAE5851-50D5-4934-8D5E-30361A8A40F3}.Debug|Any CPU.ActiveCfg = Debug|Win32
{7CAE5851-50D5-4934-8D5E-30361A8A40F3}.Debug|ARM.ActiveCfg = Debug|Win32
{7CAE5851-50D5-4934-8D5E-30361A8A40F3}.Debug|ARM64.ActiveCfg = Debug|ARM64
{7CAE5851-50D5-4934-8D5E-30361A8A40F3}.Debug|ARM64.Build.0 = Debug|ARM64
{7CAE5851-50D5-4934-8D5E-30361A8A40F3}.Debug|x64.ActiveCfg = Debug|x64
{7CAE5851-50D5-4934-8D5E-30361A8A40F3}.Debug|x64.Build.0 = Debug|x64
{7CAE5851-50D5-4934-8D5E-30361A8A40F3}.Debug|x86.ActiveCfg = Debug|Win32
{7CAE5851-50D5-4934-8D5E-30361A8A40F3}.Debug|x86.Build.0 = Debug|Win32
{7CAE5851-50D5-4934-8D5E-30361A8A40F3}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32
{7CAE5851-50D5-4934-8D5E-30361A8A40F3}.Fuzzing|ARM.ActiveCfg = Fuzzing|Win32
{7CAE5851-50D5-4934-8D5E-30361A8A40F3}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64
{7CAE5851-50D5-4934-8D5E-30361A8A40F3}.Fuzzing|x64.ActiveCfg = Fuzzing|x64
{7CAE5851-50D5-4934-8D5E-30361A8A40F3}.Fuzzing|x64.Build.0 = Fuzzing|x64
{7CAE5851-50D5-4934-8D5E-30361A8A40F3}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32
{7CAE5851-50D5-4934-8D5E-30361A8A40F3}.Release|Any CPU.ActiveCfg = Release|Win32
{7CAE5851-50D5-4934-8D5E-30361A8A40F3}.Release|ARM.ActiveCfg = Release|Win32
{7CAE5851-50D5-4934-8D5E-30361A8A40F3}.Release|ARM64.ActiveCfg = Release|ARM64
{7CAE5851-50D5-4934-8D5E-30361A8A40F3}.Release|ARM64.Build.0 = Release|ARM64
{7CAE5851-50D5-4934-8D5E-30361A8A40F3}.Release|x64.ActiveCfg = Release|x64
{7CAE5851-50D5-4934-8D5E-30361A8A40F3}.Release|x64.Build.0 = Release|x64
{7CAE5851-50D5-4934-8D5E-30361A8A40F3}.Release|x86.ActiveCfg = Release|Win32
{7CAE5851-50D5-4934-8D5E-30361A8A40F3}.Release|x86.Build.0 = Release|Win32
{1588FD7C-241E-4E7D-9113-43735F3E6BAD}.AuditMode|Any CPU.ActiveCfg = Debug|Any CPU
{1588FD7C-241E-4E7D-9113-43735F3E6BAD}.AuditMode|ARM64.ActiveCfg = Debug|Any CPU
{1588FD7C-241E-4E7D-9113-43735F3E6BAD}.AuditMode|x64.ActiveCfg = Debug|Any CPU
@@ -2508,6 +2540,7 @@ Global
{024052DE-83FB-4653-AEA4-90790D29D5BD} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB}
{067F0A06-FCB7-472C-96E9-B03B54E8E18D} = {61901E80-E97D-4D61-A9BB-E8F2FDA8B40C}
{6BAE5851-50D5-4934-8D5E-30361A8A40F3} = {81C352DB-1818-45B7-A284-18E259F1CC87}
{7CAE5851-50D5-4934-8D5E-30361A8A40F3} = {81C352DB-1818-45B7-A284-18E259F1CC87}
{1588FD7C-241E-4E7D-9113-43735F3E6BAD} = {4DAF0299-495E-4CD1-A982-9BAC16A45932}
{506FD703-BAA7-4F6E-9361-64F550EC8FCA} = {61901E80-E97D-4D61-A9BB-E8F2FDA8B40C}
{416FD703-BAA7-4F6E-9361-64F550EC8FCA} = {61901E80-E97D-4D61-A9BB-E8F2FDA8B40C}

6492
oss/md4c/md4c.c Normal file

File diff suppressed because it is too large Load Diff

407
oss/md4c/md4c.h Normal file
View File

@@ -0,0 +1,407 @@
/*
* MD4C: Markdown parser for C
* (http://github.com/mity/md4c)
*
* Copyright (c) 2016-2024 Martin Mitáš
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef MD4C_H
#define MD4C_H
#ifdef __cplusplus
extern "C" {
#endif
#if defined MD4C_USE_UTF16
/* Magic to support UTF-16. Note that in order to use it, you have to define
* the macro MD4C_USE_UTF16 both when building MD4C as well as when
* including this header in your code. */
#ifdef _WIN32
#include <windows.h>
typedef WCHAR MD_CHAR;
#else
#error MD4C_USE_UTF16 is only supported on Windows.
#endif
#else
typedef char MD_CHAR;
#endif
typedef unsigned MD_SIZE;
typedef unsigned MD_OFFSET;
/* Block represents a part of document hierarchy structure like a paragraph
* or list item.
*/
typedef enum MD_BLOCKTYPE {
/* <body>...</body> */
MD_BLOCK_DOC = 0,
/* <blockquote>...</blockquote> */
MD_BLOCK_QUOTE,
/* <ul>...</ul>
* Detail: Structure MD_BLOCK_UL_DETAIL. */
MD_BLOCK_UL,
/* <ol>...</ol>
* Detail: Structure MD_BLOCK_OL_DETAIL. */
MD_BLOCK_OL,
/* <li>...</li>
* Detail: Structure MD_BLOCK_LI_DETAIL. */
MD_BLOCK_LI,
/* <hr> */
MD_BLOCK_HR,
/* <h1>...</h1> (for levels up to 6)
* Detail: Structure MD_BLOCK_H_DETAIL. */
MD_BLOCK_H,
/* <pre><code>...</code></pre>
* Note the text lines within code blocks are terminated with '\n'
* instead of explicit MD_TEXT_BR. */
MD_BLOCK_CODE,
/* Raw HTML block. This itself does not correspond to any particular HTML
* tag. The contents of it _is_ raw HTML source intended to be put
* in verbatim form to the HTML output. */
MD_BLOCK_HTML,
/* <p>...</p> */
MD_BLOCK_P,
/* <table>...</table> and its contents.
* Detail: Structure MD_BLOCK_TABLE_DETAIL (for MD_BLOCK_TABLE),
* structure MD_BLOCK_TD_DETAIL (for MD_BLOCK_TH and MD_BLOCK_TD)
* Note all of these are used only if extension MD_FLAG_TABLES is enabled. */
MD_BLOCK_TABLE,
MD_BLOCK_THEAD,
MD_BLOCK_TBODY,
MD_BLOCK_TR,
MD_BLOCK_TH,
MD_BLOCK_TD
} MD_BLOCKTYPE;
/* Span represents an in-line piece of a document which should be rendered with
* the same font, color and other attributes. A sequence of spans forms a block
* like paragraph or list item. */
typedef enum MD_SPANTYPE {
/* <em>...</em> */
MD_SPAN_EM,
/* <strong>...</strong> */
MD_SPAN_STRONG,
/* <a href="xxx">...</a>
* Detail: Structure MD_SPAN_A_DETAIL. */
MD_SPAN_A,
/* <img src="xxx">...</a>
* Detail: Structure MD_SPAN_IMG_DETAIL.
* Note: Image text can contain nested spans and even nested images.
* If rendered into ALT attribute of HTML <IMG> tag, it's responsibility
* of the parser to deal with it.
*/
MD_SPAN_IMG,
/* <code>...</code> */
MD_SPAN_CODE,
/* <del>...</del>
* Note: Recognized only when MD_FLAG_STRIKETHROUGH is enabled.
*/
MD_SPAN_DEL,
/* For recognizing inline ($) and display ($$) equations
* Note: Recognized only when MD_FLAG_LATEXMATHSPANS is enabled.
*/
MD_SPAN_LATEXMATH,
MD_SPAN_LATEXMATH_DISPLAY,
/* Wiki links
* Note: Recognized only when MD_FLAG_WIKILINKS is enabled.
*/
MD_SPAN_WIKILINK,
/* <u>...</u>
* Note: Recognized only when MD_FLAG_UNDERLINE is enabled. */
MD_SPAN_U
} MD_SPANTYPE;
/* Text is the actual textual contents of span. */
typedef enum MD_TEXTTYPE {
/* Normal text. */
MD_TEXT_NORMAL = 0,
/* NULL character. CommonMark requires replacing NULL character with
* the replacement char U+FFFD, so this allows caller to do that easily. */
MD_TEXT_NULLCHAR,
/* Line breaks.
* Note these are not sent from blocks with verbatim output (MD_BLOCK_CODE
* or MD_BLOCK_HTML). In such cases, '\n' is part of the text itself. */
MD_TEXT_BR, /* <br> (hard break) */
MD_TEXT_SOFTBR, /* '\n' in source text where it is not semantically meaningful (soft break) */
/* Entity.
* (a) Named entity, e.g. &nbsp;
* (Note MD4C does not have a list of known entities.
* Anything matching the regexp /&[A-Za-z][A-Za-z0-9]{1,47};/ is
* treated as a named entity.)
* (b) Numerical entity, e.g. &#1234;
* (c) Hexadecimal entity, e.g. &#x12AB;
*
* As MD4C is mostly encoding agnostic, application gets the verbatim
* entity text into the MD_PARSER::text_callback(). */
MD_TEXT_ENTITY,
/* Text in a code block (inside MD_BLOCK_CODE) or inlined code (`code`).
* If it is inside MD_BLOCK_CODE, it includes spaces for indentation and
* '\n' for new lines. MD_TEXT_BR and MD_TEXT_SOFTBR are not sent for this
* kind of text. */
MD_TEXT_CODE,
/* Text is a raw HTML. If it is contents of a raw HTML block (i.e. not
* an inline raw HTML), then MD_TEXT_BR and MD_TEXT_SOFTBR are not used.
* The text contains verbatim '\n' for the new lines. */
MD_TEXT_HTML,
/* Text is inside an equation. This is processed the same way as inlined code
* spans (`code`). */
MD_TEXT_LATEXMATH
} MD_TEXTTYPE;
/* Alignment enumeration. */
typedef enum MD_ALIGN {
MD_ALIGN_DEFAULT = 0, /* When unspecified. */
MD_ALIGN_LEFT,
MD_ALIGN_CENTER,
MD_ALIGN_RIGHT
} MD_ALIGN;
/* String attribute.
*
* This wraps strings which are outside of a normal text flow and which are
* propagated within various detailed structures, but which still may contain
* string portions of different types like e.g. entities.
*
* So, for example, lets consider this image:
*
* ![image alt text](http://example.org/image.png 'foo &quot; bar')
*
* The image alt text is propagated as a normal text via the MD_PARSER::text()
* callback. However, the image title ('foo &quot; bar') is propagated as
* MD_ATTRIBUTE in MD_SPAN_IMG_DETAIL::title.
*
* Then the attribute MD_SPAN_IMG_DETAIL::title shall provide the following:
* -- [0]: "foo " (substr_types[0] == MD_TEXT_NORMAL; substr_offsets[0] == 0)
* -- [1]: "&quot;" (substr_types[1] == MD_TEXT_ENTITY; substr_offsets[1] == 4)
* -- [2]: " bar" (substr_types[2] == MD_TEXT_NORMAL; substr_offsets[2] == 10)
* -- [3]: (n/a) (n/a ; substr_offsets[3] == 14)
*
* Note that these invariants are always guaranteed:
* -- substr_offsets[0] == 0
* -- substr_offsets[LAST+1] == size
* -- Currently, only MD_TEXT_NORMAL, MD_TEXT_ENTITY, MD_TEXT_NULLCHAR
* substrings can appear. This could change only of the specification
* changes.
*/
typedef struct MD_ATTRIBUTE {
const MD_CHAR* text;
MD_SIZE size;
const MD_TEXTTYPE* substr_types;
const MD_OFFSET* substr_offsets;
} MD_ATTRIBUTE;
/* Detailed info for MD_BLOCK_UL. */
typedef struct MD_BLOCK_UL_DETAIL {
int is_tight; /* Non-zero if tight list, zero if loose. */
MD_CHAR mark; /* Item bullet character in MarkDown source of the list, e.g. '-', '+', '*'. */
} MD_BLOCK_UL_DETAIL;
/* Detailed info for MD_BLOCK_OL. */
typedef struct MD_BLOCK_OL_DETAIL {
unsigned start; /* Start index of the ordered list. */
int is_tight; /* Non-zero if tight list, zero if loose. */
MD_CHAR mark_delimiter; /* Character delimiting the item marks in MarkDown source, e.g. '.' or ')' */
} MD_BLOCK_OL_DETAIL;
/* Detailed info for MD_BLOCK_LI. */
typedef struct MD_BLOCK_LI_DETAIL {
int is_task; /* Can be non-zero only with MD_FLAG_TASKLISTS */
MD_CHAR task_mark; /* If is_task, then one of 'x', 'X' or ' '. Undefined otherwise. */
MD_OFFSET task_mark_offset; /* If is_task, then offset in the input of the char between '[' and ']'. */
} MD_BLOCK_LI_DETAIL;
/* Detailed info for MD_BLOCK_H. */
typedef struct MD_BLOCK_H_DETAIL {
unsigned level; /* Header level (1 - 6) */
} MD_BLOCK_H_DETAIL;
/* Detailed info for MD_BLOCK_CODE. */
typedef struct MD_BLOCK_CODE_DETAIL {
MD_ATTRIBUTE info;
MD_ATTRIBUTE lang;
MD_CHAR fence_char; /* The character used for fenced code block; or zero for indented code block. */
} MD_BLOCK_CODE_DETAIL;
/* Detailed info for MD_BLOCK_TABLE. */
typedef struct MD_BLOCK_TABLE_DETAIL {
unsigned col_count; /* Count of columns in the table. */
unsigned head_row_count; /* Count of rows in the table header (currently always 1) */
unsigned body_row_count; /* Count of rows in the table body */
} MD_BLOCK_TABLE_DETAIL;
/* Detailed info for MD_BLOCK_TH and MD_BLOCK_TD. */
typedef struct MD_BLOCK_TD_DETAIL {
MD_ALIGN align;
} MD_BLOCK_TD_DETAIL;
/* Detailed info for MD_SPAN_A. */
typedef struct MD_SPAN_A_DETAIL {
MD_ATTRIBUTE href;
MD_ATTRIBUTE title;
int is_autolink; /* nonzero if this is an autolink */
} MD_SPAN_A_DETAIL;
/* Detailed info for MD_SPAN_IMG. */
typedef struct MD_SPAN_IMG_DETAIL {
MD_ATTRIBUTE src;
MD_ATTRIBUTE title;
} MD_SPAN_IMG_DETAIL;
/* Detailed info for MD_SPAN_WIKILINK. */
typedef struct MD_SPAN_WIKILINK {
MD_ATTRIBUTE target;
} MD_SPAN_WIKILINK_DETAIL;
/* Flags specifying extensions/deviations from CommonMark specification.
*
* By default (when MD_PARSER::flags == 0), we follow CommonMark specification.
* The following flags may allow some extensions or deviations from it.
*/
#define MD_FLAG_COLLAPSEWHITESPACE 0x0001 /* In MD_TEXT_NORMAL, collapse non-trivial whitespace into single ' ' */
#define MD_FLAG_PERMISSIVEATXHEADERS 0x0002 /* Do not require space in ATX headers ( ###header ) */
#define MD_FLAG_PERMISSIVEURLAUTOLINKS 0x0004 /* Recognize URLs as autolinks even without '<', '>' */
#define MD_FLAG_PERMISSIVEEMAILAUTOLINKS 0x0008 /* Recognize e-mails as autolinks even without '<', '>' and 'mailto:' */
#define MD_FLAG_NOINDENTEDCODEBLOCKS 0x0010 /* Disable indented code blocks. (Only fenced code works.) */
#define MD_FLAG_NOHTMLBLOCKS 0x0020 /* Disable raw HTML blocks. */
#define MD_FLAG_NOHTMLSPANS 0x0040 /* Disable raw HTML (inline). */
#define MD_FLAG_TABLES 0x0100 /* Enable tables extension. */
#define MD_FLAG_STRIKETHROUGH 0x0200 /* Enable strikethrough extension. */
#define MD_FLAG_PERMISSIVEWWWAUTOLINKS 0x0400 /* Enable WWW autolinks (even without any scheme prefix, if they begin with 'www.') */
#define MD_FLAG_TASKLISTS 0x0800 /* Enable task list extension. */
#define MD_FLAG_LATEXMATHSPANS 0x1000 /* Enable $ and $$ containing LaTeX equations. */
#define MD_FLAG_WIKILINKS 0x2000 /* Enable wiki links extension. */
#define MD_FLAG_UNDERLINE 0x4000 /* Enable underline extension (and disables '_' for normal emphasis). */
#define MD_FLAG_HARD_SOFT_BREAKS 0x8000 /* Force all soft breaks to act as hard breaks. */
#define MD_FLAG_PERMISSIVEAUTOLINKS (MD_FLAG_PERMISSIVEEMAILAUTOLINKS | MD_FLAG_PERMISSIVEURLAUTOLINKS | MD_FLAG_PERMISSIVEWWWAUTOLINKS)
#define MD_FLAG_NOHTML (MD_FLAG_NOHTMLBLOCKS | MD_FLAG_NOHTMLSPANS)
/* Convenient sets of flags corresponding to well-known Markdown dialects.
*
* Note we may only support subset of features of the referred dialect.
* The constant just enables those extensions which bring us as close as
* possible given what features we implement.
*
* ABI compatibility note: Meaning of these can change in time as new
* extensions, bringing the dialect closer to the original, are implemented.
*/
#define MD_DIALECT_COMMONMARK 0
#define MD_DIALECT_GITHUB (MD_FLAG_PERMISSIVEAUTOLINKS | MD_FLAG_TABLES | MD_FLAG_STRIKETHROUGH | MD_FLAG_TASKLISTS)
/* Parser structure.
*/
typedef struct MD_PARSER {
/* Reserved. Set to zero.
*/
unsigned abi_version;
/* Dialect options. Bitmask of MD_FLAG_xxxx values.
*/
unsigned flags;
/* Caller-provided rendering callbacks.
*
* For some block/span types, more detailed information is provided in a
* type-specific structure pointed by the argument 'detail'.
*
* The last argument of all callbacks, 'userdata', is just propagated from
* md_parse() and is available for any use by the application.
*
* Note any strings provided to the callbacks as their arguments or as
* members of any detail structure are generally not zero-terminated.
* Application has to take the respective size information into account.
*
* Any rendering callback may abort further parsing of the document by
* returning non-zero.
*/
int (*enter_block)(MD_BLOCKTYPE /*type*/, void* /*detail*/, void* /*userdata*/);
int (*leave_block)(MD_BLOCKTYPE /*type*/, void* /*detail*/, void* /*userdata*/);
int (*enter_span)(MD_SPANTYPE /*type*/, void* /*detail*/, void* /*userdata*/);
int (*leave_span)(MD_SPANTYPE /*type*/, void* /*detail*/, void* /*userdata*/);
int (*text)(MD_TEXTTYPE /*type*/, const MD_CHAR* /*text*/, MD_SIZE /*size*/, void* /*userdata*/);
/* Debug callback. Optional (may be NULL).
*
* If provided and something goes wrong, this function gets called.
* This is intended for debugging and problem diagnosis for developers;
* it is not intended to provide any errors suitable for displaying to an
* end user.
*/
void (*debug_log)(const char* /*msg*/, void* /*userdata*/);
/* Reserved. Set to NULL.
*/
void (*syntax)(void);
} MD_PARSER;
/* For backward compatibility. Do not use in new code.
*/
typedef MD_PARSER MD_RENDERER;
/* Parse the Markdown document stored in the string 'text' of size 'size'.
* The parser provides callbacks to be called during the parsing so the
* caller can render the document on the screen or convert the Markdown
* to another format.
*
* Zero is returned on success. If a runtime error occurs (e.g. a memory
* fails), -1 is returned. If the processing is aborted due any callback
* returning non-zero, the return value of the callback is returned.
*/
int md_parse(const MD_CHAR* text, MD_SIZE size, const MD_PARSER* parser, void* userdata);
#ifdef __cplusplus
} /* extern "C" { */
#endif
#endif /* MD4C_H */

View File

@@ -1326,7 +1326,7 @@ namespace TerminalAppLocalTests
const auto& controlSettings = activeControl.Settings();
VERIFY_IS_NOT_NULL(controlSettings);
VERIFY_ARE_EQUAL(til::color{ 0xff0c0c0c }, controlSettings.DefaultBackground());
VERIFY_ARE_EQUAL(til::color{ 0xff0c0c0c }, til::color{ controlSettings.DefaultBackground() });
});
TestOnUIThread([&page]() {
@@ -1344,7 +1344,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NOT_NULL(controlSettings);
Log::Comment(L"Color should be changed to the preview");
VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, controlSettings.DefaultBackground());
VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, til::color{ controlSettings.DefaultBackground() });
// And we should have stored a function to revert the change.
VERIFY_ARE_EQUAL(1u, page->_restorePreviewFuncs.size());
@@ -1366,7 +1366,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NOT_NULL(controlSettings);
Log::Comment(L"Color should be changed");
VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, controlSettings.DefaultBackground());
VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, til::color{ controlSettings.DefaultBackground() });
// After preview there should be no more restore functions to execute.
VERIFY_ARE_EQUAL(0u, page->_restorePreviewFuncs.size());
@@ -1394,7 +1394,7 @@ namespace TerminalAppLocalTests
const auto& controlSettings = activeControl.Settings();
VERIFY_IS_NOT_NULL(controlSettings);
VERIFY_ARE_EQUAL(til::color{ 0xff0c0c0c }, controlSettings.DefaultBackground());
VERIFY_ARE_EQUAL(til::color{ 0xff0c0c0c }, til::color{ controlSettings.DefaultBackground() });
});
TestOnUIThread([&page]() {
@@ -1412,7 +1412,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NOT_NULL(controlSettings);
Log::Comment(L"Color should be changed to the preview");
VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, controlSettings.DefaultBackground());
VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, til::color{ controlSettings.DefaultBackground() });
});
TestOnUIThread([&page]() {
@@ -1428,7 +1428,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NOT_NULL(controlSettings);
Log::Comment(L"Color should be the same as it originally was");
VERIFY_ARE_EQUAL(til::color{ 0xff0c0c0c }, controlSettings.DefaultBackground());
VERIFY_ARE_EQUAL(til::color{ 0xff0c0c0c }, til::color{ controlSettings.DefaultBackground() });
});
Log::Comment(L"Sleep to let events propagate");
Sleep(250);
@@ -1450,7 +1450,7 @@ namespace TerminalAppLocalTests
const auto& controlSettings = activeControl.Settings();
VERIFY_IS_NOT_NULL(controlSettings);
VERIFY_ARE_EQUAL(til::color{ 0xff0c0c0c }, controlSettings.DefaultBackground());
VERIFY_ARE_EQUAL(til::color{ 0xff0c0c0c }, til::color{ controlSettings.DefaultBackground() });
});
TestOnUIThread([&page]() {
@@ -1467,7 +1467,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NOT_NULL(controlSettings);
Log::Comment(L"Color should be changed to the preview");
VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, controlSettings.DefaultBackground());
VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, til::color{ controlSettings.DefaultBackground() });
});
TestOnUIThread([&page]() {
@@ -1484,7 +1484,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NOT_NULL(controlSettings);
Log::Comment(L"Color should be changed to the preview");
VERIFY_ARE_EQUAL(til::color{ 0xffFAFAFA }, controlSettings.DefaultBackground());
VERIFY_ARE_EQUAL(til::color{ 0xffFAFAFA }, til::color{ controlSettings.DefaultBackground() });
});
TestOnUIThread([&page]() {
@@ -1503,7 +1503,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NOT_NULL(controlSettings);
Log::Comment(L"Color should be changed");
VERIFY_ARE_EQUAL(til::color{ 0xffFAFAFA }, controlSettings.DefaultBackground());
VERIFY_ARE_EQUAL(til::color{ 0xffFAFAFA }, til::color{ controlSettings.DefaultBackground() });
});
Log::Comment(L"Sleep to let events propagate");
Sleep(250);

View File

@@ -56,6 +56,8 @@ Author(s):
#include <winrt/windows.applicationmodel.core.h>
#include <winrt/Microsoft.Terminal.TerminalConnection.h>
#include <winrt/Microsoft.Terminal.Core.h>
#include <winrt/Microsoft.Terminal.Control.h>
#include <winrt/Microsoft.Terminal.Settings.Model.h>
#include <winrt/Microsoft.UI.Xaml.Controls.h>

View File

@@ -5,6 +5,9 @@
#include "App.h"
#include "TerminalPage.h"
#include "ScratchpadContent.h"
#include "TasksPaneContent.h"
#include "MarkdownPaneContent.h"
#include "../WinRTUtils/inc/WtExeUtils.h"
#include "../../types/inc/utils.hpp"
#include "Utils.h"
@@ -1401,7 +1404,7 @@ namespace winrt::TerminalApp::implementation
{
if (const auto activePane{ activeTab->GetActivePane() })
{
_restartPaneConnection(activePane);
_restartPaneConnection(activePane->GetContent().try_as<TerminalApp::TerminalPaneContent>(), nullptr);
}
}
args.Handled(true);
@@ -1416,10 +1419,88 @@ namespace winrt::TerminalApp::implementation
}
args.Handled(true);
}
void TerminalPage::_HandleOpenScratchpad(const IInspectable& sender,
const ActionEventArgs& args)
{
if (Feature_ScratchpadPane::IsEnabled())
{
const auto& scratchPane{ winrt::make_self<ScratchpadContent>() };
// This is maybe a little wacky - add our key event handler to the pane
// we made. So that we can get actions for keys that the content didn't
// handle.
scratchPane->GetRoot().KeyDown({ this, &TerminalPage::_KeyDownHandler });
const auto resultPane = std::make_shared<Pane>(*scratchPane);
_SplitPane(_senderOrFocusedTab(sender), SplitDirection::Automatic, 0.5f, resultPane);
args.Handled(true);
}
}
void TerminalPage::_HandleOpenAbout(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
_ShowAboutDialog();
args.Handled(true);
}
void TerminalPage::_HandleOpenTasksPane(const IInspectable& sender,
const ActionEventArgs& args)
{
if (Feature_ScratchpadPane::IsEnabled())
{
const auto& scratchPane{ winrt::make_self<TasksPaneContent>() };
scratchPane->UpdateSettings(_settings);
// This is maybe a little wacky - add our key event handler to the pane
// we made. So that we can get actions for keys that the content didn't
// handle.
scratchPane->GetRoot().KeyDown({ this, &TerminalPage::_KeyDownHandler });
scratchPane->DispatchCommandRequested({ this, &TerminalPage::_OnDispatchCommandRequested });
const auto resultPane = std::make_shared<Pane>(*scratchPane);
_SplitPane(_senderOrFocusedTab(sender), SplitDirection::Automatic, 0.5f, resultPane);
args.Handled(true);
}
}
void TerminalPage::_HandleOpenMarkdownPane(const IInspectable& sender,
const ActionEventArgs& args)
{
if (Feature_ScratchpadPane::IsEnabled())
{
winrt::hstring filePath = L"";
if (args)
{
if (const auto& realArgs = args.ActionArgs().try_as<OpenMarkdownPaneArgs>())
{
// Use the CWD of the Terminal to evaluate this path, like a
// startingDirectory. This lets the `wtd open` command work
// on relative file paths.
filePath = _evaluatePathForCwd(realArgs.Path());
}
}
const auto& scratchPane{ winrt::make_self<MarkdownPaneContent>(filePath) };
scratchPane->UpdateSettings(_settings);
// This is maybe a little wacky - add our key event handler to the pane
// we made. So that we can get actions for keys that the content didn't
// handle.
scratchPane->GetRoot().KeyDown({ this, &TerminalPage::_KeyDownHandler });
// scratchPane->DispatchCommandRequested({ this, &TerminalPage::_OnDispatchCommandRequested });
scratchPane->DispatchActionRequested([weak = get_weak()](const auto& sender, const auto& actionAndArgs) {
if (const auto& page{ weak.get() })
{
page->_actionDispatch->DoAction(sender, actionAndArgs);
}
});
const auto resultPane = std::make_shared<Pane>(*scratchPane);
_SplitPane(_senderOrFocusedTab(sender), SplitDirection::Automatic, 0.5f, resultPane);
args.Handled(true);
}
}
}

View File

@@ -209,6 +209,7 @@ void AppCommandlineArgs::_buildParser()
_buildMovePaneParser();
_buildSwapPaneParser();
_buildFocusPaneParser();
_buildOpenParser();
}
// Method Description:
@@ -537,6 +538,38 @@ void AppCommandlineArgs::_buildFocusPaneParser()
setupSubcommand(_focusPaneShort);
}
void AppCommandlineArgs::_buildOpenParser()
{
_openCommand = _app.add_subcommand("open", "Open a file in a pane");
auto setupSubcommand = [this](auto& subcommand) {
auto* pathOpt = subcommand->add_option("path",
_filePath,
"TODO! description");
// directionOpt->transform(CLI::CheckedTransformer(focusDirectionMap, CLI::ignore_case));
pathOpt->required();
// When ParseCommand is called, if this subcommand was provided, this
// callback function will be triggered on the same thread. We can be sure
// that `this` will still be safe - this function just lets us know this
// command was parsed.
subcommand->callback([&, this]() {
if (_windowTarget.empty())
{
_windowTarget = "0";
}
// Build the NewTab action from the values we've parsed on the commandline.
ActionAndArgs action{ ShortcutAction::OpenMarkdownPane,
OpenMarkdownPaneArgs{ winrt::to_hstring(_filePath) } };
_startupActions.push_back(action);
});
};
setupSubcommand(_openCommand);
}
// Method Description:
// - Add the `NewTerminalArgs` parameters to the given subcommand. This enables
// that subcommand to support all the properties in a NewTerminalArgs.
@@ -699,6 +732,7 @@ bool AppCommandlineArgs::_noCommandsProvided()
*_swapPaneCommand ||
*_focusPaneCommand ||
*_focusPaneShort ||
*_openCommand ||
*_newPaneShort.subcommand ||
*_newPaneCommand.subcommand);
}
@@ -737,6 +771,8 @@ void AppCommandlineArgs::_resetStateToDefault()
_focusPaneTarget = -1;
_loadPersistedLayoutIdx = -1;
_filePath = "";
// DON'T clear _launchMode here! This will get called once for every
// subcommand, so we don't want `wt -F new-tab ; split-pane` clearing out
// the "global" fullscreen flag (-F).

View File

@@ -92,6 +92,7 @@ private:
CLI::App* _swapPaneCommand;
CLI::App* _focusPaneCommand;
CLI::App* _focusPaneShort;
CLI::App* _openCommand;
// Are you adding a new sub-command? Make sure to update _noCommandsProvided!
@@ -121,6 +122,8 @@ private:
bool _focusPrevTab{ false };
int _focusPaneTarget{ -1 };
std::string _filePath;
// Are you adding more args here? Make sure to reset them in _resetStateToDefault
const Commandline* _currentCommandline{ nullptr };
@@ -146,6 +149,7 @@ private:
void _buildMovePaneParser();
void _buildSwapPaneParser();
void _buildFocusPaneParser();
void _buildOpenParser();
bool _noCommandsProvided();
void _resetStateToDefault();
int _handleExit(const CLI::App& command, const CLI::Error& e);

View File

@@ -0,0 +1,31 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "CodeBlock.h"
#include <LibraryResources.h>
#include "CodeBlock.g.cpp"
#include "RequestRunCommandsArgs.g.cpp"
namespace winrt
{
namespace MUX = Microsoft::UI::Xaml;
namespace WUX = Windows::UI::Xaml;
using IInspectable = Windows::Foundation::IInspectable;
}
namespace winrt::TerminalApp::implementation
{
CodeBlock::CodeBlock(const winrt::hstring& initialCommandlines) :
Commandlines(initialCommandlines)
{
InitializeComponent();
}
void CodeBlock::_playPressed(const Windows::Foundation::IInspectable&,
const Windows::UI::Xaml::Input::TappedRoutedEventArgs&)
{
auto args = winrt::make_self<RequestRunCommandsArgs>(Commandlines());
RequestRunCommands.raise(*this, *args);
}
}

View File

@@ -0,0 +1,41 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include "CodeBlock.g.h"
#include "RequestRunCommandsArgs.g.h"
#include "../../../src/cascadia/inc/cppwinrt_utils.h"
#include <til/hash.h>
namespace winrt::TerminalApp::implementation
{
struct CodeBlock : CodeBlockT<CodeBlock>
{
CodeBlock(const winrt::hstring& initialCommandlines);
til::property<winrt::hstring> Commandlines;
// TODO! this should just be til::property_changed_event but I don't havfe tht commit here
til::event<winrt::Windows::UI::Xaml::Data::PropertyChangedEventHandler> PropertyChanged;
til::typed_event<TerminalApp::CodeBlock, RequestRunCommandsArgs> RequestRunCommands;
private:
friend struct CodeBlockT<CodeBlock>; // for Xaml to bind events
void _playPressed(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::TappedRoutedEventArgs& e);
};
struct RequestRunCommandsArgs : RequestRunCommandsArgsT<RequestRunCommandsArgs>
{
RequestRunCommandsArgs(const winrt::hstring& commandlines) :
Commandlines{ commandlines } {};
til::property<winrt::hstring> Commandlines;
};
}
namespace winrt::TerminalApp::factory_implementation
{
BASIC_FACTORY(CodeBlock);
}

View File

@@ -0,0 +1,21 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
namespace TerminalApp
{
runtimeclass RequestRunCommandsArgs
{
String Commandlines { get;};
}
[default_interface] runtimeclass CodeBlock : Windows.UI.Xaml.Controls.UserControl,
Windows.UI.Xaml.Data.INotifyPropertyChanged
{
CodeBlock(String initialCommandlines);
String Commandlines { get; set; };
event Windows.Foundation.TypedEventHandler<CodeBlock, RequestRunCommandsArgs> RequestRunCommands;
};
}

View File

@@ -0,0 +1,170 @@
<!--
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.CodeBlock"
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:TerminalApp"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
d:DesignHeight="256"
d:DesignWidth="1024"
mc:Ignorable="d">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<Color x:Key="BlockBackgroundColor">#f6f8fa</Color>
<Color x:Key="BlockBorderColor">#d3d3d3</Color>
<Color x:Key="PlayButtonHoveredColor">#257f01</Color>
<Color x:Key="PlayButtonNormalColor">#88222222</Color>
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<!--<Color x:Key="BlockBackgroundColor">#161b22</Color>-->
<Color x:Key="BlockBackgroundColor">#1e1e1e</Color>
<Color x:Key="BlockBorderColor">#30363d</Color>
<Color x:Key="PlayButtonHoveredColor">#90ef90</Color>
<Color x:Key="PlayButtonNormalColor">#8888</Color>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
<Style x:Key="PlayButtonTemplate"
TargetType="Button">
<Setter Property="Margin" Value="0" />
<Setter Property="Padding" Value="4" />
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="ButtonBaseElement"
Padding="{TemplateBinding Padding}"
AutomationProperties.AccessibilityView="Raw"
Background="{TemplateBinding Background}"
BackgroundSizing="{TemplateBinding BackgroundSizing}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}">
<Viewbox Width="20"
Height="20">
<Grid>
<FontIcon x:Name="ButtonBackgroundIcon"
FontFamily="Segoe UI, Segoe Fluent Icons, Segoe MDL2 Assets"
Foreground="{ThemeResource PlayButtonHoveredColor}"
Glyph="&#xF5b0;"
Visibility="Collapsed" />
<FontIcon x:Name="ButtonOutlineIcon"
FontFamily="Segoe UI, Segoe Fluent Icons, Segoe MDL2 Assets"
Foreground="{ThemeResource PlayButtonNormalColor}"
Glyph="&#xE768;" />
<muxc:ProgressRing x:Name="StatusProgress"
IsActive="False" />
</Grid>
<!-- TODO! FontFamily="{ThemeResource SymbolThemeFontFamily}" -->
</Viewbox>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<VisualState.Setters>
<Setter Target="ButtonBackgroundIcon.Visibility" Value="Collapsed" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="PointerOver">
<VisualState.Setters>
<Setter Target="ButtonBackgroundIcon.Visibility" Value="Visible" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Disabled" />
</VisualStateGroup>
<VisualStateGroup x:Name="PlayButtonStates">
<VisualState x:Name="Ready">
<VisualState.Setters>
<Setter Target="ButtonBackgroundIcon.Glyph" Value="&#xF5b0;" />
<Setter Target="ButtonOutlineIcon.Glyph" Value="&#xE768;" />
<Setter Target="StatusProgress.IsActive" Value="False" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Running">
<VisualState.Setters>
<Setter Target="ButtonBackgroundIcon.Glyph" Value=" " />
<Setter Target="ButtonOutlineIcon.Glyph" Value=" " />
<Setter Target="StatusProgress.IsActive" Value="True" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="AlreadyRan">
<VisualState.Setters>
<Setter Target="ButtonBackgroundIcon.Glyph" Value="&#xe72c;" />
<Setter Target="ButtonOutlineIcon.Glyph" Value="&#xe72c;" />
<Setter Target="StatusProgress.IsActive" Value="False" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="CodeBlockLineTemplate"
TargetType="TextBlock">
<Setter Property="FontFamily" Value="Cascadia Code" />
</Style>
</ResourceDictionary>
</UserControl.Resources>
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button x:Name="RunButton"
Grid.Column="0"
Margin="0,9,0,0"
Padding="0"
VerticalAlignment="Top"
Style="{StaticResource PlayButtonTemplate}"
Tapped="_playPressed" />
<Border Grid.Column="1"
HorizontalAlignment="Stretch"
Background="{ThemeResource BlockBackgroundColor}"
BorderBrush="{ThemeResource BlockBorderColor}"
BorderThickness="1"
CornerRadius="4">
<StackPanel x:Name="CommandsAndOutput">
<StackPanel x:Name="CommandLines"
Padding="8">
<TextBlock FontFamily="Cascadia Code"
IsTextSelectionEnabled="True"
Text="{x:Bind Commandlines}" />
</StackPanel>
<StackPanel x:Name="OutputBlockContainer"
Visibility="Collapsed">
<!-- Put the TermControl here. -->
</StackPanel>
</StackPanel>
</Border>
</Grid>
</UserControl>

View File

@@ -21,11 +21,22 @@ namespace winrt::TerminalApp::implementation
{
// This class is a wrapper of PaletteItem, that is used as an item of a filterable list in CommandPalette.
// It manages a highlighted text that is computed by matching search filter characters to item name
FilteredCommand::FilteredCommand(const winrt::TerminalApp::PaletteItem& item) :
_Item(item),
_Filter(L""),
_Weight(0)
FilteredCommand::FilteredCommand(const winrt::TerminalApp::PaletteItem& item)
{
// Actually implement the ctor in _constructFilteredCommand
_constructFilteredCommand(item);
}
// We need to actually implement the ctor in a separate helper. This is
// because we have a FilteredTask class which derives from FilteredCommand.
// HOWEVER, for cppwinrt ~ r e a s o n s ~, it doesn't actually derive from
// FilteredCommand directly, so we can't just use the FilteredCommand ctor
// directly in the base class.
void FilteredCommand::_constructFilteredCommand(const winrt::TerminalApp::PaletteItem& item)
{
_Item = item;
_Filter = L"";
_Weight = 0;
_HighlightedName = _computeHighlightedName();
// Recompute the highlighted name if the item name changes

View File

@@ -19,7 +19,7 @@ namespace winrt::TerminalApp::implementation
FilteredCommand() = default;
FilteredCommand(const winrt::TerminalApp::PaletteItem& item);
void UpdateFilter(const winrt::hstring& filter);
virtual void UpdateFilter(const winrt::hstring& filter);
static int Compare(const winrt::TerminalApp::FilteredCommand& first, const winrt::TerminalApp::FilteredCommand& second);
@@ -29,6 +29,9 @@ namespace winrt::TerminalApp::implementation
WINRT_OBSERVABLE_PROPERTY(winrt::TerminalApp::HighlightedText, HighlightedName, PropertyChanged.raise);
WINRT_OBSERVABLE_PROPERTY(int, Weight, PropertyChanged.raise);
protected:
void _constructFilteredCommand(const winrt::TerminalApp::PaletteItem& item);
private:
winrt::TerminalApp::HighlightedText _computeHighlightedName();
int _computeWeight();

View File

@@ -6,7 +6,7 @@ import "HighlightedTextControl.idl";
namespace TerminalApp
{
[default_interface] runtimeclass FilteredCommand : Windows.UI.Xaml.Data.INotifyPropertyChanged
[default_interface] unsealed runtimeclass FilteredCommand : Windows.UI.Xaml.Data.INotifyPropertyChanged
{
FilteredCommand();
FilteredCommand(PaletteItem item);

View File

@@ -0,0 +1,56 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
namespace TerminalApp
{
runtimeclass BellEventArgs
{
Boolean FlashTaskbar { get; };
};
interface IPaneContent
{
Windows.UI.Xaml.FrameworkElement GetRoot();
void UpdateSettings(Microsoft.Terminal.Settings.Model.CascadiaSettings settings);
Windows.Foundation.Size MinimumSize { get; };
String Title { get; };
UInt64 TaskbarState { get; };
UInt64 TaskbarProgress { get; };
Boolean ReadOnly { get; };
String Icon { get; };
Windows.Foundation.IReference<Windows.UI.Color> TabColor { get; };
Windows.UI.Xaml.Media.Brush BackgroundBrush { get; };
Microsoft.Terminal.Settings.Model.NewTerminalArgs GetNewTerminalArgs(Boolean asContent);
void Focus(Windows.UI.Xaml.FocusState reason);
void Close();
event Windows.Foundation.TypedEventHandler<IPaneContent, Object> CloseRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> ConnectionStateChanged;
event Windows.Foundation.TypedEventHandler<IPaneContent, BellEventArgs> BellRequested;
event Windows.Foundation.TypedEventHandler<IPaneContent, Object> TitleChanged;
event Windows.Foundation.TypedEventHandler<IPaneContent, Object> TabColorChanged;
event Windows.Foundation.TypedEventHandler<IPaneContent, Object> TaskbarProgressChanged;
event Windows.Foundation.TypedEventHandler<IPaneContent, Object> ReadOnlyChanged;
event Windows.Foundation.TypedEventHandler<IPaneContent, Object> FocusRequested;
};
enum PaneSnapDirection
{
Width,
Height
};
interface ISnappable
{
Single SnapDownToGrid(PaneSnapDirection direction, Single sizeToSnap);
Windows.Foundation.Size GridUnitSize { get; };
};
}

View File

@@ -0,0 +1,454 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "MarkdownPaneContent.h"
#include <LibraryResources.h>
#include "MarkdownPaneContent.g.cpp"
#include "CodeBlock.h"
#define MD4C_USE_UTF16
#include "..\..\oss\md4c\md4c.h"
using namespace std::chrono_literals;
using namespace winrt::Microsoft::Terminal;
using namespace winrt::Microsoft::Terminal::Settings;
using namespace winrt::Microsoft::Terminal::Settings::Model;
namespace winrt
{
namespace MUX = Microsoft::UI::Xaml;
namespace WUX = Windows::UI::Xaml;
using IInspectable = Windows::Foundation::IInspectable;
}
namespace winrt::TerminalApp::implementation
{
struct MyMarkdownData
{
WUX::Controls::StackPanel root{};
implementation::MarkdownPaneContent* page{ nullptr };
WUX::Controls::TextBlock current{ nullptr };
WUX::Documents::Run currentRun{ nullptr };
TerminalApp::CodeBlock currentCodeBlock{ nullptr };
};
WUX::Controls::TextBlock makeDefaultTextBlock()
{
WUX::Controls::TextBlock b{};
b.IsTextSelectionEnabled(true);
b.TextWrapping(WUX::TextWrapping::WrapWholeWords);
return b;
}
int md_parser_enter_block(MD_BLOCKTYPE type, void* detail, void* userdata)
{
MyMarkdownData* data = reinterpret_cast<MyMarkdownData*>(userdata);
switch (type)
{
case MD_BLOCK_UL:
{
break;
}
case MD_BLOCK_H:
{
MD_BLOCK_H_DETAIL* headerDetail = reinterpret_cast<MD_BLOCK_H_DETAIL*>(detail);
data->current = makeDefaultTextBlock();
const auto fontSize = std::max(16u, 36u - ((headerDetail->level - 1) * 6u));
data->current.FontSize(fontSize);
data->current.FontWeight(Windows::UI::Text::FontWeights::Bold());
WUX::Documents::Run run{};
// run.Text(winrtL'#');
// Immediately add the header block
data->root.Children().Append(data->current);
if (headerDetail->level == 1)
{
// <Border Height="1" BorderThickness="1" BorderBrush="Red" HorizontalAlignment="Stretch"></Border>
WUX::Controls::Border b;
b.Height(1);
b.BorderThickness(WUX::ThicknessHelper::FromLengths(1, 1, 1, 1));
b.BorderBrush(WUX::Media::SolidColorBrush(Windows::UI::Colors::Gray()));
b.HorizontalAlignment(WUX::HorizontalAlignment::Stretch);
data->root.Children().Append(b);
}
break;
}
case MD_BLOCK_CODE:
{
MD_BLOCK_CODE_DETAIL* codeDetail = reinterpret_cast<MD_BLOCK_CODE_DETAIL*>(detail);
codeDetail;
data->currentCodeBlock = winrt::make<implementation::CodeBlock>(L"");
data->currentCodeBlock.Margin(WUX::ThicknessHelper::FromLengths(8, 8, 8, 8));
data->currentCodeBlock.RequestRunCommands({ data->page, &MarkdownPaneContent::_handleRunCommandRequest });
data->root.Children().Append(data->currentCodeBlock);
}
default:
{
break;
}
}
return 0;
}
int md_parser_leave_block(MD_BLOCKTYPE type, void* /*detail*/, void* userdata)
{
MyMarkdownData* data = reinterpret_cast<MyMarkdownData*>(userdata);
data;
switch (type)
{
case MD_BLOCK_UL:
{
break;
}
case MD_BLOCK_H:
{
// data->root.Children().Append(data->current);
data->current = nullptr;
break;
}
case MD_BLOCK_CODE:
{
// data->root.Children().Append(data->current);
data->current = nullptr;
break;
}
default:
{
break;
}
}
return 0;
}
int md_parser_enter_span(MD_SPANTYPE type, void* /*detail*/, void* userdata)
{
MyMarkdownData* data = reinterpret_cast<MyMarkdownData*>(userdata);
data;
if (data->current == nullptr)
{
data->current = makeDefaultTextBlock();
data->root.Children().Append(data->current);
}
if (data->currentRun == nullptr)
{
data->currentRun = WUX::Documents::Run();
}
auto currentRun = data->currentRun;
switch (type)
{
case MD_SPAN_STRONG:
{
currentRun.FontWeight(Windows::UI::Text::FontWeights::Bold());
break;
}
case MD_SPAN_EM:
{
currentRun.FontStyle(Windows::UI::Text::FontStyle::Italic);
break;
}
case MD_SPAN_CODE:
{
currentRun.FontFamily(WUX::Media::FontFamily{ L"Cascadia Code" });
break;
}
default:
{
break;
}
}
return 0;
}
int md_parser_leave_span(MD_SPANTYPE type, void* /*detail*/, void* userdata)
{
MyMarkdownData* data = reinterpret_cast<MyMarkdownData*>(userdata);
switch (type)
{
case MD_SPAN_EM:
case MD_SPAN_STRONG:
// {
// break;
// }
case MD_SPAN_CODE:
{
if (const auto& currentRun{ data->currentRun })
{
// data->current.Inlines().Append(currentRun);
// data->currentRun = nullptr;
}
break;
}
default:
{
break;
}
}
return 0;
}
int md_parser_text(MD_TEXTTYPE type, const MD_CHAR* text, MD_SIZE size, void* userdata)
{
MyMarkdownData* data = reinterpret_cast<MyMarkdownData*>(userdata);
winrt::hstring str{ text, size };
switch (type)
{
case MD_TEXT_BR:
case MD_TEXT_SOFTBR:
{
if (const auto& curr{ data->current })
{
data->current = makeDefaultTextBlock();
data->root.Children().Append(data->current);
}
break;
}
case MD_TEXT_CODE:
{
if (str == L"\n")
{
break;
}
if (const auto& codeBlock{ data->currentCodeBlock })
{
// code in a fenced block
auto currentText = codeBlock.Commandlines();
auto newText = currentText.empty() ? str :
currentText + winrt::hstring{ L"\r\n" } + str;
codeBlock.Commandlines(newText);
break;
}
else
{
// just normal `code` inline
// data->currentRun.Text(str);
[[fallthrough]];
}
}
case MD_TEXT_NORMAL:
default:
{
data->currentCodeBlock = nullptr;
auto run = data->currentRun ? data->currentRun : WUX::Documents::Run{};
run.Text(str);
if (data->current)
{
data->current.Inlines().Append(run);
}
else
{
WUX::Controls::TextBlock block = makeDefaultTextBlock();
block.Inlines().Append(run);
data->root.Children().Append(block);
data->current = block;
}
// data->root.Children().Append(block);
data->currentRun = nullptr;
break;
}
}
return 0;
}
int parseMarkdown(const winrt::hstring& markdown, MyMarkdownData& data)
{
MD_PARSER parser{
.abi_version = 0,
.flags = 0,
.enter_block = &md_parser_enter_block,
.leave_block = &md_parser_leave_block,
.enter_span = &md_parser_enter_span,
.leave_span = &md_parser_leave_span,
.text = &md_parser_text,
};
const auto result = md_parse(
markdown.c_str(),
(unsigned)markdown.size(),
&parser,
&data // user data
);
return result;
}
MarkdownPaneContent::MarkdownPaneContent() :
MarkdownPaneContent(L"") {}
MarkdownPaneContent::MarkdownPaneContent(const winrt::hstring& initialPath)
{
InitializeComponent();
auto res = Windows::UI::Xaml::Application::Current().Resources();
auto bg = res.Lookup(winrt::box_value(L"UnfocusedBorderBrush"));
Background(bg.try_as<WUX::Media::Brush>());
FilePathInput().Text(initialPath);
_filePath = FilePathInput().Text();
_loadFile();
}
void MarkdownPaneContent::_clearOldNotebook()
{
RenderedMarkdown().Children().Clear();
}
void MarkdownPaneContent::_loadFile()
{
// TODO! use our til::io file readers
// Read _filePath, then parse as markdown.
const wil::unique_handle file{ CreateFileW(_filePath.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, nullptr) };
if (!file)
{
return;
}
char buffer[32 * 1024];
DWORD read = 0;
for (;;)
{
if (!ReadFile(file.get(), &buffer[0], sizeof(buffer), &read, nullptr))
{
break;
}
if (read < sizeof(buffer))
{
break;
}
}
// BLINDLY TREATING TEXT AS utf-8 (I THINK)
std::string markdownContents{ buffer, read };
Editing(false);
PropertyChanged.raise(*this, WUX::Data::PropertyChangedEventArgs{ L"Editing" });
FileContents(winrt::to_hstring(markdownContents));
PropertyChanged.raise(*this, WUX::Data::PropertyChangedEventArgs{ L"FileContents" });
_renderFileContents();
}
void MarkdownPaneContent::_renderFileContents()
{
// Was the file a .md file?
if (_filePath.ends_with(L".md"))
{
_loadMarkdown();
}
else
{
_loadText();
}
}
void MarkdownPaneContent::_loadText()
{
auto block = WUX::Controls::TextBlock();
block.IsTextSelectionEnabled(true);
block.FontFamily(WUX::Media::FontFamily{ L"Cascadia Code" });
block.Text(FileContents());
RenderedMarkdown().Children().Append(block);
}
void MarkdownPaneContent::_loadMarkdown()
{
MyMarkdownData data;
data.page = this;
const auto parseResult = parseMarkdown(FileContents(), data);
if (0 == parseResult)
{
RenderedMarkdown().Children().Append(data.root);
}
}
void MarkdownPaneContent::_loadTapped(const Windows::Foundation::IInspectable&, const Windows::UI::Xaml::Input::TappedRoutedEventArgs&)
{
_filePath = FilePathInput().Text();
// Does the file exist? if not, bail
const wil::unique_handle file{ CreateFileW(_filePath.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, nullptr) };
if (!file)
{
return;
}
// It does. Clear the old one
_clearOldNotebook();
_loadFile();
}
void MarkdownPaneContent::_editTapped(const Windows::Foundation::IInspectable&, const Windows::UI::Xaml::Input::TappedRoutedEventArgs&)
{
if (Editing())
{
_clearOldNotebook();
_renderFileContents();
EditIcon().Glyph(L"\xe932"); // Label
_scrollViewer().Visibility(WUX::Visibility::Visible);
_editor().Visibility(WUX::Visibility::Collapsed);
Editing(false);
}
else
{
EditIcon().Glyph(L"\xe890"); // View
_scrollViewer().Visibility(WUX::Visibility::Collapsed);
_editor().Visibility(WUX::Visibility::Visible);
Editing(true);
}
PropertyChanged.raise(*this, WUX::Data::PropertyChangedEventArgs{ L"Editing" });
}
void MarkdownPaneContent::_closeTapped(const Windows::Foundation::IInspectable&, const Windows::UI::Xaml::Input::TappedRoutedEventArgs&)
{
// // Clear the old one
// _clearOldNotebook();
// _loadFile();
CloseRequested.raise(*this, nullptr);
}
void MarkdownPaneContent::_handleRunCommandRequest(const TerminalApp::CodeBlock& sender,
const TerminalApp::RequestRunCommandsArgs& request)
{
auto text = request.Commandlines();
sender;
text;
if (const auto& strongControl{ _control.get() })
{
Model::ActionAndArgs actionAndArgs{ ShortcutAction::SendInput, Model::SendInputArgs{ text + winrt::hstring{ L"\r" } } };
// By using the last active control as the sender here, the
// actiopn dispatch will send this to the active control,
// thinking that it is the control that requested this event.
DispatchActionRequested.raise(strongControl, actionAndArgs);
}
}
#pragma region IPaneContent
winrt::Windows::UI::Xaml::FrameworkElement MarkdownPaneContent::GetRoot()
{
return *this;
}
void MarkdownPaneContent::Close()
{
CloseRequested.raise(*this, nullptr);
}
winrt::hstring MarkdownPaneContent::Icon() const
{
static constexpr std::wstring_view glyph{ L"\xe70b" }; // QuickNote
return winrt::hstring{ glyph };
}
#pragma endregion
void MarkdownPaneContent::SetLastActiveControl(const Microsoft::Terminal::Control::TermControl& control)
{
_control = control;
}
}

View File

@@ -0,0 +1,82 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include "MarkdownPaneContent.g.h"
namespace winrt::TerminalApp::implementation
{
struct MarkdownPaneContent : MarkdownPaneContentT<MarkdownPaneContent>
{
public:
MarkdownPaneContent();
MarkdownPaneContent(const winrt::hstring& filePath);
til::property<bool> Editing{ false };
til::property<winrt::hstring> FileContents{ L"" };
void SetLastActiveControl(const Microsoft::Terminal::Control::TermControl& control);
// TODO! this should just be til::property_changed_event but I don't havfe tht commit here
til::event<winrt::Windows::UI::Xaml::Data::PropertyChangedEventHandler> PropertyChanged;
#pragma region IPaneContent
winrt::Windows::UI::Xaml::FrameworkElement GetRoot();
void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings&){};
winrt::Windows::Foundation::Size MinimumSize() { return { 1, 1 }; };
void Focus(winrt::Windows::UI::Xaml::FocusState reason = winrt::Windows::UI::Xaml::FocusState::Programmatic) { reason; };
void Close();
winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetNewTerminalArgs(const bool /*asContent*/) const { return nullptr; };
// TODO! lots of strings here and in XAML that need RS_-ifying
winrt::hstring Title() { return _filePath; }
uint64_t TaskbarState() { return 0; }
uint64_t TaskbarProgress() { return 0; }
bool ReadOnly() { return false; }
winrt::hstring Icon() const;
Windows::Foundation::IReference<winrt::Windows::UI::Color> TabColor() const noexcept { return nullptr; }
winrt::Windows::UI::Xaml::Media::Brush BackgroundBrush() { return Background(); }
til::typed_event<> ConnectionStateChanged;
til::typed_event<IPaneContent> CloseRequested;
til::typed_event<IPaneContent, winrt::TerminalApp::BellEventArgs> BellRequested;
til::typed_event<IPaneContent> TitleChanged;
til::typed_event<IPaneContent> TabColorChanged;
til::typed_event<IPaneContent> TaskbarProgressChanged;
til::typed_event<IPaneContent> ReadOnlyChanged;
til::typed_event<IPaneContent> FocusRequested;
#pragma endregion
til::typed_event<winrt::Windows::Foundation::IInspectable, Microsoft::Terminal::Settings::Model::ActionAndArgs> DispatchActionRequested;
void _handleRunCommandRequest(const TerminalApp::CodeBlock& sender,
const TerminalApp::RequestRunCommandsArgs& control);
private:
friend struct MarkdownPaneContentT<MarkdownPaneContent>; // for Xaml to bind events
winrt::hstring _filePath{};
// winrt::hstring _fileContents{};
winrt::weak_ref<Microsoft::Terminal::Control::TermControl> _control{ nullptr };
void _clearOldNotebook();
void _loadFile();
void _renderFileContents();
void _loadText();
void _loadMarkdown();
void _loadTapped(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::TappedRoutedEventArgs& e);
void _editTapped(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::TappedRoutedEventArgs& e);
void _closeTapped(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::TappedRoutedEventArgs& e);
};
}
namespace winrt::TerminalApp::factory_implementation
{
BASIC_FACTORY(MarkdownPaneContent);
}

View File

@@ -0,0 +1,25 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import "IPaneContent.idl";
import "FilteredCommand.idl";
import "CodeBlock.idl";
namespace TerminalApp
{
[default_interface] runtimeclass MarkdownPaneContent : Windows.UI.Xaml.Controls.UserControl,
IPaneContent,
Windows.UI.Xaml.Data.INotifyPropertyChanged
{
MarkdownPaneContent();
MarkdownPaneContent(String originalPath);
void SetLastActiveControl(Microsoft.Terminal.Control.TermControl control);
Boolean Editing;
String FileContents;
event Windows.Foundation.TypedEventHandler<Object, Microsoft.Terminal.Settings.Model.ActionAndArgs> DispatchActionRequested;
}
}

View File

@@ -0,0 +1,106 @@
<!--
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.MarkdownPaneContent"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:TerminalApp"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mux="using:Microsoft.UI.Xaml.Controls"
mc:Ignorable="d">
<UserControl.Resources />
<Grid x:Name="Root">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox x:Name="FilePathInput"
Grid.Column="0"
Margin="4"
PlaceholderText="Enter a path to a markdown file..."
Text="Z:\dev\simple-test.md" />
<StackPanel Grid.Column="1"
Orientation="Horizontal">
<Button Margin="4"
Tapped="_loadTapped">
<FontIcon FontFamily="Segoe UI, Segoe Fluent Icons, Segoe MDL2 Assets"
FontSize="12"
Glyph="&#xe8e5;" />
<!-- OpenFile -->
</Button>
<Button Margin="4"
Tapped="_editTapped">
<FontIcon x:Name="EditIcon"
FontFamily="Segoe UI, Segoe Fluent Icons, Segoe MDL2 Assets"
FontSize="12"
Glyph="&#xe932;" />
<!-- Label -->
</Button>
<Button Margin="4"
Tapped="_closeTapped">
<FontIcon FontFamily="Segoe UI, Segoe Fluent Icons, Segoe MDL2 Assets"
FontSize="12"
Glyph="&#xe8bb;" />
<!-- ChromeClose -->
</Button>
</StackPanel>
</Grid>
<Grid x:Name="TabContent"
Grid.Row="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid x:Name="InProcContent"
Grid.Column="0"
Padding="16"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="#ff0000" />
<TextBox x:Name="_editor"
Grid.Column="1"
Padding="3"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
AcceptsReturn="True"
FontFamily="Cascadia Code"
IsSpellCheckEnabled="False"
Text="{x:Bind FileContents, Mode=TwoWay}"
Visibility="{x:Bind Editing}" />
<ScrollViewer x:Name="_scrollViewer"
Grid.Column="1"
Padding="3"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="Transparent"
BringIntoViewOnFocusChange="True"
IsVerticalScrollChainingEnabled="True">
<StackPanel x:Name="RenderedMarkdown"
Grid.Column="1"
Padding="16"
HorizontalAlignment="Stretch"
Background="Transparent"
Orientation="Vertical" />
</ScrollViewer>
</Grid>
</Grid>
</UserControl>

View File

@@ -3,6 +3,7 @@
#include "pch.h"
#include "Pane.h"
#include "AppLogic.h"
#include "Utils.h"
@@ -33,19 +34,21 @@ 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)));
Pane::Pane(const Profile& profile, const TermControl& control, const bool lastFocused) :
_control{ control },
_lastActive{ lastFocused },
_profile{ profile }
Pane::Pane(const IPaneContent& content, const bool lastFocused) :
_content{ content },
_lastActive{ lastFocused }
{
_root.Children().Append(_borderFirst);
_borderFirst.Child(_control);
_setupControlEvents();
const auto& control{ _content.GetRoot() };
_borderFirst.Child(control);
// Register an event with the control to have it inform us when it gains focus.
_gotFocusRevoker = _control.GotFocus(winrt::auto_revoke, { this, &Pane::_ControlGotFocusHandler });
_lostFocusRevoker = _control.LostFocus(winrt::auto_revoke, { this, &Pane::_ControlLostFocusHandler });
if (control)
{
_gotFocusRevoker = control.GotFocus(winrt::auto_revoke, { this, &Pane::_ContentGotFocusHandler });
_lostFocusRevoker = control.LostFocus(winrt::auto_revoke, { this, &Pane::_ContentLostFocusHandler });
}
// When our border is tapped, make sure to transfer focus to our control.
// LOAD-BEARING: This will NOT work if the border's BorderBrush is set to
@@ -102,19 +105,6 @@ Pane::Pane(std::shared_ptr<Pane> first,
});
}
void Pane::_setupControlEvents()
{
_controlEvents._ConnectionStateChanged = _control.ConnectionStateChanged(winrt::auto_revoke, { this, &Pane::_ControlConnectionStateChangedHandler });
_controlEvents._WarningBell = _control.WarningBell(winrt::auto_revoke, { this, &Pane::_ControlWarningBellHandler });
_controlEvents._CloseTerminalRequested = _control.CloseTerminalRequested(winrt::auto_revoke, { this, &Pane::_CloseTerminalRequestedHandler });
_controlEvents._RestartTerminalRequested = _control.RestartTerminalRequested(winrt::auto_revoke, { this, &Pane::_RestartTerminalRequestedHandler });
_controlEvents._ReadOnlyChanged = _control.ReadOnlyChanged(winrt::auto_revoke, { this, &Pane::_ControlReadOnlyChangedHandler });
}
void Pane::_removeControlEvents()
{
_controlEvents = {};
}
// Method Description:
// - Extract the terminal settings from the current (leaf) pane's control
// to be used to create an equivalent control
@@ -129,55 +119,7 @@ NewTerminalArgs Pane::GetTerminalArgsForPane(const bool asContent) const
// Leaves are the only things that have controls
assert(_IsLeaf());
NewTerminalArgs args{};
auto controlSettings = _control.Settings();
args.Profile(controlSettings.ProfileName());
// If we know the user's working directory use it instead of the profile.
if (const auto dir = _control.WorkingDirectory(); !dir.empty())
{
args.StartingDirectory(dir);
}
else
{
args.StartingDirectory(controlSettings.StartingDirectory());
}
args.TabTitle(controlSettings.StartingTitle());
args.Commandline(controlSettings.Commandline());
args.SuppressApplicationTitle(controlSettings.SuppressApplicationTitle());
if (controlSettings.TabColor() || controlSettings.StartingTabColor())
{
til::color c;
// StartingTabColor is prioritized over other colors
if (const auto color = controlSettings.StartingTabColor())
{
c = til::color(color.Value());
}
else
{
c = til::color(controlSettings.TabColor().Value());
}
args.TabColor(winrt::Windows::Foundation::IReference<winrt::Windows::UI::Color>{ static_cast<winrt::Windows::UI::Color>(c) });
}
// TODO:GH#9800 - we used to be able to persist the color scheme that a
// TermControl was initialized with, by name. With the change to having the
// control own its own copy of its settings, this isn't possible anymore.
//
// We may be able to get around this by storing the Name in the Core::Scheme
// 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 ContentId if absolutely needed. If you fill in a number
// here (even 0), we'll serialize that number, AND treat that action as an
// "attach existing" rather than a "create"
if (asContent)
{
args.ContentId(_control.ContentId());
}
return args;
return _content.GetNewTerminalArgs(asContent);
}
// Method Description:
@@ -1022,181 +964,18 @@ Pane::PaneNeighborSearch Pane::_FindPaneAndNeighbor(const std::shared_ptr<Pane>
}
// Method Description:
// - Called when our attached control is closed. Triggers listeners to our close
// event, if we're a leaf pane.
// - If this was called, and we became a parent pane (due to work on another
// thread), this function will do nothing (allowing the control's new parent
// to handle the event instead).
// - Returns true if the connection state of this pane is closed.
// Arguments:
// - <none>
// Return Value:
// - <none>
winrt::fire_and_forget Pane::_ControlConnectionStateChangedHandler(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable& /*args*/)
// - true if the connection state of this Pane is closed.
bool Pane::IsConnectionClosed() const
{
auto newConnectionState = ConnectionState::Closed;
if (const auto coreState = sender.try_as<ICoreState>())
if (const auto& control{ GetTerminalControl() })
{
newConnectionState = coreState.ConnectionState();
}
const auto previousConnectionState = std::exchange(_connectionState, newConnectionState);
if (newConnectionState < ConnectionState::Closed)
{
// Pane doesn't care if the connection isn't entering a terminal state.
co_return;
}
const auto weakThis = weak_from_this();
co_await wil::resume_foreground(_root.Dispatcher());
const auto strongThis = weakThis.lock();
if (!strongThis)
{
co_return;
}
// It's possible that this event handler started being executed, scheduled
// on the UI thread, another child got created. So our control is
// actually no longer _our_ control, and instead could be a descendant.
//
// When the control's new Pane takes ownership of the control, the new
// parent will register its own event handler. That event handler will get
// fired after this handler returns, and will properly cleanup state.
if (!_IsLeaf())
{
co_return;
}
if (previousConnectionState < ConnectionState::Connected && newConnectionState >= ConnectionState::Failed)
{
// A failure to complete the connection (before it has _connected_) is not covered by "closeOnExit".
// This is to prevent a misconfiguration (closeOnExit: always, startingDirectory: garbage) resulting
// in Terminal flashing open and immediately closed.
co_return;
}
if (_profile)
{
if (_isDefTermSession && _profile.CloseOnExit() == CloseOnExitMode::Automatic)
{
// For 'automatic', we only care about the connection state if we were launched by Terminal
// Since we were launched via defterm, ignore the connection state (i.e. we treat the
// close on exit mode as 'always', see GH #13325 for discussion)
Close();
}
const auto mode = _profile.CloseOnExit();
if ((mode == CloseOnExitMode::Always) ||
((mode == CloseOnExitMode::Graceful || mode == CloseOnExitMode::Automatic) && newConnectionState == ConnectionState::Closed))
{
Close();
}
}
}
void Pane::_CloseTerminalRequestedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/,
const winrt::Windows::Foundation::IInspectable& /*args*/)
{
// It's possible that this event handler started being executed, then before
// we got the lock, another thread created another child. So our control is
// actually no longer _our_ control, and instead could be a descendant.
//
// When the control's new Pane takes ownership of the control, the new
// parent will register its own event handler. That event handler will get
// fired after this handler returns, and will properly cleanup state.
if (!_IsLeaf())
{
return;
}
Close();
}
void Pane::_RestartTerminalRequestedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/,
const winrt::Windows::Foundation::IInspectable& /*args*/)
{
if (!_IsLeaf())
{
return;
}
RestartTerminalRequested.raise(shared_from_this());
}
winrt::fire_and_forget Pane::_playBellSound(winrt::Windows::Foundation::Uri uri)
{
auto weakThis{ weak_from_this() };
co_await wil::resume_foreground(_root.Dispatcher());
if (auto pane{ weakThis.lock() })
{
if (!_bellPlayerCreated)
{
// The MediaPlayer might not exist on Windows N SKU.
try
{
_bellPlayerCreated = true;
_bellPlayer = winrt::Windows::Media::Playback::MediaPlayer();
// GH#12258: The media keys (like play/pause) should have no effect on our bell sound.
_bellPlayer.CommandManager().IsEnabled(false);
}
CATCH_LOG();
}
if (_bellPlayer)
{
const auto source{ winrt::Windows::Media::Core::MediaSource::CreateFromUri(uri) };
const auto item{ winrt::Windows::Media::Playback::MediaPlaybackItem(source) };
_bellPlayer.Source(item);
_bellPlayer.Play();
}
}
}
// Method Description:
// - Plays a warning note when triggered by the BEL control character,
// using the sound configured for the "Critical Stop" system event.`
// This matches the behavior of the Windows Console host.
// - Will also flash the taskbar if the bellStyle setting for this profile
// has the 'visual' flag set
// Arguments:
// - <unused>
void Pane::_ControlWarningBellHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/,
const winrt::Windows::Foundation::IInspectable& /*eventArgs*/)
{
if (!_IsLeaf())
{
return;
}
if (_profile)
{
// We don't want to do anything if nothing is set, so check for that first
if (static_cast<int>(_profile.BellStyle()) != 0)
{
if (WI_IsFlagSet(_profile.BellStyle(), winrt::Microsoft::Terminal::Settings::Model::BellStyle::Audible))
{
// Audible is set, play the sound
auto sounds{ _profile.BellSound() };
if (sounds && sounds.Size() > 0)
{
winrt::hstring soundPath{ wil::ExpandEnvironmentStringsW<std::wstring>(sounds.GetAt(rand() % sounds.Size()).c_str()) };
winrt::Windows::Foundation::Uri uri{ soundPath };
_playBellSound(uri);
}
else
{
const auto soundAlias = reinterpret_cast<LPCTSTR>(SND_ALIAS_SYSTEMHAND);
PlaySound(soundAlias, NULL, SND_ALIAS_ID | SND_ASYNC | SND_SENTRY);
}
}
if (WI_IsFlagSet(_profile.BellStyle(), winrt::Microsoft::Terminal::Settings::Model::BellStyle::Window))
{
_control.BellLightOn();
}
// raise the event with the bool value corresponding to the taskbar flag
PaneRaiseBell.raise(nullptr, WI_IsFlagSet(_profile.BellStyle(), winrt::Microsoft::Terminal::Settings::Model::BellStyle::Taskbar));
}
return control.ConnectionState() >= ConnectionState::Closed;
}
return false;
}
// Event Description:
@@ -1207,7 +986,7 @@ void Pane::_ControlWarningBellHandler(const winrt::Windows::Foundation::IInspect
// - <unused>
// Return Value:
// - <none>
void Pane::_ControlGotFocusHandler(const winrt::Windows::Foundation::IInspectable& sender,
void Pane::_ContentGotFocusHandler(const winrt::Windows::Foundation::IInspectable& sender,
const RoutedEventArgs& /* args */)
{
auto f = FocusState::Programmatic;
@@ -1222,7 +1001,7 @@ void Pane::_ControlGotFocusHandler(const winrt::Windows::Foundation::IInspectabl
// - Called when our control loses focus. We'll use this to trigger our LostFocus
// callback. The tab that's hosting us should have registered a callback which
// can be used to update its own internal focus state
void Pane::_ControlLostFocusHandler(const winrt::Windows::Foundation::IInspectable& /* sender */,
void Pane::_ContentLostFocusHandler(const winrt::Windows::Foundation::IInspectable& /* sender */,
const RoutedEventArgs& /* args */)
{
LostFocus.raise(shared_from_this());
@@ -1245,21 +1024,9 @@ void Pane::Close()
// and connections beneath it.
void Pane::Shutdown()
{
// Clear out our media player callbacks, and stop any playing media. This
// will prevent the callback from being triggered after we've closed, and
// also make sure that our sound stops when we're closed.
if (_bellPlayer)
{
_bellPlayer.Pause();
_bellPlayer.Source(nullptr);
_bellPlayer.Close();
_bellPlayer = nullptr;
_bellPlayerCreated = false;
}
if (_IsLeaf())
{
_control.Close();
_content.Close();
}
else
{
@@ -1314,7 +1081,7 @@ TermControl Pane::GetLastFocusedTerminalControl()
{
if (p->_IsLeaf())
{
return p->_control;
return p->GetTerminalControl();
}
pane = p;
}
@@ -1322,7 +1089,31 @@ TermControl Pane::GetLastFocusedTerminalControl()
}
return _firstChild->GetLastFocusedTerminalControl();
}
return _control;
// we _are_ a leaf.
return GetTerminalControl();
}
IPaneContent Pane::GetLastFocusedContent()
{
if (!_IsLeaf())
{
if (_lastActive)
{
auto pane = shared_from_this();
while (const auto p = pane->_parentChildPath.lock())
{
if (p->_IsLeaf())
{
return p->_content;
}
pane = p;
}
// We didn't find our child somehow, they might have closed under us.
}
return _firstChild->GetLastFocusedContent();
}
return _content;
}
// Method Description:
@@ -1332,9 +1123,16 @@ TermControl Pane::GetLastFocusedTerminalControl()
// - <none>
// Return Value:
// - nullptr if this Pane is a parent, otherwise the TermControl of this Pane.
TermControl Pane::GetTerminalControl()
TermControl Pane::GetTerminalControl() const
{
return _IsLeaf() ? _control : nullptr;
if (const auto& terminalPane{ _getTerminalContent() })
{
return terminalPane.GetTermControl();
}
else
{
return nullptr;
}
}
// Method Description:
@@ -1381,19 +1179,11 @@ void Pane::SetActive()
Profile Pane::GetFocusedProfile()
{
auto lastFocused = GetActivePane();
return lastFocused ? lastFocused->_profile : nullptr;
}
// Method Description:
// - Returns true if the connection state of this pane is closed. If this Pane is not a leaf this will
// return false.
// Arguments:
// - <none>
// Return Value:
// - true if the connection state of this Pane is closed.
bool Pane::IsConnectionClosed() const
{
return _control && _control.ConnectionState() >= ConnectionState::Closed;
if (const auto& terminalPane{ lastFocused->_getTerminalContent() })
{
return terminalPane.GetProfile();
}
return nullptr;
}
// Method Description:
@@ -1466,9 +1256,9 @@ void Pane::UpdateVisuals()
void Pane::_Focus()
{
GotFocus.raise(shared_from_this(), FocusState::Programmatic);
if (const auto& control = GetLastFocusedTerminalControl())
if (const auto& lastContent{ GetLastFocusedContent() })
{
control.Focus(FocusState::Programmatic);
lastContent.Focus(FocusState::Programmatic);
}
}
@@ -1508,20 +1298,22 @@ void Pane::_FocusFirstChild()
}
}
// Method Description:
// - Updates the settings of this pane, presuming that it is a leaf.
// Arguments:
// - settings: The new TerminalSettings to apply to any matching controls
// - profile: The profile from which these settings originated.
// Return Value:
// - <none>
void Pane::UpdateSettings(const TerminalSettingsCreateResult& settings, const Profile& profile)
void Pane::UpdateSettings(const CascadiaSettings& settings, const winrt::TerminalApp::TerminalSettingsCache& cache)
{
assert(_IsLeaf());
_profile = profile;
_control.UpdateControlSettings(settings.DefaultSettings(), settings.UnfocusedSettings());
if (_content)
{
// We need to do a bit more work here for terminal
// panes. They need to know about the profile that was used for
// them, and about the focused/unfocused settings.
if (const auto& terminalPaneContent{ _content.try_as<TerminalPaneContent>() })
{
terminalPaneContent.UpdateTerminalSettings(cache);
}
else
{
_content.UpdateSettings(settings);
}
}
}
// Method Description:
@@ -1573,7 +1365,7 @@ std::shared_ptr<Pane> Pane::DetachPane(std::shared_ptr<Pane> pane)
auto detached = isFirstChild ? _firstChild : _secondChild;
// Remove the child from the tree, replace the current node with the
// other child.
_CloseChild(isFirstChild, true);
_CloseChild(isFirstChild);
// Update the borders on this pane and any children to match if we have
// no parent.
@@ -1602,12 +1394,9 @@ std::shared_ptr<Pane> Pane::DetachPane(std::shared_ptr<Pane> pane)
// Arguments:
// - closeFirst: if true, the first child should be closed, and the second
// should be preserved, and vice-versa for false.
// - isDetaching: if true, then the pane event handlers for the closed child
// should be kept, this way they don't have to be recreated when it is later
// reattached to a tree somewhere as the control moves with the pane.
// Return Value:
// - <none>
void Pane::_CloseChild(const bool closeFirst, const bool isDetaching)
void Pane::_CloseChild(const bool closeFirst)
{
// If we're a leaf, then chances are both our children closed in close
// succession. We waited on the lock while the other child was closed, so
@@ -1643,35 +1432,16 @@ void Pane::_CloseChild(const bool closeFirst, const bool isDetaching)
_borders = _GetCommonBorders();
// take the control, profile, id and isDefTermSession of the pane that _wasn't_ closed.
_control = remainingChild->_control;
_connectionState = remainingChild->_connectionState;
_profile = remainingChild->_profile;
_content = remainingChild->_content;
_id = remainingChild->Id();
_isDefTermSession = remainingChild->_isDefTermSession;
// Add our new event handler before revoking the old one.
_setupControlEvents();
// Revoke the old event handlers. Remove both the handlers for the panes
// themselves closing, and remove their handlers for their controls
// closing. At this point, if the remaining child's control is closed,
// they'll trigger only our event handler for the control's close.
// However, if we are detaching the pane we want to keep its control
// handlers since it is just getting moved.
if (!isDetaching)
{
closedChild->WalkTree([](auto p) {
if (p->_IsLeaf())
{
p->_removeControlEvents();
}
});
}
closedChild->Closed(closedChildClosedToken);
remainingChild->Closed(remainingChildClosedToken);
remainingChild->_removeControlEvents();
// If we or either of our children was focused, we want to take that
// focus from them.
@@ -1691,7 +1461,8 @@ void Pane::_CloseChild(const bool closeFirst, const bool isDetaching)
// Reattach the TermControl to our grid.
_root.Children().Append(_borderFirst);
_borderFirst.Child(_control);
const auto& control{ _content.GetRoot() };
_borderFirst.Child(control);
// Make sure to set our _splitState before focusing the control. If you
// fail to do this, when the tab handles the GotFocus event and asks us
@@ -1700,14 +1471,17 @@ void Pane::_CloseChild(const bool closeFirst, const bool isDetaching)
_splitState = SplitState::None;
// re-attach our handler for the control's GotFocus event.
_gotFocusRevoker = _control.GotFocus(winrt::auto_revoke, { this, &Pane::_ControlGotFocusHandler });
_lostFocusRevoker = _control.LostFocus(winrt::auto_revoke, { this, &Pane::_ControlLostFocusHandler });
if (control)
{
_gotFocusRevoker = control.GotFocus(winrt::auto_revoke, { this, &Pane::_ContentGotFocusHandler });
_lostFocusRevoker = control.LostFocus(winrt::auto_revoke, { this, &Pane::_ContentLostFocusHandler });
}
// If we're inheriting the "last active" state from one of our children,
// focus our control now. This should trigger our own GotFocus event.
if (usedToFocusClosedChildsTerminal || _lastActive)
{
_control.Focus(FocusState::Programmatic);
_content.Focus(FocusState::Programmatic);
// See GH#7252
// Manually fire off the GotFocus event. Typically, this is done
@@ -1746,15 +1520,6 @@ void Pane::_CloseChild(const bool closeFirst, const bool isDetaching)
// Remove the event handlers on the old children
remainingChild->Closed(remainingChildClosedToken);
closedChild->Closed(closedChildClosedToken);
if (!isDetaching)
{
closedChild->WalkTree([](auto p) {
if (p->_IsLeaf())
{
p->_removeControlEvents();
}
});
}
// Reset our UI:
_root.Children().Clear();
@@ -1847,7 +1612,7 @@ void Pane::_CloseChildRoutine(const bool closeFirst)
// this one doesn't seem to.
if (!animationsEnabledInOS || !animationsEnabledInApp || eitherChildZoomed)
{
_CloseChild(closeFirst, false);
_CloseChild(closeFirst);
return;
}
@@ -1950,7 +1715,7 @@ void Pane::_CloseChildRoutine(const bool closeFirst)
{
// We don't need to manually undo any of the above trickiness.
// We're going to re-parent the child's content into us anyways
pane->_CloseChild(closeFirst, false);
pane->_CloseChild(closeFirst);
}
});
}
@@ -2173,7 +1938,7 @@ void Pane::_SetupEntranceAnimation()
auto child = isFirstChild ? _firstChild : _secondChild;
auto childGrid = child->_root;
// If we are splitting a parent pane this may be null
auto control = child->_control;
auto control = child->_content ? child->_content.GetRoot() : nullptr;
// Build up our animation:
// * it'll take as long as our duration (200ms)
// * it'll change the value of our property from 0 to secondSize
@@ -2494,9 +2259,6 @@ std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::_Split(SplitDirect
if (_IsLeaf())
{
// revoke our handler - the child will take care of the control now.
_removeControlEvents();
// Remove our old GotFocus handler from the control. We don't want the
// control telling us that it's now focused, we want it telling its new
// parent.
@@ -2525,11 +2287,8 @@ std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::_Split(SplitDirect
else
{
// Move our control, guid, isDefTermSession into the first one.
_firstChild = std::make_shared<Pane>(_profile, _control);
_firstChild->_connectionState = std::exchange(_connectionState, ConnectionState::NotConnected);
_profile = nullptr;
_control = { nullptr };
_firstChild->_isDefTermSession = _isDefTermSession;
_firstChild = std::make_shared<Pane>(_content);
_content = nullptr;
_firstChild->_broadcastEnabled = _broadcastEnabled;
}
@@ -2852,8 +2611,16 @@ float Pane::CalcSnappedDimension(const bool widthOrHeight, const float dimension
// If requested size is already snapped, then both returned values equal this value.
Pane::SnapSizeResult Pane::_CalcSnappedDimension(const bool widthOrHeight, const float dimension) const
{
const auto direction{ widthOrHeight ? PaneSnapDirection::Width : PaneSnapDirection::Height };
if (_IsLeaf())
{
const auto& snappable{ _content.try_as<ISnappable>() };
if (!snappable)
{
return { dimension, dimension };
}
// If we're a leaf pane, align to the grid of controlling terminal
const auto minSize = _GetMinSize();
@@ -2864,8 +2631,10 @@ Pane::SnapSizeResult Pane::_CalcSnappedDimension(const bool widthOrHeight, const
return { minDimension, minDimension };
}
auto lower = _control.SnapDimensionToGrid(widthOrHeight, dimension);
if (widthOrHeight)
auto lower = snappable.SnapDownToGrid(widthOrHeight ? PaneSnapDirection::Width : PaneSnapDirection::Height,
dimension);
if (direction == PaneSnapDirection::Width)
{
lower += WI_IsFlagSet(_borders, Borders::Left) ? PaneBorderSize : 0;
lower += WI_IsFlagSet(_borders, Borders::Right) ? PaneBorderSize : 0;
@@ -2884,8 +2653,10 @@ Pane::SnapSizeResult Pane::_CalcSnappedDimension(const bool widthOrHeight, const
}
else
{
const auto cellSize = _control.CharacterDimensions();
const auto higher = lower + (widthOrHeight ? cellSize.Width : cellSize.Height);
const auto cellSize = snappable.GridUnitSize();
const auto higher = lower + (direction == PaneSnapDirection::Width ?
cellSize.Width :
cellSize.Height);
return { lower, higher };
}
}
@@ -2931,21 +2702,34 @@ void Pane::_AdvanceSnappedDimension(const bool widthOrHeight, LayoutSizeNode& si
{
if (_IsLeaf())
{
// We're a leaf pane, so just add one more row or column (unless isMinimumSize
// is true, see below).
if (sizeNode.isMinimumSize)
const auto& snappable{ _content.try_as<ISnappable>() };
if (snappable)
{
// If the node is of its minimum size, this size might not be snapped (it might
// be, say, half a character, or fixed 10 pixels), so snap it upward. It might
// however be already snapped, so add 1 to make sure it really increases
// (not strictly necessary but to avoid surprises).
sizeNode.size = _CalcSnappedDimension(widthOrHeight, sizeNode.size + 1).higher;
// We're a leaf pane, so just add one more row or column (unless isMinimumSize
// is true, see below).
if (sizeNode.isMinimumSize)
{
// If the node is of its minimum size, this size might not be snapped (it might
// be, say, half a character, or fixed 10 pixels), so snap it upward. It might
// however be already snapped, so add 1 to make sure it really increases
// (not strictly necessary but to avoid surprises).
sizeNode.size = _CalcSnappedDimension(widthOrHeight, sizeNode.size + 1).higher;
}
else
{
const auto cellSize = snappable.GridUnitSize();
sizeNode.size += widthOrHeight ? cellSize.Width : cellSize.Height;
}
}
else
{
const auto cellSize = _control.CharacterDimensions();
sizeNode.size += widthOrHeight ? cellSize.Width : cellSize.Height;
// If we're a leaf that didn't have a TermControl, then just increment
// by one. We have to increment by _some_ value, because this is used in
// a while() loop to find the next bigger size we can snap to. But since
// a non-terminal control doesn't really care what size it's snapped to,
// we can just say "one pixel larger is the next snap point"
sizeNode.size += 1;
}
}
else
@@ -3050,7 +2834,7 @@ Size Pane::_GetMinSize() const
{
if (_IsLeaf())
{
auto controlSize = _control.MinimumSize();
auto controlSize = _content.MinimumSize();
auto newWidth = controlSize.Width;
auto newHeight = controlSize.Height;
@@ -3148,14 +2932,17 @@ int Pane::GetLeafPaneCount() const noexcept
// created via default handoff
void Pane::FinalizeConfigurationGivenDefault()
{
_isDefTermSession = true;
if (const auto& terminalPane{ _content.try_as<TerminalPaneContent>() })
{
terminalPane.MarkAsDefterm();
}
}
// Method Description:
// - Returns true if the pane or one of its descendants is read-only
bool Pane::ContainsReadOnly() const
{
return _IsLeaf() ? _control.ReadOnly() : (_firstChild->ContainsReadOnly() || _secondChild->ContainsReadOnly());
return _IsLeaf() ? _content.ReadOnly() : (_firstChild->ContainsReadOnly() || _secondChild->ContainsReadOnly());
}
// Method Description:
@@ -3170,8 +2957,8 @@ void Pane::CollectTaskbarStates(std::vector<winrt::TerminalApp::TaskbarState>& s
{
if (_IsLeaf())
{
auto tbState{ winrt::make<winrt::TerminalApp::implementation::TaskbarState>(_control.TaskbarState(),
_control.TaskbarProgress()) };
auto tbState{ winrt::make<winrt::TerminalApp::implementation::TaskbarState>(_content.TaskbarState(),
_content.TaskbarProgress()) };
states.push_back(tbState);
}
else
@@ -3186,9 +2973,12 @@ void Pane::EnableBroadcast(bool enabled)
if (_IsLeaf())
{
_broadcastEnabled = enabled;
_control.CursorVisibility(enabled ?
CursorDisplayState::Shown :
CursorDisplayState::Default);
if (const auto& termControl{ GetTerminalControl() })
{
termControl.CursorVisibility(enabled ?
CursorDisplayState::Shown :
CursorDisplayState::Default);
}
UpdateVisuals();
}
else
@@ -3205,9 +2995,12 @@ void Pane::BroadcastKey(const winrt::Microsoft::Terminal::Control::TermControl&
const bool keyDown)
{
WalkTree([&](const auto& pane) {
if (pane->_IsLeaf() && pane->_control != sourceControl && !pane->_control.ReadOnly())
if (const auto& termControl{ pane->GetTerminalControl() })
{
pane->_control.RawWriteKeyEvent(vkey, scanCode, modifiers, keyDown);
if (termControl != sourceControl && !termControl.ReadOnly())
{
termControl.RawWriteKeyEvent(vkey, scanCode, modifiers, keyDown);
}
}
});
}
@@ -3218,9 +3011,12 @@ void Pane::BroadcastChar(const winrt::Microsoft::Terminal::Control::TermControl&
const winrt::Microsoft::Terminal::Core::ControlKeyStates modifiers)
{
WalkTree([&](const auto& pane) {
if (pane->_IsLeaf() && pane->_control != sourceControl && !pane->_control.ReadOnly())
if (const auto& termControl{ pane->GetTerminalControl() })
{
pane->_control.RawWriteChar(character, scanCode, modifiers);
if (termControl != sourceControl && !termControl.ReadOnly())
{
termControl.RawWriteChar(character, scanCode, modifiers);
}
}
});
}
@@ -3229,19 +3025,16 @@ void Pane::BroadcastString(const winrt::Microsoft::Terminal::Control::TermContro
const winrt::hstring& text)
{
WalkTree([&](const auto& pane) {
if (pane->_IsLeaf() && pane->_control != sourceControl && !pane->_control.ReadOnly())
if (const auto& termControl{ pane->GetTerminalControl() })
{
pane->_control.RawWriteString(text);
if (termControl != sourceControl && !termControl.ReadOnly())
{
termControl.RawWriteString(text);
}
}
});
}
void Pane::_ControlReadOnlyChangedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/,
const winrt::Windows::Foundation::IInspectable& /*e*/)
{
UpdateVisuals();
}
winrt::Windows::UI::Xaml::Media::SolidColorBrush Pane::_ComputeBorderColor()
{
if (_lastActive)
@@ -3249,7 +3042,7 @@ winrt::Windows::UI::Xaml::Media::SolidColorBrush Pane::_ComputeBorderColor()
return _themeResources.focusedBorderBrush;
}
if (_broadcastEnabled && (_IsLeaf() && !_control.ReadOnly()))
if (_broadcastEnabled && (_IsLeaf() && !_content.ReadOnly()))
{
return _themeResources.broadcastBorderBrush;
}

View File

@@ -21,6 +21,7 @@
#pragma once
#include "TaskbarState.h"
#include "TerminalPaneContent.h"
// fwdecl unittest classes
namespace TerminalAppLocalTests
@@ -61,8 +62,7 @@ struct PaneResources
class Pane : public std::enable_shared_from_this<Pane>
{
public:
Pane(const winrt::Microsoft::Terminal::Settings::Model::Profile& profile,
const winrt::Microsoft::Terminal::Control::TermControl& control,
Pane(const winrt::TerminalApp::IPaneContent& content,
const bool lastFocused = false);
Pane(std::shared_ptr<Pane> first,
@@ -73,7 +73,8 @@ public:
std::shared_ptr<Pane> GetActivePane();
winrt::Microsoft::Terminal::Control::TermControl GetLastFocusedTerminalControl();
winrt::Microsoft::Terminal::Control::TermControl GetTerminalControl();
winrt::TerminalApp::IPaneContent GetLastFocusedContent();
winrt::Microsoft::Terminal::Control::TermControl GetTerminalControl() const;
winrt::Microsoft::Terminal::Settings::Model::Profile GetFocusedProfile();
bool IsConnectionClosed() const;
@@ -82,10 +83,15 @@ public:
// - If this is a branch/root pane, return nullptr.
winrt::Microsoft::Terminal::Settings::Model::Profile GetProfile() const
{
return _profile;
if (const auto& c{ _content.try_as<winrt::TerminalApp::TerminalPaneContent>() })
{
return c.GetProfile();
}
return nullptr;
}
winrt::Windows::UI::Xaml::Controls::Grid GetRootElement();
winrt::TerminalApp::IPaneContent GetContent() const noexcept { return _IsLeaf() ? _content : nullptr; }
bool WasLastFocused() const noexcept;
void UpdateVisuals();
@@ -102,8 +108,7 @@ public:
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);
void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings, const winrt::TerminalApp::TerminalSettingsCache& cache);
bool ResizePane(const winrt::Microsoft::Terminal::Settings::Model::ResizeDirection& direction);
std::shared_ptr<Pane> NavigateDirection(const std::shared_ptr<Pane> sourcePane,
const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction,
@@ -217,9 +222,7 @@ public:
til::event<gotFocusArgs> GotFocus;
til::event<winrt::delegate<std::shared_ptr<Pane>>> LostFocus;
til::event<winrt::Windows::Foundation::EventHandler<bool>> PaneRaiseBell;
til::event<winrt::delegate<std::shared_ptr<Pane>>> Detached;
til::event<winrt::delegate<std::shared_ptr<Pane>>> RestartTerminalRequested;
private:
struct PanePoint;
@@ -239,10 +242,8 @@ private:
std::shared_ptr<Pane> _secondChild{ nullptr };
SplitState _splitState{ SplitState::None };
float _desiredSplitPosition;
winrt::Microsoft::Terminal::Control::TermControl _control{ nullptr };
winrt::Microsoft::Terminal::TerminalConnection::ConnectionState _connectionState{ winrt::Microsoft::Terminal::TerminalConnection::ConnectionState::NotConnected };
winrt::Microsoft::Terminal::Settings::Model::Profile _profile{ nullptr };
bool _isDefTermSession{ false };
winrt::TerminalApp::IPaneContent _content{ nullptr };
#pragma endregion
std::optional<uint32_t> _id;
@@ -252,17 +253,6 @@ private:
winrt::event_token _firstClosedToken{ 0 };
winrt::event_token _secondClosedToken{ 0 };
struct ControlEventTokens
{
winrt::Microsoft::Terminal::Control::TermControl::ConnectionStateChanged_revoker _ConnectionStateChanged;
winrt::Microsoft::Terminal::Control::TermControl::WarningBell_revoker _WarningBell;
winrt::Microsoft::Terminal::Control::TermControl::CloseTerminalRequested_revoker _CloseTerminalRequested;
winrt::Microsoft::Terminal::Control::TermControl::RestartTerminalRequested_revoker _RestartTerminalRequested;
winrt::Microsoft::Terminal::Control::TermControl::ReadOnlyChanged_revoker _ReadOnlyChanged;
} _controlEvents;
void _setupControlEvents();
void _removeControlEvents();
winrt::Windows::UI::Xaml::UIElement::GotFocus_revoker _gotFocusRevoker;
winrt::Windows::UI::Xaml::UIElement::LostFocus_revoker _lostFocusRevoker;
@@ -271,13 +261,14 @@ private:
bool _zoomed{ false };
bool _broadcastEnabled{ false };
winrt::Windows::Media::Playback::MediaPlayer _bellPlayer{ nullptr };
bool _bellPlayerCreated{ false };
bool _IsLeaf() const noexcept;
bool _HasFocusedChild() const noexcept;
void _SetupChildCloseHandlers();
bool _HasChild(const std::shared_ptr<Pane> child);
winrt::TerminalApp::TerminalPaneContent _getTerminalContent() const
{
return _IsLeaf() ? _content.try_as<winrt::TerminalApp::TerminalPaneContent>() : nullptr;
}
std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> _Split(winrt::Microsoft::Terminal::Settings::Model::SplitDirection splitType,
const float splitSize,
@@ -303,24 +294,16 @@ private:
const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction,
const PanePoint offset);
void _CloseChild(const bool closeFirst, const bool isDetaching);
void _CloseChild(const bool closeFirst);
void _CloseChildRoutine(const bool closeFirst);
void _Focus();
void _FocusFirstChild();
winrt::fire_and_forget _ControlConnectionStateChangedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& /*args*/);
void _ControlWarningBellHandler(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable& e);
void _ControlGotFocusHandler(const winrt::Windows::Foundation::IInspectable& sender,
void _ContentGotFocusHandler(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::UI::Xaml::RoutedEventArgs& e);
void _ControlLostFocusHandler(const winrt::Windows::Foundation::IInspectable& sender,
void _ContentLostFocusHandler(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::UI::Xaml::RoutedEventArgs& e);
void _ControlReadOnlyChangedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& e);
void _CloseTerminalRequestedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& /*args*/);
void _RestartTerminalRequestedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& /*args*/);
std::pair<float, float> _CalcChildrenSizes(const float fullSize) const;
SnapChildrenSizeResult _CalcSnappedChildrenSizes(const bool widthOrHeight, const float fullSize) const;
SnapSizeResult _CalcSnappedDimension(const bool widthOrHeight, const float dimension) const;
@@ -331,8 +314,6 @@ private:
SplitState _convertAutomaticOrDirectionalSplitState(const winrt::Microsoft::Terminal::Settings::Model::SplitDirection& splitType) const;
winrt::fire_and_forget _playBellSound(winrt::Windows::Foundation::Uri uri);
// Function Description:
// - Returns true if the given direction can be used with the given split
// type.

View File

@@ -0,0 +1,67 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "ScratchpadContent.h"
#include "ScratchpadContent.g.cpp"
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Microsoft::Terminal::Settings::Model;
namespace winrt::TerminalApp::implementation
{
ScratchpadContent::ScratchpadContent()
{
_root = winrt::Windows::UI::Xaml::Controls::Grid{};
// Vertical and HorizontalAlignment are Stretch by default
auto res = Windows::UI::Xaml::Application::Current().Resources();
auto bg = res.Lookup(winrt::box_value(L"UnfocusedBorderBrush"));
_root.Background(bg.try_as<Media::Brush>());
_box = winrt::Windows::UI::Xaml::Controls::TextBox{};
_box.Margin({ 10, 10, 10, 10 });
_box.AcceptsReturn(true);
_box.TextWrapping(TextWrapping::Wrap);
_root.Children().Append(_box);
}
void ScratchpadContent::UpdateSettings(const CascadiaSettings& /*settings*/)
{
// Nothing to do.
}
winrt::Windows::UI::Xaml::FrameworkElement ScratchpadContent::GetRoot()
{
return _root;
}
winrt::Windows::Foundation::Size ScratchpadContent::MinimumSize()
{
return { 1, 1 };
}
void ScratchpadContent::Focus(winrt::Windows::UI::Xaml::FocusState reason)
{
_box.Focus(reason);
}
void ScratchpadContent::Close()
{
CloseRequested.raise(*this, nullptr);
}
NewTerminalArgs ScratchpadContent::GetNewTerminalArgs(const bool /* asContent */) const
{
return nullptr;
}
winrt::hstring ScratchpadContent::Icon() const
{
static constexpr std::wstring_view glyph{ L"\xe70b" }; // QuickNote
return winrt::hstring{ glyph };
}
winrt::Windows::UI::Xaml::Media::Brush ScratchpadContent::BackgroundBrush()
{
return _root.Background();
}
}

View File

@@ -0,0 +1,44 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include "ScratchpadContent.g.h"
namespace winrt::TerminalApp::implementation
{
struct ScratchpadContent : ScratchpadContentT<ScratchpadContent>
{
ScratchpadContent();
winrt::Windows::UI::Xaml::FrameworkElement GetRoot();
void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings);
winrt::Windows::Foundation::Size MinimumSize();
void Focus(winrt::Windows::UI::Xaml::FocusState reason = winrt::Windows::UI::Xaml::FocusState::Programmatic);
void Close();
winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetNewTerminalArgs(const bool asContent) const;
winrt::hstring Title() { return L"Scratchpad"; }
uint64_t TaskbarState() { return 0; }
uint64_t TaskbarProgress() { return 0; }
bool ReadOnly() { return false; }
winrt::hstring Icon() const;
Windows::Foundation::IReference<winrt::Windows::UI::Color> TabColor() const noexcept { return nullptr; }
winrt::Windows::UI::Xaml::Media::Brush BackgroundBrush();
til::typed_event<> ConnectionStateChanged;
til::typed_event<IPaneContent> CloseRequested;
til::typed_event<IPaneContent, winrt::TerminalApp::BellEventArgs> BellRequested;
til::typed_event<IPaneContent> TitleChanged;
til::typed_event<IPaneContent> TabColorChanged;
til::typed_event<IPaneContent> TaskbarProgressChanged;
til::typed_event<IPaneContent> ReadOnlyChanged;
til::typed_event<IPaneContent> FocusRequested;
private:
winrt::Windows::UI::Xaml::Controls::Grid _root{ nullptr };
winrt::Windows::UI::Xaml::Controls::TextBox _box{ nullptr };
};
}

View File

@@ -0,0 +1,85 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "SettingsPaneContent.h"
#include "SettingsPaneContent.g.cpp"
#include "Utils.h"
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Microsoft::Terminal::Settings::Model;
#define ASSERT_UI_THREAD() assert(_sui.Dispatcher().HasThreadAccess())
namespace winrt::TerminalApp::implementation
{
SettingsPaneContent::SettingsPaneContent(CascadiaSettings settings)
{
_sui = winrt::Microsoft::Terminal::Settings::Editor::MainPage{ settings };
// Stash away the current requested theme of the app. We'll need that in
// _BackgroundBrush() to do a theme-aware resource lookup
_requestedTheme = settings.GlobalSettings().CurrentTheme().RequestedTheme();
}
void SettingsPaneContent::UpdateSettings(const CascadiaSettings& settings)
{
ASSERT_UI_THREAD();
_sui.UpdateSettings(settings);
_requestedTheme = settings.GlobalSettings().CurrentTheme().RequestedTheme();
}
winrt::Windows::UI::Xaml::FrameworkElement SettingsPaneContent::GetRoot()
{
return _sui;
}
winrt::Windows::Foundation::Size SettingsPaneContent::MinimumSize()
{
return { 1, 1 };
}
void SettingsPaneContent::Focus(winrt::Windows::UI::Xaml::FocusState reason)
{
if (reason != FocusState::Unfocused)
{
_sui.as<Controls::Page>().Focus(reason);
}
}
void SettingsPaneContent::Close()
{
CloseRequested.raise(*this, nullptr);
}
NewTerminalArgs SettingsPaneContent::GetNewTerminalArgs(const bool /* asContent */) const
{
// For now, we're doing a terrible thing in TerminalTab itself to
// generate an OpenSettings action manually, without asking for the pane
// structure.
return nullptr;
}
winrt::hstring SettingsPaneContent::Icon() const
{
// This is the Setting icon (looks like a gear)
static constexpr std::wstring_view glyph{ L"\xE713" };
return winrt::hstring{ glyph };
}
Windows::Foundation::IReference<winrt::Windows::UI::Color> SettingsPaneContent::TabColor() const noexcept
{
return nullptr;
}
winrt::Windows::UI::Xaml::Media::Brush SettingsPaneContent::BackgroundBrush()
{
// Look up the color we should use for the settings tab item from our
// resources. This should only be used for when "terminalBackground" is
// requested.
static const auto key = winrt::box_value(L"SettingsUiTabBrush");
// You can't just do a Application::Current().Resources().TryLookup
// lookup, cause the app theme never changes! Do the hacky version
// instead.
return ThemeLookup(Application::Current().Resources(), _requestedTheme, key).try_as<winrt::Windows::UI::Xaml::Media::Brush>();
}
}

View File

@@ -0,0 +1,50 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include "SettingsPaneContent.g.h"
#include <LibraryResources.h>
namespace winrt::TerminalApp::implementation
{
struct SettingsPaneContent : SettingsPaneContentT<SettingsPaneContent>
{
SettingsPaneContent(winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings settings);
void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings);
winrt::Windows::UI::Xaml::FrameworkElement GetRoot();
winrt::Microsoft::Terminal::Settings::Editor::MainPage SettingsUI() { return _sui; }
winrt::Windows::Foundation::Size MinimumSize();
void Focus(winrt::Windows::UI::Xaml::FocusState reason = winrt::Windows::UI::Xaml::FocusState::Programmatic);
void Close();
winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetNewTerminalArgs(const bool asContent) const;
winrt::hstring Title() { return RS_(L"SettingsTab"); }
uint64_t TaskbarState() { return 0; }
uint64_t TaskbarProgress() { return 0; }
bool ReadOnly() { return false; }
winrt::hstring Icon() const;
Windows::Foundation::IReference<winrt::Windows::UI::Color> TabColor() const noexcept;
winrt::Windows::UI::Xaml::Media::Brush BackgroundBrush();
til::typed_event<> ConnectionStateChanged;
til::typed_event<IPaneContent> CloseRequested;
til::typed_event<IPaneContent, winrt::TerminalApp::BellEventArgs> BellRequested;
til::typed_event<IPaneContent> TitleChanged;
til::typed_event<IPaneContent> TabColorChanged;
til::typed_event<IPaneContent> TaskbarProgressChanged;
til::typed_event<IPaneContent> ReadOnlyChanged;
til::typed_event<IPaneContent> FocusRequested;
private:
winrt::Microsoft::Terminal::Settings::Editor::MainPage _sui{ nullptr };
winrt::Windows::UI::Xaml::ElementTheme _requestedTheme;
};
}
namespace winrt::TerminalApp::factory_implementation
{
BASIC_FACTORY(SettingsPaneContent);
}

View File

@@ -1,131 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include <LibraryResources.h>
#include "SettingsTab.h"
#include "SettingsTab.g.cpp"
#include "Utils.h"
using namespace winrt;
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Core;
using namespace winrt::Microsoft::Terminal::Control;
using namespace winrt::Microsoft::Terminal::Settings::Model;
using namespace winrt::Microsoft::Terminal::Settings::Editor;
using namespace winrt::Windows::System;
namespace winrt
{
namespace MUX = Microsoft::UI::Xaml;
namespace WUX = Windows::UI::Xaml;
}
#define ASSERT_UI_THREAD() assert(TabViewItem().Dispatcher().HasThreadAccess())
namespace winrt::TerminalApp::implementation
{
SettingsTab::SettingsTab(MainPage settingsUI,
winrt::Windows::UI::Xaml::ElementTheme requestedTheme)
{
Content(settingsUI);
_requestedTheme = requestedTheme;
_MakeTabViewItem();
_CreateContextMenu();
_CreateIcon();
}
void SettingsTab::UpdateSettings(CascadiaSettings settings)
{
ASSERT_UI_THREAD();
auto settingsUI{ Content().as<MainPage>() };
settingsUI.UpdateSettings(settings);
// Stash away the current requested theme of the app. We'll need that in
// _BackgroundBrush() to do a theme-aware resource lookup
_requestedTheme = settings.GlobalSettings().CurrentTheme().RequestedTheme();
}
// Method Description:
// - Creates a list of actions that can be run to recreate the state of this tab
// Arguments:
// - asContent: unused. There's nothing different we need to do when
// serializing the settings tab for moving to another window. If we ever
// really want to support opening the SUI to a specific page, we can
// re-evaluate including that arg in this action then.
// Return Value:
// - The list of actions.
std::vector<ActionAndArgs> SettingsTab::BuildStartupActions(const bool /*asContent*/) const
{
ASSERT_UI_THREAD();
ActionAndArgs action;
action.Action(ShortcutAction::OpenSettings);
OpenSettingsArgs args{ SettingsTarget::SettingsUI };
action.Args(args);
return std::vector{ std::move(action) };
}
// Method Description:
// - Focus the settings UI
// Arguments:
// - focusState: The FocusState mode by which focus is to be obtained.
// Return Value:
// - <none>
void SettingsTab::Focus(WUX::FocusState focusState)
{
ASSERT_UI_THREAD();
_focusState = focusState;
if (_focusState != FocusState::Unfocused)
{
Content().as<WUX::Controls::Page>().Focus(focusState);
}
}
// Method Description:
// - Initializes a TabViewItem for this Tab instance.
// Arguments:
// - <none>
// Return Value:
// - <none>
void SettingsTab::_MakeTabViewItem()
{
TabBase::_MakeTabViewItem();
Title(RS_(L"SettingsTab"));
TabViewItem().Header(winrt::box_value(Title()));
}
// Method Description:
// - Set the icon on the TabViewItem for this tab.
// Arguments:
// - <none>
// Return Value:
// - <none>
void SettingsTab::_CreateIcon()
{
// This is the Setting icon (looks like a gear)
static constexpr std::wstring_view glyph{ L"\xE713" };
// The TabViewItem Icon needs MUX while the IconSourceElement in the CommandPalette needs WUX...
Icon(winrt::hstring{ glyph });
TabViewItem().IconSource(Microsoft::Terminal::UI::IconPathConverter::IconSourceMUX(glyph, false));
}
winrt::Windows::UI::Xaml::Media::Brush SettingsTab::_BackgroundBrush()
{
// Look up the color we should use for the settings tab item from our
// resources. This should only be used for when "terminalBackground" is
// requested.
static const auto key = winrt::box_value(L"SettingsUiTabBrush");
// You can't just do a Application::Current().Resources().TryLookup
// lookup, cause the app theme never changes! Do the hacky version
// instead.
return ThemeLookup(Application::Current().Resources(), _requestedTheme, key).try_as<winrt::Windows::UI::Xaml::Media::Brush>();
}
}

View File

@@ -1,43 +0,0 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- SettingsTab.h
Abstract:
- The SettingsTab is a tab whose content is a Settings UI control. They can
coexist in a TabView with all other types of tabs, like the TerminalTab.
There should only be at most one SettingsTab open at any given time.
Author(s):
- Leon Liang - October 2020
--*/
#pragma once
#include "TabBase.h"
#include "SettingsTab.g.h"
namespace winrt::TerminalApp::implementation
{
struct SettingsTab : SettingsTabT<SettingsTab, TabBase>
{
public:
SettingsTab(winrt::Microsoft::Terminal::Settings::Editor::MainPage settingsUI,
winrt::Windows::UI::Xaml::ElementTheme requestedTheme);
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 bool asContent = false) const override;
private:
winrt::Windows::UI::Xaml::ElementTheme _requestedTheme;
void _MakeTabViewItem() override;
void _CreateIcon();
virtual winrt::Windows::UI::Xaml::Media::Brush _BackgroundBrush() override;
};
}

View File

@@ -1,12 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import "TabBase.idl";
namespace TerminalApp
{
[default_interface] runtimeclass SettingsTab : TabBase
{
void UpdateSettings(Microsoft.Terminal.Settings.Model.CascadiaSettings settings);
}
}

View File

@@ -19,7 +19,6 @@
#include "TabRowControl.h"
#include "ColorHelper.h"
#include "DebugTapConnection.h"
#include "SettingsTab.h"
#include "..\TerminalSettingsModel\FileUtils.h"
#include <shlobj.h>
@@ -63,7 +62,7 @@ namespace winrt::TerminalApp::implementation
// - existingConnection: An optional connection that is already established to a PTY
// for this tab to host instead of creating one.
// If not defined, the tab will create the connection.
HRESULT TerminalPage::_OpenNewTab(const NewTerminalArgs& newTerminalArgs, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection)
HRESULT TerminalPage::_OpenNewTab(const NewTerminalArgs& newTerminalArgs)
try
{
const auto profile{ _settings.GetProfileForArgs(newTerminalArgs) };
@@ -86,7 +85,7 @@ namespace winrt::TerminalApp::implementation
//
// This call to _MakePane won't return nullptr, we already checked that
// case above with the _maybeElevate call.
_CreateNewTabFromPane(_MakePane(newTerminalArgs, nullptr, existingConnection));
_CreateNewTabFromPane(_MakePane(newTerminalArgs, nullptr));
return S_OK;
}
CATCH_RETURN();
@@ -135,6 +134,16 @@ namespace winrt::TerminalApp::implementation
// for it. The Title change will be propagated upwards through the tab's
// PropertyChanged event handler.
newTabImpl->ActivePaneChanged([weakTab, weakThis{ get_weak() }]() {
// TODO!
//
// * Make this a method on TerminalPage.
// * Convert ActivePaneChanged to a typed event, so it sends the sender (so we don't need to make all these lambdas)
// * Stash the task pane as a member on the Terminal? if one was opened.
// * If the tab does have a taskpane, then tell the taskpane the active pane changed
//
// wait don't do any of that. just do that in TerminalTab directly
// before we even raise the event you donkey
auto page{ weakThis.get() };
auto tab{ weakTab.get() };
@@ -171,17 +180,8 @@ namespace winrt::TerminalApp::implementation
auto tabViewItem = newTabImpl->TabViewItem();
_tabView.TabItems().InsertAt(insertPosition, tabViewItem);
// Set this tab's icon to the icon from the user's profile
if (const auto profile{ newTabImpl->GetFocusedProfile() })
{
const auto& icon = profile.EvaluatedIcon();
if (!icon.empty())
{
const auto theme = _settings.GlobalSettings().CurrentTheme();
const auto iconStyle = (theme && theme.Tab()) ? theme.Tab().IconStyle() : IconStyle::Default;
newTabImpl->UpdateIcon(icon, iconStyle);
}
}
// Set this tab's icon to the icon from the content
_UpdateTabIcon(*newTabImpl);
tabViewItem.PointerReleased({ this, &TerminalPage::_OnTabClick });
@@ -226,13 +226,15 @@ namespace winrt::TerminalApp::implementation
// Arguments:
// - pane: The pane to use as the root.
// - insertPosition: Optional parameter to indicate the position of tab.
void TerminalPage::_CreateNewTabFromPane(std::shared_ptr<Pane> pane, uint32_t insertPosition)
TerminalApp::TerminalTab TerminalPage::_CreateNewTabFromPane(std::shared_ptr<Pane> pane, uint32_t insertPosition)
{
if (pane)
{
auto newTabImpl = winrt::make_self<TerminalTab>(pane);
_InitializeTab(newTabImpl, insertPosition);
return *newTabImpl;
}
return nullptr;
}
// Method Description:
@@ -242,11 +244,13 @@ namespace winrt::TerminalApp::implementation
// - tab: the Tab to update the title for.
void TerminalPage::_UpdateTabIcon(TerminalTab& tab)
{
if (const auto profile = tab.GetFocusedProfile())
if (const auto content{ tab.GetActiveContent() })
{
const auto& icon{ content.Icon() };
const auto theme = _settings.GlobalSettings().CurrentTheme();
const auto iconStyle = (theme && theme.Tab()) ? theme.Tab().IconStyle() : IconStyle::Default;
tab.UpdateIcon(profile.EvaluatedIcon(), iconStyle);
tab.UpdateIcon(icon, iconStyle);
}
}
@@ -800,14 +804,6 @@ namespace winrt::TerminalApp::implementation
}
}
}
else if (auto index{ _GetFocusedTabIndex() })
{
const auto tab{ _tabs.GetAt(*index) };
if (tab.try_as<TerminalApp::SettingsTab>())
{
_HandleCloseTabRequested(tab);
}
}
}
// Method Description:

View File

@@ -0,0 +1,125 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "TasksPaneContent.h"
#include "TasksPaneContent.g.cpp"
#include "FilteredTask.g.cpp"
using namespace winrt::Windows::Foundation;
using namespace winrt::Microsoft::Terminal::Settings;
using namespace winrt::Microsoft::Terminal::Settings::Model;
namespace winrt
{
namespace WUX = Windows::UI::Xaml;
namespace MUX = Microsoft::UI::Xaml;
using IInspectable = Windows::Foundation::IInspectable;
}
namespace winrt::TerminalApp::implementation
{
TasksPaneContent::TasksPaneContent()
{
InitializeComponent();
auto res = Windows::UI::Xaml::Application::Current().Resources();
auto bg = res.Lookup(winrt::box_value(L"UnfocusedBorderBrush"));
Background(bg.try_as<WUX::Media::Brush>());
}
void TasksPaneContent::_updateFilteredCommands()
{
const auto& queryString = _filterBox().Text();
// DON'T replace the itemSource here. If you do, it'll un-expand all the
// nested items the user has expanded. Instead, just update the filter.
// That'll also trigger a PropertyChanged for the Visibility property.
for (const auto& t : _allTasks)
{
t.UpdateFilter(queryString);
}
}
void TasksPaneContent::UpdateSettings(const CascadiaSettings& settings)
{
_settings = settings;
// You'd think that `FilterToSendInput(queryString)` would work. It
// doesn't! That uses the queryString as the current command the user
// has typed, then relies on the sxnui to _also_ filter with that
// string.
const auto tasks = _settings.GlobalSettings().ActionMap().FilterToSendInput(L""); // IVector<Model::Command>
_allTasks = winrt::single_threaded_observable_vector<TerminalApp::FilteredTask>();
for (const auto& t : tasks)
{
const auto& filtered{ winrt::make<FilteredTask>(t) };
_allTasks.Append(filtered);
}
_treeView().ItemsSource(_allTasks);
_updateFilteredCommands();
}
void TasksPaneContent::_filterTextChanged(const IInspectable& /*sender*/,
const Windows::UI::Xaml::RoutedEventArgs& /*args*/)
{
_updateFilteredCommands();
}
winrt::Windows::UI::Xaml::FrameworkElement TasksPaneContent::GetRoot()
{
return *this;
}
winrt::Windows::Foundation::Size TasksPaneContent::MinimumSize()
{
return { 1, 1 };
}
void TasksPaneContent::Focus(winrt::Windows::UI::Xaml::FocusState reason)
{
reason;
// _box.Focus(reason);
}
void TasksPaneContent::Close()
{
CloseRequested.raise(*this, nullptr);
}
NewTerminalArgs TasksPaneContent::GetNewTerminalArgs(const bool /* asContent */) const
{
return nullptr;
}
winrt::hstring TasksPaneContent::Icon() const
{
static constexpr std::wstring_view glyph{ L"\xe70b" }; // QuickNote
return winrt::hstring{ glyph };
}
winrt::Windows::UI::Xaml::Media::Brush TasksPaneContent::BackgroundBrush()
{
return Background();
}
void TasksPaneContent::SetLastActiveControl(const Microsoft::Terminal::Control::TermControl& control)
{
_control = control;
}
void TasksPaneContent::_runCommandButtonClicked(const Windows::Foundation::IInspectable& sender,
const Windows::UI::Xaml::RoutedEventArgs&)
{
if (const auto& taskVM{ sender.try_as<WUX::Controls::Button>().DataContext().try_as<FilteredTask>() })
{
if (const auto& strongControl{ _control.get() })
{
// By using the last active control as the sender here, the
// actiopn dispatch will send this to the active control,
// thinking that it is the control that requested this event.
DispatchCommandRequested.raise(strongControl, taskVM->Command());
}
}
}
}

View File

@@ -0,0 +1,140 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include "TasksPaneContent.g.h"
#include "FilteredTask.g.h"
#include "FilteredCommand.h"
#include "ActionPaletteItem.h"
namespace winrt::TerminalApp::implementation
{
struct TasksPaneContent : TasksPaneContentT<TasksPaneContent>
{
TasksPaneContent();
winrt::Windows::UI::Xaml::FrameworkElement GetRoot();
void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings);
winrt::Windows::Foundation::Size MinimumSize();
void Focus(winrt::Windows::UI::Xaml::FocusState reason = winrt::Windows::UI::Xaml::FocusState::Programmatic);
void Close();
winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetNewTerminalArgs(const bool asContent) const;
// TODO! lots of strings here and in XAML that need RS_-ifying
winrt::hstring Title() { return L"Tasks"; }
uint64_t TaskbarState() { return 0; }
uint64_t TaskbarProgress() { return 0; }
bool ReadOnly() { return false; }
winrt::hstring Icon() const;
Windows::Foundation::IReference<winrt::Windows::UI::Color> TabColor() const noexcept { return nullptr; }
winrt::Windows::UI::Xaml::Media::Brush BackgroundBrush();
void SetLastActiveControl(const Microsoft::Terminal::Control::TermControl& control);
til::typed_event<> ConnectionStateChanged;
til::typed_event<IPaneContent> CloseRequested;
til::typed_event<IPaneContent, winrt::TerminalApp::BellEventArgs> BellRequested;
til::typed_event<IPaneContent> TitleChanged;
til::typed_event<IPaneContent> TabColorChanged;
til::typed_event<IPaneContent> TaskbarProgressChanged;
til::typed_event<IPaneContent> ReadOnlyChanged;
til::typed_event<IPaneContent> FocusRequested;
til::typed_event<winrt::Windows::Foundation::IInspectable, Microsoft::Terminal::Settings::Model::Command> DispatchCommandRequested;
private:
friend struct TasksPaneContentT<TasksPaneContent>; // for Xaml to bind events
winrt::weak_ref<Microsoft::Terminal::Control::TermControl> _control{ nullptr };
winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings _settings{ nullptr };
winrt::Windows::Foundation::Collections::IObservableVector<TerminalApp::FilteredTask> _allTasks{ nullptr };
void _runCommandButtonClicked(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs&);
void _filterTextChanged(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args);
void _updateFilteredCommands();
};
struct FilteredTask : FilteredTaskT<FilteredTask, TerminalApp::implementation::FilteredCommand>
{
FilteredTask() = default;
FilteredTask(const winrt::Microsoft::Terminal::Settings::Model::Command& command)
{
_constructFilteredCommand(winrt::make<winrt::TerminalApp::implementation::ActionPaletteItem>(command));
_command = command;
// The Children() method must always return a non-null vector
_children = winrt::single_threaded_observable_vector<TerminalApp::FilteredTask>();
if (_command.HasNestedCommands())
{
for (const auto& [_, child] : _command.NestedCommands())
{
auto vm{ winrt::make<FilteredTask>(child) };
_children.Append(vm);
}
}
}
void UpdateFilter(const winrt::hstring& filter) override
{
TerminalApp::implementation::FilteredCommand::UpdateFilter(filter);
for (const auto& c : _children)
{
c.UpdateFilter(filter);
}
PropertyChanged.raise(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"Visibility" });
}
winrt::hstring Input()
{
if (const auto& actionItem{ _Item.try_as<winrt::TerminalApp::ActionPaletteItem>() })
{
if (const auto& command{ actionItem.Command() })
{
if (const auto& sendInput{ command.ActionAndArgs().Args().try_as<winrt::Microsoft::Terminal::Settings::Model::SendInputArgs>() })
{
return sendInput.Input();
}
}
}
return L"";
};
winrt::Windows::Foundation::Collections::IObservableVector<TerminalApp::FilteredTask> Children() { return _children; }
winrt::Microsoft::Terminal::Settings::Model::Command Command() { return _command; }
// Used to control if this item is visible in the TreeView. Turns out,
// TreeView is in fact sane enough to remove items entirely if they're
// Collapsed.
winrt::Windows::UI::Xaml::Visibility Visibility()
{
// Is there no filter, or do we match it?
if (_Filter.empty() || _Weight > 0)
{
return winrt::Windows::UI::Xaml::Visibility::Visible;
}
// If we don't match, maybe one of our children does
auto totalWeight = _Weight;
for (const auto& c : _children)
{
totalWeight += c.Weight();
}
return totalWeight > 0 ? winrt::Windows::UI::Xaml::Visibility::Visible : winrt::Windows::UI::Xaml::Visibility::Collapsed;
};
private:
winrt::Microsoft::Terminal::Settings::Model::Command _command{ nullptr };
winrt::Windows::Foundation::Collections::IObservableVector<TerminalApp::FilteredTask> _children{ nullptr };
};
}
namespace winrt::TerminalApp::factory_implementation
{
BASIC_FACTORY(TasksPaneContent);
}

View File

@@ -0,0 +1,143 @@
<!--
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.TasksPaneContent"
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:mtu="using:Microsoft.Terminal.UI"
xmlns:mux="using:Microsoft.UI.Xaml.Controls"
AllowFocusOnInteraction="True"
IsTabStop="True"
TabNavigation="Cycle"
mc:Ignorable="d">
<UserControl.Resources>
<ResourceDictionary>
<DataTemplate x:Key="TaskItemTemplate"
x:DataType="local:FilteredTask">
<mux:TreeViewItem x:Name="rootItem"
ItemsSource="{x:Bind Children}"
Visibility="{x:Bind Visibility, Mode=OneWay}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ContentPresenter Grid.Column="0">
<IconSourceElement Width="16"
Height="16"
IconSource="{x:Bind mtu:IconPathConverter.IconSourceWUX(Item.Icon)}"
Visibility="Collapsed" />
</ContentPresenter>
<Button Grid.Row="0"
Grid.RowSpan="1"
Grid.Column="0"
Padding="3"
VerticalAlignment="Bottom"
Background="Transparent"
BorderBrush="Transparent"
Click="_runCommandButtonClicked"
ToolTipService.ToolTip="Input this command">
<Button.Content>
<FontIcon FontFamily="Segoe Fluent Icons, Segoe MDL2 Assets"
FontSize="12"
Glyph="&#xE768;" />
<!--
xE768 is Play, which is just an outline.
xF5B0 is PlaySolid, also a good option. Seemed
better to have a lightweight outline though
-->
</Button.Content>
<Button.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="ButtonForegroundPointerOver"
Color="{StaticResource SystemAccentColor}" />
<SolidColorBrush x:Key="ButtonForegroundPressed"
Color="{StaticResource SystemAccentColor}" />
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<SolidColorBrush x:Key="ButtonForegroundPointerOver"
Color="{StaticResource SystemAccentColor}" />
<SolidColorBrush x:Key="ButtonForegroundPressed"
Color="{StaticResource SystemAccentColor}" />
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<SolidColorBrush x:Key="ButtonBackground"
Color="{ThemeResource SystemColorButtonFaceColor}" />
<SolidColorBrush x:Key="ButtonBackgroundPointerOver"
Color="{ThemeResource SystemColorHighlightColor}" />
<SolidColorBrush x:Key="ButtonBackgroundPressed"
Color="{ThemeResource SystemColorHighlightColor}" />
<SolidColorBrush x:Key="ButtonForeground"
Color="{ThemeResource SystemColorButtonTextColor}" />
<SolidColorBrush x:Key="ButtonForegroundPointerOver"
Color="{ThemeResource SystemColorHighlightTextColor}" />
<SolidColorBrush x:Key="ButtonForegroundPressed"
Color="{ThemeResource SystemColorHighlightTextColor}" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Button.Resources>
</Button>
<local:HighlightedTextControl Grid.Column="1"
HorizontalAlignment="Left"
Text="{x:Bind HighlightedName, Mode=OneWay}" />
<TextBlock Grid.Row="1"
Grid.Column="1"
Margin="12,0,0,6"
FontFamily="Cascadia Mono, Consolas"
IsTextSelectionEnabled="True"
Style="{ThemeResource BodyTextBlockStyle}"
Text="{x:Bind Input}"
Visibility="{Binding ElementName=rootItem, Path=IsSelected}" />
</Grid>
</mux:TreeViewItem>
</DataTemplate>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock x:Name="_title"
Grid.Row="0"
Margin="9"
FontSize="24"
Text="Tasks" />
<TextBox x:Name="_filterBox"
Grid.Row="1"
Margin="8"
PlaceholderText="Filter tasks..."
TextChanged="_filterTextChanged" />
<mux:TreeView x:Name="_treeView"
Grid.Row="2"
CanDragItems="False"
CanReorderItems="False"
ItemTemplate="{StaticResource TaskItemTemplate}" />
</Grid>
</UserControl>

View File

@@ -71,6 +71,15 @@
<Page Include="SuggestionsControl.xaml">
<SubType>Designer</SubType>
</Page>
<Page Include="TasksPaneContent.xaml">
<SubType>Designer</SubType>
</Page>
<Page Include="CodeBlock.xaml">
<SubType>Designer</SubType>
</Page>
<Page Include="MarkdownPaneContent.xaml">
<SubType>Designer</SubType>
</Page>
</ItemGroup>
<!-- ========================= Headers ======================== -->
<ItemGroup>
@@ -88,9 +97,6 @@
<DependentUpon>PaletteItemTemplateSelector.idl</DependentUpon>
<SubType>Code</SubType>
</ClInclude>
<ClInclude Include="SettingsTab.h">
<DependentUpon>SettingsTab.idl</DependentUpon>
</ClInclude>
<ClInclude Include="PaletteItem.h" />
<ClInclude Include="TabBase.h">
<DependentUpon>TabBase.idl</DependentUpon>
@@ -158,10 +164,31 @@
<ClInclude Include="SettingsLoadEventArgs.h">
<DependentUpon>TerminalWindow.idl</DependentUpon>
</ClInclude>
<ClInclude Include="TerminalPaneContent.h">
<DependentUpon>TerminalPaneContent.idl</DependentUpon>
</ClInclude>
<ClInclude Include="ScratchpadContent.h">
<DependentUpon>TerminalPaneContent.idl</DependentUpon>
</ClInclude>
<ClInclude Include="TasksPaneContent.h">
<DependentUpon>TasksPaneContent.xaml</DependentUpon>
</ClInclude>
<ClInclude Include="SettingsPaneContent.h">
<DependentUpon>TerminalPaneContent.idl</DependentUpon>
</ClInclude>
<ClInclude Include="Toast.h" />
<ClInclude Include="TerminalSettingsCache.h">
<DependentUpon>TerminalSettingsCache.idl</DependentUpon>
</ClInclude>
<ClInclude Include="SuggestionsControl.h">
<DependentUpon>SuggestionsControl.xaml</DependentUpon>
</ClInclude>
<ClInclude Include="CodeBlock.h">
<DependentUpon>CodeBlock.xaml</DependentUpon>
</ClInclude>
<ClInclude Include="MarkdownPaneContent.h">
<DependentUpon>MarkdownPaneContent.xaml</DependentUpon>
</ClInclude>
</ItemGroup>
<!-- ========================= Cpp Files ======================== -->
<ItemGroup>
@@ -179,9 +206,6 @@
<DependentUpon>PaletteItemTemplateSelector.idl</DependentUpon>
<SubType>Code</SubType>
</ClCompile>
<ClCompile Include="SettingsTab.cpp">
<DependentUpon>SettingsTab.idl</DependentUpon>
</ClCompile>
<ClCompile Include="PaletteItem.cpp" />
<ClCompile Include="TabBase.cpp">
<DependentUpon>TabBase.idl</DependentUpon>
@@ -262,11 +286,34 @@
<ClCompile Include="TerminalWindow.cpp">
<DependentUpon>TerminalWindow.idl</DependentUpon>
</ClCompile>
<ClCompile Include="TerminalPaneContent.cpp">
<DependentUpon>TerminalPaneContent.idl</DependentUpon>
</ClCompile>
<ClCompile Include="ScratchpadContent.cpp">
<DependentUpon>TerminalPaneContent.idl</DependentUpon>
</ClCompile>
<ClCompile Include="TasksPaneContent.cpp">
<DependentUpon>TasksPaneContent.xaml</DependentUpon>
</ClCompile>
<ClCompile Include="SettingsPaneContent.cpp">
<DependentUpon>TerminalPaneContent.idl</DependentUpon>
</ClCompile>
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
<ClCompile Include="Toast.cpp" />
<ClCompile Include="TerminalSettingsCache.cpp">
<DependentUpon>TerminalSettingsCache.idl</DependentUpon>
</ClCompile>
<ClCompile Include="SuggestionsControl.cpp">
<DependentUpon>SuggestionsControl.xaml</DependentUpon>
</ClCompile>
<ClCompile Include="CodeBlock.cpp">
<DependentUpon>CodeBlock.xaml</DependentUpon>
<SubType>Code</SubType>
</ClCompile>
<ClCompile Include="MarkdownPaneContent.cpp">
<DependentUpon>MarkdownPaneContent.xaml</DependentUpon>
<SubType>Code</SubType>
</ClCompile>
</ItemGroup>
<!-- ========================= idl Files ======================== -->
<ItemGroup>
@@ -283,7 +330,6 @@
<Midl Include="PaletteItemTemplateSelector.idl">
<SubType>Designer</SubType>
</Midl>
<Midl Include="SettingsTab.idl" />
<Midl Include="PaletteItem.idl" />
<Midl Include="ShortcutActionDispatch.idl" />
<Midl Include="AppKeyBindings.idl" />
@@ -334,6 +380,20 @@
<SubType>Code</SubType>
</Midl>
<Midl Include="FilteredCommand.idl" />
<Midl Include="IPaneContent.idl" />
<Midl Include="TerminalSettingsCache.idl" />
<Midl Include="TerminalPaneContent.idl" >
<DependentUpon>TaskPaneContent.xaml</DependentUpon>
<SubType>Code</SubType>
</Midl>
<Midl Include="CodeBlock.idl">
<DependentUpon>CodeBlock.xaml</DependentUpon>
<SubType>Code</SubType>
</Midl>
<Midl Include="MarkdownPaneContent.idl">
<DependentUpon>MarkdownPaneContent.xaml</DependentUpon>
<SubType>Code</SubType>
</Midl>
</ItemGroup>
<!-- ========================= Misc Files ======================== -->
<ItemGroup>
@@ -353,6 +413,9 @@
<Project>{CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}</Project>
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference>
<ProjectReference Include="$(OpenConsoleDir)src\dep\md4c\md4c.vcxproj">
<Project>{7CAE5851-50D5-4934-8D5E-30361A8A40F3}</Project>
</ProjectReference>
<!-- For whatever reason, we can't include the TerminalControl and
TerminalSettings projects' winmds via project references. So we'll have to
manually include the winmds as References below

View File

@@ -24,9 +24,6 @@
<ClCompile Include="Tab.cpp">
<Filter>tab</Filter>
</ClCompile>
<ClCompile Include="SettingsTab.cpp">
<Filter>tab</Filter>
</ClCompile>
<ClCompile Include="FilteredCommand.cpp">
<Filter>commandPalette</Filter>
</ClCompile>
@@ -64,9 +61,6 @@
<ClInclude Include="Tab.h">
<Filter>tab</Filter>
</ClInclude>
<ClInclude Include="SettingsTab.h">
<Filter>tab</Filter>
</ClInclude>
<ClInclude Include="FilteredCommand.h">
<Filter>commandPalette</Filter>
</ClInclude>
@@ -98,6 +92,7 @@
<Midl Include="ShortcutActionDispatch.idl">
<Filter>settings</Filter>
</Midl>
<Midl Include="IDirectKeyListener.idl" />
<Midl Include="SettingsTab.idl">
<Filter>tab</Filter>
</Midl>

View File

@@ -21,7 +21,7 @@
#include "App.h"
#include "ColorHelper.h"
#include "DebugTapConnection.h"
#include "SettingsTab.h"
#include "SettingsPaneContent.h"
#include "TabRowControl.h"
#include "Utils.h"
@@ -449,10 +449,10 @@ namespace winrt::TerminalApp::implementation
// - command - command to dispatch
// Return Value:
// - <none>
void TerminalPage::_OnDispatchCommandRequested(const IInspectable& /*sender*/, const Microsoft::Terminal::Settings::Model::Command& command)
void TerminalPage::_OnDispatchCommandRequested(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::Command& command)
{
const auto& actionAndArgs = command.ActionAndArgs();
_actionDispatch->DoAction(actionAndArgs);
_actionDispatch->DoAction(sender, actionAndArgs);
}
// Method Description:
@@ -1297,15 +1297,20 @@ namespace winrt::TerminalApp::implementation
return connection;
}
TerminalConnection::ITerminalConnection TerminalPage::_duplicateConnectionForRestart(std::shared_ptr<Pane> pane)
TerminalConnection::ITerminalConnection TerminalPage::_duplicateConnectionForRestart(const TerminalApp::TerminalPaneContent& paneContent)
{
const auto& control{ pane->GetTerminalControl() };
if (paneContent == nullptr)
{
return nullptr;
}
const auto& control{ paneContent.GetTermControl() };
if (control == nullptr)
{
return nullptr;
}
const auto& connection = control.Connection();
auto profile{ pane->GetProfile() };
auto profile{ paneContent.GetProfile() };
TerminalSettingsCreateResult controlSettings{ nullptr };
@@ -1724,11 +1729,8 @@ namespace winrt::TerminalApp::implementation
// Add an event handler for when the terminal or tab wants to set a
// progress indicator on the taskbar
hostingTab.TaskbarProgressChanged({ get_weak(), &TerminalPage::_SetTaskbarProgressHandler });
}
void TerminalPage::_RegisterPaneEvents(std::shared_ptr<Pane>& pane)
{
pane->RestartTerminalRequested({ get_weak(), &TerminalPage::_restartPaneConnection });
hostingTab.RestartTerminalRequested({ get_weak(), &TerminalPage::_restartPaneConnection });
}
// Method Description:
@@ -2356,6 +2358,12 @@ namespace winrt::TerminalApp::implementation
}
}
// For now, prevent splitting the _settingsTab. We can always revisit this later.
if (*activeTab == _settingsTab)
{
return;
}
// If the caller is calling us with the return value of _MakePane
// directly, it's possible that nullptr was returned, if the connections
// was supposed to be launched in an elevated window. In that case, do
@@ -2377,13 +2385,6 @@ namespace winrt::TerminalApp::implementation
_UnZoomIfNeeded();
auto [original, _] = activeTab->SplitPane(*realSplitType, splitSize, newPane);
// When we split the pane, the Pane itself will create a _new_ Pane
// instance for the original content. We need to make sure we also
// re-add our event handler to that newly created pane.
//
// _MakePane will already call this for the newly created pane.
_RegisterPaneEvents(original);
// After GH#6586, the control will no longer focus itself
// automatically when it's finished being laid out. Manually focus
// the control here instead.
@@ -3114,10 +3115,9 @@ namespace winrt::TerminalApp::implementation
// Don't need to worry about duplicating or anything - we'll
// serialize the actual profile's GUID along with the content guid.
const auto& profile = _settings.GetProfileForArgs(newTerminalArgs);
const auto control = _AttachControlToContent(newTerminalArgs.ContentId());
return std::make_shared<Pane>(profile, control);
auto paneContent{ winrt::make<TerminalPaneContent>(profile, control) };
return std::make_shared<Pane>(paneContent);
}
TerminalSettingsCreateResult controlSettings{ nullptr };
@@ -3173,13 +3173,15 @@ namespace winrt::TerminalApp::implementation
const auto control = _CreateNewControlAndContent(controlSettings, connection);
auto resultPane = std::make_shared<Pane>(profile, control);
auto paneContent{ winrt::make<TerminalPaneContent>(profile, control) };
auto resultPane = std::make_shared<Pane>(paneContent);
if (debugConnection) // this will only be set if global debugging is on and tap is active
{
auto newControl = _CreateNewControlAndContent(controlSettings, debugConnection);
// Split (auto) with the debug tap.
auto debugPane = std::make_shared<Pane>(profile, newControl);
auto debugContent{ winrt::make<TerminalPaneContent>(profile, newControl) };
auto debugPane = std::make_shared<Pane>(debugContent);
// Since we're doing this split directly on the pane (instead of going through TerminalTab,
// we need to handle the panes 'active' states
@@ -3193,16 +3195,20 @@ namespace winrt::TerminalApp::implementation
original->SetActive();
}
_RegisterPaneEvents(resultPane);
return resultPane;
}
void TerminalPage::_restartPaneConnection(const std::shared_ptr<Pane>& pane)
void TerminalPage::_restartPaneConnection(
const TerminalApp::TerminalPaneContent& paneContent,
const winrt::Windows::Foundation::IInspectable&)
{
if (const auto& connection{ _duplicateConnectionForRestart(pane) })
// Note: callers are likely passing in `nullptr` as the args here, as
// the TermControl.RestartTerminalRequested event doesn't actually pass
// any args upwards itself. If we ever change this, make sure you check
// for nulls
if (const auto& connection{ _duplicateConnectionForRestart(paneContent) })
{
pane->GetTerminalControl().Connection(connection);
paneContent.GetTermControl().Connection(connection);
connection.Start();
}
}
@@ -3283,50 +3289,18 @@ namespace winrt::TerminalApp::implementation
// Refresh UI elements
// Mapping by GUID isn't _excellent_ because the defaults profile doesn't have a stable GUID; however,
// when we stabilize its guid this will become fully safe.
std::unordered_map<winrt::guid, std::pair<Profile, TerminalSettingsCreateResult>> profileGuidSettingsMap;
const auto profileDefaults{ _settings.ProfileDefaults() };
const auto allProfiles{ _settings.AllProfiles() };
profileGuidSettingsMap.reserve(allProfiles.Size() + 1);
// Include the Defaults profile for consideration
profileGuidSettingsMap.insert_or_assign(profileDefaults.Guid(), std::pair{ profileDefaults, nullptr });
for (const auto& newProfile : allProfiles)
{
// Avoid creating a TerminalSettings right now. They're not totally cheap, and we suspect that users with many
// panes may not be using all of their profiles at the same time. Lazy evaluation is king!
profileGuidSettingsMap.insert_or_assign(newProfile.Guid(), std::pair{ newProfile, nullptr });
}
// Recreate the TerminalSettings cache here. We'll use that as we're
// updating terminal panes, so that we don't have to build a _new_
// TerminalSettings for every profile we update - we can just look them
// up the previous ones we built.
_terminalSettingsCache = TerminalApp::TerminalSettingsCache{ _settings, *_bindings };
for (const auto& tab : _tabs)
{
if (auto terminalTab{ _GetTerminalTabImpl(tab) })
{
terminalTab->UpdateSettings();
// Manually enumerate the panes in each tab; this will let us recycle TerminalSettings
// objects but only have to iterate one time.
terminalTab->GetRootPane()->WalkTree([&](auto&& pane) {
if (const auto profile{ pane->GetProfile() })
{
const auto found{ profileGuidSettingsMap.find(profile.Guid()) };
// GH#2455: If there are any panes with controls that had been
// initialized with a Profile that no longer exists in our list of
// profiles, we'll leave it unmodified. The profile doesn't exist
// anymore, so we can't possibly update its settings.
if (found != profileGuidSettingsMap.cend())
{
auto& pair{ found->second };
if (!pair.second)
{
pair.second = TerminalSettings::CreateWithProfile(_settings, pair.first, *_bindings);
}
pane->UpdateSettings(pair.second, pair.first);
}
}
});
// Let the tab know that there are new settings. It's up to each content to decide what to do with them.
terminalTab->UpdateSettings(_settings, _terminalSettingsCache);
// Update the icon of the tab for the currently focused profile in that tab.
// Only do this for TerminalTabs. Other types of tabs won't have multiple panes
@@ -3336,10 +3310,6 @@ namespace winrt::TerminalApp::implementation
// Force the TerminalTab to re-grab its currently active control's title.
terminalTab->UpdateTitle();
}
else if (auto settingsTab = tab.try_as<TerminalApp::SettingsTab>())
{
settingsTab.UpdateSettings(_settings);
}
auto tabImpl{ winrt::get_self<TabBase>(tab) };
tabImpl->SetActionMap(_settings.ActionMap());
@@ -3901,7 +3871,10 @@ namespace winrt::TerminalApp::implementation
}
}
winrt::Microsoft::Terminal::Settings::Editor::MainPage sui{ _settings };
// Create the SUI pane content
auto settingsContent{ winrt::make_self<SettingsPaneContent>(_settings) };
auto sui = settingsContent->SettingsUI();
if (_hostingHwnd)
{
sui.SetHostingWindow(reinterpret_cast<uint64_t>(*_hostingHwnd));
@@ -3917,54 +3890,9 @@ namespace winrt::TerminalApp::implementation
}
});
auto newTabImpl = winrt::make_self<SettingsTab>(sui, _settings.GlobalSettings().CurrentTheme().RequestedTheme());
// Add the new tab to the list of our tabs.
_tabs.Append(*newTabImpl);
_mruTabs.Append(*newTabImpl);
newTabImpl->SetDispatch(*_actionDispatch);
newTabImpl->SetActionMap(_settings.ActionMap());
// Give the tab its index in the _tabs vector so it can manage its own SwitchToTab command.
_UpdateTabIndices();
// Don't capture a strong ref to the tab. If the tab is removed as this
// is called, we don't really care anymore about handling the event.
auto weakTab = make_weak(newTabImpl);
auto tabViewItem = newTabImpl->TabViewItem();
_tabView.TabItems().Append(tabViewItem);
tabViewItem.PointerPressed({ this, &TerminalPage::_OnTabClick });
// When the tab requests close, try to close it (prompt for approval, if required)
newTabImpl->CloseRequested([weakTab, weakThis{ get_weak() }](auto&& /*s*/, auto&& /*e*/) {
auto page{ weakThis.get() };
auto tab{ weakTab.get() };
if (page && tab)
{
page->_HandleCloseTabRequested(*tab);
}
});
// When the tab is closed, remove it from our list of tabs.
newTabImpl->Closed([weakTab, weakThis{ get_weak() }](auto&& /*s*/, auto&& /*e*/) {
const auto page = weakThis.get();
const auto tab = weakTab.get();
if (page && tab)
{
page->_RemoveTab(*tab);
}
});
_settingsTab = *newTabImpl;
// This kicks off TabView::SelectionChanged, in response to which
// we'll attach the terminal's Xaml control to the Xaml root.
_tabView.SelectedItem(tabViewItem);
// Create the tab
auto resultPane = std::make_shared<Pane>(*settingsContent);
_settingsTab = _CreateNewTabFromPane(resultPane);
}
else
{
@@ -4583,13 +4511,15 @@ namespace winrt::TerminalApp::implementation
til::color bgColor = backgroundSolidBrush.Color();
Media::Brush terminalBrush{ nullptr };
if (const auto& control{ _GetActiveControl() })
if (const auto tab{ _GetFocusedTabImpl() })
{
terminalBrush = control.BackgroundBrush();
}
else if (const auto& settingsTab{ _GetFocusedTab().try_as<TerminalApp::SettingsTab>() })
{
terminalBrush = settingsTab.Content().try_as<Settings::Editor::MainPage>().BackgroundBrush();
if (const auto& pane{ tab->GetActivePane() })
{
if (const auto& lastContent{ pane->GetLastFocusedContent() })
{
terminalBrush = lastContent.BackgroundBrush();
}
}
}
if (_settings.GlobalSettings().UseAcrylicInTabRow())

View File

@@ -223,7 +223,7 @@ namespace winrt::TerminalApp::implementation
void _UpdateTabIndices();
TerminalApp::SettingsTab _settingsTab{ nullptr };
TerminalApp::TerminalTab _settingsTab{ nullptr };
bool _isInFocusMode{ false };
bool _isFullscreen{ false };
@@ -271,6 +271,8 @@ namespace winrt::TerminalApp::implementation
TerminalApp::ContentManager _manager{ nullptr };
TerminalApp::TerminalSettingsCache _terminalSettingsCache{ nullptr };
struct StashedDragData
{
winrt::com_ptr<winrt::TerminalApp::implementation::TabBase> draggedTab{ nullptr };
@@ -301,14 +303,14 @@ namespace winrt::TerminalApp::implementation
winrt::Windows::UI::Xaml::Controls::MenuFlyoutItem _CreateNewTabFlyoutProfile(const Microsoft::Terminal::Settings::Model::Profile profile, int profileIndex);
void _OpenNewTabDropdown();
HRESULT _OpenNewTab(const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection = nullptr);
void _CreateNewTabFromPane(std::shared_ptr<Pane> pane, uint32_t insertPosition = -1);
HRESULT _OpenNewTab(const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs);
TerminalApp::TerminalTab _CreateNewTabFromPane(std::shared_ptr<Pane> pane, uint32_t insertPosition = -1);
std::wstring _evaluatePathForCwd(std::wstring_view path);
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _CreateConnectionFromSettings(Microsoft::Terminal::Settings::Model::Profile profile, Microsoft::Terminal::Settings::Model::TerminalSettings settings, const bool inheritCursor);
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _duplicateConnectionForRestart(std::shared_ptr<Pane> pane);
void _restartPaneConnection(const std::shared_ptr<Pane>& pane);
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _duplicateConnectionForRestart(const TerminalApp::TerminalPaneContent& paneContent);
void _restartPaneConnection(const TerminalApp::TerminalPaneContent&, const winrt::Windows::Foundation::IInspectable&);
winrt::fire_and_forget _OpenNewWindow(const Microsoft::Terminal::Settings::Model::NewTerminalArgs newTerminalArgs);
@@ -344,7 +346,6 @@ namespace winrt::TerminalApp::implementation
void _InitializeTab(winrt::com_ptr<TerminalTab> newTabImpl, uint32_t insertPosition = -1);
void _RegisterTerminalEvents(Microsoft::Terminal::Control::TermControl term);
void _RegisterTabEvents(TerminalTab& hostingTab);
void _RegisterPaneEvents(std::shared_ptr<Pane>& pane);
void _DismissTabContextMenus();
void _FocusCurrentTab(const bool focusAlways);

View File

@@ -0,0 +1,356 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "TerminalPaneContent.h"
#include "TerminalPaneContent.g.cpp"
#include "BellEventArgs.g.cpp"
#include <Mmsystem.h>
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Microsoft::Terminal::Settings::Model;
using namespace winrt::Microsoft::Terminal::Control;
using namespace winrt::Microsoft::Terminal::TerminalConnection;
namespace winrt::TerminalApp::implementation
{
TerminalPaneContent::TerminalPaneContent(const winrt::Microsoft::Terminal::Settings::Model::Profile& profile,
const winrt::Microsoft::Terminal::Control::TermControl& control) :
_control{ control },
_profile{ profile }
{
_setupControlEvents();
}
void TerminalPaneContent::_setupControlEvents()
{
_controlEvents._ConnectionStateChanged = _control.ConnectionStateChanged(winrt::auto_revoke, { this, &TerminalPaneContent::_controlConnectionStateChangedHandler });
_controlEvents._WarningBell = _control.WarningBell(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlWarningBellHandler });
_controlEvents._CloseTerminalRequested = _control.CloseTerminalRequested(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_closeTerminalRequestedHandler });
_controlEvents._RestartTerminalRequested = _control.RestartTerminalRequested(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_restartTerminalRequestedHandler });
_controlEvents._TitleChanged = _control.TitleChanged(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlTitleChanged });
_controlEvents._TabColorChanged = _control.TabColorChanged(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlTabColorChanged });
_controlEvents._SetTaskbarProgress = _control.SetTaskbarProgress(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlSetTaskbarProgress });
_controlEvents._ReadOnlyChanged = _control.ReadOnlyChanged(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlReadOnlyChanged });
_controlEvents._FocusFollowMouseRequested = _control.FocusFollowMouseRequested(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlFocusFollowMouseRequested });
}
void TerminalPaneContent::_removeControlEvents()
{
_controlEvents = {};
}
winrt::Windows::UI::Xaml::FrameworkElement TerminalPaneContent::GetRoot()
{
return _control;
}
winrt::Microsoft::Terminal::Control::TermControl TerminalPaneContent::GetTermControl()
{
return _control;
}
winrt::Windows::Foundation::Size TerminalPaneContent::MinimumSize()
{
return _control.MinimumSize();
}
void TerminalPaneContent::Focus(winrt::Windows::UI::Xaml::FocusState reason)
{
_control.Focus(reason);
}
void TerminalPaneContent::Close()
{
_removeControlEvents();
// Clear out our media player callbacks, and stop any playing media. This
// will prevent the callback from being triggered after we've closed, and
// also make sure that our sound stops when we're closed.
if (_bellPlayer)
{
_bellPlayer.Pause();
_bellPlayer.Source(nullptr);
_bellPlayer.Close();
_bellPlayer = nullptr;
_bellPlayerCreated = false;
}
CloseRequested.raise(*this, nullptr);
}
winrt::hstring TerminalPaneContent::Icon() const
{
return _profile.EvaluatedIcon();
}
Windows::Foundation::IReference<winrt::Windows::UI::Color> TerminalPaneContent::TabColor() const noexcept
{
return _control.TabColor();
}
NewTerminalArgs TerminalPaneContent::GetNewTerminalArgs(const bool asContent) const
{
NewTerminalArgs args{};
const auto& controlSettings = _control.Settings();
args.Profile(controlSettings.ProfileName());
// If we know the user's working directory use it instead of the profile.
if (const auto dir = _control.WorkingDirectory(); !dir.empty())
{
args.StartingDirectory(dir);
}
else
{
args.StartingDirectory(controlSettings.StartingDirectory());
}
args.TabTitle(controlSettings.StartingTitle());
args.Commandline(controlSettings.Commandline());
args.SuppressApplicationTitle(controlSettings.SuppressApplicationTitle());
if (controlSettings.TabColor() || controlSettings.StartingTabColor())
{
til::color c;
// StartingTabColor is prioritized over other colors
if (const auto color = controlSettings.StartingTabColor())
{
c = til::color(color.Value());
}
else
{
c = til::color(controlSettings.TabColor().Value());
}
args.TabColor(winrt::Windows::Foundation::IReference<winrt::Windows::UI::Color>{ static_cast<winrt::Windows::UI::Color>(c) });
}
// TODO:GH#9800 - we used to be able to persist the color scheme that a
// TermControl was initialized with, by name. With the change to having the
// control own its own copy of its settings, this isn't possible anymore.
//
// We may be able to get around this by storing the Name in the Core::Scheme
// 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 ContentId if absolutely needed. If you fill in a number
// here (even 0), we'll serialize that number, AND treat that action as an
// "attach existing" rather than a "create"
if (asContent)
{
args.ContentId(_control.ContentId());
}
return args;
}
void TerminalPaneContent::_controlTitleChanged(const IInspectable&, const IInspectable&)
{
TitleChanged.raise(*this, nullptr);
}
void TerminalPaneContent::_controlTabColorChanged(const IInspectable&, const IInspectable&)
{
TabColorChanged.raise(*this, nullptr);
}
void TerminalPaneContent::_controlSetTaskbarProgress(const IInspectable&, const IInspectable&)
{
TaskbarProgressChanged.raise(*this, nullptr);
}
void TerminalPaneContent::_controlReadOnlyChanged(const IInspectable&, const IInspectable&)
{
ReadOnlyChanged.raise(*this, nullptr);
}
void TerminalPaneContent::_controlFocusFollowMouseRequested(const IInspectable&, const IInspectable&)
{
FocusRequested.raise(*this, nullptr);
}
// Method Description:
// - Called when our attached control is closed. Triggers listeners to our close
// event, if we're a leaf pane.
// - If this was called, and we became a parent pane (due to work on another
// thread), this function will do nothing (allowing the control's new parent
// to handle the event instead).
// Arguments:
// - <none>
// Return Value:
// - <none>
winrt::fire_and_forget TerminalPaneContent::_controlConnectionStateChangedHandler(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable& args)
{
ConnectionStateChanged.raise(sender, args);
auto newConnectionState = ConnectionState::Closed;
if (const auto coreState = sender.try_as<ICoreState>())
{
newConnectionState = coreState.ConnectionState();
}
const auto previousConnectionState = std::exchange(_connectionState, newConnectionState);
if (newConnectionState < ConnectionState::Closed)
{
// Pane doesn't care if the connection isn't entering a terminal state.
co_return;
}
const auto weakThis = get_weak();
co_await wil::resume_foreground(_control.Dispatcher());
const auto strongThis = weakThis.get();
if (!strongThis)
{
co_return;
}
// It's possible that this event handler started being executed, scheduled
// on the UI thread, another child got created. So our control is
// actually no longer _our_ control, and instead could be a descendant.
//
// When the control's new Pane takes ownership of the control, the new
// parent will register its own event handler. That event handler will get
// fired after this handler returns, and will properly cleanup state.
if (previousConnectionState < ConnectionState::Connected && newConnectionState >= ConnectionState::Failed)
{
// A failure to complete the connection (before it has _connected_) is not covered by "closeOnExit".
// This is to prevent a misconfiguration (closeOnExit: always, startingDirectory: garbage) resulting
// in Terminal flashing open and immediately closed.
co_return;
}
if (_profile)
{
if (_isDefTermSession && _profile.CloseOnExit() == CloseOnExitMode::Automatic)
{
// For 'automatic', we only care about the connection state if we were launched by Terminal
// Since we were launched via defterm, ignore the connection state (i.e. we treat the
// close on exit mode as 'always', see GH #13325 for discussion)
Close();
}
const auto mode = _profile.CloseOnExit();
if ((mode == CloseOnExitMode::Always) ||
((mode == CloseOnExitMode::Graceful || mode == CloseOnExitMode::Automatic) && newConnectionState == ConnectionState::Closed))
{
Close();
}
}
}
// Method Description:
// - Plays a warning note when triggered by the BEL control character,
// using the sound configured for the "Critical Stop" system event.`
// This matches the behavior of the Windows Console host.
// - Will also flash the taskbar if the bellStyle setting for this profile
// has the 'visual' flag set
// Arguments:
// - <unused>
void TerminalPaneContent::_controlWarningBellHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/,
const winrt::Windows::Foundation::IInspectable& /*eventArgs*/)
{
if (_profile)
{
// We don't want to do anything if nothing is set, so check for that first
if (static_cast<int>(_profile.BellStyle()) != 0)
{
if (WI_IsFlagSet(_profile.BellStyle(), winrt::Microsoft::Terminal::Settings::Model::BellStyle::Audible))
{
// Audible is set, play the sound
auto sounds{ _profile.BellSound() };
if (sounds && sounds.Size() > 0)
{
winrt::hstring soundPath{ wil::ExpandEnvironmentStringsW<std::wstring>(sounds.GetAt(rand() % sounds.Size()).c_str()) };
winrt::Windows::Foundation::Uri uri{ soundPath };
_playBellSound(uri);
}
else
{
const auto soundAlias = reinterpret_cast<LPCTSTR>(SND_ALIAS_SYSTEMHAND);
PlaySound(soundAlias, NULL, SND_ALIAS_ID | SND_ASYNC | SND_SENTRY);
}
}
if (WI_IsFlagSet(_profile.BellStyle(), winrt::Microsoft::Terminal::Settings::Model::BellStyle::Window))
{
_control.BellLightOn();
}
// raise the event with the bool value corresponding to the taskbar flag
BellRequested.raise(*this,
*winrt::make_self<TerminalApp::implementation::BellEventArgs>(WI_IsFlagSet(_profile.BellStyle(), BellStyle::Taskbar)));
}
}
}
winrt::fire_and_forget TerminalPaneContent::_playBellSound(winrt::Windows::Foundation::Uri uri)
{
auto weakThis{ get_weak() };
co_await wil::resume_foreground(_control.Dispatcher());
if (auto pane{ weakThis.get() })
{
if (!_bellPlayerCreated)
{
// The MediaPlayer might not exist on Windows N SKU.
try
{
_bellPlayerCreated = true;
_bellPlayer = winrt::Windows::Media::Playback::MediaPlayer();
// GH#12258: The media keys (like play/pause) should have no effect on our bell sound.
_bellPlayer.CommandManager().IsEnabled(false);
}
CATCH_LOG();
}
if (_bellPlayer)
{
const auto source{ winrt::Windows::Media::Core::MediaSource::CreateFromUri(uri) };
const auto item{ winrt::Windows::Media::Playback::MediaPlaybackItem(source) };
_bellPlayer.Source(item);
_bellPlayer.Play();
}
}
}
void TerminalPaneContent::_closeTerminalRequestedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/,
const winrt::Windows::Foundation::IInspectable& /*args*/)
{
Close();
}
void TerminalPaneContent::_restartTerminalRequestedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/,
const winrt::Windows::Foundation::IInspectable& /*args*/)
{
RestartTerminalRequested.raise(*this, nullptr);
}
void TerminalPaneContent::UpdateSettings(const CascadiaSettings& /*settings*/)
{
// Do nothing. We'll later be updated manually by
// UpdateTerminalSettings, which we need for profile and
// focused/unfocused settings.
assert(false); // If you hit this, you done goofed.
}
void TerminalPaneContent::UpdateTerminalSettings(const TerminalApp::TerminalSettingsCache& cache)
{
if (const auto& settings{ cache.TryLookup(_profile) })
{
_control.UpdateControlSettings(settings.DefaultSettings(), settings.UnfocusedSettings());
}
}
// Method Description:
// - Should be called when this pane is created via a default terminal handoff
// - Finalizes our configuration given the information that we have been
// created via default handoff
void TerminalPaneContent::MarkAsDefterm()
{
_isDefTermSession = true;
}
winrt::Windows::UI::Xaml::Media::Brush TerminalPaneContent::BackgroundBrush()
{
return _control.BackgroundBrush();
}
float TerminalPaneContent::SnapDownToGrid(const TerminalApp::PaneSnapDirection direction, const float sizeToSnap)
{
return _control.SnapDimensionToGrid(direction == PaneSnapDirection::Width, sizeToSnap);
}
Windows::Foundation::Size TerminalPaneContent::GridUnitSize()
{
return _control.CharacterDimensions();
}
}

View File

@@ -0,0 +1,106 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include "TerminalPaneContent.g.h"
#include "BellEventArgs.g.h"
namespace winrt::TerminalApp::implementation
{
struct BellEventArgs : public BellEventArgsT<BellEventArgs>
{
public:
BellEventArgs(bool flashTaskbar) :
FlashTaskbar(flashTaskbar) {}
til::property<bool> FlashTaskbar;
};
struct TerminalPaneContent : TerminalPaneContentT<TerminalPaneContent>
{
TerminalPaneContent(const winrt::Microsoft::Terminal::Settings::Model::Profile& profile,
const winrt::Microsoft::Terminal::Control::TermControl& control);
winrt::Windows::UI::Xaml::FrameworkElement GetRoot();
winrt::Microsoft::Terminal::Control::TermControl GetTermControl();
winrt::Windows::Foundation::Size MinimumSize();
void Focus(winrt::Windows::UI::Xaml::FocusState reason = winrt::Windows::UI::Xaml::FocusState::Programmatic);
void Close();
winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetNewTerminalArgs(const bool asContent) const;
void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings);
void UpdateTerminalSettings(const TerminalApp::TerminalSettingsCache& cache);
void MarkAsDefterm();
winrt::Microsoft::Terminal::Settings::Model::Profile GetProfile() const
{
return _profile;
}
winrt::hstring Title() { return _control.Title(); }
uint64_t TaskbarState() { return _control.TaskbarState(); }
uint64_t TaskbarProgress() { return _control.TaskbarProgress(); }
bool ReadOnly() { return _control.ReadOnly(); }
winrt::hstring Icon() const;
Windows::Foundation::IReference<winrt::Windows::UI::Color> TabColor() const noexcept;
winrt::Windows::UI::Xaml::Media::Brush BackgroundBrush();
float SnapDownToGrid(const TerminalApp::PaneSnapDirection direction, const float sizeToSnap);
Windows::Foundation::Size GridUnitSize();
til::typed_event<TerminalApp::TerminalPaneContent, winrt::Windows::Foundation::IInspectable> RestartTerminalRequested;
til::typed_event<> ConnectionStateChanged;
til::typed_event<IPaneContent> CloseRequested;
til::typed_event<IPaneContent, winrt::TerminalApp::BellEventArgs> BellRequested;
til::typed_event<IPaneContent> TitleChanged;
til::typed_event<IPaneContent> TabColorChanged;
til::typed_event<IPaneContent> TaskbarProgressChanged;
til::typed_event<IPaneContent> ReadOnlyChanged;
til::typed_event<IPaneContent> FocusRequested;
private:
winrt::Microsoft::Terminal::Control::TermControl _control{ nullptr };
winrt::Microsoft::Terminal::TerminalConnection::ConnectionState _connectionState{ winrt::Microsoft::Terminal::TerminalConnection::ConnectionState::NotConnected };
winrt::Microsoft::Terminal::Settings::Model::Profile _profile{ nullptr };
bool _isDefTermSession{ false };
winrt::Windows::Media::Playback::MediaPlayer _bellPlayer{ nullptr };
bool _bellPlayerCreated{ false };
struct ControlEventTokens
{
winrt::Microsoft::Terminal::Control::TermControl::ConnectionStateChanged_revoker _ConnectionStateChanged;
winrt::Microsoft::Terminal::Control::TermControl::WarningBell_revoker _WarningBell;
winrt::Microsoft::Terminal::Control::TermControl::CloseTerminalRequested_revoker _CloseTerminalRequested;
winrt::Microsoft::Terminal::Control::TermControl::RestartTerminalRequested_revoker _RestartTerminalRequested;
winrt::Microsoft::Terminal::Control::TermControl::TitleChanged_revoker _TitleChanged;
winrt::Microsoft::Terminal::Control::TermControl::TabColorChanged_revoker _TabColorChanged;
winrt::Microsoft::Terminal::Control::TermControl::SetTaskbarProgress_revoker _SetTaskbarProgress;
winrt::Microsoft::Terminal::Control::TermControl::ReadOnlyChanged_revoker _ReadOnlyChanged;
winrt::Microsoft::Terminal::Control::TermControl::FocusFollowMouseRequested_revoker _FocusFollowMouseRequested;
} _controlEvents;
void _setupControlEvents();
void _removeControlEvents();
winrt::fire_and_forget _playBellSound(winrt::Windows::Foundation::Uri uri);
winrt::fire_and_forget _controlConnectionStateChangedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& /*args*/);
void _controlWarningBellHandler(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable& e);
void _controlReadOnlyChangedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& e);
void _controlTitleChanged(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args);
void _controlTabColorChanged(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args);
void _controlSetTaskbarProgress(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args);
void _controlReadOnlyChanged(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args);
void _controlFocusFollowMouseRequested(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args);
void _closeTerminalRequestedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& /*args*/);
void _restartTerminalRequestedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& /*args*/);
};
}

View File

@@ -0,0 +1,48 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import "IPaneContent.idl";
import "TerminalSettingsCache.idl";
import "FilteredCommand.idl";
namespace TerminalApp
{
[default_interface] runtimeclass TerminalPaneContent : IPaneContent, ISnappable
{
Microsoft.Terminal.Control.TermControl GetTermControl();
void UpdateTerminalSettings(TerminalSettingsCache cache);
void MarkAsDefterm();
Microsoft.Terminal.Settings.Model.Profile GetProfile();
event Windows.Foundation.TypedEventHandler<TerminalPaneContent, Object> RestartTerminalRequested;
}
[default_interface] runtimeclass ScratchpadContent : IPaneContent
{
}
[default_interface] runtimeclass SettingsPaneContent : IPaneContent
{
SettingsPaneContent(Microsoft.Terminal.Settings.Model.CascadiaSettings settings);
}
[default_interface] runtimeclass FilteredTask : TerminalApp.FilteredCommand
{
String Input{ get; };
Windows.Foundation.Collections.IObservableVector<FilteredTask> Children { get; };
Windows.UI.Xaml.Visibility Visibility { get; };
}
[default_interface] runtimeclass TasksPaneContent : Windows.UI.Xaml.Controls.UserControl, IPaneContent
{
TasksPaneContent();
void SetLastActiveControl(Microsoft.Terminal.Control.TermControl control);
event Windows.Foundation.TypedEventHandler<Object, Microsoft.Terminal.Settings.Model.Command> DispatchCommandRequested;
}
}

View File

@@ -0,0 +1,57 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "TerminalSettingsCache.h"
#include "TerminalSettingsCache.g.cpp"
namespace winrt
{
namespace MUX = Microsoft::UI::Xaml;
namespace WUX = Windows::UI::Xaml;
namespace MTSM = Microsoft::Terminal::Settings::Model;
}
namespace winrt::TerminalApp::implementation
{
TerminalSettingsCache::TerminalSettingsCache(const MTSM::CascadiaSettings& settings, const TerminalApp::AppKeyBindings& bindings) :
_settings{ settings },
_bindings{ bindings }
{
// Mapping by GUID isn't _excellent_ because the defaults profile doesn't have a stable GUID; however,
// when we stabilize its guid this will become fully safe.
const auto profileDefaults{ _settings.ProfileDefaults() };
const auto allProfiles{ _settings.AllProfiles() };
profileGuidSettingsMap.reserve(allProfiles.Size() + 1);
// Include the Defaults profile for consideration
profileGuidSettingsMap.insert_or_assign(profileDefaults.Guid(), std::pair{ profileDefaults, nullptr });
for (const auto& newProfile : allProfiles)
{
// Avoid creating a TerminalSettings right now. They're not totally cheap, and we suspect that users with many
// panes may not be using all of their profiles at the same time. Lazy evaluation is king!
profileGuidSettingsMap.insert_or_assign(newProfile.Guid(), std::pair{ newProfile, nullptr });
}
}
MTSM::TerminalSettingsCreateResult TerminalSettingsCache::TryLookup(const MTSM::Profile& profile)
{
const auto found{ profileGuidSettingsMap.find(profile.Guid()) };
// GH#2455: If there are any panes with controls that had been
// initialized with a Profile that no longer exists in our list of
// profiles, we'll leave it unmodified. The profile doesn't exist
// anymore, so we can't possibly update its settings.
if (found != profileGuidSettingsMap.cend())
{
auto& pair{ found->second };
if (!pair.second)
{
pair.second = MTSM::TerminalSettings::CreateWithProfile(_settings, pair.first, _bindings);
}
return pair.second;
}
return nullptr;
}
}

View File

@@ -0,0 +1,37 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Class Name:
- TerminalSettingsCache.h
Abstract:
- This is a helper class used as we update the settings for panes. This class
contains a single map of guid -> TerminalSettings, so that as we update all
the panes during a settings reload, we only need to create a TerminalSettings
once per profile.
--*/
#pragma once
#include "TerminalSettingsCache.g.h"
#include <inc/cppwinrt_utils.h>
namespace winrt::TerminalApp::implementation
{
class TerminalSettingsCache : public TerminalSettingsCacheT<TerminalSettingsCache>
{
public:
TerminalSettingsCache(const Microsoft::Terminal::Settings::Model::CascadiaSettings& settings, const TerminalApp::AppKeyBindings& bindings);
Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult TryLookup(const Microsoft::Terminal::Settings::Model::Profile& profile);
private:
Microsoft::Terminal::Settings::Model::CascadiaSettings _settings{ nullptr };
TerminalApp::AppKeyBindings _bindings{ nullptr };
std::unordered_map<winrt::guid, std::pair<Microsoft::Terminal::Settings::Model::Profile, Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult>> profileGuidSettingsMap;
};
}
namespace winrt::TerminalApp::factory_implementation
{
BASIC_FACTORY(TerminalSettingsCache);
}

View File

@@ -0,0 +1,12 @@
import "AppKeyBindings.idl";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
namespace TerminalApp
{
[default_interface] runtimeclass TerminalSettingsCache
{
TerminalSettingsCache(Microsoft.Terminal.Settings.Model.CascadiaSettings settings, AppKeyBindings bindings);
Microsoft.Terminal.Settings.Model.TerminalSettingsCreateResult TryLookup(Microsoft.Terminal.Settings.Model.Profile profile);
}
}

View File

@@ -186,6 +186,11 @@ namespace winrt::TerminalApp::implementation
return nullptr;
}
IPaneContent TerminalTab::GetActiveContent() const
{
return _activePane ? _activePane->GetContent() : nullptr;
}
// Method Description:
// - Called after construction of a Tab object to bind event handlers to its
// associated Pane and TermControl objects
@@ -200,9 +205,9 @@ namespace winrt::TerminalApp::implementation
_rootPane->WalkTree([&](std::shared_ptr<Pane> pane) {
// Attach event handlers to each new pane
_AttachEventHandlersToPane(pane);
if (auto control = pane->GetTerminalControl())
if (auto content = pane->GetContent())
{
_AttachEventHandlersToControl(pane->Id().value(), control);
_AttachEventHandlersToContent(pane->Id().value(), content);
}
});
}
@@ -261,12 +266,18 @@ namespace winrt::TerminalApp::implementation
// of the settings that apply to all tabs.
// Return Value:
// - <none>
void TerminalTab::UpdateSettings()
void TerminalTab::UpdateSettings(const CascadiaSettings& settings, const TerminalApp::TerminalSettingsCache& cache)
{
ASSERT_UI_THREAD();
// The tabWidthMode may have changed, update the header control accordingly
_UpdateHeaderControlMaxWidth();
// Update the settings on all our panes.
_rootPane->WalkTree([&](auto pane) {
pane->UpdateSettings(settings, cache);
return false;
});
}
// Method Description:
@@ -275,7 +286,7 @@ namespace winrt::TerminalApp::implementation
// - iconPath: The new path string to use as the IconPath for our TabViewItem
// Return Value:
// - <none>
void TerminalTab::UpdateIcon(const winrt::hstring iconPath, const winrt::Microsoft::Terminal::Settings::Model::IconStyle iconStyle)
void TerminalTab::UpdateIcon(const winrt::hstring& iconPath, const winrt::Microsoft::Terminal::Settings::Model::IconStyle iconStyle)
{
ASSERT_UI_THREAD();
@@ -377,8 +388,8 @@ namespace winrt::TerminalApp::implementation
{
return RS_(L"MultiplePanes");
}
const auto lastFocusedControl = GetActiveTerminalControl();
return lastFocusedControl ? lastFocusedControl.Title() : L"";
const auto activeContent = GetActiveContent();
return activeContent ? activeContent.Title() : winrt::hstring{ L"" };
}
// Method Description:
@@ -433,9 +444,32 @@ namespace winrt::TerminalApp::implementation
// 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, asContent);
Pane::BuildStartupState state;
// HORRIBLE
//
// Workaround till we know how we actually want to handle state
// restoring other kinda of panes. If this is a settings tab, just
// restore it as a settings tab. Don't bother recreating terminal args
// for every pane.
//
// In the future, we'll want to definitely get rid of
// Pane::GetTerminalArgsForPane, and somehow instead find a better way
// of re-creating the pane state. Probably through a combo of ResizePane
// actions and SetPaneOrientation actions.
if (const auto& settings{ _rootPane->GetContent().try_as<SettingsPaneContent>() })
{
ActionAndArgs action;
action.Action(ShortcutAction::OpenSettings);
OpenSettingsArgs args{ SettingsTarget::SettingsUI };
action.Args(args);
state.args = std::vector{ std::move(action) };
}
else
{
state = _rootPane->BuildStartupActions(0, 1, asContent);
ActionAndArgs newTabAction{};
newTabAction.Action(ShortcutAction::NewTab);
NewTabArgs newTabArgs{ state.firstPane->GetTerminalArgsForPane(asContent) };
@@ -513,7 +547,10 @@ namespace winrt::TerminalApp::implementation
if (p->_IsLeaf())
{
p->Id(_nextPaneId);
_AttachEventHandlersToControl(p->Id().value(), p->_control);
if (const auto& content{ p->GetContent() })
{
_AttachEventHandlersToContent(p->Id().value(), content);
}
_nextPaneId++;
}
return false;
@@ -626,7 +663,11 @@ namespace winrt::TerminalApp::implementation
if (p->_IsLeaf())
{
p->Id(_nextPaneId);
_AttachEventHandlersToControl(p->Id().value(), p->_control);
if (const auto& content{ p->GetContent() })
{
_AttachEventHandlersToContent(p->Id().value(), content);
}
_nextPaneId++;
}
});
@@ -883,29 +924,17 @@ namespace winrt::TerminalApp::implementation
// the control itself doesn't have a particular ID and its pointer is
// unstable since it is moved when panes split.
// Arguments:
// - paneId: The ID of the pane that contains the given control.
// - control: the control to remove events from.
// - paneId: The ID of the pane that contains the given content.
// Return Value:
// - <none>
void TerminalTab::_DetachEventHandlersFromControl(const uint32_t paneId, const TermControl& control)
void TerminalTab::_DetachEventHandlersFromContent(const uint32_t paneId)
{
auto it = _controlEvents.find(paneId);
if (it != _controlEvents.end())
auto it = _contentEvents.find(paneId);
if (it != _contentEvents.end())
{
auto& events = it->second;
control.TitleChanged(events.titleToken);
control.TabColorChanged(events.colorToken);
control.SetTaskbarProgress(events.taskbarToken);
control.ConnectionStateChanged(events.stateToken);
control.ReadOnlyChanged(events.readOnlyToken);
control.FocusFollowMouseRequested(events.focusToken);
events.KeySent.revoke();
events.CharSent.revoke();
events.StringSent.revoke();
_controlEvents.erase(paneId);
// revoke the event handlers by resetting the event struct
// and remove it from the map
_contentEvents.erase(paneId);
}
}
@@ -920,87 +949,159 @@ namespace winrt::TerminalApp::implementation
// - control: the TermControl to add events to.
// Return Value:
// - <none>
void TerminalTab::_AttachEventHandlersToControl(const uint32_t paneId, const TermControl& control)
void TerminalTab::_AttachEventHandlersToContent(const uint32_t paneId, const TerminalApp::IPaneContent& content)
{
auto weakThis{ get_weak() };
auto dispatcher = TabViewItem().Dispatcher();
ControlEventTokens events{};
ContentEventTokens events{};
events.titleToken = control.TitleChanged([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget {
// The lambda lives in the `std::function`-style container owned by `control`. That is, when the
// `control` gets destroyed the lambda struct also gets destroyed. In other words, we need to
// copy `weakThis` onto the stack, because that's the only thing that gets captured in coroutines.
// See: https://devblogs.microsoft.com/oldnewthing/20211103-00/?p=105870
const auto weakThisCopy = weakThis;
co_await wil::resume_foreground(dispatcher);
if (auto tab{ weakThisCopy.get() })
{
// The title of the control changed, but not necessarily the title of the tab.
// Set the tab's text to the active panes' text.
tab->UpdateTitle();
}
});
events.CloseRequested = content.CloseRequested(
winrt::auto_revoke,
[dispatcher, weakThis](auto sender, auto&&) -> winrt::fire_and_forget {
// Don't forget! this ^^^^^^^^ sender can't be a reference, this is a async callback.
events.colorToken = control.TabColorChanged([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget {
const auto weakThisCopy = weakThis;
co_await wil::resume_foreground(dispatcher);
if (auto tab{ weakThisCopy.get() })
{
// The control's tabColor changed, but it is not necessarily the
// active control in this tab. We'll just recalculate the
// current color anyways.
tab->_RecalculateAndApplyTabColor();
}
});
events.taskbarToken = control.SetTaskbarProgress([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget {
const auto weakThisCopy = weakThis;
co_await wil::resume_foreground(dispatcher);
if (auto tab{ weakThisCopy.get() })
{
tab->_UpdateProgressState();
}
});
events.stateToken = control.ConnectionStateChanged([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget {
const auto weakThisCopy = weakThis;
co_await wil::resume_foreground(dispatcher);
if (auto tab{ weakThisCopy.get() })
{
tab->_UpdateConnectionClosedState();
}
});
events.readOnlyToken = control.ReadOnlyChanged([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget {
const auto weakThisCopy = weakThis;
co_await wil::resume_foreground(dispatcher);
if (auto tab{ weakThisCopy.get() })
{
tab->_RecalculateAndApplyReadOnly();
}
});
events.focusToken = control.FocusFollowMouseRequested([dispatcher, weakThis](auto sender, auto) -> winrt::fire_and_forget {
const auto weakThisCopy = weakThis;
co_await wil::resume_foreground(dispatcher);
if (const auto tab{ weakThisCopy.get() })
{
if (tab->_focused())
// The lambda lives in the `std::function`-style container owned by `control`. That is, when the
// `control` gets destroyed the lambda struct also gets destroyed. In other words, we need to
// copy `weakThis` onto the stack, because that's the only thing that gets captured in coroutines.
// See: https://devblogs.microsoft.com/oldnewthing/20211103-00/?p=105870
const auto weakThisCopy = weakThis;
co_await wil::resume_foreground(dispatcher);
// Check if Tab's lifetime has expired
if (auto tab{ weakThisCopy.get() })
{
if (const auto termControl{ sender.try_as<winrt::Microsoft::Terminal::Control::TermControl>() })
if (const auto content{ sender.try_as<TerminalApp::IPaneContent>() })
{
termControl.Focus(FocusState::Pointer);
tab->_rootPane->WalkTree([content](std::shared_ptr<Pane> pane) {
if (pane->GetContent() == content)
{
pane->Close();
}
});
}
}
}
});
});
events.TitleChanged = content.TitleChanged(
winrt::auto_revoke,
[dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget {
// The lambda lives in the `std::function`-style container owned by `control`. That is, when the
// `control` gets destroyed the lambda struct also gets destroyed. In other words, we need to
// copy `weakThis` onto the stack, because that's the only thing that gets captured in coroutines.
// See: https://devblogs.microsoft.com/oldnewthing/20211103-00/?p=105870
const auto weakThisCopy = weakThis;
co_await wil::resume_foreground(dispatcher);
// Check if Tab's lifetime has expired
if (auto tab{ weakThisCopy.get() })
{
// The title of the control changed, but not necessarily the title of the tab.
// Set the tab's text to the active panes' text.
tab->UpdateTitle();
}
});
events.TabColorChanged = content.TabColorChanged(
winrt::auto_revoke,
[dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget {
const auto weakThisCopy = weakThis;
co_await wil::resume_foreground(dispatcher);
if (auto tab{ weakThisCopy.get() })
{
// The control's tabColor changed, but it is not necessarily the
// active control in this tab. We'll just recalculate the
// current color anyways.
tab->_RecalculateAndApplyTabColor();
}
});
events.TaskbarProgressChanged = content.TaskbarProgressChanged(
winrt::auto_revoke,
[dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget {
const auto weakThisCopy = weakThis;
co_await wil::resume_foreground(dispatcher);
// Check if Tab's lifetime has expired
if (auto tab{ weakThisCopy.get() })
{
tab->_UpdateProgressState();
}
});
events.ConnectionStateChanged = content.ConnectionStateChanged(
winrt::auto_revoke,
[dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget {
const auto weakThisCopy = weakThis;
co_await wil::resume_foreground(dispatcher);
if (auto tab{ weakThisCopy.get() })
{
tab->_UpdateConnectionClosedState();
}
});
events.ReadOnlyChanged = content.ReadOnlyChanged(
winrt::auto_revoke,
[dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget {
const auto weakThisCopy = weakThis;
co_await wil::resume_foreground(dispatcher);
if (auto tab{ weakThis.get() })
{
tab->_RecalculateAndApplyReadOnly();
}
});
events.FocusRequested = content.FocusRequested(
winrt::auto_revoke,
[dispatcher, weakThis](TerminalApp::IPaneContent sender, auto) -> winrt::fire_and_forget {
const auto weakThisCopy = weakThis;
co_await wil::resume_foreground(dispatcher);
if (const auto tab{ weakThisCopy.get() })
{
if (tab->_focused())
{
sender.Focus(FocusState::Pointer);
}
}
});
events.BellRequested = content.BellRequested(
winrt::auto_revoke,
[dispatcher, weakThis](TerminalApp::IPaneContent sender, auto bellArgs) -> winrt::fire_and_forget {
const auto weakThisCopy = weakThis;
co_await wil::resume_foreground(dispatcher);
if (const auto tab{ weakThisCopy.get() })
{
if (bellArgs.FlashTaskbar())
{
// If visual is set, we need to bubble this event all the way to app host to flash the taskbar
// In this part of the chain we bubble it from the hosting tab to the page
tab->TabRaiseVisualBell.raise();
}
// Show the bell indicator in the tab header
tab->ShowBellIndicator(true);
// If this tab is focused, activate the bell indicator timer, which will
// remove the bell indicator once it fires
// (otherwise, the indicator is removed when the tab gets focus)
if (tab->_focusState != WUX::FocusState::Unfocused)
{
tab->ActivateBellIndicatorTimer();
}
}
});
if (const auto& terminal{ content.try_as<TerminalApp::TerminalPaneContent>() })
{
events.RestartTerminalRequested = terminal.RestartTerminalRequested(winrt::auto_revoke, { get_weak(), &TerminalTab::_bubbleRestartTerminalRequested });
}
if (_tabStatus.IsInputBroadcastActive())
{
_addBroadcastHandlers(control, events);
if (const auto& termContent{ content.try_as<TerminalApp::TerminalPaneContent>() })
{
_addBroadcastHandlers(termContent.GetTermControl(), events);
}
}
_controlEvents[paneId] = std::move(events);
_contentEvents[paneId] = std::move(events);
}
// Method Description:
@@ -1156,6 +1257,22 @@ namespace winrt::TerminalApp::implementation
// Raise our own ActivePaneChanged event.
ActivePaneChanged.raise();
const auto content{ pane->GetContent() };
if (const auto termContent{ content.try_as<winrt::TerminalApp::TerminalPaneContent>() })
{
const auto& termControl{ termContent.GetTermControl() };
_rootPane->WalkTree([termControl](const auto& p) {
if (const auto& taskPane{ p->GetContent().try_as<TasksPaneContent>() })
{
taskPane.SetLastActiveControl(termControl);
}
else if (const auto& taskPane{ p->GetContent().try_as<MarkdownPaneContent>() })
{
taskPane.SetLastActiveControl(termControl);
}
});
}
}
// Method Description:
@@ -1250,36 +1367,12 @@ namespace winrt::TerminalApp::implementation
}
});
// Add a PaneRaiseBell event handler to the Pane
auto bellToken = pane->PaneRaiseBell([weakThis](auto&& /*s*/, auto&& visual) {
if (auto tab{ weakThis.get() })
{
if (visual)
{
// If visual is set, we need to bubble this event all the way to app host to flash the taskbar
// In this part of the chain we bubble it from the hosting tab to the page
tab->TabRaiseVisualBell.raise();
}
// Show the bell indicator in the tab header
tab->ShowBellIndicator(true);
// If this tab is focused, activate the bell indicator timer, which will
// remove the bell indicator once it fires
// (otherwise, the indicator is removed when the tab gets focus)
if (tab->_focusState != WUX::FocusState::Unfocused)
{
tab->ActivateBellIndicatorTimer();
}
}
});
// box the event token so that we can give a reference to it in the
// event handler.
auto detachedToken = std::make_shared<winrt::event_token>();
// Add a Detached event handler to the Pane to clean up tab state
// and other event handlers when a pane is removed from this tab.
*detachedToken = pane->Detached([weakThis, weakPane, gotFocusToken, lostFocusToken, closedToken, bellToken, detachedToken](std::shared_ptr<Pane> /*sender*/) {
*detachedToken = pane->Detached([weakThis, weakPane, gotFocusToken, lostFocusToken, closedToken, detachedToken](std::shared_ptr<Pane> /*sender*/) {
// Make sure we do this at most once
if (auto pane{ weakPane.lock() })
{
@@ -1287,14 +1380,10 @@ namespace winrt::TerminalApp::implementation
pane->GotFocus(gotFocusToken);
pane->LostFocus(lostFocusToken);
pane->Closed(closedToken);
pane->PaneRaiseBell(bellToken);
if (auto tab{ weakThis.get() })
{
if (auto control = pane->GetTerminalControl())
{
tab->_DetachEventHandlersFromControl(pane->Id().value(), control);
}
tab->_DetachEventHandlersFromContent(pane->Id().value());
for (auto i = tab->_mruPanes.begin(); i != tab->_mruPanes.end(); ++i)
{
@@ -1495,7 +1584,12 @@ namespace winrt::TerminalApp::implementation
// GH#10112 - if we're opening the tab renamer, don't
// immediately toss focus to the control. We don't want to steal
// focus from the tab renamer.
if (!tab->_headerControl.InRename() && !tab->GetActiveTerminalControl().SearchBoxEditInFocus())
const auto& terminalControl{ tab->GetActiveTerminalControl() }; // maybe null
// If we're
// * NOT in a rename
// * AND (the content isn't a TermControl, OR the term control doesn't have focus in the search box)
if (!tab->_headerControl.InRename() &&
(terminalControl == nullptr || !terminalControl.SearchBoxEditInFocus()))
{
tab->RequestFocusActiveControl.raise();
}
@@ -1517,12 +1611,12 @@ namespace winrt::TerminalApp::implementation
{
ASSERT_UI_THREAD();
std::optional<winrt::Windows::UI::Color> controlTabColor;
if (const auto& control = GetActiveTerminalControl())
std::optional<winrt::Windows::UI::Color> contentTabColor;
if (const auto& content{ GetActiveContent() })
{
if (const auto color = control.TabColor())
if (const auto color = content.TabColor())
{
controlTabColor = color.Value();
contentTabColor = color.Value();
}
}
@@ -1532,7 +1626,7 @@ namespace winrt::TerminalApp::implementation
// Color | | Set by
// -------------------- | -- | --
// Runtime Color | _optional_ | Color Picker / `setTabColor` action
// Control Tab Color | _optional_ | Profile's `tabColor`, or a color set by VT
// Content Tab Color | _optional_ | Profile's `tabColor`, or a color set by VT (whatever the tab's content wants)
// Theme Tab Background | _optional_ | `tab.backgroundColor` in the theme (handled in _RecalculateAndApplyTabColor)
// Tab Default Color | **default** | TabView in XAML
//
@@ -1541,7 +1635,7 @@ namespace winrt::TerminalApp::implementation
// tabview color" (and clear out any colors we've set).
return til::coalesce(_runtimeTabColor,
controlTabColor,
contentTabColor,
std::optional<Windows::UI::Color>(std::nullopt));
}
@@ -1580,7 +1674,7 @@ namespace winrt::TerminalApp::implementation
winrt::Windows::UI::Xaml::Media::Brush TerminalTab::_BackgroundBrush()
{
Media::Brush terminalBrush{ nullptr };
if (const auto& c{ GetActiveTerminalControl() })
if (const auto& c{ GetActiveContent() })
{
terminalBrush = c.BackgroundBrush();
}
@@ -1695,6 +1789,18 @@ namespace winrt::TerminalApp::implementation
return _zoomedPane != nullptr;
}
TermControl _termControlFromPane(const auto& pane)
{
if (const auto content{ pane->GetContent() })
{
if (const auto termContent{ content.try_as<winrt::TerminalApp::TerminalPaneContent>() })
{
return termContent.GetTermControl();
}
}
return nullptr;
}
// Method Description:
// - Toggle read-only mode on the active pane
// - If a parent pane is selected, this will ensure that all children have
@@ -1706,14 +1812,14 @@ namespace winrt::TerminalApp::implementation
auto hasReadOnly = false;
auto allReadOnly = true;
_activePane->WalkTree([&](const auto& p) {
if (const auto& control{ p->GetTerminalControl() })
if (const auto& control{ _termControlFromPane(p) })
{
hasReadOnly |= control.ReadOnly();
allReadOnly &= control.ReadOnly();
}
});
_activePane->WalkTree([&](const auto& p) {
if (const auto& control{ p->GetTerminalControl() })
if (const auto& control{ _termControlFromPane(p) })
{
// If all controls have the same read only state then just toggle
if (allReadOnly || !hasReadOnly)
@@ -1738,14 +1844,14 @@ namespace winrt::TerminalApp::implementation
auto hasReadOnly = false;
auto allReadOnly = true;
_activePane->WalkTree([&](const auto& p) {
if (const auto& control{ p->GetTerminalControl() })
if (const auto& control{ _termControlFromPane(p) })
{
hasReadOnly |= control.ReadOnly();
allReadOnly &= control.ReadOnly();
}
});
_activePane->WalkTree([&](const auto& p) {
if (const auto& control{ p->GetTerminalControl() })
if (const auto& control{ _termControlFromPane(p) })
{
// If all controls have the same read only state then just disable
if (allReadOnly || !hasReadOnly)
@@ -1776,6 +1882,10 @@ namespace winrt::TerminalApp::implementation
ReadOnly(_rootPane->ContainsReadOnly());
_updateIsClosable();
// Update all the visuals on all our panes, so they can update their
// border colors accordingly.
_rootPane->WalkTree([](const auto& p) { p->UpdateVisuals(); });
}
std::shared_ptr<Pane> TerminalTab::GetActivePane() const
@@ -1826,10 +1936,10 @@ namespace winrt::TerminalApp::implementation
{
return;
}
if (const auto& control{ p->GetTerminalControl() })
if (const auto& control{ _termControlFromPane(p) })
{
auto it = _controlEvents.find(*paneId);
if (it != _controlEvents.end())
auto it = _contentEvents.find(*paneId);
if (it != _contentEvents.end())
{
auto& events = it->second;
@@ -1847,7 +1957,7 @@ namespace winrt::TerminalApp::implementation
});
}
void TerminalTab::_addBroadcastHandlers(const TermControl& termControl, ControlEventTokens& events)
void TerminalTab::_addBroadcastHandlers(const TermControl& termControl, ContentEventTokens& events)
{
auto weakThis{ get_weak() };
// ADD EVENT HANDLERS HERE
@@ -1937,4 +2047,9 @@ namespace winrt::TerminalApp::implementation
ActionAndArgs actionAndArgs{ ShortcutAction::Find, nullptr };
_dispatch.DoAction(*this, actionAndArgs);
}
void TerminalTab::_bubbleRestartTerminalRequested(TerminalApp::TerminalPaneContent sender,
const winrt::Windows::Foundation::IInspectable& args)
{
RestartTerminalRequested.raise(sender, args);
}
}

View File

@@ -25,6 +25,7 @@ namespace winrt::TerminalApp::implementation
winrt::Microsoft::Terminal::Control::TermControl GetActiveTerminalControl() const;
winrt::Microsoft::Terminal::Settings::Model::Profile GetFocusedProfile() const noexcept;
winrt::TerminalApp::IPaneContent GetActiveContent() const;
void Focus(winrt::Windows::UI::Xaml::FocusState focusState) override;
@@ -41,7 +42,7 @@ namespace winrt::TerminalApp::implementation
std::shared_ptr<Pane> newPane);
void ToggleSplitOrientation();
void UpdateIcon(const winrt::hstring iconPath, const winrt::Microsoft::Terminal::Settings::Model::IconStyle iconStyle);
void UpdateIcon(const winrt::hstring& iconPath, const winrt::Microsoft::Terminal::Settings::Model::IconStyle iconStyle);
void HideIcon(const bool hide);
void ShowBellIndicator(const bool show);
@@ -57,7 +58,7 @@ namespace winrt::TerminalApp::implementation
bool SwapPane(const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction);
bool FocusPane(const uint32_t id);
void UpdateSettings();
void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings, const TerminalApp::TerminalSettingsCache& cache);
void UpdateTitle();
void Shutdown() override;
@@ -96,6 +97,8 @@ namespace winrt::TerminalApp::implementation
return _tabStatus;
}
til::typed_event<TerminalApp::TerminalPaneContent> RestartTerminalRequested;
til::event<winrt::delegate<>> ActivePaneChanged;
til::event<winrt::delegate<>> TabRaiseVisualBell;
til::typed_event<IInspectable, IInspectable> TaskbarProgressChanged;
@@ -122,20 +125,25 @@ namespace winrt::TerminalApp::implementation
winrt::event_token _colorClearedToken;
winrt::event_token _pickerClosedToken;
struct ControlEventTokens
struct ContentEventTokens
{
winrt::event_token titleToken;
winrt::event_token colorToken;
winrt::event_token taskbarToken;
winrt::event_token stateToken;
winrt::event_token readOnlyToken;
winrt::event_token focusToken;
winrt::TerminalApp::IPaneContent::BellRequested_revoker BellRequested;
winrt::TerminalApp::IPaneContent::TitleChanged_revoker TitleChanged;
winrt::TerminalApp::IPaneContent::TabColorChanged_revoker TabColorChanged;
winrt::TerminalApp::IPaneContent::TaskbarProgressChanged_revoker TaskbarProgressChanged;
winrt::TerminalApp::IPaneContent::ConnectionStateChanged_revoker ConnectionStateChanged;
winrt::TerminalApp::IPaneContent::ReadOnlyChanged_revoker ReadOnlyChanged;
winrt::TerminalApp::IPaneContent::FocusRequested_revoker FocusRequested;
winrt::TerminalApp::IPaneContent::CloseRequested_revoker CloseRequested;
// These events literally only apply if the content is a TermControl.
winrt::Microsoft::Terminal::Control::TermControl::KeySent_revoker KeySent;
winrt::Microsoft::Terminal::Control::TermControl::CharSent_revoker CharSent;
winrt::Microsoft::Terminal::Control::TermControl::StringSent_revoker StringSent;
winrt::TerminalApp::TerminalPaneContent::RestartTerminalRequested_revoker RestartTerminalRequested;
};
std::unordered_map<uint32_t, ControlEventTokens> _controlEvents;
std::unordered_map<uint32_t, ContentEventTokens> _contentEvents;
winrt::event_token _rootClosedToken{};
@@ -162,8 +170,8 @@ namespace winrt::TerminalApp::implementation
void _CreateContextMenu() override;
virtual winrt::hstring _CreateToolTipTitle() override;
void _DetachEventHandlersFromControl(const uint32_t paneId, const winrt::Microsoft::Terminal::Control::TermControl& control);
void _AttachEventHandlersToControl(const uint32_t paneId, const winrt::Microsoft::Terminal::Control::TermControl& control);
void _DetachEventHandlersFromContent(const uint32_t paneId);
void _AttachEventHandlersToContent(const uint32_t paneId, const winrt::TerminalApp::IPaneContent& content);
void _AttachEventHandlersToPane(std::shared_ptr<Pane> pane);
void _UpdateActivePane(std::shared_ptr<Pane> pane);
@@ -181,7 +189,7 @@ namespace winrt::TerminalApp::implementation
virtual winrt::Windows::UI::Xaml::Media::Brush _BackgroundBrush() override;
void _addBroadcastHandlers(const winrt::Microsoft::Terminal::Control::TermControl& control, ControlEventTokens& events);
void _addBroadcastHandlers(const winrt::Microsoft::Terminal::Control::TermControl& control, ContentEventTokens& events);
void _chooseColorClicked(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::RoutedEventArgs& e);
void _renameTabClicked(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::RoutedEventArgs& e);
@@ -192,6 +200,8 @@ namespace winrt::TerminalApp::implementation
void _moveTabToNewWindowClicked(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::RoutedEventArgs& e);
void _findClicked(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::RoutedEventArgs& e);
void _bubbleRestartTerminalRequested(TerminalApp::TerminalPaneContent sender, const winrt::Windows::Foundation::IInspectable& args);
friend class ::TerminalAppLocalTests::TabTests;
};
}

View File

@@ -36,7 +36,6 @@
<ClInclude Include="../TabRowControl.h" />
<ClInclude Include="../App.h" />
<ClInclude Include="../TerminalTab.h" />
<ClInclude Include="../SettingsTab.h" />
</ItemGroup>
<!-- ========================= Cpp Files ======================== -->
<ItemGroup>

View File

@@ -84,6 +84,7 @@ TRACELOGGING_DECLARE_PROVIDER(g_hTerminalAppProvider);
// Manually include til after we include Windows.Foundation to give it winrt superpowers
#include "til.h"
#include <til/winrt.h>
#include <SafeDispatcherTimer.h>

View File

@@ -96,7 +96,10 @@ static constexpr std::string_view ShowContextMenuKey{ "showContextMenu" };
static constexpr std::string_view ExpandSelectionToWordKey{ "expandSelectionToWord" };
static constexpr std::string_view RestartConnectionKey{ "restartConnection" };
static constexpr std::string_view ToggleBroadcastInputKey{ "toggleBroadcastInput" };
static constexpr std::string_view OpenScratchpadKey{ "experimental.openScratchpad" };
static constexpr std::string_view OpenAboutKey{ "openAbout" };
static constexpr std::string_view OpenTasksPaneKey{ "experimental.openTasks" };
static constexpr std::string_view OpenMarkdownPaneKey{ "experimental.openMarkdown" };
static constexpr std::string_view ActionKey{ "action" };
@@ -431,7 +434,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{ ShortcutAction::ExpandSelectionToWord, RS_(L"ExpandSelectionToWordCommandKey") },
{ ShortcutAction::RestartConnection, RS_(L"RestartConnectionKey") },
{ ShortcutAction::ToggleBroadcastInput, RS_(L"ToggleBroadcastInputCommandKey") },
{ ShortcutAction::OpenScratchpad, RS_(L"OpenScratchpadKey") },
{ ShortcutAction::OpenAbout, RS_(L"OpenAboutCommandKey") },
{ ShortcutAction::OpenTasksPane, RS_(L"OpenTasksPaneCommandKey") },
{ ShortcutAction::OpenMarkdownPane, MustGenerate },
};
}();

View File

@@ -48,6 +48,7 @@
#include "SelectCommandArgs.g.cpp"
#include "SelectOutputArgs.g.cpp"
#include "ColorSelectionArgs.g.cpp"
#include "OpenMarkdownPaneArgs.g.cpp"
#include <LibraryResources.h>
#include <WtExeUtils.h>
@@ -1076,4 +1077,22 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
}
return L"";
}
winrt::hstring OpenMarkdownPaneArgs::GenerateName() const
{
if (!Path().empty())
{
// "Open markdown file {path}"
return winrt::hstring{
fmt::format(std::wstring_view(RS_(L"OpenMarkdownFromPathCommandKey")),
Path())
};
}
else
{
// "Open markdown pane"
return RS_(L"OpenMarkdownCommandKey");
}
}
}

View File

@@ -50,6 +50,7 @@
#include "SelectCommandArgs.g.h"
#include "SelectOutputArgs.g.h"
#include "ColorSelectionArgs.g.h"
#include "OpenMarkdownPaneArgs.g.h"
#include "JsonUtils.h"
#include "HashUtils.h"
@@ -278,6 +279,10 @@ private: \
X(winrt::Microsoft::Terminal::Control::SelectionColor, Background, "background", false, nullptr) \
X(winrt::Microsoft::Terminal::Core::MatchMode, MatchMode, "matchMode", false, winrt::Microsoft::Terminal::Core::MatchMode::None)
////////////////////////////////////////////////////////////////////////////////
#define OPEN_MARKDOWN_PANE_ARGS(X) \
X(winrt::hstring, Path, "path", false, L"")
////////////////////////////////////////////////////////////////////////////////
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
@@ -819,6 +824,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
ACTION_ARGS_STRUCT(ColorSelectionArgs, COLOR_SELECTION_ARGS);
ACTION_ARGS_STRUCT(OpenMarkdownPaneArgs, OPEN_MARKDOWN_PANE_ARGS);
}
namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
@@ -833,6 +840,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
BASIC_FACTORY(SetTabColorArgs);
BASIC_FACTORY(RenameTabArgs);
BASIC_FACTORY(SwapPaneArgs);
BASIC_FACTORY(SendInputArgs);
BASIC_FACTORY(SplitPaneArgs);
BASIC_FACTORY(SetFocusModeArgs);
BASIC_FACTORY(SetFullScreenArgs);
@@ -857,4 +865,5 @@ namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
BASIC_FACTORY(SuggestionsArgs);
BASIC_FACTORY(SelectCommandArgs);
BASIC_FACTORY(SelectOutputArgs);
BASIC_FACTORY(OpenMarkdownPaneArgs);
}

View File

@@ -215,6 +215,7 @@ namespace Microsoft.Terminal.Settings.Model
[default_interface] runtimeclass SendInputArgs : IActionArgs
{
SendInputArgs(String input);
String Input { get; };
};
@@ -433,5 +434,11 @@ namespace Microsoft.Terminal.Settings.Model
SelectOutputDirection Direction { get; };
}
[default_interface] runtimeclass OpenMarkdownPaneArgs : IActionArgs
{
OpenMarkdownPaneArgs(String path);
String Path { get; };
}
}

View File

@@ -945,17 +945,27 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
cmdImpl.copy_from(winrt::get_self<implementation::Command>(command));
const auto inArgs{ command.ActionAndArgs().Args().try_as<Model::SendInputArgs>() };
const auto inputString{ (std::wstring_view)(inArgs ? inArgs.Input() : L"") };
auto args = winrt::make_self<SendInputArgs>(
winrt::hstring{ fmt::format(FMT_COMPILE(L"{:\x7f^{}}{}"),
L"",
numBackspaces,
(std::wstring_view)(inArgs ? inArgs.Input() : L"")) });
inputString) });
Model::ActionAndArgs actionAndArgs{ ShortcutAction::SendInput, *args };
auto copy = cmdImpl->Copy();
copy->ActionAndArgs(actionAndArgs);
if (!copy->HasName())
{
// Here, we want to manually generate a send input name, but
// without visualizing space and backspace
//
// TODO! Do we want to include `Send Input: ` in the generated
// string? I think it looks better without it tbh
copy->Name(winrt::hstring{ til::visualize_nonspace_control_codes(std::wstring{ inputString }) });
}
return *copy;
};

View File

@@ -110,7 +110,10 @@
ON_ALL_ACTIONS(CloseOtherPanes) \
ON_ALL_ACTIONS(RestartConnection) \
ON_ALL_ACTIONS(ToggleBroadcastInput) \
ON_ALL_ACTIONS(OpenAbout)
ON_ALL_ACTIONS(OpenScratchpad) \
ON_ALL_ACTIONS(OpenAbout) \
ON_ALL_ACTIONS(OpenMarkdownPane) \
ON_ALL_ACTIONS(OpenTasksPane)
#define ALL_SHORTCUT_ACTIONS_WITH_ARGS \
ON_ALL_ACTIONS_WITH_ARGS(AdjustFontSize) \
@@ -155,4 +158,5 @@
ON_ALL_ACTIONS_WITH_ARGS(Suggestions) \
ON_ALL_ACTIONS_WITH_ARGS(SelectCommand) \
ON_ALL_ACTIONS_WITH_ARGS(SelectOutput) \
ON_ALL_ACTIONS_WITH_ARGS(ColorSelection)
ON_ALL_ACTIONS_WITH_ARGS(ColorSelection) \
ON_ALL_ACTIONS_WITH_ARGS(OpenMarkdownPane)

View File

@@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
@@ -26,36 +26,36 @@
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
@@ -700,6 +700,9 @@
<data name="RestartConnectionKey" xml:space="preserve">
<value>Restart connection</value>
</data>
<data name="OpenScratchpadKey" xml:space="preserve">
<value>Open scratchpad</value>
</data>
<data name="SelectOutputNextCommandKey" xml:space="preserve">
<value>Select next command output</value>
</data>
@@ -724,4 +727,16 @@
<value>Open about dialog</value>
<comment>This will open the "about" dialog, to display version info and other documentation</comment>
</data>
<data name="OpenTasksPaneCommandKey" xml:space="preserve">
<value>Open tasks pane</value>
<comment>This will open a pane with a list of the users's saved commands in it</comment>
</data>
<data name="OpenMarkdownFromPathCommandKey" xml:space="preserve">
<value>Open markdown file {0}</value>
<comment>{0} will be replaced with a user-specified path to a markdown file to open</comment>
</data>
<data name="OpenMarkdownCommandKey" xml:space="preserve">
<value>Open markdown pane</value>
<comment>This will open a pane which can display markdown in it</comment>
</data>
</root>

View File

@@ -378,6 +378,7 @@
{ "command": "quit" },
{ "command": "restoreLastClosed" },
{ "command": "openAbout" },
{ "command": "experimental.openTasks" },
// Tab Management
// "command": "closeTab" is unbound by default.

View File

@@ -133,7 +133,7 @@ public: \
_##name = value; \
} \
\
private: \
protected: \
type _##name{ __VA_ARGS__ };
// Use this macro to quickly implement both the getter and setter for an
@@ -158,7 +158,7 @@ public:
} \
}; \
\
private: \
protected: \
type _##name{ __VA_ARGS__ }; \
void _set##name(const type& value) \
{ \

32
src/dep/md4c/md4c.vcxproj Normal file
View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectGuid>{7cae5851-50d5-4934-8d5e-30361a8a40f3}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>md4c</RootNamespace>
<ProjectName>md4c</ProjectName>
<TargetName>md4c</TargetName>
<ConfigurationType>StaticLibrary</ConfigurationType>
</PropertyGroup>
<Import Project="$(SolutionDir)src\common.build.pre.props" />
<ItemDefinitionGroup>
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<AdditionalIncludeDirectories>$(SolutionDir)\oss\md4c;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<WarningLevel>Level3</WarningLevel>
<TreatWarningAsError>false</TreatWarningAsError>
<DisableSpecificWarnings>4242;4244;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<TreatSpecificWarningsAsErrors>4189;4100;4389</TreatSpecificWarningsAsErrors>
<PreprocessorDefinitions>MD4C_USE_UTF16;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="$(SolutionDir)oss/md4c/md4c.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="$(SolutionDir)oss/md4c/md4c.h" />
</ItemGroup>
<!-- Careful reordering these. Some default props (contained in these files) are order sensitive. -->
<Import Project="$(SolutionDir)src\common.build.post.props" />
</Project>

View File

@@ -159,6 +159,17 @@
</alwaysEnabledBrandingTokens>
</feature>
<feature>
<name>Feature_ScratchpadPane</name>
<description>Allow the user to create scratchpad panes. Mostly just exists to validate non-terminal panes.</description>
<id>997</id>
<stage>AlwaysDisabled</stage>
<alwaysEnabledBrandingTokens>
<brandingToken>Dev</brandingToken>
<brandingToken>Canary</brandingToken>
</alwaysEnabledBrandingTokens>
</feature>
<feature>
<name>Feature_KeypadModeEnabled</name>
<description>Enables the DECKPAM, DECKPNM sequences to work as intended </description>

View File

@@ -24,6 +24,26 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
}
return str;
}
_TIL_INLINEPREFIX std::wstring visualize_nonspace_control_codes(std::wstring str) noexcept
{
for (auto& ch : str)
{
// NOT backspace!
if (ch < 0x20 && ch != 0x08)
{
ch += 0x2400;
}
// else if (ch == 0x20)
// {
// ch = 0x2423; // replace space with ␣
// }
else if (ch == 0x7f)
{
ch = 0x2421; // replace del with ␡
}
}
return str;
}
_TIL_INLINEPREFIX std::wstring visualize_control_codes(std::wstring_view str)
{