Compare commits

...

289 Commits

Author SHA1 Message Date
Mike Griese
1c8e99689f PRE-MERGE #17352 Fix some search highlights scenarios 2024-06-04 05:56:02 -05:00
Mike Griese
274010f2f3 PRE-MERGE #17358 Fix two panes being closed when just one is 2024-06-04 05:55:26 -05:00
Leonard Hecker
3606188eec Fix two panes being closed when just one is 2024-06-04 02:04:39 +02:00
Mike Griese
4485e50cbc PRE-MERGE #16848 Add support for custom CommandNotFound OSC 2024-06-03 16:18:06 -05:00
Mike Griese
ab25a1e286 PRE-MERGE #16895 Add support for resizing panes with mouse 2024-06-03 16:08:45 -05:00
Mike Griese
7802e20e40 PRE-MERGE #16916 Implement grapheme clusters 2024-06-03 15:45:53 -05:00
Mike Griese
e321c009a2 PRE-MERGE #17330 Add a snippets pane 2024-06-03 15:45:34 -05:00
Mike Griese
b968f9188c PRE-MERGE #17143 Improve Viewport and Viewport::WalkInBounds 2024-06-03 15:02:53 -05:00
Mike Griese
5460324d59 PRE-MERGE #16513 Add ability to save input action from command line 2024-06-03 14:58:40 -05:00
Mike Griese
a2adcd38e9 PRE-MERGE #16479 Theme for settings 2024-06-03 14:41:00 -05:00
Mike Griese
d14a98409d PRE-MERGE #17281 Allow actions in the new tab dropdown 2024-06-03 14:40:33 -05:00
Mike Griese
cf50777605 PRE-MERGE #17215 Remove command's knowledge of its keys 2024-06-03 13:35:47 -05:00
Pankaj Bhojwani
253dedf477 conflict 2024-06-03 11:31:47 -07:00
Carlos Zamora
ec1fbc2f9c Merge branch 'main' into dev/cazamor/cmdNotFound 2024-06-03 11:27:06 -07:00
Mike Griese
d699a0874d PRE-MERGE #17162 Refactor ActionMap and Command to use ActionIDs 2024-06-03 13:26:46 -05:00
Leonard Hecker
bb47e9ea9a Less diff, again 2024-06-03 14:54:33 +02:00
Leonard Hecker
af6392302a Less diff, Less problems 2024-06-02 18:14:59 +02:00
Leonard Hecker
eb3094ded2 Fix build 2024-06-02 14:24:39 +02:00
Leonard Hecker
23894780f8 Fixed tests, Some simplifications 2024-06-02 03:39:42 +02:00
Tushar Singh
3d9d32bb1f format 2024-06-01 20:49:59 +05:30
Tushar Singh
ba9a82068e fix some search highlights scenarios 2024-06-01 20:30:43 +05:30
Pankaj Bhojwani
406312f2f8 fix loops 2024-05-31 19:33:07 -07:00
Pankaj Bhojwani
625753cc41 x86 hash 2024-05-31 17:52:20 -07:00
Pankaj Bhojwani
a80316dfa5 1 more test 2024-05-31 16:56:34 -07:00
Pankaj Bhojwani
9703815f59 nits n fixes 2024-05-31 16:32:21 -07:00
Leonard Hecker
07efae4bee AuditMode fix 2024-05-31 23:30:34 +02:00
Leonard Hecker
f3b4d82185 Address all of the things, mostly 2024-05-31 23:14:34 +02:00
Leonard Hecker
f11137a604 Merge remote-tracking branch 'origin/main' into dev/lhecker/8000-graphemes 2024-05-31 23:10:14 +02:00
Pankaj Bhojwani
0dff336556 nits, schema 2024-05-31 10:51:36 -07:00
Pankaj Bhojwani
b88a8c5884 eraseif 2024-05-30 13:03:03 -07:00
Eric Nelson
3f15d6e4c0 Fix for save input toast text boxes not hiding when blank 2024-05-29 00:08:54 -07:00
Mike Griese
cf56a9a987 cherry-pick into save-input PR (mostly stashing so I make sure I don't lose this) 2024-05-28 17:34:26 -07:00
Eric Nelson
49a74c810f Move save input behind velocity 2024-05-28 17:10:12 -07:00
Eric Nelson
addf247d09 Merge remote-tracking branch 'origin/main' into save_input_action 2024-05-28 16:10:51 -07:00
Mike Griese
2d7a9d8949 Updates from review 2024-05-28 14:56:08 -05:00
Mike Griese
a6f8d8c5f5 Merge remote-tracking branch 'origin/main' into light_settings_v2 2024-05-28 14:33:54 -05:00
Mike Griese
5fd110016f Rename this, so the code doesn't reflect the old name 2024-05-28 14:31:28 -05:00
Mike Griese
e923513d2a spel 2024-05-28 13:49:54 -05:00
Mike Griese
3ac5414352 last nits before review 2024-05-28 10:03:36 -05:00
Mike Griese
57930ad432 Merge remote-tracking branch 'origin/main' into dev/migrie/f/snippets-pane 2024-05-28 10:03:19 -05:00
Mike Griese
2dc16e43a7 the cool thing with the hovering 2024-05-24 15:53:34 -05:00
Mike Griese
32234cafeb nits for polish 2024-05-24 15:46:05 -05:00
Pankaj Bhojwani
6c6dd46e02 leonard comments 2024-05-20 15:06:31 -07:00
Pankaj Bhojwani
51528e9cfd spaces 2024-05-16 16:59:35 -07:00
Pankaj Bhojwani
40b4aa2c94 works 2024-05-16 16:50:56 -07:00
Leonard Hecker
ee6af59f10 Merge remote-tracking branch 'origin/main' into dev/lhecker/viewport-walking 2024-05-13 16:08:03 +02:00
Pankaj Bhojwani
d4d216ca33 don't need helper anymore 2024-05-08 10:48:33 -07:00
Pankaj Bhojwani
7d00b25fbf spelling 2024-05-08 10:11:16 -07:00
Pankaj Bhojwani
ba375ec2a2 remove keys from command 2024-05-08 10:00:45 -07:00
Pankaj Bhojwani
5e48a45ba2 update add action 2024-05-07 15:56:52 -07:00
Pankaj Bhojwani
14d83b5f5c delete user actions that are identical to inbox actions 2024-05-06 16:50:01 -07:00
Pankaj Bhojwani
3e31bda6f2 generate here instead 2024-05-06 15:29:24 -07:00
Pankaj Bhojwani
4d35c14966 schema conflict 2024-05-03 14:54:09 -07:00
Pankaj Bhojwani
abef25d29c move this to header 2024-05-03 14:51:51 -07:00
Pankaj Bhojwani
7793c5c5bc schema 2024-05-03 13:30:09 -07:00
Mike Griese
7b8b60bdef resources to localize 2024-05-03 14:44:40 -05:00
Pankaj Bhojwani
ccf1cc9e83 nits 2024-05-02 19:06:51 -07:00
Pankaj Bhojwani
ebc03e98f4 another test 2024-05-02 18:44:44 -07:00
Pankaj Bhojwani
80fc299c10 some new tests 2024-05-02 18:11:46 -07:00
Mike Griese
43d46dbc99 hey it runs again 2024-05-02 17:04:58 -05:00
Mike Griese
0ec5aef050 Squashed commit of the following:
commit 727b051fd4
Merge: 37b4b7b6c 7c4dfff45
Author: Mike Griese <migrie@microsoft.com>
Date:   Thu May 2 16:27:09 2024 -0500

    Merge branch 'dev/migrie/f/sui-panes' into dev/migrie/fhl/tasks-pane

commit 7c4dfff451
Merge: a0e014f27 67ae9f6c3
Author: Mike Griese <migrie@microsoft.com>
Date:   Wed Apr 3 11:03:46 2024 -0500

    Merge remote-tracking branch 'origin/main' into dev/migrie/f/sui-panes

commit a0e014f277
Merge: e05b2bbe3 2bcbe6b49
Author: Mike Griese <migrie@microsoft.com>
Date:   Wed Apr 3 10:13:40 2024 -0500

    Merge remote-tracking branch 'origin/main' into dev/migrie/f/sui-panes

commit e05b2bbe37
Merge: ef560bf48 75dea24d6
Author: Mike Griese <migrie@microsoft.com>
Date:   Fri Mar 29 15:13:01 2024 -0500

    Merge branch 'main' into dev/migrie/f/sui-panes

commit ef560bf48e
Merge: 10e1e4694 7243d220e
Author: Mike Griese <migrie@microsoft.com>
Date:   Tue Mar 26 13:56:21 2024 -0500

    Merge branch 'dev/migrie/fhl/scratchpad-pane' into dev/migrie/f/sui-panes

commit 7243d220e5
Merge: 1d2059918 501522d69
Author: Mike Griese <migrie@microsoft.com>
Date:   Tue Mar 26 13:55:54 2024 -0500

    Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/scratchpad-pane

commit 10e1e46945
Author: Mike Griese <migrie@microsoft.com>
Date:   Tue Mar 26 13:55:11 2024 -0500

    ALSO doesn't really need to be projected

commit ddc88c83b2
Merge: 216cc3fa6 1d2059918
Author: Mike Griese <migrie@microsoft.com>
Date:   Tue Mar 26 13:26:16 2024 -0500

    Merge branch 'dev/migrie/fhl/scratchpad-pane' into dev/migrie/f/sui-panes

commit 1d20599186
Author: Mike Griese <migrie@microsoft.com>
Date:   Tue Mar 26 11:38:47 2024 -0500

    un fix this file

commit b6e4b62e15
Author: Mike Griese <migrie@microsoft.com>
Date:   Tue Mar 26 11:37:35 2024 -0500

    Doesn't really need to be projected

commit 0979cd6c44
Merge: d41793470 61e952c58
Author: Mike Griese <migrie@microsoft.com>
Date:   Tue Mar 26 11:22:45 2024 -0500

    Merge branch 'dev/migrie/fhl/non-terminal-panes-2023' into dev/migrie/fhl/scratchpad-pane

commit 61e952c58e
Merge: df73d7554 08dc34612
Author: Mike Griese <migrie@microsoft.com>
Date:   Tue Mar 26 11:22:27 2024 -0500

    Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/non-terminal-panes-2023

commit 37b4b7b6c0
Author: Mike Griese <migrie@microsoft.com>
Date:   Fri Mar 22 06:54:29 2024 -0500

    fix builds

commit 6576f949c4
Merge: 66878d2dd 216cc3fa6
Author: Mike Griese <migrie@microsoft.com>
Date:   Thu Mar 21 14:02:45 2024 -0500

    Merge branch 'dev/migrie/f/sui-panes' into dev/migrie/fhl/tasks-pane

commit 216cc3fa66
Merge: 77022e92f d41793470
Author: Mike Griese <migrie@microsoft.com>
Date:   Thu Mar 21 14:02:24 2024 -0500

    Merge branch 'dev/migrie/fhl/scratchpad-pane' into dev/migrie/f/sui-panes

commit d41793470f
Merge: f1ab16e7d df73d7554
Author: Mike Griese <migrie@microsoft.com>
Date:   Thu Mar 21 13:51:52 2024 -0500

    Merge branch 'dev/migrie/fhl/non-terminal-panes-2023' into dev/migrie/fhl/scratchpad-pane

commit df73d75541
Author: Mike Griese <migrie@microsoft.com>
Date:   Thu Mar 21 13:51:34 2024 -0500

    derp

commit 66878d2ddc
Merge: 7b84041ae 77022e92f
Author: Mike Griese <migrie@microsoft.com>
Date:   Thu Mar 21 13:21:51 2024 -0500

    Merge branch 'dev/migrie/f/sui-panes' into dev/migrie/fhl/tasks-pane

commit 77022e92f1
Merge: bcceb8505 f1ab16e7d
Author: Mike Griese <migrie@microsoft.com>
Date:   Thu Mar 21 13:17:20 2024 -0500

    Merge branch 'dev/migrie/fhl/scratchpad-pane' into dev/migrie/f/sui-panes

commit f1ab16e7d5
Merge: e0bb8409b 2083b2ff9
Author: Mike Griese <migrie@microsoft.com>
Date:   Thu Mar 21 13:16:08 2024 -0500

    Merge branch 'dev/migrie/fhl/non-terminal-panes-2023' into dev/migrie/fhl/scratchpad-pane

commit 2083b2ff9e
Merge: 52970ef85 b9a0cae01
Author: Mike Griese <migrie@microsoft.com>
Date:   Thu Mar 21 13:14:32 2024 -0500

    Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/non-terminal-panes-2023

commit 7b84041ae1
Merge: 8f2a129a1 bcceb8505
Author: Mike Griese <migrie@microsoft.com>
Date:   Wed Mar 20 09:38:28 2024 -0500

    Merge branch 'dev/migrie/f/sui-panes' into dev/migrie/fhl/tasks-pane

commit bcceb85057
Merge: 352e0a211 e0bb8409b
Author: Mike Griese <migrie@microsoft.com>
Date:   Wed Mar 20 09:20:16 2024 -0500

    Merge branch 'dev/migrie/fhl/scratchpad-pane' into dev/migrie/f/sui-panes

commit 352e0a211a
Author: Mike Griese <migrie@microsoft.com>
Date:   Wed Mar 20 09:14:28 2024 -0500

    fix settings pane for merge

    (cherry picked from commit 0c6a353967)

commit e0bb8409b3
Author: Mike Griese <migrie@microsoft.com>
Date:   Wed Mar 20 09:13:54 2024 -0500

    Fix scratch pane for merge

    (cherry picked from commit 591080db39)

commit 8f2a129a1e
Merge: 5b4747f7b c8d0c0aab
Author: Mike Griese <migrie@microsoft.com>
Date:   Wed Mar 20 06:50:28 2024 -0500

    Merge branch 'dev/migrie/f/sui-panes' into dev/migrie/fhl/tasks-pane

commit c8d0c0aab7
Merge: 863840ee0 2357653de
Author: Mike Griese <migrie@microsoft.com>
Date:   Wed Mar 20 06:40:43 2024 -0500

    Merge branch 'dev/migrie/fhl/scratchpad-pane' into dev/migrie/f/sui-panes

commit 2357653de5
Merge: 1951f3043 52970ef85
Author: Mike Griese <migrie@microsoft.com>
Date:   Wed Mar 20 06:40:04 2024 -0500

    Merge branch 'dev/migrie/fhl/non-terminal-panes-2023' into dev/migrie/fhl/scratchpad-pane

commit 52970ef854
Author: Mike Griese <migrie@microsoft.com>
Date:   Tue Mar 19 16:30:00 2024 -0500

    RegisterBigTimeEncapsulationViolatingTerminalPaneContentEvents

commit 826fc087b0
Author: Mike Griese <migrie@microsoft.com>
Date:   Tue Mar 19 15:50:58 2024 -0500

    hey there buddy, did you get lost?

commit a7533faf45
Author: Mike Griese <migrie@microsoft.com>
Date:   Tue Mar 19 15:15:08 2024 -0500

    eh these events are from pane content anyways!

commit 052dc78af5
Author: Mike Griese <migrie@microsoft.com>
Date:   Tue Mar 19 14:54:30 2024 -0500

    more nits

commit fd8b083a46
Author: Mike Griese <migrie@microsoft.com>
Date:   Tue Mar 19 13:55:11 2024 -0500

    get rid of this file

commit 6789ec0765
Author: Mike Griese <migrie@microsoft.com>
Date:   Tue Mar 19 13:34:35 2024 -0500

    some of the easier nits

commit 863840ee0f
Merge: 978fd6e2b 1951f3043
Author: Mike Griese <migrie@microsoft.com>
Date:   Mon Mar 18 13:20:43 2024 -0500

    Merge branch 'dev/migrie/fhl/scratchpad-pane' into dev/migrie/f/sui-panes

commit 1951f30434
Merge: 35651bc92 524d65869
Author: Mike Griese <migrie@microsoft.com>
Date:   Mon Mar 18 13:20:26 2024 -0500

    Merge branch 'dev/migrie/fhl/non-terminal-panes-2023' into dev/migrie/fhl/scratchpad-pane

commit 524d658699
Author: Mike Griese <migrie@microsoft.com>
Date:   Fri Mar 15 12:01:42 2024 -0500

    GREAT-GREAT-GRANDPARENT: Hey when a pane wants to get closed, we should close it

commit ef775a87c9
Merge: a3fbc6438 287422b21
Author: Mike Griese <migrie@microsoft.com>
Date:   Mon Mar 18 13:19:26 2024 -0500

    Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/non-terminal-panes-2023

commit 5b4747f7b7
Merge: 14a00c8ab 978fd6e2b
Author: Mike Griese <migrie@microsoft.com>
Date:   Thu Mar 14 14:10:21 2024 -0500

    Merge remote-tracking branch 'origin/dev/migrie/f/sui-panes' into dev/migrie/fhl/tasks-pane

commit 978fd6e2ba
Merge: b6254f829 35651bc92
Author: Mike Griese <migrie@microsoft.com>
Date:   Fri Mar 8 10:36:17 2024 -0600

    Merge branch 'dev/migrie/fhl/scratchpad-pane' into dev/migrie/f/sui-panes

commit 35651bc92c
Merge: 25a885198 a3fbc6438
Author: Mike Griese <migrie@microsoft.com>
Date:   Fri Mar 8 10:33:31 2024 -0600

    Merge branch 'dev/migrie/fhl/non-terminal-panes-2023' into dev/migrie/fhl/scratchpad-pane

commit a3fbc64384
Merge: de5f7af25 8a1e8ace9
Author: Mike Griese <migrie@microsoft.com>
Date:   Fri Mar 8 10:33:00 2024 -0600

    Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/non-terminal-panes-2023

commit b6254f8294
Author: Mike Griese <migrie@microsoft.com>
Date:   Thu Mar 7 09:49:48 2024 -0600

    GREAT-GRANDPARENT: This fixes a crash in parent pane selection

    (cherry picked from commit 91a0d0e26d)

commit 4d47cd5866
Author: Mike Griese <migrie@microsoft.com>
Date:   Mon Mar 4 16:34:36 2024 -0600

    cleanup

commit 0a11643f1d
Author: Mike Griese <migrie@microsoft.com>
Date:   Mon Mar 4 16:30:16 2024 -0600

    sanely pass around a cache instead of... whatever that was.

commit 17075d6744
Merge: 092b3558f 25a885198
Author: Mike Griese <migrie@microsoft.com>
Date:   Wed Feb 28 11:50:29 2024 -0600

    Merge branch 'dev/migrie/fhl/scratchpad-pane' into dev/migrie/f/sui-panes

commit 25a8851986
Merge: c2446334e de5f7af25
Author: Mike Griese <migrie@microsoft.com>
Date:   Wed Feb 28 11:36:02 2024 -0600

    Merge branch 'dev/migrie/fhl/non-terminal-panes-2023' into dev/migrie/fhl/scratchpad-pane

commit de5f7af25d
Merge: 398235818 94e74d22c
Author: Mike Griese <migrie@microsoft.com>
Date:   Wed Feb 28 11:32:40 2024 -0600

    Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/non-terminal-panes-2023

commit 14a00c8abf
Author: Mike Griese <migrie@microsoft.com>
Date:   Wed Feb 21 12:14:47 2024 -0600

    lots of cleanup

commit 02a5593f5b
Author: Mike Griese <migrie@microsoft.com>
Date:   Wed Feb 21 12:07:45 2024 -0600

    this is amazing, I'm amazing, everything is awesome

commit 2960476019
Author: Mike Griese <migrie@microsoft.com>
Date:   Wed Feb 21 11:39:58 2024 -0600

    holy fuck this is better than I imagined a week ago

commit 9300647b58
Author: Mike Griese <migrie@microsoft.com>
Date:   Wed Feb 21 11:20:33 2024 -0600

    turns out Visibility works exactly like you'd want for a TreeView

commit b706a6d679
Author: Mike Griese <migrie@microsoft.com>
Date:   Wed Feb 21 11:01:36 2024 -0600

    yes yes subclasses are hard in C

commit 17d70f5221
Author: Mike Griese <migrie@microsoft.com>
Date:   Wed Feb 21 10:57:05 2024 -0600

    weird but works surprisingly well

commit 69cb54509c
Author: Mike Griese <migrie@microsoft.com>
Date:   Wed Feb 21 10:16:41 2024 -0600

    Blindly, what if TaskViewModel was a FilteredCommand?

commit def66306a1
Author: Mike Griese <migrie@microsoft.com>
Date:   Wed Feb 21 09:32:50 2024 -0600

    I wanted to start adding a filter box, but that got tricky

commit d86bd57876
Merge: 01ca03f43 afa7a2478
Author: Mike Griese <migrie@microsoft.com>
Date:   Wed Feb 21 09:18:46 2024 -0600

    Merge branch 'dev/migrie/fhl/tasks-pane' of https://github.com/microsoft/terminal into dev/migrie/fhl/tasks-pane

commit 01ca03f433
Author: Mike Griese <migrie@microsoft.com>
Date:   Wed Feb 21 09:17:44 2024 -0600

    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)

commit c94c00b760
Author: Mike Griese <migrie@microsoft.com>
Date:   Tue Feb 20 16:12:12 2024 -0600

    more better styling

commit afa7a2478b
Author: Mike Griese <migrie@microsoft.com>
Date:   Tue Feb 20 16:12:12 2024 -0600

    more better styling

commit db427f2cd1
Author: Mike Griese <migrie@microsoft.com>
Date:   Tue Feb 20 16:11:34 2024 -0600

    better styling

commit 2f3ecf1ea0
Author: Mike Griese <migrie@microsoft.com>
Date:   Tue Feb 20 15:41:10 2024 -0600

    plumb the action through to the task pane

commit d7a6b1899c
Author: Mike Griese <migrie@microsoft.com>
Date:   Tue Feb 20 09:28:42 2024 -0600

    re-add the visibility hack; add a play button that does nothing

commit 365c068da1
Author: Mike Griese <migrie@microsoft.com>
Date:   Mon Feb 19 12:42:24 2024 -0600

    this fixes that random item reuse problem

commit 8418d6a1cb
Author: Mike Griese <migrie@microsoft.com>
Date:   Fri Feb 16 15:54:21 2024 -0600

    omg bind to the IsSelected

commit 0bb13f9eee
Author: Mike Griese <migrie@microsoft.com>
Date:   Fri Feb 16 09:23:50 2024 -0600

    real xaml

commit e72b1bf835
Author: Mike Griese <migrie@microsoft.com>
Date:   Fri Feb 16 08:40:29 2024 -0600

    nested commands are easy guys

commit 81b35ff143
Author: Mike Griese <migrie@microsoft.com>
Date:   Fri Feb 16 08:32:30 2024 -0600

    this worked surprisingly quickly

commit 092b3558f3
Merge: 389ba20a9 c2446334e
Author: Mike Griese <migrie@microsoft.com>
Date:   Thu Feb 8 09:47:33 2024 -0600

    Merge branch 'dev/migrie/fhl/scratchpad-pane' into dev/migrie/f/sui-panes

commit c2446334e6
Merge: 7bc1457d4 398235818
Author: Mike Griese <migrie@microsoft.com>
Date:   Thu Feb 8 09:46:46 2024 -0600

    Merge branch 'dev/migrie/fhl/non-terminal-panes-2023' into dev/migrie/fhl/scratchpad-pane

commit 3982358188
Merge: 0d528f84f 71c35cf24
Author: Mike Griese <migrie@microsoft.com>
Date:   Thu Feb 8 09:40:19 2024 -0600

    Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/non-terminal-panes-2023

commit 0d528f84f2
Merge: 6bc711de0 92f9ff948
Author: Mike Griese <migrie@microsoft.com>
Date:   Fri Jan 19 16:25:31 2024 -0600

    Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/non-terminal-panes-2023

commit 6bc711de06
Author: Mike Griese <migrie@microsoft.com>
Date:   Wed Nov 8 11:10:58 2023 -0600

    maybe I'm not that good at coding

commit f622d80004
Merge: 4cec7e9b4 077d63e6a
Author: Mike Griese <migrie@microsoft.com>
Date:   Wed Nov 8 05:55:27 2023 -0600

    Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/non-terminal-panes-2023

commit 4cec7e9b4b
Author: Mike Griese <migrie@microsoft.com>
Date:   Mon Nov 6 06:01:55 2023 -0600

    try to remove a few of these but ultimately, eh

commit cf920e7d58
Merge: 58e8f3c11 0289cb043
Author: Mike Griese <migrie@microsoft.com>
Date:   Thu Nov 2 06:13:22 2023 -0500

    Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/non-terminal-panes-2023

commit 389ba20a98
Author: Mike Griese <migrie@microsoft.com>
Date:   Wed Oct 25 14:41:57 2023 -0500

    spel

commit dd8606ff9b
Merge: fb74fc8c6 7bc1457d4
Author: Mike Griese <migrie@microsoft.com>
Date:   Wed Oct 25 11:04:43 2023 -0500

    Merge branch 'dev/migrie/fhl/scratchpad-pane' into dev/migrie/f/sui-panes

commit 7bc1457d42
Author: Mike Griese <migrie@microsoft.com>
Date:   Wed Oct 25 11:03:41 2023 -0500

    nits and such

commit e9e04d4e70
Merge: b49997b4b 58e8f3c11
Author: Mike Griese <migrie@microsoft.com>
Date:   Wed Oct 25 09:37:37 2023 -0500

    Merge branch 'dev/migrie/fhl/non-terminal-panes-2023' into dev/migrie/fhl/scratchpad-pane

commit 58e8f3c11c
Author: Mike Griese <migrie@microsoft.com>
Date:   Wed Oct 25 09:37:23 2023 -0500

    mostly nits

commit 8df9523a77
Merge: fd0640997 d0d303996
Author: Mike Griese <migrie@microsoft.com>
Date:   Wed Oct 25 09:04:35 2023 -0500

    Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/non-terminal-panes-2023

commit fd0640997d
Author: Mike Griese <migrie@microsoft.com>
Date:   Fri Oct 13 15:17:38 2023 -0500

    annoying build break

commit fb74fc8c6a
Author: Mike Griese <migrie@microsoft.com>
Date:   Fri Oct 13 14:58:19 2023 -0500

    dead code

commit 5f4087ff00
Author: Mike Griese <migrie@microsoft.com>
Date:   Fri Oct 13 14:56:50 2023 -0500

    finish exorcising SettingsTab

commit 81889a685c
Author: Mike Griese <migrie@microsoft.com>
Date:   Fri Oct 13 14:08:49 2023 -0500

    derp

commit e82c627ebe
Author: Mike Griese <migrie@microsoft.com>
Date:   Fri Oct 13 12:09:08 2023 -0500

    dead code removal

commit d726165330
Author: Mike Griese <migrie@microsoft.com>
Date:   Fri Oct 13 12:06:59 2023 -0500

    terrible, but it works

commit 57e1f26d14
Merge: 6107c3e55 b49997b4b
Author: Mike Griese <migrie@microsoft.com>
Date:   Fri Oct 13 11:36:27 2023 -0500

    Merge branch 'dev/migrie/fhl/scratchpad-pane' into dev/migrie/f/sui-panes

commit b49997b4b4
Merge: 46469aa5e 2086e0f3a
Author: Mike Griese <migrie@microsoft.com>
Date:   Fri Oct 13 11:12:24 2023 -0500

    Merge branch 'dev/migrie/fhl/non-terminal-panes-2023' into dev/migrie/fhl/scratchpad-pane

commit 2086e0f3af
Merge: c869b47e1 544cdd78a
Author: Mike Griese <migrie@microsoft.com>
Date:   Fri Oct 13 10:39:02 2023 -0500

    Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/non-terminal-panes-2023

commit 6107c3e551
Merge: 953106953 46469aa5e
Author: Mike Griese <migrie@microsoft.com>
Date:   Mon Sep 11 05:43:06 2023 -0500

    Merge branch 'dev/migrie/fhl/scratchpad-pane' into dev/migrie/f/sui-panes

commit 46469aa5e3
Merge: 1cc983545 c869b47e1
Author: Mike Griese <migrie@microsoft.com>
Date:   Mon Sep 11 05:24:30 2023 -0500

    Merge branch 'dev/migrie/fhl/non-terminal-panes-2023' into dev/migrie/fhl/scratchpad-pane

commit c869b47e13
Merge: e0b003ad4 4ddfc3eaf
Author: Mike Griese <migrie@microsoft.com>
Date:   Mon Sep 11 05:22:43 2023 -0500

    Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/non-terminal-panes-2023

commit 9531069538
Author: Mike Griese <migrie@microsoft.com>
Date:   Mon Aug 7 15:17:09 2023 -0500

    background brush, done

commit 521e301541
Author: Mike Griese <migrie@microsoft.com>
Date:   Thu Aug 3 13:50:11 2023 -0500

    update settings should work now

commit 842326daa5
Author: Mike Griese <migrie@microsoft.com>
Date:   Thu Aug 3 11:31:57 2023 -0500

    icons for non-terminal pane content

commit fb7c80938b
Author: Mike Griese <migrie@microsoft.com>
Date:   Tue Aug 1 11:37:10 2023 -0500

    derp

commit 29d0d57656
Author: Mike Griese <migrie@microsoft.com>
Date:   Thu Jul 27 16:29:26 2023 -0500

    this works better than it has any right to

commit cbd61b0a7d
Author: Mike Griese <migrie@microsoft.com>
Date:   Thu Jul 27 15:55:05 2023 -0500

    POC: yea, this works

commit 1cc9835454
Author: Mike Griese <migrie@microsoft.com>
Date:   Thu Jul 27 13:58:44 2023 -0500

    feature flags too

commit 86914bdfc1
Merge: a23c1a24d e0b003ad4
Author: Mike Griese <migrie@microsoft.com>
Date:   Tue Jul 25 13:28:25 2023 -0500

    Merge branch 'dev/migrie/fhl/non-terminal-panes-2023' into dev/migrie/fhl/scratchpad-pane

commit e0b003ad4d
Merge: f89368c19 5daf4983d
Author: Mike Griese <migrie@microsoft.com>
Date:   Mon Jul 24 14:10:40 2023 -0500

    Merge branch 'main' into dev/migrie/fhl/non-terminal-panes-2023

commit f89368c19b
Author: Mike Griese <migrie@microsoft.com>
Date:   Tue Jul 18 13:47:38 2023 -0500

    [PARENT] try to use GetActiveTerminalControl less in TerminalTab

    (cherry picked from commit 262d95aae5)

commit 5582e1bcc8
Author: Mike Griese <migrie@microsoft.com>
Date:   Tue Jul 18 13:21:18 2023 -0500

    [PARENT] You know what, I just went for it.

    (cherry picked from commit 63ba8e19fd)

commit a23c1a24dc
Author: Mike Griese <migrie@microsoft.com>
Date:   Thu Jul 20 07:39:02 2023 -0500

    keybindings too

commit 5f9add4000
Merge: 2d4030683 11126f9b3
Author: Mike Griese <migrie@microsoft.com>
Date:   Thu Jul 20 07:04:10 2023 -0500

    Merge branch 'dev/migrie/fhl/non-terminal-panes-2023' into dev/migrie/fhl/scratchpad-pane

commit 11126f9b37
Merge: e31202b0b 701062649
Author: Mike Griese <migrie@microsoft.com>
Date:   Thu Jul 20 07:02:16 2023 -0500

    Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/non-terminal-panes-2023

commit e31202b0b8
Merge: e6dc314c1 6a10ea5a7
Author: Mike Griese <migrie@microsoft.com>
Date:   Thu Jul 20 07:02:04 2023 -0500

    Merge commit '6a10ea5' into dev/migrie/fhl/non-terminal-panes-2023

commit e6dc314c17
Merge: 049c04327 b4042eaaf
Author: Mike Griese <migrie@microsoft.com>
Date:   Wed Jul 19 16:22:03 2023 -0500

    Merge commit 'b4042ea' into dev/migrie/fhl/non-terminal-panes-2023

commit 2d4030683a
Author: Mike Griese <migrie@microsoft.com>
Date:   Tue Jul 18 13:47:58 2023 -0500

    Let's just make it experimental

commit 262d95aae5
Author: Mike Griese <migrie@microsoft.com>
Date:   Tue Jul 18 13:47:38 2023 -0500

    [PARENT] try to use GetActiveTerminalControl less in TerminalTab

commit 63ba8e19fd
Author: Mike Griese <migrie@microsoft.com>
Date:   Tue Jul 18 13:21:18 2023 -0500

    [PARENT] You know what, I just went for it.

commit 1b39db7ab0
Author: Mike Griese <migrie@microsoft.com>
Date:   Tue Jul 18 12:58:55 2023 -0500

    Single commit that adds the whole scratchpad and action

commit 2dd8f409b2
Author: Mike Griese <migrie@microsoft.com>
Date:   Tue Jul 18 11:48:33 2023 -0500

    [TO PARENT] dead code

commit 049c043279
Author: Mike Griese <migrie@microsoft.com>
Date:   Tue Jul 18 10:26:32 2023 -0500

    some last cleanups

commit a1da6c117e
Author: Mike Griese <migrie@microsoft.com>
Date:   Tue Jul 18 10:13:44 2023 -0500

    huge shuffling so that pane content can raise events instead of relying on termcontrol

commit 7c9ffb0e02
Author: Mike Griese <migrie@microsoft.com>
Date:   Tue Jul 18 06:06:07 2023 -0500

    snapping now uses an interface, so that it's not TermControl-specific

commit 84df8197d4
Author: Mike Griese <migrie@microsoft.com>
Date:   Mon Jul 17 14:22:12 2023 -0500

    close event

commit 5b3aa54b56
Author: Mike Griese <migrie@microsoft.com>
Date:   Mon Jul 17 12:42:43 2023 -0500

    move GetNewTerminalArgs into IPaneContent

commit ef6bb8a73c
Author: Mike Griese <migrie@microsoft.com>
Date:   Mon Jul 17 12:35:27 2023 -0500

    hey look, it builds now

commit 4e144425f0
Merge: f353323a2 f4bcbfbad
Author: Mike Griese <migrie@microsoft.com>
Date:   Mon Jul 17 10:53:56 2023 -0500

    Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/non-terminal-panes-2023

commit f353323a23
Author: Mike Griese <migrie@microsoft.com>
Date:   Fri May 12 13:32:12 2023 -0500

    I wanted to do this in one shot but _zelda_
2024-05-02 16:29:33 -05:00
Pankaj Bhojwani
02a1e37aae correct GH todo 2024-05-01 18:05:06 -07:00
Pankaj Bhojwani
0480d651cb this is better 2024-05-01 17:54:46 -07:00
Pankaj Bhojwani
193e5733bf fix remaining tests 2024-05-01 17:36:31 -07:00
Pankaj Bhojwani
2b16acd4cf check for name, fix some tests 2024-05-01 14:48:37 -07:00
Pankaj Bhojwani
f35bf206e6 Merge branch 'main' of https://github.com/microsoft/terminal into dev/pabhoj/action_refactor 2024-05-01 10:50:02 -07:00
Leonard Hecker
78711f91d5 Fix #17138 merge 2024-05-01 18:07:49 +02:00
Leonard Hecker
44dfa21748 Merge remote-tracking branch 'origin/main' into dev/lhecker/viewport-walking 2024-05-01 18:07:27 +02:00
Leonard Hecker
c78a5302e8 Fix tests 2024-05-01 17:16:44 +02:00
Pankaj Bhojwani
cdb907d94d mark todo 2024-04-30 17:43:25 -07:00
Pankaj Bhojwani
3e601f5b66 better if 2024-04-30 17:36:37 -07:00
Pankaj Bhojwani
c2c75c80ed bandaid temporary fix for name 2024-04-30 17:33:53 -07:00
Pankaj Bhojwani
3c6015d97b remove _getcumulativeactions 2024-04-30 16:45:59 -07:00
Pankaj Bhojwani
45cfcd6eca just add duplicate pane auto to defaults 2024-04-30 15:24:33 -07:00
Pankaj Bhojwani
ca4015f5f9 only one tojson 2024-04-30 15:19:33 -07:00
Pankaj Bhojwani
4c744e6ab3 misc 2024-04-30 14:56:42 -07:00
Pankaj Bhojwani
6437b9f508 fix user defaults file 2024-04-30 14:36:09 -07:00
Pankaj Bhojwani
428821b40c remove _idwasgenerated 2024-04-30 13:04:14 -07:00
Pankaj Bhojwani
3d92f27de7 format 2024-04-30 11:18:12 -07:00
Pankaj Bhojwani
db00b90306 spelling things 2024-04-30 11:14:21 -07:00
Pankaj Bhojwani
e725f1e936 resolve conflict 2024-04-30 11:09:51 -07:00
Pankaj Bhojwani
e62dfa2177 some comments 2024-04-30 11:09:15 -07:00
Pankaj Bhojwani
2b4aeb2b11 don't check for special in standard 2024-04-30 11:04:01 -07:00
Pankaj Bhojwani
2f1d8d2dca update defaults 2024-04-30 10:34:58 -07:00
Pankaj Bhojwani
754bf04ab3 mark gh todo 2024-04-30 10:27:25 -07:00
Pankaj Bhojwani
c51558ff4c unmark these 2024-04-30 10:20:07 -07:00
Pankaj Bhojwani
5a1b822833 reimplement populating all known keybindings 2024-04-29 21:33:33 -07:00
Carlos Zamora
8c0d7c6191 clear quick fix on interaction 2024-04-29 14:07:24 -07:00
Pankaj Bhojwani
b3e9c267f5 remove check for invalid 2024-04-27 16:22:14 -07:00
Pankaj Bhojwani
936afd6b01 _getactionbyid no longer returns optional 2024-04-27 16:16:25 -07:00
Pankaj Bhojwani
dc874c3b3f rename to special/standard 2024-04-27 15:36:17 -07:00
Pankaj Bhojwani
ae16a5e0e1 started stage 3 2024-04-26 15:43:06 -07:00
Leonard Hecker
bdadbc9f39 Clamp out of bound moves 2024-04-27 00:05:44 +02:00
Carlos Zamora
d8c8807ee7 feature flag; fix scroll bug; clear QF on enter 2024-04-26 14:55:26 -07:00
Leonard Hecker
75e4f06230 Improve Viewport and Viewport::WalkInBounds 2024-04-26 23:48:39 +02:00
Pankaj Bhojwani
3e7ab3861a sui works? 2024-04-26 11:26:10 -07:00
Pankaj Bhojwani
ddfac907f4 Merge branch 'main' of https://github.com/microsoft/terminal into dev/pabhoj/action_refactor 2024-04-26 09:56:46 -07:00
Pankaj Bhojwani
f1633e0360 overwritten IDs and overwritten keychords show up properly in the SUI 2024-04-25 21:25:17 -07:00
Pankaj Bhojwani
12a61c595e shows up in sui and all keybindings work 2024-04-25 19:16:27 -07:00
Carlos Zamora
48a36be263 Merge branch 'main' into dev/cazamor/cmdNotFound 2024-04-25 11:30:12 -07:00
Carlos Zamora
88b64cda6a fix design 2024-04-25 11:27:06 -07:00
Pankaj Bhojwani
d0938e2a24 ugly way to make sure we fixup 2024-04-24 18:15:37 -07:00
Pankaj Bhojwani
f425746169 remove keysmap 2024-04-24 16:21:22 -07:00
Pankaj Bhojwani
e28d47888c some todos for later 2024-04-24 16:04:39 -07:00
Pankaj Bhojwani
0a3e17eebb edge cases 2024-04-24 15:33:35 -07:00
Pankaj Bhojwani
22ab9363ef works?? 2024-04-24 14:33:01 -07:00
Pankaj Bhojwani
c134402507 about to test stage 1 2024-04-24 11:24:40 -07:00
Pankaj Bhojwani
85933e2231 midpoint 2024-04-23 09:49:27 -07:00
Carlos Zamora
cfaa09d63a moar progress! (useTransitions -> crash!) 2024-04-22 17:54:03 -07:00
Pankaj Bhojwani
ca3eb87301 rename and comment 2024-04-17 16:58:04 -07:00
Pankaj Bhojwani
5e70911a68 remove 0 2024-04-17 16:49:58 -07:00
Pankaj Bhojwani
360b92e567 fmt_compile, fix test 2024-04-17 16:38:52 -07:00
Pankaj Bhojwani
5ee630ec82 fmt is smart 2024-04-12 15:16:36 -07:00
Pankaj Bhojwani
aa4921268e null check 2024-04-12 15:08:53 -07:00
Pankaj Bhojwani
12f3aa9d06 truncate and hex, debug assert 2024-04-12 15:04:23 -07:00
Pankaj Bhojwani
bdf42c2d9c first round of nits 2024-04-11 16:05:35 -07:00
Carlos Zamora
a2e57b4b49 Merge branch 'main' into dev/cazamor/cmdNotFound 2024-04-11 15:48:36 -07:00
Pankaj Bhojwani
af2d22f343 defaults conflict 2024-04-11 09:42:42 -07:00
Mike Griese
aa6f9bcb4c derp; 2024-04-04 16:00:24 -05:00
Mike Griese
194f37e9dd well, definitely this 2024-04-03 14:52:55 -05:00
Mike Griese
96c6a442ca Merge branch 'dev/migrie/f/sui-panes' into dev/migrie/f/992-redux-redux 2024-04-03 11:06:28 -05:00
Mike Griese
7c4dfff451 Merge remote-tracking branch 'origin/main' into dev/migrie/f/sui-panes 2024-04-03 11:03:46 -05:00
Mike Griese
a0e014f277 Merge remote-tracking branch 'origin/main' into dev/migrie/f/sui-panes 2024-04-03 10:13:40 -05:00
Pankaj Bhojwani
6e293a5ee8 Everytime 2024-04-01 10:26:54 -07:00
Pankaj Bhojwani
dd25ed762f change tests 2024-04-01 10:23:23 -07:00
Pankaj Bhojwani
dca7df50c8 excess line 2024-03-29 13:49:33 -07:00
Pankaj Bhojwani
9fc69721c9 add tests 2024-03-29 13:40:53 -07:00
Mike Griese
e05b2bbe37 Merge branch 'main' into dev/migrie/f/sui-panes 2024-03-29 15:13:01 -05:00
Pankaj Bhojwani
5c2307c531 fix test 2024-03-28 11:48:51 -07:00
Pankaj Bhojwani
d57c7a1f03 move this 2024-03-27 18:04:12 -07:00
Pankaj Bhojwani
71bf90f295 even better, also get the ID from json 2024-03-27 18:02:17 -07:00
Pankaj Bhojwani
10d1fc8d60 this way is better 2024-03-27 17:30:55 -07:00
Pankaj Bhojwani
44510dce1b move id generation to fixupusersettings 2024-03-27 17:14:21 -07:00
Carlos Zamora
6a361ef276 address feedback; winget integration still in progress 2024-03-27 16:29:30 -07:00
Pankaj Bhojwani
eccd87f303 update comment 2024-03-27 15:38:50 -07:00
Pankaj Bhojwani
6c3253968f string of numbers is unsightly but it works 2024-03-27 15:37:19 -07:00
Carlos Zamora
c2417bb3f5 add a lil polish 2024-03-26 15:31:25 -07:00
Carlos Zamora
68ed03d4f3 use suggestions UI instead 2024-03-26 15:09:49 -07:00
Mike Griese
ef560bf48e Merge branch 'dev/migrie/fhl/scratchpad-pane' into dev/migrie/f/sui-panes 2024-03-26 13:56:21 -05:00
Mike Griese
7243d220e5 Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/scratchpad-pane 2024-03-26 13:55:54 -05:00
Mike Griese
10e1e46945 ALSO doesn't really need to be projected 2024-03-26 13:55:11 -05:00
Pankaj Bhojwani
2093660ac1 line 2024-03-26 11:45:44 -07:00
Pankaj Bhojwani
b43191d2c5 spacing 2024-03-26 11:44:47 -07:00
Pankaj Bhojwani
7c907fed6e nits 2024-03-26 11:43:50 -07:00
Pankaj Bhojwani
db528c94fc generate IDs for user commands 2024-03-26 11:29:12 -07:00
Mike Griese
ddc88c83b2 Merge branch 'dev/migrie/fhl/scratchpad-pane' into dev/migrie/f/sui-panes 2024-03-26 13:26:16 -05:00
Carlos Zamora
c1e1eab81d remove info bar code; fix branch 2024-03-26 11:14:02 -07:00
Carlos Zamora
534c2d9d1c add debug code & dismiss button 2024-03-26 10:51:33 -07:00
Carlos Zamora
c8d13d4270 [Broken] Properly search through WinGet's catalog 2024-03-26 10:50:12 -07:00
Carlos Zamora
12100c506e Add support for custom CommandNotFound OSC 2024-03-26 10:49:27 -07:00
Mike Griese
1d20599186 un fix this file 2024-03-26 11:38:47 -05:00
Mike Griese
b6e4b62e15 Doesn't really need to be projected 2024-03-26 11:37:35 -05:00
Mike Griese
0979cd6c44 Merge branch 'dev/migrie/fhl/non-terminal-panes-2023' into dev/migrie/fhl/scratchpad-pane 2024-03-26 11:22:45 -05:00
Mike Griese
61e952c58e Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/non-terminal-panes-2023 2024-03-26 11:22:27 -05:00
Leonard Hecker
cc40fb2ce9 AuditMode fix 2024-03-22 18:11:51 +01:00
Leonard Hecker
b529557669 A setting doesn't seem feasible 2024-03-22 17:54:19 +01:00
Leonard Hecker
ac1048632a Fix the build, Some fine-tuning 2024-03-22 17:38:25 +01:00
Leonard Hecker
a28864ff6c Merge remote-tracking branch 'origin/main' into dev/lhecker/8000-graphemes 2024-03-22 14:24:29 +01:00
Mike Griese
a0d62ab40b Merge branch 'dev/migrie/f/sui-panes' into dev/migrie/f/992-redux-redux 2024-03-21 14:03:02 -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
4f77204325 Merge branch 'dev/migrie/f/sui-panes' into dev/migrie/f/992-redux-redux 2024-03-21 13:19:00 -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
Leonard Hecker
62b2cdbb52 Implement grapheme clusters 2024-03-21 18:02:11 +01:00
Eric Nelson
50baa7b48a Update save input subcommand to x-save 2024-03-20 23:22:57 -07:00
Eric Nelson
d4eaec37f0 Update so that save input toasts do not show display params for values
that are not filled in
2024-03-20 23:11:05 -07:00
Eric Nelson
0ad6296665 Fix for keychord not being saved 2024-03-20 20:57:58 -07:00
Mike Griese
7300b5bb7a Merge branch 'dev/migrie/f/sui-panes' into dev/migrie/f/992-redux-redux 2024-03-20 09:37:50 -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
0946a43ee8 Merge branch 'dev/migrie/f/sui-panes' into dev/migrie/f/992-redux-redux 2024-03-20 06:41:13 -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
Pankaj Bhojwani
be193b21eb merge origin 2024-03-19 15:08:08 -07: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
Pankaj Bhojwani
2bb1b6c6ad conflict 2024-03-19 12:18:06 -07: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
6bf09df7ad Merge branch 'dev/migrie/f/sui-panes' into dev/migrie/f/992-redux-redux 2024-03-19 05:38:11 -05:00
Mike Griese
e0b047762f format 2024-03-19 05:30:20 -05:00
Mike Griese
77d56e0b15 dead code 2024-03-18 16:15:10 -05:00
Mike Griese
36cefec30a comments, comments, comments 2024-03-18 16:02:17 -05:00
Mike Griese
03f57acd3e Merge branch 'main' into save_input_action 2024-03-18 13:55:16 -07:00
Mike Griese
dde4d0d1fa AHAHAHA IT WORKS 2024-03-18 15:30:40 -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
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
6ba704d036 a thought 2024-03-07 16:47:55 -06:00
Mike Griese
032d15f1f3 Trying to figure out why the drag starting in the control continues into the border 2024-03-07 16:44:02 -06:00
Mike Griese
5e5e13ed14 VERY IMPORTANT 2024-03-07 16:43:21 -06:00
Mike Griese
ba5fab4009 beautiful elegance 2024-03-07 11:43:14 -06:00
Mike Griese
63ad45a2de endless joy. This is so much more elegant 2024-03-07 11:29:09 -06:00
Mike Griese
921c94b69c cleanup before I blow it all up 2024-03-07 11:01:39 -06:00
Mike Griese
8f89dd4efa Again, even closer, but now the middle bottom left pane doesn't resize on the left. That moves the rightmost split instead 2024-03-07 10:58:05 -06:00
Mike Griese
47b06e0b01 GAH this is SO close.
Only one that doesn't work is the pane in the middle. Resizing it's right resizes the left border.
2024-03-07 10:19:17 -06:00
Mike Griese
91a0d0e26d GREAT-GRANDPARENT: This fixes a crash in parent pane selection 2024-03-07 09:49:48 -06:00
Mike Griese
18bd6a847f OMG this is so close. the _Resize instead of ResizePane is LOAD-BEARING 2024-03-07 09:09:21 -06:00
Pankaj Bhojwani
66fe08f964 default ids 2024-03-06 17:24:39 -08:00
Pankaj Bhojwani
642d0ab2b7 inbox makes more sense 2024-03-05 15:47:49 -08:00
Pankaj Bhojwani
8cc82de489 generated 2024-03-05 15:42:38 -08:00
Pankaj Bhojwani
052d063686 ah one of the tests uses this 2024-03-05 14:36:45 -08:00
Pankaj Bhojwani
8bcbd0bd42 fix tests 2024-03-05 14:20:14 -08:00
Mike Griese
93a789cbc1 This almost works right
It doesn't manipulate if you started the drag in a termcontrol, mostly. There's
some edge cases where you can start a selection, then drag across a border, and
then we _start_ manipulating the border. But not always! So that's weird.

There are also edge cases still where if you
* drag an inactive border, it no resize-y
* drag the left border, it resizes the right one. That kinda makes sense, at least.
2024-03-05 15:52:45 -06:00
Pankaj Bhojwani
9dff28f23d update calls in tests 2024-03-05 13:51:18 -08:00
Mike Griese
67c1128201 commenting out a lot of failed ideas 2024-03-05 14:51:07 -06:00
Pankaj Bhojwani
90627b3ae5 add origin tag 2024-03-05 11:35:26 -08:00
Mike Griese
112cdf52ff this seems to work, but only on the right/bottom edges of a pane. Never the top/left 2024-03-05 10:59:00 -06:00
Mike Griese
7a27354fb3 this double-notifies the parent again, but again, not the end of the world 2024-03-05 10:19:47 -06:00
Mike Griese
04870c90f8 this totally works, except for the parent pane 2024-03-05 06:50:23 -06:00
Mike Griese
38f30c3ecb horizontal <---> resizing works great 2024-03-05 06:28:42 -06:00
Mike Griese
254f3ee50a this does resize panes but as noted, DPI is wack 2024-03-05 06:21:19 -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
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
Eric Nelson
e54b4a4143 Clean up of Save Action ActionArgs 2024-01-19 20:04:55 -08: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
Eric Nelson
eec4f6bf4a Saved Input: Use TextBlocks instead of TextBoxes for toast, exclude
blank arges from generated name
2024-01-09 18:20:03 -08:00
Eric Nelson
8bec5ec4a0 Handle key chord parse failure 2024-01-02 04:22:16 -08:00
Eric Nelson
dd46338942 Added command line options for Save Input 2024-01-01 15:51:45 -08:00
Eric Nelson
83e3b8ca60 Add ability to save input action from command line
- Save to disk
- If command line is empty use selection
- Show toast
2024-01-01 10:22:59 -08:00
David
f7971af392 Theme for settings
Updated with JSON entries.
Update _UpdateBackgroundForMica
2023-12-15 22:06:39 +01: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
160 changed files with 6835 additions and 2408 deletions

View File

@@ -146,6 +146,7 @@ bytebuffer
cac
cacafire
CALLCONV
CANDRABINDU
capslock
CARETBLINKINGENABLED
CARRIAGERETURN
@@ -156,6 +157,7 @@ CBash
cbiex
CBN
cbt
Ccc
CCCBB
cch
CCHAR
@@ -293,7 +295,6 @@ CREATESTRUCT
CREATESTRUCTW
createvpack
crisman
CRLFs
crloew
CRTLIBS
csbi
@@ -593,6 +594,7 @@ fesb
FFAF
ffd
FFDE
FFFD
FFFDb
fgbg
FGCOLOR
@@ -613,6 +615,7 @@ FINDREGEX
FINDSTRINGEXACT
FINDUP
FIter
FITZPATRICK
FIXEDFILEINFO
Flg
flyouts
@@ -879,10 +882,12 @@ jconcpp
JLO
JOBOBJECT
JOBOBJECTINFOCLASS
JONGSEONG
JPN
jsoncpp
jsprovider
jumplist
JUNGSEONG
KAttrs
kawa
Kazu
@@ -901,6 +906,7 @@ keyups
KILLACTIVE
KILLFOCUS
kinda
KIYEOK
KLF
KLMNO
KLMNOPQRST
@@ -1010,6 +1016,7 @@ luma
lval
LVB
LVERTICAL
LVT
LWA
LWIN
lwkmvj
@@ -1205,6 +1212,7 @@ ntuser
NTVDM
ntverp
nugetversions
NUKTA
nullness
nullonfailure
nullopts
@@ -1467,7 +1475,6 @@ READMODE
rectread
redef
redefinable
Redir
redist
REDSCROLL
REFCLSID
@@ -1485,6 +1492,7 @@ renderengine
rendersize
reparented
reparenting
REPH
replatformed
Replymessage
repositorypath
@@ -1514,6 +1522,7 @@ rgw
RIGHTALIGN
RIGHTBUTTON
riid
ris
RIS
roadmap
robomac
@@ -1919,6 +1928,7 @@ vga
vgaoem
viewkind
viewports
VIRAMA
Virt
VIRTTERM
vkey
@@ -1969,8 +1979,8 @@ wchars
WCIA
WCIW
WCSHELPER
wcsicmp
wcsrev
wcswidth
wddm
wddmcon
WDDMCONSOLECONTEXT
@@ -2125,6 +2135,7 @@ XFORM
XIn
XManifest
XMath
XNamespace
xorg
XPan
XResource
@@ -2156,6 +2167,7 @@ Zabcdefghijklmn
Zabcdefghijklmnopqrstuvwxyz
ZCmd
ZCtrl
ZWJs
zxcvbnm
ZYXWVU
ZYXWVUTd

View File

@@ -629,7 +629,8 @@
"folder",
"separator",
"remainingProfiles",
"matchProfiles"
"matchProfiles",
"action"
]
},
"NewTabMenuEntry": {
@@ -781,6 +782,28 @@
}
]
},
"ActionEntry": {
"description": "An action in the new tab dropdown",
"allOf": [
{
"$ref": "#/$defs/NewTabMenuEntry"
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "action"
},
"profile": {
"type": "string",
"default": "",
"description": "The ID of the action to show in this entry"
}
}
}
]
},
"SwitchToAdjacentTabArgs": {
"oneOf": [
{
@@ -2054,11 +2077,14 @@
},
{
"$ref": "#/$defs/RemainingProfilesEntry"
},
{
"$ref": "#/$defs/ActionEntry"
}
]
}
},
"Keybinding": {
"FullCommand": {
"additionalProperties": false,
"properties": {
"command": {
@@ -2186,21 +2212,6 @@
}
]
},
"keys": {
"description": "Defines the key combinations used to call the command. It must be composed of...\n -any number of modifiers (ctrl/alt/shift)\n -a non-modifier key",
"oneOf": [
{
"$ref": "#/$defs/KeyChordSegment"
},
{
"items": {
"$ref": "#/$defs/KeyChordSegment"
},
"minItems": 1,
"type": "array"
}
]
},
"icon": {
"$ref": "#/$defs/Icon"
},
@@ -2235,10 +2246,10 @@
"type": "object",
"properties": {
"command": {
"$ref": "#/$defs/Keybinding/properties/command"
"$ref": "#/$defs/FullCommand/properties/command"
},
"name": {
"$ref": "#/$defs/Keybinding/properties/name"
"$ref": "#/$defs/FullCommand/properties/name"
}
}
},
@@ -2261,6 +2272,44 @@
],
"type": "object"
},
"Keybinding": {
"additionalProperties": false,
"properties": {
"id": {
"description": "The ID of the command this keybinding should execute.",
"type": "string"
},
"keys": {
"description": "Defines the key combinations used to call the command. It must be composed of...\n -any number of modifiers (ctrl/alt/shift)\n -a non-modifier key",
"oneOf": [
{
"$ref": "#/$defs/KeyChordSegment"
},
{
"items": {
"$ref": "#/$defs/KeyChordSegment"
},
"minItems": 1,
"type": "array"
}
]
}
},
"anyOf": [
{
"required": [
"keys",
"id"
]
},
{
"required": [
"keys"
]
}
],
"type": "object"
},
"Globals": {
"additionalProperties": true,
"description": "Properties that affect the entire window, regardless of the profile settings.",
@@ -2464,12 +2513,12 @@
"actions": {
"description": "Properties are specific to each custom action.",
"items": {
"$ref": "#/$defs/Keybinding"
"$ref": "#/$defs/FullCommand"
},
"type": "array"
},
"keybindings": {
"description": "[deprecated] Use actions instead.",
"description": "A list of keychords bound to action IDs",
"deprecated": true,
"items": {
"$ref": "#/$defs/Keybinding"

View File

@@ -5,10 +5,8 @@
#include "Row.hpp"
#include <isa_availability.h>
#include <til/unicode.h>
#include "textBuffer.hpp"
#include "../../types/inc/GlyphWidth.hpp"
#include "../../types/inc/CodepointWidthDetector.hpp"
// It would be nice to add checked array access in the future, but it's a little annoying to do so without impacting
// performance (including Debug performance). Other languages are a little bit more ergonomic there than C++.
@@ -568,6 +566,7 @@ void ROW::ReplaceAttributes(const til::CoordType beginIndex, const til::CoordTyp
void ROW::ReplaceCharacters(til::CoordType columnBegin, til::CoordType width, const std::wstring_view& chars)
try
{
assert(width >= 1 && width <= 2);
WriteHelper h{ *this, columnBegin, _columnCount, chars };
if (!h.IsValid())
{
@@ -666,56 +665,89 @@ catch (...)
[[msvc::forceinline]] void ROW::WriteHelper::_replaceTextUnicode(size_t ch, std::wstring_view::const_iterator it) noexcept
{
const auto end = chars.end();
auto& cwd = CodepointWidthDetector::Singleton();
while (it != end)
// Check if the new text joins with the existing contents of the row to form a single grapheme cluster.
if (it == chars.begin())
{
unsigned int width = 1;
auto ptr = &*it;
const auto wch = *ptr;
size_t advance = 1;
++it;
// Even in our slow-path we can avoid calling IsGlyphFullWidth if the current character is ASCII.
// It also allows us to skip the surrogate pair decoding at the same time.
if (wch >= 0x80)
auto colPrev = colBeg;
while (colPrev > 0 && row._uncheckedIsTrailer(--colPrev))
{
if (til::is_surrogate(wch))
}
const auto chPrev = row._uncheckedCharOffset(colPrev);
const std::wstring_view charsPrev{ row._chars.data() + chPrev, ch - chPrev };
GraphemeState state;
cwd.GraphemeNext(state, charsPrev);
cwd.GraphemeNext(state, chars);
if (state.len > 0)
{
colBegDirty = colPrev;
colEnd = colPrev;
const auto colEndNew = gsl::narrow_cast<uint16_t>(colEnd + state.width);
if (colEndNew > colLimit)
{
if (it != end && til::is_leading_surrogate(wch) && til::is_trailing_surrogate(*it))
{
advance = 2;
++it;
}
else
{
ptr = &UNICODE_REPLACEMENT;
}
colEndDirty = colLimit;
charsConsumed = ch - chBeg;
return;
}
width = IsGlyphFullWidth({ ptr, advance }) + 1u;
}
// Fill our char-offset buffer with 1 entry containing the mapping from the
// current column (colEnd) to the start of the glyph in the string (ch)...
til::at(row._charOffsets, colEnd++) = gsl::narrow_cast<uint16_t>(chPrev);
// ...followed by 0-N entries containing an indication that the
// columns are just a wide-glyph extension of the preceding one.
while (colEnd < colEndNew)
{
til::at(row._charOffsets, colEnd++) = gsl::narrow_cast<uint16_t>(chPrev | CharOffsetsTrailer);
}
const auto colEndNew = gsl::narrow_cast<uint16_t>(colEnd + width);
if (colEndNew > colLimit)
ch += state.len;
it += state.len;
}
}
else
{
// The non-ASCII character we have encountered may be a combining mark, like "a^" which is then displayed as "â".
// In order to recognize both characters as a single grapheme, we need to back up by 1 ASCII character
// and let MeasureNext() find the next proper grapheme boundary.
--colEnd;
--ch;
--it;
}
if (const auto end = chars.end(); it != end)
{
GraphemeState state{ .beg = &*it };
do
{
colEndDirty = colLimit;
charsConsumed = ch - chBeg;
return;
}
cwd.GraphemeNext(state, chars);
// Fill our char-offset buffer with 1 entry containing the mapping from the
// current column (colEnd) to the start of the glyph in the string (ch)...
til::at(row._charOffsets, colEnd++) = gsl::narrow_cast<uint16_t>(ch);
// ...followed by 0-N entries containing an indication that the
// columns are just a wide-glyph extension of the preceding one.
while (colEnd < colEndNew)
{
til::at(row._charOffsets, colEnd++) = gsl::narrow_cast<uint16_t>(ch | CharOffsetsTrailer);
}
const auto colEndNew = gsl::narrow_cast<uint16_t>(colEnd + state.width);
if (colEndNew > colLimit)
{
colEndDirty = colLimit;
charsConsumed = ch - chBeg;
return;
}
ch += advance;
// Fill our char-offset buffer with 1 entry containing the mapping from the
// current column (colEnd) to the start of the glyph in the string (ch)...
til::at(row._charOffsets, colEnd++) = gsl::narrow_cast<uint16_t>(ch);
// ...followed by 0-N entries containing an indication that the
// columns are just a wide-glyph extension of the preceding one.
while (colEnd < colEndNew)
{
til::at(row._charOffsets, colEnd++) = gsl::narrow_cast<uint16_t>(ch | CharOffsetsTrailer);
}
ch += state.len;
it += state.len;
} while (it != end);
}
colEndDirty = colEnd;
@@ -1058,7 +1090,7 @@ std::wstring_view ROW::GetText() const noexcept
std::wstring_view ROW::GetText(til::CoordType columnBegin, til::CoordType columnEnd) const noexcept
{
const til::CoordType columns = _columnCount;
const auto columns = GetReadableColumnCount();
const auto colBeg = clamp(columnBegin, 0, columns);
const auto colEnd = clamp(columnEnd, colBeg, columns);
const size_t chBeg = _uncheckedCharOffset(gsl::narrow_cast<size_t>(colBeg));

View File

@@ -2,14 +2,12 @@
// Licensed under the MIT license.
#include "precomp.h"
#include "textBuffer.hpp"
#include <til/hash.h>
#include <til/unicode.h>
#include "UTextAdapter.h"
#include "../../types/inc/GlyphWidth.hpp"
#include "../../types/inc/CodepointWidthDetector.hpp"
#include "../renderer/base/renderer.hpp"
#include "../types/inc/utils.hpp"
#include "search.h"
@@ -408,17 +406,23 @@ void TextBuffer::_PrepareForDoubleByteSequence(const DbcsAttribute dbcsAttribute
// Given the character offset `position` in the `chars` string, this function returns the starting position of the next grapheme.
// For instance, given a `chars` of L"x\uD83D\uDE42y" and a `position` of 1 it'll return 3.
// GraphemePrev would do the exact inverse of this operation.
// In the future, these functions are expected to also deliver information about how many columns a grapheme occupies.
// (I know that mere UTF-16 code point iteration doesn't handle graphemes, but that's what we're working towards.)
size_t TextBuffer::GraphemeNext(const std::wstring_view& chars, size_t position) noexcept
{
return til::utf16_iterate_next(chars, position);
auto& cwd = CodepointWidthDetector::Singleton();
#pragma warning(suppress : 26481) // Don't use pointer arithmetic. Use span instead (bounds.1).
GraphemeState state{ .beg = chars.data() + position };
cwd.GraphemeNext(state, chars);
return position + state.len;
}
// It's the counterpart to GraphemeNext. See GraphemeNext.
size_t TextBuffer::GraphemePrev(const std::wstring_view& chars, size_t position) noexcept
{
return til::utf16_iterate_prev(chars, position);
auto& cwd = CodepointWidthDetector::Singleton();
#pragma warning(suppress : 26481) // Don't use pointer arithmetic. Use span instead (bounds.1).
GraphemeState state{ .beg = chars.data() + position };
cwd.GraphemePrev(state, chars);
return position - state.len;
}
// Ever wondered how much space a piece of text needs before inserting it? This function will tell you!
@@ -445,7 +449,7 @@ size_t TextBuffer::FitTextIntoColumns(const std::wstring_view& chars, til::Coord
{
}
const auto dist = gsl::narrow_cast<size_t>(it - beg);
auto dist = gsl::narrow_cast<size_t>(it - beg);
auto col = gsl::narrow_cast<til::CoordType>(dist);
if (it == asciiEnd) [[likely]]
@@ -455,33 +459,26 @@ size_t TextBuffer::FitTextIntoColumns(const std::wstring_view& chars, til::Coord
}
// Unicode slow-path where we need to count text and columns separately.
for (;;)
auto& cwd = CodepointWidthDetector::Singleton();
const auto len = chars.size();
// The non-ASCII character we have encountered may be a combining mark, like "a^" which is then displayed as "â".
// In order to recognize both characters as a single grapheme, we need to back up by 1 ASCII character
// and let GraphemeNext() find the next proper grapheme boundary.
if (dist != 0)
{
auto ptr = &*it;
const auto wch = *ptr;
size_t len = 1;
dist--;
col--;
}
col++;
#pragma warning(suppress : 26481) // Don't use pointer arithmetic. Use span instead (bounds.1).
GraphemeState state{ .beg = chars.data() + dist };
// Even in our slow-path we can avoid calling IsGlyphFullWidth if the current character is ASCII.
// It also allows us to skip the surrogate pair decoding at the same time.
if (wch >= 0x80)
{
if (til::is_surrogate(wch))
{
const auto it2 = it + 1;
if (til::is_leading_surrogate(wch) && it2 != end && til::is_trailing_surrogate(*it2))
{
len = 2;
}
else
{
ptr = &UNICODE_REPLACEMENT;
}
}
col += IsGlyphFullWidth({ ptr, len });
}
while (dist < len)
{
cwd.GraphemeNext(state, chars);
dist += state.len;
col += state.width;
// If we ran out of columns, we need to always return `columnLimit` and not `cols`,
// because if we tried inserting a wide glyph into just 1 remaining column it will
@@ -490,17 +487,13 @@ size_t TextBuffer::FitTextIntoColumns(const std::wstring_view& chars, til::Coord
if (col > columnLimit)
{
columns = columnLimit;
return gsl::narrow_cast<size_t>(it - beg);
}
// But if we simply ran out of text we just need to return the actual number of columns.
it += len;
if (it == end)
{
columns = col;
return chars.size();
return dist;
}
}
// But if we simply ran out of text we just need to return the actual number of columns.
columns = col;
return chars.size();
}
// Pretend as if `position` is a regular cursor in the TextBuffer.
@@ -993,7 +986,7 @@ const til::CoordType TextBuffer::GetFirstRowIndex() const noexcept
const Viewport TextBuffer::GetSize() const noexcept
{
return Viewport::FromDimensions({ _width, _height });
return Viewport::FromDimensions({}, { _width, _height });
}
void TextBuffer::_SetFirstRowIndex(const til::CoordType FirstRowIndex) noexcept
@@ -1597,7 +1590,7 @@ til::point TextBuffer::_GetWordEndForSelection(const til::point target, const st
}
}
bufferSize.IncrementInBoundsCircular(result);
bufferSize.IncrementInBounds(result);
}
if (_GetDelimiterClassAt(result, wordDelimiters) != initialDelimiter)

View File

@@ -174,6 +174,23 @@
</ItemGroup>
</Target>
<!-- We need to include some additional files in the package for things like WinGet's Command Not Found -->
<Target Name="IncludeAdditionalFilesInPackage">
<PropertyGroup>
<WinGetAdditionalPackageFileRoot>$(SolutionDir)\src\cascadia\TerminalControl</WinGetAdditionalPackageFileRoot>
</PropertyGroup>
<ItemGroup>
<WinGetAdditionalPackageFile Include="$(WinGetAdditionalPackageFileRoot)\Microsoft.Management.Deployment.winmd">
<PackagePath>Microsoft.Management.Deployment.winmd</PackagePath>
</WinGetAdditionalPackageFile>
</ItemGroup>
<ItemGroup>
<AppxPackagePayload Include="%(WinGetAdditionalPackageFile.Identity))">
<TargetPath>%(WinGetAdditionalPackageFile.PackagePath)</TargetPath>
</AppxPackagePayload>
</ItemGroup>
</Target>
<!-- This is required to get the package dependency in the AppXManifest. -->
<Import Project="$(OpenConsoleDir)src\common.nugetversions.targets" />

View File

@@ -18,11 +18,11 @@ using namespace winrt::Microsoft::Terminal::Settings::Model;
namespace winrt::TerminalApp::implementation
{
ActionPaletteItem::ActionPaletteItem(const Microsoft::Terminal::Settings::Model::Command& command) :
ActionPaletteItem::ActionPaletteItem(const Microsoft::Terminal::Settings::Model::Command& command, const winrt::hstring keyChordText) :
_Command(command)
{
Name(command.Name());
KeyChordText(command.KeyChordText());
KeyChordText(keyChordText);
Icon(command.IconPath());
}
}

View File

@@ -11,7 +11,7 @@ namespace winrt::TerminalApp::implementation
struct ActionPaletteItem : ActionPaletteItemT<ActionPaletteItem, PaletteItem>
{
ActionPaletteItem() = default;
ActionPaletteItem(const Microsoft::Terminal::Settings::Model::Command& command);
ActionPaletteItem(const Microsoft::Terminal::Settings::Model::Command& command, const winrt::hstring keyChordText);
WINRT_PROPERTY(Microsoft::Terminal::Settings::Model::Command, Command, nullptr);

View File

@@ -7,7 +7,7 @@ namespace TerminalApp
{
[default_interface] runtimeclass ActionPaletteItem : PaletteItem
{
ActionPaletteItem(Microsoft.Terminal.Settings.Model.Command command);
ActionPaletteItem(Microsoft.Terminal.Settings.Model.Command command, String keyChordText);
Microsoft.Terminal.Settings.Model.Command Command { get; };
}

View File

@@ -1264,6 +1264,54 @@ namespace winrt::TerminalApp::implementation
}
}
void TerminalPage::_HandleSaveTask(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (Feature_SaveTask::IsEnabled())
{
if (args)
{
if (const auto& realArgs = args.ActionArgs().try_as<SaveTaskArgs>())
{
if (realArgs.Commandline().empty())
{
if (const auto termControl{ _GetActiveControl() })
{
if (termControl.HasSelection())
{
const auto selections{ termControl.SelectedText(true) };
const auto selection = std::accumulate(selections.begin(), selections.end(), std::wstring());
realArgs.Commandline(selection);
}
}
}
try
{
winrt::Microsoft::Terminal::Control::KeyChord keyChord = nullptr;
if (!realArgs.KeyChord().empty())
{
keyChord = KeyChordSerialization::FromString(winrt::to_hstring(realArgs.KeyChord()));
}
_settings.GlobalSettings().ActionMap().AddSendInputAction(realArgs.Name(), realArgs.Commandline(), keyChord);
_settings.WriteSettingsToDisk();
ActionSaved(realArgs.Commandline(), realArgs.Name(), realArgs.KeyChord());
}
catch (const winrt::hresult_error& ex)
{
auto code = ex.code();
auto message = ex.message();
ActionSaveFailed(message);
args.Handled(true);
return;
}
args.Handled(true);
}
}
}
}
void TerminalPage::_HandleSelectCommand(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
@@ -1340,7 +1388,7 @@ namespace winrt::TerminalApp::implementation
// requires context from the control)
// then get that here.
const bool shouldGetContext = realArgs.UseCommandline() ||
WI_IsFlagSet(source, SuggestionsSource::CommandHistory);
WI_IsAnyFlagSet(source, SuggestionsSource::CommandHistory | SuggestionsSource::QuickFixes);
if (shouldGetContext)
{
if (const auto& control{ _GetActiveControl() })
@@ -1373,7 +1421,19 @@ namespace winrt::TerminalApp::implementation
if (WI_IsFlagSet(source, SuggestionsSource::CommandHistory) &&
context != nullptr)
{
const auto recentCommands = Command::HistoryToCommands(context.History(), currentCommandline, false);
// \ue81c --> History icon
const auto recentCommands = Command::HistoryToCommands(context.History(), currentCommandline, false, hstring{ L"\ue81c" });
for (const auto& t : recentCommands)
{
commandsCollection.push_back(t);
}
}
if (WI_IsFlagSet(source, SuggestionsSource::QuickFixes) &&
context != nullptr)
{
// \ue74c --> OEM icon
const auto recentCommands = Command::HistoryToCommands(context.QuickFixes(), hstring{ L"" }, false, hstring{ L"\ue74c" });
for (const auto& t : recentCommands)
{
commandsCollection.push_back(t);
@@ -1473,4 +1533,5 @@ namespace winrt::TerminalApp::implementation
_ShowAboutDialog();
args.Handled(true);
}
}

View File

@@ -209,6 +209,7 @@ void AppCommandlineArgs::_buildParser()
_buildMovePaneParser();
_buildSwapPaneParser();
_buildFocusPaneParser();
_buildSaveParser();
}
// Method Description:
@@ -537,6 +538,72 @@ void AppCommandlineArgs::_buildFocusPaneParser()
setupSubcommand(_focusPaneShort);
}
void AppCommandlineArgs::_buildSaveParser()
{
_saveCommand = _app.add_subcommand("x-save", RS_A(L"SaveActionDesc"));
auto setupSubcommand = [this](auto* subcommand) {
subcommand->add_option("--name,-n", _saveInputName, RS_A(L"SaveActionArgDesc"));
subcommand->add_option("--keychord,-k", _keyChordOption, RS_A(L"KeyChordArgDesc"));
subcommand->add_option("command,", _commandline, RS_A(L"CmdCommandArgDesc"));
subcommand->positionals_at_end(true);
// 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]() {
// Build the NewTab action from the values we've parsed on the commandline.
ActionAndArgs saveAction{};
saveAction.Action(ShortcutAction::SaveTask);
// _getNewTerminalArgs MUST be called before parsing any other options,
// as it might clear those options while finding the commandline
SaveTaskArgs args{};
if (!_commandline.empty())
{
std::ostringstream cmdlineBuffer;
for (const auto& arg : _commandline)
{
if (cmdlineBuffer.tellp() != 0)
{
// If there's already something in here, prepend a space
cmdlineBuffer << ' ';
}
if (arg.find(" ") != std::string::npos)
{
cmdlineBuffer << '"' << arg << '"';
}
else
{
cmdlineBuffer << arg;
}
}
args.Commandline(winrt::to_hstring(cmdlineBuffer.str()));
}
if (!_keyChordOption.empty())
{
args.KeyChord(winrt::to_hstring(_keyChordOption));
}
if (!_saveInputName.empty())
{
winrt::hstring hString = winrt::to_hstring(_saveInputName);
args.Name(hString);
}
saveAction.Args(args);
_startupActions.push_back(saveAction);
});
};
setupSubcommand(_saveCommand);
}
// Method Description:
// - Add the `NewTerminalArgs` parameters to the given subcommand. This enables
// that subcommand to support all the properties in a NewTerminalArgs.
@@ -710,7 +777,8 @@ bool AppCommandlineArgs::_noCommandsProvided()
*_focusPaneCommand ||
*_focusPaneShort ||
*_newPaneShort.subcommand ||
*_newPaneCommand.subcommand);
*_newPaneCommand.subcommand ||
*_saveCommand);
}
// Method Description:

View File

@@ -93,6 +93,7 @@ private:
CLI::App* _swapPaneCommand;
CLI::App* _focusPaneCommand;
CLI::App* _focusPaneShort;
CLI::App* _saveCommand;
// Are you adding a new sub-command? Make sure to update _noCommandsProvided!
@@ -123,6 +124,8 @@ private:
bool _focusPrevTab{ false };
int _focusPaneTarget{ -1 };
std::string _saveInputName;
std::string _keyChordOption;
// Are you adding more args here? Make sure to reset them in _resetStateToDefault
const Commandline* _currentCommandline{ nullptr };
@@ -141,6 +144,7 @@ private:
winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs _getNewTerminalArgs(NewTerminalSubcommand& subcommand);
void _addNewTerminalArgs(NewTerminalSubcommand& subcommand);
void _buildParser();
void _buildSaveParser();
void _buildNewTabParser();
void _buildSplitPaneParser();
void _buildFocusTabParser();

View File

@@ -3,16 +3,18 @@
#include "pch.h"
#include "AppLogic.h"
#include "../inc/WindowingBehavior.h"
#include "AppLogic.g.cpp"
#include "FindTargetWindowResult.g.cpp"
#include "SettingsLoadEventArgs.h"
#include <LibraryResources.h>
#include <WtExeUtils.h>
#include <wil/token_helpers.h>
#include "SettingsLoadEventArgs.h"
#include "../../types/inc/CodepointWidthDetector.hpp"
#include "../../types/inc/utils.hpp"
#include "../inc/WindowingBehavior.h"
#include "AppLogic.g.cpp"
#include "FindTargetWindowResult.g.cpp"
using namespace winrt::Windows::ApplicationModel;
using namespace winrt::Windows::ApplicationModel::DataTransfer;
@@ -433,13 +435,6 @@ namespace winrt::TerminalApp::implementation
}
}
if (initialLoad)
{
// Register for directory change notification.
_RegisterSettingsChange();
return;
}
// Here, we successfully reloaded the settings, and created a new
// TerminalSettings object.

View File

@@ -950,21 +950,27 @@ namespace winrt::TerminalApp::implementation
void CommandPalette::SetActionMap(const Microsoft::Terminal::Settings::Model::IActionMapView& actionMap)
{
_actionMap = actionMap;
_setCommands();
}
void CommandPalette::SetCommands(const Collections::IVector<Command>& actions)
void CommandPalette::_setCommands()
{
_allCommands.Clear();
for (const auto& action : actions)
if (_actionMap)
{
auto actionPaletteItem{ winrt::make<winrt::TerminalApp::implementation::ActionPaletteItem>(action) };
auto filteredCommand{ winrt::make<FilteredCommand>(actionPaletteItem) };
_allCommands.Append(filteredCommand);
}
_allCommands.Clear();
const auto expandedCommands{ _actionMap.ExpandedCommands() };
for (const auto& action : expandedCommands)
{
const auto keyChordText{ KeyChordSerialization::ToString(_actionMap.GetKeyBindingForAction(action.ID())) };
auto actionPaletteItem{ winrt::make<winrt::TerminalApp::implementation::ActionPaletteItem>(action, keyChordText) };
auto filteredCommand{ winrt::make<FilteredCommand>(actionPaletteItem) };
_allCommands.Append(filteredCommand);
}
if (Visibility() == Visibility::Visible && _currentMode == CommandPaletteMode::ActionMode)
{
_updateFilteredActions();
if (Visibility() == Visibility::Visible && _currentMode == CommandPaletteMode::ActionMode)
{
_updateFilteredActions();
}
}
}
@@ -1178,7 +1184,8 @@ namespace winrt::TerminalApp::implementation
for (const auto& nameAndCommand : parentCommand.NestedCommands())
{
const auto action = nameAndCommand.Value();
auto nestedActionPaletteItem{ winrt::make<winrt::TerminalApp::implementation::ActionPaletteItem>(action) };
// nested commands cannot have keys bound to them, so just pass in the command and no keys
auto nestedActionPaletteItem{ winrt::make<winrt::TerminalApp::implementation::ActionPaletteItem>(action, L"") };
auto nestedFilteredCommand{ winrt::make<FilteredCommand>(nestedActionPaletteItem) };
_currentNestedCommands.Append(nestedFilteredCommand);
}

View File

@@ -31,7 +31,6 @@ namespace winrt::TerminalApp::implementation
Windows::Foundation::Collections::IObservableVector<winrt::TerminalApp::FilteredCommand> FilteredActions();
void SetCommands(const Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::Command>& actions);
void SetTabs(const Windows::Foundation::Collections::IObservableVector<winrt::TerminalApp::TabBase>& tabs, const Windows::Foundation::Collections::IObservableVector<winrt::TerminalApp::TabBase>& mruTabs);
void SetActionMap(const Microsoft::Terminal::Settings::Model::IActionMapView& actionMap);
@@ -81,6 +80,8 @@ namespace winrt::TerminalApp::implementation
bool _lastFilterTextWasEmpty{ true };
void _setCommands();
void _filterTextChanged(const Windows::Foundation::IInspectable& sender,
const Windows::UI::Xaml::RoutedEventArgs& args);
void _previewKeyDownHandler(const Windows::Foundation::IInspectable& sender,

View File

@@ -20,8 +20,6 @@ namespace TerminalApp
Windows.Foundation.Collections.IObservableVector<FilteredCommand> FilteredActions { get; };
void SetCommands(Windows.Foundation.Collections.IVector<Microsoft.Terminal.Settings.Model.Command> actions);
void SetTabs(Windows.Foundation.Collections.IObservableVector<TabBase> tabs, Windows.Foundation.Collections.IObservableVector<TabBase> mruTabs);
void SetActionMap(Microsoft.Terminal.Settings.Model.IActionMapView actionMap);

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

@@ -27,10 +27,10 @@ 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 IPaneContent& content, const bool lastFocused) :
_content{ content },
Pane::Pane(IPaneContent content, const bool lastFocused) :
_lastActive{ lastFocused }
{
_setPaneContent(std::move(content));
_root.Children().Append(_borderFirst);
const auto& control{ _content.GetRoot() };
@@ -43,6 +43,9 @@ Pane::Pane(const IPaneContent& content, const bool lastFocused) :
_lostFocusRevoker = control.LostFocus(winrt::auto_revoke, { this, &Pane::_ContentLostFocusHandler });
}
_manipulationDeltaRevoker = _root.ManipulationDelta(winrt::auto_revoke, { this, &Pane::_ManipulationDeltaHandler });
_manipulationStartedRevoker = _root.ManipulationStarted(winrt::auto_revoke, { this, &Pane::_ManipulationStartedHandler });
// 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
// Colors::Transparent! The border won't get Tapped events, and they'll fall
@@ -73,6 +76,8 @@ Pane::Pane(std::shared_ptr<Pane> first,
_root.Children().Append(_borderFirst);
_root.Children().Append(_borderSecond);
_manipulationDeltaRevoker = _root.ManipulationDelta(winrt::auto_revoke, { this, &Pane::_ManipulationDeltaHandler });
_ApplySplitDefinitions();
// Register event handlers on our children to handle their Close events
@@ -243,14 +248,13 @@ Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId, uint32_t n
// decreasing the size of our first child.
// Return Value:
// - false if we couldn't resize this pane in the given direction, else true.
bool Pane::_Resize(const ResizeDirection& direction)
bool Pane::_Resize(const ResizeDirection& direction, float amount)
{
if (!DirectionMatchesSplit(direction, _splitState))
{
return false;
}
auto amount = .05f;
if (direction == ResizeDirection::Right || direction == ResizeDirection::Down)
{
amount = -amount;
@@ -284,7 +288,7 @@ bool Pane::_Resize(const ResizeDirection& direction)
// - direction: The direction to move the separator in.
// Return Value:
// - true if we or a child handled this resize request.
bool Pane::ResizePane(const ResizeDirection& direction)
bool Pane::ResizePane(const ResizeDirection& direction, float amount)
{
// If we're a leaf, do nothing. We can't possibly have a descendant with a
// separator the correct direction.
@@ -301,7 +305,7 @@ bool Pane::ResizePane(const ResizeDirection& direction)
const auto secondIsFocused = _secondChild->_lastActive;
if (firstIsFocused || secondIsFocused)
{
return _Resize(direction);
return _Resize(direction, amount);
}
// If neither of our children were the focused pane, then recurse into
@@ -315,17 +319,200 @@ bool Pane::ResizePane(const ResizeDirection& direction)
// either.
if ((!_firstChild->_IsLeaf()) && _firstChild->_HasFocusedChild())
{
return _firstChild->ResizePane(direction) || _Resize(direction);
return _firstChild->ResizePane(direction, amount) || _Resize(direction, amount);
}
if ((!_secondChild->_IsLeaf()) && _secondChild->_HasFocusedChild())
{
return _secondChild->ResizePane(direction) || _Resize(direction);
return _secondChild->ResizePane(direction, amount) || _Resize(direction, amount);
}
return false;
}
// Handler for the _root's ManipulationStarted event. We use this to check if a
// manipulation (read: drag) started inside our content. If it did, we _don't_
// want to do our normal pane dragging.
//
// Consider the case that the TermControl might be selecting text, and the user
// drags the mouse over the pane border. We don't want that to start moving the
// border!
void Pane::_ManipulationStartedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/,
const winrt::Windows::UI::Xaml::Input::ManipulationStartedRoutedEventArgs& args)
{
// This is added to each _root. But it also bubbles, so only leaves should actually try to handle this.
if (args.Handled())
{
return;
}
args.Handled(true);
assert(_IsLeaf());
const auto contentSize = _content.GetRoot().ActualSize();
auto transformCurrentPos = args.Position();
auto transformOrigin = transformCurrentPos;
const auto transform_contentFromOurRoot = _root.TransformToVisual(_content.GetRoot());
const auto transformInControlSpace = transform_contentFromOurRoot.TransformPoint(transformOrigin);
// If we clicked on the control. bail, and don't allow any manipulations
// for this series of events.
_shouldManipulate = !((transformInControlSpace.X >= 0 && transformInControlSpace.X < contentSize.x) &&
(transformInControlSpace.Y >= 0 && transformInControlSpace.Y < contentSize.y));
}
// Handler for the _root's ManipulationDelta event. This is the event raised
// when a user clicks and drags somewhere inside the pane. We're going to use
// this to determine if the user clicked on one of our borders. If they did,
// we're going to need to ask our parent pane (or other ancestors) to resize
// their split.
//
// Recall that a leaf itself is responsible for having the right borders, but it
// is the parent of the leaf that actually controls how big a split is.
//
// When we do want to be resized, we'll pass the delta from this event upwards
// via ManipulationRequested, which will be handled in
// Pane::_handleOrBubbleManipulation.
void Pane::_ManipulationDeltaHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/,
const winrt::Windows::UI::Xaml::Input::ManipulationDeltaRoutedEventArgs& args)
{
// sender is ORIGINALLY the root Grid of a leaf, and the leaf may or may not
// have a border.
if (args.Handled())
{
return;
}
if (!_shouldManipulate)
{
// Using our stored _shouldManipulate set up in
// _ManipulationStartedHandler, bail if the manipulation didn't start
// _on the border_.
return;
}
assert(_IsLeaf());
const auto delta = args.Delta().Translation;
const auto transformOrigin = args.Position();
const auto contentSize = _content.GetRoot().ActualSize();
const auto transform_contentFromOurRoot = _root.TransformToVisual(_content.GetRoot());
// This is the position of the drag relative to the bounds of our content.
const auto transformInControlSpace = transform_contentFromOurRoot.TransformPoint(transformOrigin);
// Did we click somewhere in the bounds of our content?
if ((transformInControlSpace.X >= 0 && transformInControlSpace.X < contentSize.x) &&
(transformInControlSpace.Y >= 0 && transformInControlSpace.Y < contentSize.y))
{
// We did! Bail.
return;
}
// Now, we know we clicked somewhere outside the bounds of our content. Set
// border flags based on the side that was clicked on.
Borders clicked = Borders::None;
clicked |= (transformInControlSpace.X < 0) ? Borders::Left : Borders::None;
clicked |= (transformInControlSpace.Y < 0) ? Borders::Top : Borders::None;
clicked |= (transformInControlSpace.X > contentSize.x) ? Borders::Right : Borders::None;
clicked |= (transformInControlSpace.Y > contentSize.y) ? Borders::Bottom : Borders::None;
// Ask our parent to resize their split.
ManipulationRequested.raise(shared_from_this(), delta, clicked);
}
// Handler for our child's own ManipulationRequested event. They will pass to us
// (their immediate parent) the delta and the side that was clicked on.
// * If we control that border, then we'll handle the resize ourself in _handleManipulation.
// * If not, then we'll ask our own parent to try and resize that same border.
void Pane::_handleOrBubbleManipulation(std::shared_ptr<Pane> sender,
const winrt::Windows::Foundation::Point delta,
Borders side)
{
if (side == Borders::None || _splitState == SplitState::None)
{
return;
}
const bool isFirstChild = sender == _firstChild;
// We want to handle this drag in the following cases
// * In a vertical split: if we're dragging the right of the first pane or the left of the second
// * In a horizontal split: if we're dragging the bottom of the first pane or the top of the second
const auto sideMatched = (_splitState == SplitState::Vertical) ? (isFirstChild && WI_IsFlagSet(side, Borders::Right)) || (!isFirstChild && WI_IsFlagSet(side, Borders::Left)) :
(_splitState == SplitState::Horizontal) ? (isFirstChild && WI_IsFlagSet(side, Borders::Bottom)) || (!isFirstChild && WI_IsFlagSet(side, Borders::Top)) :
false;
if (sideMatched)
{
_handleManipulation(delta);
}
else
{
// Bubble, with us as the new sender.
ManipulationRequested.raise(shared_from_this(), delta, side);
}
}
// Actually handle resizing our split in response to a drag event. If we're
// being called, then we know that the delta that's passed to us should be
// applied to our own split. The delta that's passed in here is in PIXELS, not
// DIPs.
void Pane::_handleManipulation(const winrt::Windows::Foundation::Point delta)
{
const auto scaleFactor = DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel();
const auto weAreVertical = _splitState == SplitState::Vertical;
const winrt::Windows::Foundation::Point translationForUs = (weAreVertical) ? Point{ delta.X, 0 } : Point{ 0, delta.Y };
// Decide on direction based on delta
ResizeDirection dir = ResizeDirection::None;
if (_splitState == SplitState::Vertical)
{
if (translationForUs.X < 0)
{
dir = ResizeDirection::Left;
}
else if (translationForUs.X > 0)
{
dir = ResizeDirection::Right;
}
}
else if (_splitState == SplitState::Horizontal)
{
if (translationForUs.Y < 0)
{
dir = ResizeDirection::Up;
}
else if (translationForUs.Y > 0)
{
dir = ResizeDirection::Down;
}
}
// Resize in the given direction
if (dir != ResizeDirection::None)
{
// turn delta into a percentage
base::ClampedNumeric<float> amount;
base::ClampedNumeric<float> actualDimension;
if (dir == ResizeDirection::Left || dir == ResizeDirection::Right)
{
amount = translationForUs.X;
actualDimension = base::ClampedNumeric<float>(_root.ActualWidth());
}
else if (dir == ResizeDirection::Up || dir == ResizeDirection::Down)
{
amount = translationForUs.Y;
actualDimension = base::ClampedNumeric<float>(_root.ActualHeight());
}
const auto scaledAmount = amount * scaleFactor;
const auto percentDelta = scaledAmount / actualDimension;
_Resize(dir, percentDelta.Abs());
}
}
// Method Description:
// - Attempt to navigate from the sourcePane according to direction.
// - If the direction is NextInOrder or PreviousInOrder, the next or previous
@@ -985,17 +1172,7 @@ void Pane::_ContentLostFocusHandler(const winrt::Windows::Foundation::IInspectab
// - <none>
void Pane::Close()
{
// Pane has two events, CloseRequested and Closed. CloseRequested is raised by the content asking to be closed,
// but also by the window who owns the tab when it's closing. The event is then caught by the TerminalTab which
// calls Close() which then raises the Closed event. Now, if this is the last pane in the window, this will result
// in the window raising CloseRequested again which leads to infinite recursion, so we need to guard against that.
// Ideally we would have just a single event in the future.
if (_closed)
{
return;
}
_closed = true;
_setPaneContent(nullptr);
// Fire our Closed event to tell our parent that we should be removed.
Closed.raise(nullptr, nullptr);
}
@@ -1007,7 +1184,7 @@ void Pane::Shutdown()
{
if (_IsLeaf())
{
_content.Close();
_setPaneContent(nullptr);
}
else
{
@@ -1411,7 +1588,7 @@ void Pane::_CloseChild(const bool closeFirst)
_borders = _GetCommonBorders();
// take the control, profile, id and isDefTermSession of the pane that _wasn't_ closed.
_content = remainingChild->_content;
_setPaneContent(remainingChild->_takePaneContent());
_id = remainingChild->Id();
// Revoke the old event handlers. Remove both the handlers for the panes
@@ -1716,6 +1893,34 @@ void Pane::_SetupChildCloseHandlers()
});
}
// With this method you take ownership of the control from this Pane.
// Assign it to another Pane with _setPaneContent() or Close() it.
IPaneContent Pane::_takePaneContent()
{
_closeRequestedRevoker.revoke();
return std::move(_content);
}
// This method safely sets the content of the Pane. It'll ensure to revoke and
// assign event handlers, and to Close() the existing content if there's any.
// The new content can be nullptr to remove any content.
void Pane::_setPaneContent(IPaneContent content)
{
// The IPaneContent::Close() implementation may be buggy and raise the CloseRequested event again.
// _takePaneContent() avoids this as it revokes the event handler.
if (const auto c = _takePaneContent())
{
c.Close();
}
_content = std::move(content);
if (_content)
{
_closeRequestedRevoker = _content.CloseRequested(winrt::auto_revoke, [this](auto&&, auto&&) { Close(); });
}
}
// Method Description:
// - Sets up row/column definitions for this pane. There are three total
// row/cols. The middle one is for the separator. The first and third are for
@@ -1847,6 +2052,9 @@ Borders Pane::_GetCommonBorders()
// - <none>
void Pane::_ApplySplitDefinitions()
{
// Remove our old handler, if we had one.
_manipulationDeltaRevoker.revoke();
if (_splitState == SplitState::Vertical)
{
Controls::Grid::SetColumn(_borderFirst, 0);
@@ -1871,6 +2079,17 @@ void Pane::_ApplySplitDefinitions()
_firstChild->_ApplySplitDefinitions();
_secondChild->_ApplySplitDefinitions();
}
else
{
assert(_IsLeaf());
// If we're a leaf, then add a ManipulationDelta handler.
_manipulationDeltaRevoker = _root.ManipulationDelta(winrt::auto_revoke, { this, &Pane::_ManipulationDeltaHandler });
}
_root.ManipulationMode(Xaml::Input::ManipulationModes::TranslateX |
Xaml::Input::ManipulationModes::TranslateRailsX |
Xaml::Input::ManipulationModes::TranslateY |
Xaml::Input::ManipulationModes::TranslateRailsY);
_UpdateBorders();
}
@@ -2254,6 +2473,9 @@ std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::_Split(SplitDirect
// Create a new pane from ourself
if (!_IsLeaf())
{
_firstChild->ManipulationRequested(_firstManipulatedToken);
_secondChild->ManipulationRequested(_secondManipulatedToken);
// Since we are a parent we don't have borders normally,
// so set them temporarily for when we update our split definition.
_borders = _GetCommonBorders();
@@ -2266,8 +2488,7 @@ 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>(_content);
_content = nullptr;
_firstChild = std::make_shared<Pane>(_takePaneContent());
_firstChild->_broadcastEnabled = _broadcastEnabled;
}
@@ -2292,6 +2513,9 @@ std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::_Split(SplitDirect
_ApplySplitDefinitions();
_firstManipulatedToken = _firstChild->ManipulationRequested({ this, &Pane::_handleOrBubbleManipulation });
_secondManipulatedToken = _secondChild->ManipulationRequested({ this, &Pane::_handleOrBubbleManipulation });
// Register event handlers on our children to handle their Close events
_SetupChildCloseHandlers();
@@ -2462,6 +2686,11 @@ bool Pane::_HasChild(const std::shared_ptr<Pane> child)
});
}
winrt::TerminalApp::TerminalPaneContent Pane::_getTerminalContent() const
{
return _IsLeaf() ? _content.try_as<winrt::TerminalApp::TerminalPaneContent>() : nullptr;
}
// Method Description:
// - Recursive function that finds a pane with the given ID
// Arguments:

View File

@@ -62,7 +62,7 @@ struct PaneResources
class Pane : public std::enable_shared_from_this<Pane>
{
public:
Pane(const winrt::TerminalApp::IPaneContent& content,
Pane(winrt::TerminalApp::IPaneContent content,
const bool lastFocused = false);
Pane(std::shared_ptr<Pane> first,
@@ -109,7 +109,8 @@ public:
winrt::Microsoft::Terminal::Settings::Model::INewContentArgs GetTerminalArgsForPane(winrt::TerminalApp::BuildStartupKind kind) const;
void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings);
bool ResizePane(const winrt::Microsoft::Terminal::Settings::Model::ResizeDirection& direction);
bool ResizePane(const winrt::Microsoft::Terminal::Settings::Model::ResizeDirection& direction, float amount = .05f);
std::shared_ptr<Pane> NavigateDirection(const std::shared_ptr<Pane> sourcePane,
const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction,
const std::vector<uint32_t>& mruPanes);
@@ -223,6 +224,7 @@ public:
til::event<gotFocusArgs> GotFocus;
til::event<winrt::delegate<std::shared_ptr<Pane>>> LostFocus;
til::event<winrt::delegate<std::shared_ptr<Pane>>> Detached;
til::event<winrt::delegate<std::shared_ptr<Pane>, winrt::Windows::Foundation::Point, Borders>> ManipulationRequested;
private:
struct PanePoint;
@@ -248,14 +250,22 @@ private:
std::optional<uint32_t> _id;
std::weak_ptr<Pane> _parentChildPath{};
bool _closed{ false };
bool _lastActive{ false };
winrt::event_token _firstClosedToken{ 0 };
winrt::event_token _secondClosedToken{ 0 };
winrt::event_token _firstManipulatedToken{ 0 };
winrt::event_token _secondManipulatedToken{ 0 };
winrt::Windows::UI::Xaml::UIElement::GotFocus_revoker _gotFocusRevoker;
winrt::Windows::UI::Xaml::UIElement::LostFocus_revoker _lostFocusRevoker;
winrt::Windows::UI::Xaml::UIElement::ManipulationDelta_revoker _manipulationDeltaRevoker;
winrt::Windows::UI::Xaml::UIElement::ManipulationStarted_revoker _manipulationStartedRevoker;
bool _shouldManipulate{ false };
winrt::TerminalApp::IPaneContent::CloseRequested_revoker _closeRequestedRevoker;
Borders _borders{ Borders::None };
bool _zoomed{ false };
@@ -264,11 +274,10 @@ private:
bool _IsLeaf() const noexcept;
bool _HasFocusedChild() const noexcept;
void _SetupChildCloseHandlers();
winrt::TerminalApp::IPaneContent _takePaneContent();
void _setPaneContent(winrt::TerminalApp::IPaneContent content);
bool _HasChild(const std::shared_ptr<Pane> child);
winrt::TerminalApp::TerminalPaneContent _getTerminalContent() const
{
return _IsLeaf() ? _content.try_as<winrt::TerminalApp::TerminalPaneContent>() : nullptr;
}
winrt::TerminalApp::TerminalPaneContent _getTerminalContent() const;
std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> _Split(winrt::Microsoft::Terminal::Settings::Model::SplitDirection splitType,
const float splitSize,
@@ -281,7 +290,9 @@ private:
Borders _GetCommonBorders();
winrt::Windows::UI::Xaml::Media::SolidColorBrush _ComputeBorderColor();
bool _Resize(const winrt::Microsoft::Terminal::Settings::Model::ResizeDirection& direction);
void _handleOrBubbleManipulation(std::shared_ptr<Pane> sender, const winrt::Windows::Foundation::Point delta, Borders side);
void _handleManipulation(const winrt::Windows::Foundation::Point delta);
bool _Resize(const winrt::Microsoft::Terminal::Settings::Model::ResizeDirection& direction, float amount);
std::shared_ptr<Pane> _FindParentOfPane(const std::shared_ptr<Pane> pane);
std::pair<PanePoint, PanePoint> _GetOffsetsForPane(const PanePoint parentOffset) const;
@@ -304,6 +315,11 @@ private:
void _ContentLostFocusHandler(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::UI::Xaml::RoutedEventArgs& e);
void _ManipulationStartedHandler(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::UI::Xaml::Input::ManipulationStartedRoutedEventArgs& e);
void _ManipulationDeltaHandler(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::UI::Xaml::Input::ManipulationDeltaRoutedEventArgs& e);
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;

View File

@@ -288,6 +288,15 @@
<data name="CmdCommandArgDesc" xml:space="preserve">
<value>An optional command, with arguments, to be spawned in the new tab or pane</value>
</data>
<data name="SaveActionDesc" xml:space="preserve">
<value>Save command line as input action</value>
</data>
<data name="SaveActionArgDesc" xml:space="preserve">
<value>An optional argument</value>
</data>
<data name="KeyChordArgDesc" xml:space="preserve">
<value>An optional argument</value>
</data>
<data name="CmdFocusTabDesc" xml:space="preserve">
<value>Move focus to another tab</value>
</data>
@@ -898,4 +907,21 @@
<data name="RestartConnectionToolTip" xml:space="preserve">
<value>Restart the active pane connection</value>
</data>
<data name="ActionSavedToast.Title" xml:space="preserve">
<value>Action saved</value>
</data>
<data name="ActionSaveFailedToast.Title" xml:space="preserve">
<value>Action save failed</value>
</data>
<data name="SnippetPaneTitle.Text" xml:space="preserve">
<value>Snippets</value>
<comment>Header for a page that includes small snippets of text for the user to enter</comment>
</data>
<data name="SnippetsFilterBox.PlaceholderText" xml:space="preserve">
<value>Filter snippets...</value>
<comment>Placeholder text for a text box to filter snippets (on the same page as the 'SnippetPaneTitle')</comment>
</data>
<data name="SnippetPlayButton.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
<value>Input this command</value>
</data>
</root>

View File

@@ -45,7 +45,6 @@ namespace winrt::TerminalApp::implementation
}
void ScratchpadContent::Close()
{
CloseRequested.raise(*this, nullptr);
}
INewContentArgs ScratchpadContent::GetNewTerminalArgs(const BuildStartupKind /* kind */) const

View File

@@ -47,7 +47,6 @@ namespace winrt::TerminalApp::implementation
}
void SettingsPaneContent::Close()
{
CloseRequested.raise(*this, nullptr);
}
INewContentArgs SettingsPaneContent::GetNewTerminalArgs(const BuildStartupKind /*kind*/) const

View File

@@ -0,0 +1,125 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "SnippetsPaneContent.h"
#include "SnippetsPaneContent.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
{
SnippetsPaneContent::SnippetsPaneContent()
{
InitializeComponent();
// auto res = Windows::UI::Xaml::Application::Current().Resources();
auto bg = Resources().Lookup(winrt::box_value(L"PageBackground"));
Background(bg.try_as<WUX::Media::Brush>());
}
void SnippetsPaneContent::_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 SnippetsPaneContent::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 suggestions UI 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 SnippetsPaneContent::_filterTextChanged(const IInspectable& /*sender*/,
const Windows::UI::Xaml::RoutedEventArgs& /*args*/)
{
_updateFilteredCommands();
}
winrt::Windows::UI::Xaml::FrameworkElement SnippetsPaneContent::GetRoot()
{
return *this;
}
winrt::Windows::Foundation::Size SnippetsPaneContent::MinimumSize()
{
return { 1, 1 };
}
void SnippetsPaneContent::Focus(winrt::Windows::UI::Xaml::FocusState reason)
{
reason;
// _box.Focus(reason);
}
void SnippetsPaneContent::Close()
{
CloseRequested.raise(*this, nullptr);
}
INewContentArgs SnippetsPaneContent::GetNewTerminalArgs(BuildStartupKind /*kind*/) const
{
return BaseContentArgs(L"snippets");
}
winrt::hstring SnippetsPaneContent::Icon() const
{
static constexpr std::wstring_view glyph{ L"\xe70b" }; // QuickNote
return winrt::hstring{ glyph };
}
winrt::Windows::UI::Xaml::Media::Brush SnippetsPaneContent::BackgroundBrush()
{
return Background();
}
void SnippetsPaneContent::SetLastActiveControl(const Microsoft::Terminal::Control::TermControl& control)
{
_control = control;
}
void SnippetsPaneContent::_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
// action 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 "SnippetsPaneContent.g.h"
#include "FilteredTask.g.h"
#include "FilteredCommand.h"
#include "ActionPaletteItem.h"
#include <LibraryResources.h>
namespace winrt::TerminalApp::implementation
{
struct SnippetsPaneContent : SnippetsPaneContentT<SnippetsPaneContent>
{
SnippetsPaneContent();
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::INewContentArgs GetNewTerminalArgs(BuildStartupKind kind) const;
winrt::hstring Title() { return RS_(L"SnippetPaneTitle/Text"); }
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 SnippetsPaneContentT<SnippetsPaneContent>; // 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, L""));
_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; }
bool HasChildren() { return _children.Size() > 0; }
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(SnippetsPaneContent);
}

View File

@@ -0,0 +1,236 @@
<!--
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.SnippetsPaneContent"
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>
<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="14"
Height="14">
<Grid>
<FontIcon x:Name="ButtonBackgroundIcon"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
Foreground="{ThemeResource PlayButtonHoveredColor}"
Glyph="&#xF5b0;"
Visibility="Collapsed" />
<FontIcon x:Name="ButtonOutlineIcon"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
Foreground="{ThemeResource PlayButtonNormalColor}"
Glyph="&#xE768;" />
</Grid>
</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>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<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), Mode=OneTime}"
Visibility="Collapsed" />
</ContentPresenter>
<Button x:Uid="SnippetPlayButton"
Grid.Row="0"
Grid.RowSpan="1"
Grid.Column="0"
Margin="-28,0,0,0"
Padding="3"
VerticalAlignment="Bottom"
Background="Transparent"
BorderBrush="Transparent"
Click="_runCommandButtonClicked"
Style="{StaticResource PlayButtonTemplate}"
Visibility="{x:Bind mtu:Converters.InvertedBooleanToVisibility(HasChildren), Mode=OneWay}">
<Button.Content>
<FontIcon FontFamily="{ThemeResource SymbolThemeFontFamily}"
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.ThemeDictionaries>
<!-- same as in MainPage, this is SolidBackgroundFillColorTertiary -->
<ResourceDictionary x:Key="Dark">
<Color x:Key="PageBackground">#282828</Color>
<Color x:Key="PlayButtonHoveredColor">#90ef90</Color>
<Color x:Key="PlayButtonNormalColor">#8888</Color>
</ResourceDictionary>
<ResourceDictionary x:Key="Light">
<Color x:Key="PageBackground">#F9F9F9</Color>
<Color x:Key="PlayButtonHoveredColor">#257f01</Color>
<Color x:Key="PlayButtonNormalColor">#88222222</Color>
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<!-- Define resources for HighContrast mode here -->
<StaticResource x:Key="PageBackground"
ResourceKey="SystemColorWindowColorBrush" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid Background="{ThemeResource PageBackground}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock x:Name="_title"
x:Uid="SnippetPaneTitle"
Grid.Row="0"
Margin="4"
FontSize="24" />
<TextBox x:Name="_filterBox"
x:Uid="SnippetsFilterBox"
Grid.Row="1"
Margin="8,0,8,8"
TextChanged="_filterTextChanged" />
<mux:TreeView x:Name="_treeView"
Grid.Row="2"
CanDragItems="False"
CanReorderItems="False"
ItemTemplate="{StaticResource TaskItemTemplate}" />
</Grid>
</UserControl>

View File

@@ -508,7 +508,7 @@ namespace winrt::TerminalApp::implementation
automationPeer.RaiseNotificationEvent(
Automation::Peers::AutomationNotificationKind::ItemAdded,
Automation::Peers::AutomationNotificationProcessing::MostRecent,
paletteItem.Name() + L" " + paletteItem.KeyChordText(),
paletteItem.Name(),
L"SuggestionsControlSelectedItemChanged" /* unique name for this notification category */);
}
}
@@ -751,17 +751,13 @@ namespace winrt::TerminalApp::implementation
return _filteredActions;
}
void SuggestionsControl::SetActionMap(const Microsoft::Terminal::Settings::Model::IActionMapView& actionMap)
{
_actionMap = actionMap;
}
void SuggestionsControl::SetCommands(const Collections::IVector<Command>& actions)
{
_allCommands.Clear();
for (const auto& action : actions)
{
auto actionPaletteItem{ winrt::make<winrt::TerminalApp::implementation::ActionPaletteItem>(action) };
// key chords aren't relevant in the suggestions control, so make the palette item with just the command and no keys
auto actionPaletteItem{ winrt::make<winrt::TerminalApp::implementation::ActionPaletteItem>(action, L"") };
auto filteredCommand{ winrt::make<FilteredCommand>(actionPaletteItem) };
_allCommands.Append(filteredCommand);
}
@@ -915,7 +911,7 @@ namespace winrt::TerminalApp::implementation
for (const auto& nameAndCommand : parentCommand.NestedCommands())
{
const auto action = nameAndCommand.Value();
auto nestedActionPaletteItem{ winrt::make<winrt::TerminalApp::implementation::ActionPaletteItem>(action) };
auto nestedActionPaletteItem{ winrt::make<winrt::TerminalApp::implementation::ActionPaletteItem>(action, L"") };
auto nestedFilteredCommand{ winrt::make<FilteredCommand>(nestedActionPaletteItem) };
_currentNestedCommands.Append(nestedFilteredCommand);
}

View File

@@ -24,7 +24,6 @@ namespace winrt::TerminalApp::implementation
Windows::Foundation::Collections::IObservableVector<winrt::TerminalApp::FilteredCommand> FilteredActions();
void SetCommands(const Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::Command>& actions);
void SetActionMap(const Microsoft::Terminal::Settings::Model::IActionMapView& actionMap);
bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down);

View File

@@ -36,7 +36,6 @@ namespace TerminalApp
SuggestionsMode Mode { get; set; };
void SetCommands(Windows.Foundation.Collections.IVector<Microsoft.Terminal.Settings.Model.Command> actions);
void SetActionMap(Microsoft.Terminal.Settings.Model.IActionMapView actionMap);
void SelectNextItem(Boolean moveDown);
void Open(SuggestionsMode mode, IVector<Microsoft.Terminal.Settings.Model.Command> commands, String filterText, Windows.Foundation.Point anchor, Windows.Foundation.Size space, Single characterHeight);

View File

@@ -31,7 +31,6 @@
MinHeight="0"
Padding="16,0,12,0"
HorizontalContentAlignment="Stretch"
AutomationProperties.AcceleratorKey="{x:Bind Item.KeyChordText, Mode=OneWay}"
AutomationProperties.Name="{x:Bind Item.Name, Mode=OneWay}"
FontSize="12" />
</DataTemplate>

View File

@@ -192,7 +192,8 @@ namespace winrt::TerminalApp::implementation
// - <none>
void TabBase::_UpdateSwitchToTabKeyChord()
{
const auto keyChord = _actionMap ? _actionMap.GetKeyBindingForAction(ShortcutAction::SwitchToTab, SwitchToTabArgs{ _TabViewIndex }) : nullptr;
const auto id = fmt::format(FMT_COMPILE(L"Terminal.SwitchToTab{}"), _TabViewIndex);
const auto keyChord{ _actionMap.GetKeyBindingForAction(id) };
const auto keyChordText = keyChord ? KeyChordSerialization::ToString(keyChord) : L"";
if (_keyChord == keyChordText)

View File

@@ -71,6 +71,9 @@
<Page Include="SuggestionsControl.xaml">
<SubType>Designer</SubType>
</Page>
<Page Include="SnippetsPaneContent.xaml">
<SubType>Designer</SubType>
</Page>
</ItemGroup>
<!-- ========================= Headers ======================== -->
<ItemGroup>
@@ -161,6 +164,9 @@
<ClInclude Include="ScratchpadContent.h">
<DependentUpon>TerminalPaneContent.idl</DependentUpon>
</ClInclude>
<ClInclude Include="SnippetsPaneContent.h">
<DependentUpon>SnippetsPaneContent.xaml</DependentUpon>
</ClInclude>
<ClInclude Include="SettingsPaneContent.h">
<DependentUpon>TerminalPaneContent.idl</DependentUpon>
</ClInclude>
@@ -274,6 +280,9 @@
<ClCompile Include="ScratchpadContent.cpp">
<DependentUpon>TerminalPaneContent.idl</DependentUpon>
</ClCompile>
<ClCompile Include="SnippetsPaneContent.cpp">
<DependentUpon>SnippetsPaneContent.xaml</DependentUpon>
</ClCompile>
<ClCompile Include="SettingsPaneContent.cpp">
<DependentUpon>TerminalPaneContent.idl</DependentUpon>
</ClCompile>
@@ -352,12 +361,17 @@
</Midl>
<Midl Include="FilteredCommand.idl" />
<Midl Include="IPaneContent.idl" />
<Midl Include="TerminalPaneContent.idl" />
<Midl Include="TerminalPaneContent.idl" >
<DependentUpon>TaskPaneContent.xaml</DependentUpon>
<SubType>Code</SubType>
</Midl>
<Midl Include="TerminalSettingsCache.idl" />
</ItemGroup>
<!-- ========================= Misc Files ======================== -->
<ItemGroup>
<PRIResource Include="Resources\en-US\Resources.resw" />
<PRIResource Include="Resources\en-US\Resources.resw">
<SubType>Designer</SubType>
</PRIResource>
<PRIResource Include="Resources\en-US\ContextMenu.resw" />
<OCResourceDirectory Include="Resources" />
</ItemGroup>
@@ -466,10 +480,8 @@
</ItemDefinitionGroup>
<!-- ========================= Globals ======================== -->
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
<!-- This -must- go after cppwinrt.build.post.props because that includes many VS-provided props including appcontainer.common.props, which stomps on what cppwinrt.targets did. -->
<Import Project="$(OpenConsoleDir)src\common.nugetversions.targets" />
<!--
By default, the PRI file will contain resource paths beginning with the
project name. Since we enabled XBF embedding, this *also* includes App.xbf.
@@ -490,4 +502,4 @@
</ItemGroup>
</Target>
<Import Project="$(SolutionDir)build\rules\CollectWildcardResources.targets" />
</Project>
</Project>

View File

@@ -4,18 +4,11 @@
#include "pch.h"
#include "TerminalPage.h"
#include "TerminalPage.g.cpp"
#include "RenameWindowRequestedArgs.g.cpp"
#include "RequestMoveContentArgs.g.cpp"
#include "RequestReceiveContentArgs.g.cpp"
#include "LaunchPositionRequest.g.cpp"
#include <filesystem>
#include <inc/WindowingBehavior.h>
#include <LibraryResources.h>
#include <TerminalCore/ControlKeyStates.hpp>
#include <til/latch.h>
#include <Utils.h>
#include "../../types/inc/utils.hpp"
#include "App.h"
@@ -23,8 +16,14 @@
#include "DebugTapConnection.h"
#include "SettingsPaneContent.h"
#include "ScratchpadContent.h"
#include "SnippetsPaneContent.h"
#include "TabRowControl.h"
#include "Utils.h"
#include "TerminalPage.g.cpp"
#include "RenameWindowRequestedArgs.g.cpp"
#include "RequestMoveContentArgs.g.cpp"
#include "RequestReceiveContentArgs.g.cpp"
#include "LaunchPositionRequest.g.cpp"
using namespace winrt;
using namespace winrt::Microsoft::Terminal::Control;
@@ -123,7 +122,6 @@ namespace winrt::TerminalApp::implementation
// to happen before the Settings UI is reloaded and tries to re-read those values.
if (const auto p = CommandPaletteElement())
{
p.SetCommands(_settings.GlobalSettings().ActionMap().ExpandedCommands());
p.SetActionMap(_settings.ActionMap());
}
@@ -454,10 +452,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:
@@ -826,7 +824,7 @@ namespace winrt::TerminalApp::implementation
newTabFlyout.Items().Append(settingsItem);
auto actionMap = _settings.ActionMap();
const auto settingsKeyChord{ actionMap.GetKeyBindingForAction(ShortcutAction::OpenSettings, OpenSettingsArgs{ SettingsTarget::SettingsUI }) };
const auto settingsKeyChord{ actionMap.GetKeyBindingForAction(L"Terminal.OpenSettingsUI") };
if (settingsKeyChord)
{
_SetAcceleratorForMenuItem(settingsItem, settingsKeyChord);
@@ -848,7 +846,7 @@ namespace winrt::TerminalApp::implementation
commandPaletteFlyout.Click({ this, &TerminalPage::_CommandPaletteButtonOnClick });
newTabFlyout.Items().Append(commandPaletteFlyout);
const auto commandPaletteKeyChord{ actionMap.GetKeyBindingForAction(ShortcutAction::ToggleCommandPalette) };
const auto commandPaletteKeyChord{ actionMap.GetKeyBindingForAction(L"Terminal.ToggleCommandPalette") };
if (commandPaletteKeyChord)
{
_SetAcceleratorForMenuItem(commandPaletteFlyout, commandPaletteKeyChord);
@@ -1005,6 +1003,18 @@ namespace winrt::TerminalApp::implementation
items.push_back(profileItem);
break;
}
case NewTabMenuEntryType::Action:
{
const auto actionEntry = entry.as<ActionEntry>();
const auto actionId = actionEntry.ActionId();
if (_settings.ActionMap().GetActionById(actionId))
{
auto actionItem = _CreateNewTabFlyoutAction(actionId);
items.push_back(actionItem);
}
break;
}
}
}
@@ -1023,7 +1033,8 @@ namespace winrt::TerminalApp::implementation
// NewTab(ProfileIndex=N) action
NewTerminalArgs newTerminalArgs{ profileIndex };
NewTabArgs newTabArgs{ newTerminalArgs };
auto profileKeyChord{ _settings.ActionMap().GetKeyBindingForAction(ShortcutAction::NewTab, newTabArgs) };
const auto id = fmt::format(FMT_COMPILE(L"Terminal.OpenNewTabProfile{}"), profileIndex);
const auto profileKeyChord{ _settings.ActionMap().GetKeyBindingForAction(id) };
// make sure we find one to display
if (profileKeyChord)
@@ -1094,6 +1105,42 @@ namespace winrt::TerminalApp::implementation
return profileMenuItem;
}
// Method Description:
// - This method creates a flyout menu item for a given action
// It makes sure to set the correct icon, keybinding, and click-action.
WUX::Controls::MenuFlyoutItem TerminalPage::_CreateNewTabFlyoutAction(const winrt::hstring& actionId)
{
auto actionMenuItem = WUX::Controls::MenuFlyoutItem{};
const auto action{ _settings.ActionMap().GetActionById(actionId) };
const auto actionKeyChord{ _settings.ActionMap().GetKeyBindingForAction(actionId) };
if (actionKeyChord)
{
_SetAcceleratorForMenuItem(actionMenuItem, actionKeyChord);
}
const auto actionName = action.Name();
actionMenuItem.Text(actionName);
// If there's an icon set for this action, set it as the icon for
// this flyout item
const auto& iconPath = action.IconPath();
if (!iconPath.empty())
{
const auto icon = _CreateNewTabFlyoutIcon(iconPath);
actionMenuItem.Icon(icon);
}
actionMenuItem.Click([action, weakThis{ get_weak() }](auto&&, auto&&) {
if (auto page{ weakThis.get() })
{
page->_actionDispatch->DoAction(action.ActionAndArgs());
}
});
return actionMenuItem;
}
// Method Description:
// - Helper method to create an IconElement that can be passed to MenuFlyoutItems and
// MenuFlyoutSubItems
@@ -1211,6 +1258,26 @@ namespace winrt::TerminalApp::implementation
TerminalSettings settings,
const bool inheritCursor)
{
// The only way to create string references to literals in WinRT is through std::optional. Fun!
static std::optional<winrt::param::hstring> textMeasurement;
static const auto textMeasurementInit = [&]() {
switch (_settings.GlobalSettings().TextMeasurement())
{
case TextMeasurement::Graphemes:
textMeasurement.emplace(L"graphemes");
break;
case TextMeasurement::Wcswidth:
textMeasurement.emplace(L"wcswidth");
break;
case TextMeasurement::Console:
textMeasurement.emplace(L"console");
break;
default:
break;
}
return true;
}();
TerminalConnection::ITerminalConnection connection{ nullptr };
auto connectionType = profile.ConnectionType();
@@ -1282,6 +1349,11 @@ namespace winrt::TerminalApp::implementation
}
}
if (textMeasurement)
{
valueSet.Insert(L"textMeasurement", Windows::Foundation::PropertyValue::CreateString(*textMeasurement));
}
if (const auto id = settings.SessionId(); id != winrt::guid{})
{
valueSet.Insert(L"sessionId", Windows::Foundation::PropertyValue::CreateGuid(id));
@@ -1671,6 +1743,8 @@ namespace winrt::TerminalApp::implementation
term.ShowWindowChanged({ get_weak(), &TerminalPage::_ShowWindowChangedHandler });
term.SearchMissingCommand({ get_weak(), &TerminalPage::_SearchMissingCommandHandler });
// Don't even register for the event if the feature is compiled off.
if constexpr (Feature_ShellCompletions::IsEnabled())
{
@@ -1689,6 +1763,12 @@ namespace winrt::TerminalApp::implementation
page->_PopulateContextMenu(weakTerm.get(), sender.try_as<MUX::Controls::CommandBarFlyout>(), true);
}
});
term.QuickFixMenu().Opening([weak = get_weak(), weakTerm](auto&& sender, auto&& /*args*/) {
if (const auto& page{ weak.get() })
{
page->_PopulateQuickFixMenu(weakTerm.get(), sender.try_as<Controls::MenuFlyout>());
}
});
}
// Method Description:
@@ -1827,7 +1907,6 @@ namespace winrt::TerminalApp::implementation
{
const auto p = FindName(L"CommandPaletteElement").as<CommandPalette>();
p.SetCommands(_settings.GlobalSettings().ActionMap().ExpandedCommands());
p.SetActionMap(_settings.ActionMap());
// When the visibility of the command palette changes to "collapsed",
@@ -2922,6 +3001,30 @@ namespace winrt::TerminalApp::implementation
ShowWindowChanged.raise(*this, args);
}
winrt::fire_and_forget TerminalPage::_SearchMissingCommandHandler(const IInspectable /*sender*/, const Microsoft::Terminal::Control::SearchMissingCommandEventArgs args)
{
assert(!Dispatcher().HasThreadAccess());
if (!Feature_QuickFix::IsEnabled())
{
co_return;
}
std::vector<hstring> suggestions;
suggestions.reserve(1);
suggestions.emplace_back(fmt::format(L"winget install {}", args.MissingCommand()));
co_await wil::resume_foreground(Dispatcher());
auto term = _GetActiveControl();
if (!term)
{
co_return;
}
term.UpdateWinGetSuggestions(single_threaded_vector<hstring>(std::move(suggestions)));
term.ShowQuickFixMenu();
}
// Method Description:
// - Paste text from the Windows Clipboard to the focused terminal
void TerminalPage::_PasteText()
@@ -3205,6 +3308,15 @@ namespace winrt::TerminalApp::implementation
{
content = _makeSettingsContent();
}
else if (paneType == L"snippets")
{
const auto& tasksContent{ winrt::make_self<SnippetsPaneContent>() };
tasksContent->UpdateSettings(_settings);
tasksContent->GetRoot().KeyDown({ this, &TerminalPage::_KeyDownHandler });
tasksContent->DispatchCommandRequested({ this, &TerminalPage::_OnDispatchCommandRequested });
content = *tasksContent;
}
assert(content);
@@ -4138,6 +4250,66 @@ namespace winrt::TerminalApp::implementation
}
}
winrt::fire_and_forget TerminalPage::ActionSaved(winrt::hstring input, winrt::hstring name, winrt::hstring keyChord)
{
auto weakThis{ get_weak() };
co_await wil::resume_foreground(Dispatcher());
if (auto page{ weakThis.get() })
{
// If we haven't ever loaded the TeachingTip, then do so now and
// create the toast for it.
if (page->_actionSavedToast == nullptr)
{
if (auto tip{ page->FindName(L"ActionSavedToast").try_as<MUX::Controls::TeachingTip>() })
{
page->_actionSavedToast = std::make_shared<Toast>(tip);
// Make sure to use the weak ref when setting up this
// callback.
tip.Closed({ page->get_weak(), &TerminalPage::_FocusActiveControl });
}
}
_UpdateTeachingTipTheme(ActionSavedToast().try_as<winrt::Windows::UI::Xaml::FrameworkElement>());
SavedActionName(name);
SavedActionKeyChord(keyChord);
SavedActionCommandLine(input);
if (page->_actionSavedToast != nullptr)
{
page->_actionSavedToast->Open();
}
}
}
winrt::fire_and_forget TerminalPage::ActionSaveFailed(winrt::hstring message)
{
auto weakThis{ get_weak() };
co_await wil::resume_foreground(Dispatcher());
if (auto page{ weakThis.get() })
{
// If we haven't ever loaded the TeachingTip, then do so now and
// create the toast for it.
if (page->_actionSaveFailedToast == nullptr)
{
if (auto tip{ page->FindName(L"ActionSaveFailedToast").try_as<MUX::Controls::TeachingTip>() })
{
page->_actionSaveFailedToast = std::make_shared<Toast>(tip);
// Make sure to use the weak ref when setting up this
// callback.
tip.Closed({ page->get_weak(), &TerminalPage::_FocusActiveControl });
}
}
_UpdateTeachingTipTheme(ActionSaveFailedToast().try_as<winrt::Windows::UI::Xaml::FrameworkElement>());
ActionSaveFailedMessage().Text(message);
if (page->_actionSaveFailedToast != nullptr)
{
page->_actionSaveFailedToast->Open();
}
}
}
// Method Description:
// - Called when an attempt to rename the window has failed. This will open
// the toast displaying a message to the user that the attempt to rename
@@ -4842,6 +5014,62 @@ namespace winrt::TerminalApp::implementation
makeItem(RS_(L"TabClose"), L"\xE711", ActionAndArgs{ ShortcutAction::CloseTab, CloseTabArgs{ _GetFocusedTabIndex().value() } });
}
void TerminalPage::_PopulateQuickFixMenu(const TermControl& control,
const Controls::MenuFlyout& menu)
{
if (!control || !menu)
{
return;
}
// Helper lambda for dispatching a SendInput ActionAndArgs onto the
// ShortcutActionDispatch. Used below to wire up each menu entry to the
// respective action. Then clear the quick fix menu.
auto weak = get_weak();
auto makeCallback = [weak](const hstring& suggestion) {
return [weak, suggestion](auto&&, auto&&) {
if (auto page{ weak.get() })
{
const auto actionAndArgs = ActionAndArgs{ ShortcutAction::SendInput, SendInputArgs{ hstring{ L"\u0003" } + suggestion } };
page->_actionDispatch->DoAction(actionAndArgs);
if (auto ctrl = page->_GetActiveControl())
{
ctrl.ClearQuickFix();
}
}
};
};
auto makeItem = [&menu, &makeCallback](const winrt::hstring& label,
const winrt::hstring& icon,
const winrt::hstring& suggestion) {
MenuFlyoutItem item{};
if (!icon.empty())
{
auto iconElement = UI::IconPathConverter::IconWUX(icon);
Automation::AutomationProperties::SetAccessibilityView(iconElement, Automation::Peers::AccessibilityView::Raw);
item.Icon(iconElement);
}
item.Text(label);
item.Click(makeCallback(suggestion));
menu.Items().Append(item);
};
// Wire up each item to the action that should be performed. By actually
// connecting these to actions, we ensure the implementation is
// consistent. This also leaves room for customizing this menu with
// actions in the future.
menu.Items().Clear();
const auto quickFixes = control.CommandHistory().QuickFixes();
for (const auto& qf : quickFixes)
{
makeItem(qf, L"\ue74c", qf);
}
}
// Handler for our WindowProperties's PropertyChanged event. We'll use this
// to pop the "Identify Window" toast when the user renames our window.
winrt::fire_and_forget TerminalPage::_windowPropertyChanged(const IInspectable& /*sender*/,

View File

@@ -146,6 +146,8 @@ namespace winrt::TerminalApp::implementation
winrt::hstring KeyboardServiceDisabledText();
winrt::fire_and_forget IdentifyWindow();
winrt::fire_and_forget ActionSaved(winrt::hstring input, winrt::hstring name, winrt::hstring keyChord);
winrt::fire_and_forget ActionSaveFailed(winrt::hstring message);
winrt::fire_and_forget RenameFailed();
winrt::fire_and_forget ShowTerminalWorkingDirectory();
@@ -199,6 +201,10 @@ namespace winrt::TerminalApp::implementation
WINRT_OBSERVABLE_PROPERTY(winrt::Windows::UI::Xaml::Media::Brush, TitlebarBrush, PropertyChanged.raise, nullptr);
WINRT_OBSERVABLE_PROPERTY(winrt::Windows::UI::Xaml::Media::Brush, FrameBrush, PropertyChanged.raise, nullptr);
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, SavedActionName, PropertyChanged.raise, L"");
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, SavedActionKeyChord, PropertyChanged.raise, L"");
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, SavedActionCommandLine, PropertyChanged.raise, L"");
private:
friend struct TerminalPageT<TerminalPage>; // for Xaml to bind events
std::optional<HWND> _hostingHwnd;
@@ -258,6 +264,8 @@ namespace winrt::TerminalApp::implementation
bool _isEmbeddingInboundListener{ false };
std::shared_ptr<Toast> _windowIdToast{ nullptr };
std::shared_ptr<Toast> _actionSavedToast{ nullptr };
std::shared_ptr<Toast> _actionSaveFailedToast{ nullptr };
std::shared_ptr<Toast> _windowRenameFailedToast{ nullptr };
std::shared_ptr<Toast> _windowCwdToast{ nullptr };
@@ -300,6 +308,7 @@ namespace winrt::TerminalApp::implementation
std::vector<winrt::Windows::UI::Xaml::Controls::MenuFlyoutItemBase> _CreateNewTabFlyoutItems(winrt::Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::NewTabMenuEntry> entries);
winrt::Windows::UI::Xaml::Controls::IconElement _CreateNewTabFlyoutIcon(const winrt::hstring& icon);
winrt::Windows::UI::Xaml::Controls::MenuFlyoutItem _CreateNewTabFlyoutProfile(const Microsoft::Terminal::Settings::Model::Profile profile, int profileIndex);
winrt::Windows::UI::Xaml::Controls::MenuFlyoutItem _CreateNewTabFlyoutAction(const winrt::hstring& actionId);
void _OpenNewTabDropdown();
HRESULT _OpenNewTab(const Microsoft::Terminal::Settings::Model::INewContentArgs& newContentArgs);
@@ -520,6 +529,7 @@ namespace winrt::TerminalApp::implementation
void _OpenSuggestions(const Microsoft::Terminal::Control::TermControl& sender, Windows::Foundation::Collections::IVector<winrt::Microsoft::Terminal::Settings::Model::Command> commandsCollection, winrt::TerminalApp::SuggestionsMode mode, winrt::hstring filterText);
void _ShowWindowChangedHandler(const IInspectable sender, const winrt::Microsoft::Terminal::Control::ShowWindowArgs args);
winrt::fire_and_forget _SearchMissingCommandHandler(const IInspectable sender, const winrt::Microsoft::Terminal::Control::SearchMissingCommandEventArgs args);
winrt::fire_and_forget _windowPropertyChanged(const IInspectable& sender, const winrt::Windows::UI::Xaml::Data::PropertyChangedEventArgs& args);
@@ -537,6 +547,7 @@ namespace winrt::TerminalApp::implementation
void _sendDraggedTabToWindow(const winrt::hstring& windowId, const uint32_t tabIndex, std::optional<til::point> dragPoint);
void _PopulateContextMenu(const Microsoft::Terminal::Control::TermControl& control, const Microsoft::UI::Xaml::Controls::CommandBarFlyout& sender, const bool withSelection);
void _PopulateQuickFixMenu(const Microsoft::Terminal::Control::TermControl& control, const Windows::UI::Xaml::Controls::MenuFlyout& sender);
winrt::Windows::UI::Xaml::Controls::MenuFlyout _CreateRunAsAdminFlyout(int profileIndex);
winrt::Microsoft::Terminal::Control::TermControl _senderOrActiveControl(const winrt::Windows::Foundation::IInspectable& sender);

View File

@@ -72,6 +72,10 @@ namespace TerminalApp
void IdentifyWindow();
void RenameFailed();
String SavedActionName { get; };
String SavedActionKeyChord { get; };
String SavedActionCommandLine { get; };
// We cannot use the default XAML APIs because we want to make sure
// that there's only one application-global dialog visible at a time,
// and because of GH#5224.

View File

@@ -8,6 +8,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:TerminalApp"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mtu="using:Microsoft.Terminal.UI"
xmlns:mux="using:Microsoft.UI.Xaml.Controls"
Background="Transparent"
mc:Ignorable="d">
@@ -204,5 +205,43 @@
Title="{x:Bind WindowProperties.VirtualWorkingDirectory, Mode=OneWay}"
x:Load="False"
IsLightDismissEnabled="True" />
<mux:TeachingTip x:Name="ActionSavedToast"
x:Uid="ActionSavedToast"
Title="Action Saved"
HorizontalAlignment="Stretch"
x:Load="False"
IsLightDismissEnabled="True">
<mux:TeachingTip.Content>
<StackPanel HorizontalAlignment="Stretch"
Orientation="Vertical">
<TextBlock x:Name="ActionSavedNameText"
Visibility="{x:Bind mtu:Converters.StringNotEmptyToVisibility(SavedActionName), Mode=OneWay}">
<Run Text="Name: " />
<Run Text="{x:Bind SavedActionName, Mode=OneWay}" />
</TextBlock>
<TextBlock x:Name="ActionSavedKeyChordText"
Visibility="{x:Bind mtu:Converters.StringNotEmptyToVisibility(SavedActionKeyChord), Mode=OneWay}">
<Run Text="Key Chord: " />
<Run Text="{x:Bind SavedActionKeyChord, Mode=OneWay}" />
</TextBlock>
<TextBlock x:Name="ActionSavedCommandLineText"
Visibility="{x:Bind mtu:Converters.StringNotEmptyToVisibility(SavedActionCommandLine), Mode=OneWay}">
<Run Text="Input: " />
<Run Text="{x:Bind SavedActionCommandLine, Mode=OneWay}" />
</TextBlock>
</StackPanel>
</mux:TeachingTip.Content>
</mux:TeachingTip>
<mux:TeachingTip x:Name="ActionSaveFailedToast"
x:Uid="ActionSaveFailedToast"
Title="Action Save Failed"
x:Load="False"
IsLightDismissEnabled="True">
<mux:TeachingTip.Content>
<TextBlock x:Name="ActionSaveFailedMessage"
Text="" />
</mux:TeachingTip.Content>
</mux:TeachingTip>
</Grid>
</Page>

View File

@@ -78,8 +78,6 @@ namespace winrt::TerminalApp::implementation
_bellPlayer = nullptr;
_bellPlayerCreated = false;
}
CloseRequested.raise(*this, nullptr);
}
winrt::hstring TerminalPaneContent::Icon() const
@@ -239,19 +237,20 @@ namespace winrt::TerminalApp::implementation
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))
if (
// This one is obvious: If the user asked for "always" we do just that.
(mode == CloseOnExitMode::Always) ||
// Otherwise, and unless the user asked for the opposite of "always",
// close the pane when the connection closed gracefully (not failed).
(mode != CloseOnExitMode::Never && newConnectionState == ConnectionState::Closed) ||
// However, defterm handoff can result in Windows Terminal randomly opening which may be annoying,
// so by default we should at least always close the pane, even if the command failed.
// See GH #13325 for discussion.
(mode == CloseOnExitMode::Automatic && _isDefTermSession))
{
Close();
CloseRequested.raise(nullptr, nullptr);
}
}
}
@@ -331,7 +330,7 @@ namespace winrt::TerminalApp::implementation
void TerminalPaneContent::_closeTerminalRequestedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/,
const winrt::Windows::Foundation::IInspectable& /*args*/)
{
Close();
CloseRequested.raise(nullptr, nullptr);
}
void TerminalPaneContent::_restartTerminalRequestedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/,

View File

@@ -51,6 +51,7 @@ namespace winrt::TerminalApp::implementation
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;

View File

@@ -3,6 +3,7 @@
import "IPaneContent.idl";
import "TerminalSettingsCache.idl";
import "FilteredCommand.idl";
namespace TerminalApp
{
@@ -16,4 +17,21 @@ namespace TerminalApp
event Windows.Foundation.TypedEventHandler<TerminalPaneContent, Object> RestartTerminalRequested;
}
[default_interface] runtimeclass FilteredTask : TerminalApp.FilteredCommand
{
String Input{ get; };
Windows.Foundation.Collections.IObservableVector<FilteredTask> Children { get; };
Boolean HasChildren { get; };
Windows.UI.Xaml.Visibility Visibility { get; };
}
[default_interface] runtimeclass SnippetsPaneContent : Windows.UI.Xaml.Controls.UserControl, IPaneContent
{
SnippetsPaneContent();
void SetLastActiveControl(Microsoft.Terminal.Control.TermControl control);
event Windows.Foundation.TypedEventHandler<Object, Microsoft.Terminal.Settings.Model.Command> DispatchCommandRequested;
}
}

View File

@@ -947,26 +947,6 @@ namespace winrt::TerminalApp::implementation
auto dispatcher = TabViewItem().Dispatcher();
ContentEventTokens events{};
events.CloseRequested = content.CloseRequested(
winrt::auto_revoke,
[this](auto&& sender, auto&&) {
if (const auto content{ sender.try_as<TerminalApp::IPaneContent>() })
{
// Calling Close() while walking the tree is not safe, because Close() mutates the tree.
const auto pane = _rootPane->_FindPane([&](const auto& p) -> std::shared_ptr<Pane> {
if (p->GetContent() == content)
{
return p;
}
return {};
});
if (pane)
{
pane->Close();
}
}
});
events.TitleChanged = content.TitleChanged(
winrt::auto_revoke,
[dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget {
@@ -1243,6 +1223,18 @@ namespace winrt::TerminalApp::implementation
// Raise our own ActivePaneChanged event.
ActivePaneChanged.raise(*this, nullptr);
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<SnippetsPaneContent>() })
{
taskPane.SetLastActiveControl(termControl);
}
});
}
}
// Method Description:

View File

@@ -134,7 +134,6 @@ namespace winrt::TerminalApp::implementation
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;

View File

@@ -5,7 +5,6 @@
#include "ConptyConnection.h"
#include <conpty-static.h>
#include <til/string.h>
#include <winternl.h>
#include "CTerminalHandoff.h"
@@ -259,11 +258,39 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
_cols = unbox_prop_or<uint32_t>(settings, L"initialCols", _cols);
_sessionId = unbox_prop_or<winrt::guid>(settings, L"sessionId", _sessionId);
_environment = settings.TryLookup(L"environment").try_as<Windows::Foundation::Collections::ValueSet>();
_inheritCursor = unbox_prop_or<bool>(settings, L"inheritCursor", _inheritCursor);
_profileGuid = unbox_prop_or<winrt::guid>(settings, L"profileGuid", _profileGuid);
const auto& initialEnvironment{ unbox_prop_or<winrt::hstring>(settings, L"initialEnvironment", L"") };
_flags = PSEUDOCONSOLE_RESIZE_QUIRK;
// If we're using an existing buffer, we want the new connection
// to reuse the existing cursor. When not setting this flag, the
// PseudoConsole sends a clear screen VT code which our renderer
// interprets into making all the previous lines be outside the
// current viewport.
const auto inheritCursor = unbox_prop_or<bool>(settings, L"inheritCursor", false);
if (inheritCursor)
{
_flags |= PSEUDOCONSOLE_INHERIT_CURSOR;
}
const auto textMeasurement = unbox_prop_or<winrt::hstring>(settings, L"textMeasurement", winrt::hstring{});
if (!textMeasurement.empty())
{
if (textMeasurement == L"graphemes")
{
_flags |= PSEUDOCONSOLE_GLYPH_WIDTH_GRAPHEMES;
}
else if (textMeasurement == L"wcswidth")
{
_flags |= PSEUDOCONSOLE_GLYPH_WIDTH_WCSWIDTH;
}
else if (textMeasurement == L"console")
{
_flags |= PSEUDOCONSOLE_GLYPH_WIDTH_CONSOLE;
}
}
const auto& initialEnvironment{ unbox_prop_or<winrt::hstring>(settings, L"initialEnvironment", L"") };
const bool reloadEnvironmentVariables = unbox_prop_or<bool>(settings, L"reloadEnvironmentVariables", false);
if (reloadEnvironmentVariables)
@@ -318,19 +345,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
// handoff from an already-started PTY process.
if (!_inPipe)
{
DWORD flags = PSEUDOCONSOLE_RESIZE_QUIRK;
// If we're using an existing buffer, we want the new connection
// to reuse the existing cursor. When not setting this flag, the
// PseudoConsole sends a clear screen VT code which our renderer
// interprets into making all the previous lines be outside the
// current viewport.
if (_inheritCursor)
{
flags |= PSEUDOCONSOLE_INHERIT_CURSOR;
}
THROW_IF_FAILED(_CreatePseudoConsoleAndPipes(til::unwrap_coord_size(dimensions), flags, &_inPipe, &_outPipe, &_hPC));
THROW_IF_FAILED(_CreatePseudoConsoleAndPipes(til::unwrap_coord_size(dimensions), _flags, &_inPipe, &_outPipe, &_hPC));
if (_initialParentHwnd != 0)
{
@@ -431,12 +446,10 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
try
{
// GH#11556 - make sure to format the error code to this string as an UNSIGNED int
winrt::hstring exitText{ fmt::format(std::wstring_view{ RS_(L"ProcessExited") }, fmt::format(_errorFormat, status)) };
TerminalOutput.raise(L"\r\n");
TerminalOutput.raise(exitText);
TerminalOutput.raise(L"\r\n");
TerminalOutput.raise(RS_(L"CtrlDToClose"));
TerminalOutput.raise(L"\r\n");
const auto msg1 = fmt::format(std::wstring_view{ RS_(L"ProcessExited") }, fmt::format(_errorFormat, status));
const auto msg2 = RS_(L"CtrlDToClose");
const auto msg = fmt::format(FMT_COMPILE(L"\r\n{}\r\n{}\r\n"), msg1, msg2);
TerminalOutput.raise(msg);
}
CATCH_LOG();
}

View File

@@ -90,7 +90,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
til::u8state _u8State{};
std::wstring _u16Str{};
std::array<char, 4096> _buffer{};
bool _inheritCursor{ false };
DWORD _flags{ 0 };
til::env _initialEnv{};
guid _profileGuid{};

View File

@@ -13,15 +13,18 @@
#include <unicode.hpp>
#include <utils.hpp>
#include <WinUser.h>
//#include <winrt/Microsoft.Management.Deployment.h>
#include "EventArgs.h"
#include "../../renderer/atlas/AtlasEngine.h"
#include "../../renderer/base/renderer.hpp"
#include "../../renderer/uia/UiaRenderer.hpp"
#include "../../types/inc/CodepointWidthDetector.hpp"
#include "ControlCore.g.cpp"
#include "SelectionColor.g.cpp"
//using namespace winrt::Microsoft::Management::Deployment;
using namespace ::Microsoft::Console::Types;
using namespace ::Microsoft::Console::VirtualTerminal;
using namespace ::Microsoft::Terminal::Core;
@@ -71,6 +74,23 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_desiredFont{ DEFAULT_FONT_FACE, 0, DEFAULT_FONT_WEIGHT, DEFAULT_FONT_SIZE, CP_UTF8 },
_actualFont{ DEFAULT_FONT_FACE, 0, DEFAULT_FONT_WEIGHT, { 0, DEFAULT_FONT_SIZE }, CP_UTF8, false }
{
static const auto textMeasurementInit = [&]() {
TextMeasurementMode mode = TextMeasurementMode::Graphemes;
switch (settings.TextMeasurement())
{
case TextMeasurement::Wcswidth:
mode = TextMeasurementMode::Wcswidth;
break;
case TextMeasurement::Console:
mode = TextMeasurementMode::Console;
break;
default:
break;
}
CodepointWidthDetector::Singleton().Reset(mode);
return true;
}();
_settings = winrt::make_self<implementation::ControlSettings>(settings, unfocusedAppearance);
_terminal = std::make_shared<::Microsoft::Terminal::Core::Terminal>();
const auto lock = _terminal->LockForWriting();
@@ -110,6 +130,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
auto pfnCompletionsChanged = [=](auto&& menuJson, auto&& replaceLength) { _terminalCompletionsChanged(menuJson, replaceLength); };
_terminal->CompletionsChangedCallback(pfnCompletionsChanged);
auto pfnSearchMissingCommand = [this](auto&& PH1) { _terminalSearchMissingCommand(std::forward<decltype(PH1)>(PH1)); };
_terminal->SetSearchMissingCommandCallback(pfnSearchMissingCommand);
auto pfnClearQuickFix = [this] { _terminalClearQuickFix(); };
_terminal->SetClearQuickFixCallback(pfnClearQuickFix);
// MSFT 33353327: Initialize the renderer in the ctor instead of Initialize().
// We need the renderer to be ready to accept new engines before the SwapChainPanel is ready to go.
// If we wait, a screen reader may try to get the AutomationPeer (aka the UIA Engine), and we won't be able to attach
@@ -1609,6 +1635,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_midiAudio.PlayNote(reinterpret_cast<HWND>(_owningHwnd), noteNumber, velocity, std::chrono::duration_cast<std::chrono::milliseconds>(duration));
}
void ControlCore::_terminalSearchMissingCommand(std::wstring_view missingCommand)
{
SearchMissingCommand.raise(*this, make<implementation::SearchMissingCommandEventArgs>(hstring{ missingCommand }));
}
void ControlCore::_terminalClearQuickFix()
{
ClearQuickFix.raise(*this, nullptr);
}
bool ControlCore::HasSelection() const
{
const auto lock = _terminal->LockForReading();
@@ -2277,11 +2313,20 @@ namespace winrt::Microsoft::Terminal::Control::implementation
commands.pop_back();
}
auto context = winrt::make_self<CommandHistoryContext>(std::move(commands));
context->CurrentCommandline(trimmedCurrentCommand);
// TODO CARLOS: should we delete this after a new command is run? Or delete it after a suggestion is used? Or just after the next winget suggestion (current impl)?
// No clue which we should do. Thoughts?
context->QuickFixes(_cachedQuickFixes);
return *context;
}
void ControlCore::UpdateQuickFixes(const Windows::Foundation::Collections::IVector<hstring>& quickFixes)
{
_cachedQuickFixes = quickFixes;
}
Core::Scheme ControlCore::ColorScheme() const noexcept
{
Core::Scheme s;
@@ -2645,21 +2690,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// Select the region of text between [s.start, s.end), in buffer space
void ControlCore::_selectSpan(til::point_span s)
{
// s.end is an _exclusive_ point. We need an inclusive one. But
// decrement in bounds wants an inclusive one. If you pass an exclusive
// one, then it might assert at you for being out of bounds. So we also
// take care of the case that the end point is outside the viewport
// manually.
// s.end is an _exclusive_ point. We need an inclusive one.
const auto bufferSize{ _terminal->GetTextBuffer().GetSize() };
til::point inclusiveEnd = s.end;
if (s.end.x == bufferSize.Width())
{
inclusiveEnd = til::point{ std::max(0, s.end.x - 1), s.end.y };
}
else
{
bufferSize.DecrementInBounds(inclusiveEnd);
}
bufferSize.DecrementInBounds(inclusiveEnd);
_terminal->SelectNewRegion(s.start, inclusiveEnd);
_renderer->TriggerSelection();

View File

@@ -68,6 +68,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
til::property<Windows::Foundation::Collections::IVector<winrt::hstring>> History;
til::property<winrt::hstring> CurrentCommandline;
til::property<Windows::Foundation::Collections::IVector<winrt::hstring>> QuickFixes;
CommandHistoryContext(std::vector<winrt::hstring>&& history)
{
@@ -241,6 +242,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
hstring ReadEntireBuffer() const;
Control::CommandHistoryContext CommandHistory() const;
void UpdateQuickFixes(const Windows::Foundation::Collections::IVector<hstring>& quickFixes);
void AdjustOpacity(const float opacity, const bool relative);
@@ -283,6 +285,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
til::typed_event<IInspectable, Control::UpdateSelectionMarkersEventArgs> UpdateSelectionMarkers;
til::typed_event<IInspectable, Control::OpenHyperlinkEventArgs> OpenHyperlink;
til::typed_event<IInspectable, Control::CompletionsChangedEventArgs> CompletionsChanged;
til::typed_event<IInspectable, Control::SearchMissingCommandEventArgs> SearchMissingCommand;
til::typed_event<> ClearQuickFix;
til::typed_event<> CloseTerminalRequested;
til::typed_event<> RestartTerminalRequested;
@@ -352,6 +356,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
til::point _contextMenuBufferPosition{ 0, 0 };
Windows::Foundation::Collections::IVector<int32_t> _cachedSearchResultRows{ nullptr };
Windows::Foundation::Collections::IVector<hstring> _cachedQuickFixes{ nullptr };
void _setupDispatcherAndCallbacks();
bool _setFontSizeUnderLock(float fontSize);
@@ -375,6 +382,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void _terminalPlayMidiNote(const int noteNumber,
const int velocity,
const std::chrono::microseconds duration);
void _terminalSearchMissingCommand(std::wstring_view missingCommand);
void _terminalClearQuickFix();
winrt::fire_and_forget _terminalCompletionsChanged(std::wstring_view menuJson, unsigned int replaceLength);

View File

@@ -70,6 +70,7 @@ namespace Microsoft.Terminal.Control
{
IVector<String> History { get; };
String CurrentCommandline { get; };
IVector<String> QuickFixes { get; };
};
[default_interface] runtimeclass ControlCore : ICoreState
@@ -174,6 +175,8 @@ namespace Microsoft.Terminal.Control
event Windows.Foundation.TypedEventHandler<Object, Object> TaskbarProgressChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> RendererEnteredErrorState;
event Windows.Foundation.TypedEventHandler<Object, ShowWindowArgs> ShowWindowChanged;
event Windows.Foundation.TypedEventHandler<Object, SearchMissingCommandEventArgs> SearchMissingCommand;
event Windows.Foundation.TypedEventHandler<Object, Object> ClearQuickFix;
// These events are always called from the UI thread (bugs aside)
event Windows.Foundation.TypedEventHandler<Object, FontSizeChangedArgs> FontSizeChanged;

View File

@@ -18,3 +18,4 @@
#include "KeySentEventArgs.g.cpp"
#include "CharSentEventArgs.g.cpp"
#include "StringSentEventArgs.g.cpp"
#include "SearchMissingCommandEventArgs.g.cpp"

View File

@@ -18,6 +18,7 @@
#include "KeySentEventArgs.g.h"
#include "CharSentEventArgs.g.h"
#include "StringSentEventArgs.g.h"
#include "SearchMissingCommandEventArgs.g.h"
namespace winrt::Microsoft::Terminal::Control::implementation
{
@@ -211,6 +212,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
WINRT_PROPERTY(winrt::hstring, Text);
};
struct SearchMissingCommandEventArgs : public SearchMissingCommandEventArgsT<SearchMissingCommandEventArgs>
{
public:
SearchMissingCommandEventArgs(const winrt::hstring& missingCommand) :
_MissingCommand(missingCommand) {}
WINRT_PROPERTY(winrt::hstring, MissingCommand);
};
}
namespace winrt::Microsoft::Terminal::Control::factory_implementation

View File

@@ -18,6 +18,13 @@ namespace Microsoft.Terminal.Control
Direct3D11,
};
enum TextMeasurement
{
Graphemes,
Wcswidth,
Console,
};
runtimeclass FontSizeChangedArgs
{
Int32 Width { get; };
@@ -119,4 +126,9 @@ namespace Microsoft.Terminal.Control
{
String Text { get; };
}
runtimeclass SearchMissingCommandEventArgs
{
String MissingCommand { get; };
}
}

View File

@@ -356,7 +356,7 @@ HRESULT HwndTerminal::Refresh(const til::size windowSize, _Out_ til::size* dimen
_renderer->TriggerRedrawAll();
// Convert our new dimensions to characters
const auto viewInPixels = Viewport::FromDimensions(windowSize);
const auto viewInPixels = Viewport::FromDimensions({}, windowSize);
const auto vp = _renderEngine->GetViewportInCharacters(viewInPixels);
// Guard against resizing the window to 0 columns/rows, which the text buffer classes don't really support.
@@ -464,7 +464,7 @@ try
Viewport viewInPixels;
{
const auto viewInCharacters = Viewport::FromDimensions(dimensionsInCharacters);
const auto viewInCharacters = Viewport::FromDimensions({}, dimensionsInCharacters);
const auto lock = publicTerminal->_terminal->LockForReading();
viewInPixels = publicTerminal->_renderEngine->GetViewportInPixels(viewInCharacters);
}
@@ -491,7 +491,7 @@ try
{
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
const auto viewInPixels = Viewport::FromDimensions({ width, height });
const auto viewInPixels = Viewport::FromDimensions({}, { width, height });
const auto lock = publicTerminal->_terminal->LockForReading();
const auto viewInCharacters = publicTerminal->_renderEngine->GetViewportInCharacters(viewInPixels);

View File

@@ -62,6 +62,7 @@ namespace Microsoft.Terminal.Control
Microsoft.Terminal.Control.GraphicsAPI GraphicsAPI { get; };
Boolean DisablePartialInvalidation { get; };
Boolean SoftwareRendering { get; };
Microsoft.Terminal.Control.TextMeasurement TextMeasurement { get; };
Boolean ShowMarks { get; };
Boolean UseBackgroundImageForWindow { get; };
Boolean RightClickContextMenu { get; };

View File

@@ -296,6 +296,12 @@ Please either install the missing font or choose another one.</value>
<value>Select output</value>
<comment>The tooltip for a button for selecting all of a command's output</comment>
</data>
<data name="QuickFixButton.ToolTipService.ToolTip" xml:space="preserve">
<value>Quick fix</value>
</data>
<data name="QuickFixButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Quick fix</value>
</data>
<data name="SessionRestoreMessage" xml:space="preserve">
<value>Restored</value>
<comment>"Restored" as in "This content was restored"</comment>

View File

@@ -155,12 +155,18 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// search box remains in Visible state (though not really *visible*) during the
// first load. So, we only need to apply this check here (after checking that
// we're done initializing).
if (Visibility() == Visibility::Visible)
if (IsOpen())
{
callback();
return;
}
// Stop ongoing close animation if any
if (CloseAnimation().GetCurrentState() == Media::Animation::ClockState::Active)
{
CloseAnimation().Stop();
}
VisualStateManager::GoToState(*this, L"Opened", false);
// Call the callback only after we're in Opened state. Setting focus
@@ -196,6 +202,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
}
bool SearchBoxControl::IsOpen()
{
return Visibility() == Visibility::Visible && CloseAnimation().GetCurrentState() != Media::Animation::ClockState::Active;
}
winrt::hstring SearchBoxControl::Text()
{
return TextBox().Text();

View File

@@ -34,6 +34,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void TextBoxKeyDown(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Windows::UI::Xaml::Input::KeyRoutedEventArgs& e);
void Open(std::function<void()> callback);
void Close();
bool IsOpen();
winrt::hstring Text();
bool GoForward();

View File

@@ -40,6 +40,9 @@ constexpr const auto UpdatePatternLocationsInterval = std::chrono::milliseconds(
// The minimum delay between emitting warning bells
constexpr const auto TerminalWarningBellInterval = std::chrono::milliseconds(1000);
constexpr std::wstring_view StateNormal{ L"Normal" };
constexpr std::wstring_view StateCollapsed{ L"Collapsed" };
DEFINE_ENUM_FLAG_OPERATORS(winrt::Microsoft::Terminal::Control::CopyFormat);
DEFINE_ENUM_FLAG_OPERATORS(winrt::Microsoft::Terminal::Control::MouseButtonState);
@@ -220,9 +223,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_revokers.CloseTerminalRequested = _core.CloseTerminalRequested(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleCloseTerminalRequested });
_revokers.CompletionsChanged = _core.CompletionsChanged(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleCompletionsChanged });
_revokers.RestartTerminalRequested = _core.RestartTerminalRequested(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleRestartTerminalRequested });
_revokers.SearchMissingCommand = _core.SearchMissingCommand(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleSearchMissingCommand });
_revokers.PasteFromClipboard = _interactivity.PasteFromClipboard(winrt::auto_revoke, { get_weak(), &TermControl::_bubblePasteFromClipboard });
_revokers.ClearQuickFix = _core.ClearQuickFix(winrt::auto_revoke, { get_weak(), &TermControl::_clearQuickFix });
// Initialize the terminal only once the swapchainpanel is loaded - that
// way, we'll be able to query the real pixel size it got on layout
_layoutUpdatedRevoker = SwapChainPanel().LayoutUpdated(winrt::auto_revoke, [this](auto /*s*/, auto /*e*/) {
@@ -332,6 +338,29 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
}
});
if (Feature_QuickFix::IsEnabled())
{
auto quickFixBtn{ QuickFixButton() };
quickFixBtn.PointerEntered({ get_weak(), &TermControl::QuickFixButton_PointerEntered });
quickFixBtn.PointerExited({ get_weak(), &TermControl::QuickFixButton_PointerExited });
}
}
void TermControl::QuickFixButton_PointerEntered(const IInspectable& /*sender*/, const PointerRoutedEventArgs& /*e*/)
{
if (!_IsClosing() && _quickFixButtonCollapsible)
{
VisualStateManager::GoToState(*this, StateNormal, false);
}
}
void TermControl::QuickFixButton_PointerExited(const IInspectable& /*sender*/, const PointerRoutedEventArgs& /*e*/)
{
if (!_IsClosing() && _quickFixButtonCollapsible)
{
VisualStateManager::GoToState(*this, StateCollapsed, false);
}
}
// Function Description:
@@ -493,7 +522,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
}
if (_searchBox && _searchBox->Visibility() == Visibility::Visible)
if (_searchBox && _searchBox->IsOpen())
{
const auto core = winrt::get_self<ControlCore>(_core);
const auto& searchMatches = core->SearchResultRows();
@@ -538,6 +567,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// but since code paths differ, extra work is required to ensure correctness.
if (!_core.HasMultiLineSelection())
{
_core.SnapSearchResultToSelection(true);
const auto selectedLine{ _core.SelectedText(true) };
_searchBox->PopulateTextbox(selectedLine);
}
@@ -554,13 +584,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
}
// This is called when a Find Next/Previous Match action is triggered.
void TermControl::SearchMatch(const bool goForward)
{
if (_IsClosing())
{
return;
}
if (!_searchBox || _searchBox->Visibility() != Visibility::Visible)
if (!_searchBox || !_searchBox->IsOpen())
{
CreateSearchBoxControl();
}
@@ -602,7 +633,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
// Method Description:
// - The handler for the "search criteria changed" event. Clears selection and initiates a new search.
// - The handler for the "search criteria changed" event. Initiates a new search.
// Arguments:
// - text: the text to search
// - goForward: indicates whether the search should be performed forward (if set to true) or backward
@@ -616,7 +647,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
if (_searchBox && _searchBox->Visibility() == Visibility::Visible)
{
_handleSearchResults(_core.Search(text, goForward, caseSensitive, regularExpression, false));
// We only want to update the search results based on the new text. Set
// `resetOnly` to true so we don't accidentally update the current match index.
const auto result = _core.Search(text, goForward, caseSensitive, regularExpression, true);
_handleSearchResults(result);
}
}
@@ -634,6 +668,19 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_searchBox->Close();
_core.ClearSearch();
// Clear search highlights scroll marks (by triggering an update after closing the search box)
if (_showMarksInScrollbar)
{
const auto scrollBar = ScrollBar();
ScrollBarUpdate update{
.newValue = scrollBar.Value(),
.newMaximum = scrollBar.Maximum(),
.newMinimum = scrollBar.Minimum(),
.newViewportSize = scrollBar.ViewportSize(),
};
_updateScrollBar->Run(update);
}
// Set focus back to terminal control
this->Focus(FocusState::Programmatic);
}
@@ -808,6 +855,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// When we hot reload the settings, the core will send us a scrollbar
// update. If we enabled scrollbar marks, then great, when we handle
// that message, we'll redraw them.
if (Feature_QuickFix::IsEnabled())
{
// update the position of the quick fix menu (in case we changed the padding)
ShowQuickFixMenu();
}
}
// Method Description:
@@ -2313,6 +2366,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
_updateSelectionMarkers(nullptr, winrt::make<UpdateSelectionMarkersEventArgs>(false));
}
ShowQuickFixMenu();
}
hstring TermControl::Title()
@@ -3472,7 +3527,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const Control::FontSizeChangedArgs& args)
{
// scale the selection markers to be the size of a cell
auto scaleMarker = [args, dpiScale{ SwapChainPanel().CompositionScaleX() }](const Windows::UI::Xaml::Shapes::Path& shape) {
const auto dpiScale = SwapChainPanel().CompositionScaleX();
auto scaleMarker = [args, &dpiScale](const Windows::UI::Xaml::Shapes::Path& shape) {
// The selection markers were designed to be 5x14 in size,
// so use those dimensions below for the scaling
const auto scaleX = args.Width() / 5.0 / dpiScale;
@@ -3488,6 +3544,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
};
scaleMarker(SelectionStartMarker());
scaleMarker(SelectionEndMarker());
if (Feature_QuickFix::IsEnabled())
{
auto quickFixBtn = QuickFixButton();
quickFixBtn.Height(args.Height() / dpiScale);
QuickFixIcon().FontSize(std::min(static_cast<double>(args.Width() / dpiScale), GetPadding().Left));
ShowQuickFixMenu();
}
}
void TermControl::_coreRaisedNotice(const IInspectable& /*sender*/,
@@ -3556,6 +3620,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return _core.CommandHistory();
}
void TermControl::UpdateWinGetSuggestions(Windows::Foundation::Collections::IVector<hstring> suggestions)
{
get_self<ControlCore>(_core)->UpdateQuickFixes(suggestions);
}
Core::Scheme TermControl::ColorScheme() const noexcept
{
return _core.ColorScheme();
@@ -3595,7 +3664,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void TermControl::_refreshSearch()
{
if (!_searchBox || _searchBox->Visibility() != Visibility::Visible)
if (!_searchBox || !_searchBox->IsOpen())
{
return;
}
@@ -3619,7 +3688,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return;
}
_searchBox->SetStatus(results.TotalMatches, results.CurrentMatch, results.SearchRegexInvalid);
// Only show status when we have a search term
if (_searchBox->Text().empty())
{
_searchBox->ClearStatus();
}
else
{
_searchBox->SetStatus(results.TotalMatches, results.CurrentMatch, results.SearchRegexInvalid);
}
if (results.SearchInvalidated)
{
@@ -3770,6 +3847,67 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_showContextMenuAt(_toControlOrigin(cursorPos));
}
double TermControl::CalculateQuickFixButtonWidth()
{
return GetPadding().Left;
}
double TermControl::CalculateQuickFixButtonCollapsedWidth()
{
return GetPadding().Left / 3.0;
}
void TermControl::ShowQuickFixMenu()
{
if (!_quickFixesAvailable)
{
QuickFixButton().Visibility(Visibility::Collapsed);
return;
}
auto quickFixBtn{ QuickFixButton() };
// If the gutter is narrow, display the collapsed version
const auto& termPadding = GetPadding();
_quickFixButtonCollapsible = termPadding.Left < CharacterDimensions().Width;
VisualStateManager::GoToState(*this, !_quickFixButtonCollapsible ? StateNormal : StateCollapsed, false);
const auto rd = get_self<ControlCore>(_core)->GetRenderData();
rd->LockConsole();
const auto viewportBufferPosition = rd->GetViewport();
const auto cursorBufferPosition = rd->GetCursorPosition();
rd->UnlockConsole();
if (cursorBufferPosition.y < viewportBufferPosition.Top() || cursorBufferPosition.y > viewportBufferPosition.BottomExclusive())
{
quickFixBtn.Visibility(Visibility::Collapsed);
return;
}
// draw the button in the gutter
const auto& cursorPosInDips = CursorPositionInDips();
Controls::Canvas::SetLeft(quickFixBtn, -termPadding.Left);
Controls::Canvas::SetTop(quickFixBtn, cursorPosInDips.Y);
quickFixBtn.Visibility(Visibility::Visible);
}
void TermControl::_bubbleSearchMissingCommand(const IInspectable& /*sender*/, const Control::SearchMissingCommandEventArgs& args)
{
_quickFixesAvailable = true;
SearchMissingCommand.raise(*this, args);
}
void TermControl::_clearQuickFix(const IInspectable& /*sender*/, const IInspectable& /*args*/)
{
_quickFixesAvailable = false;
ShowQuickFixMenu();
}
void TermControl::ClearQuickFix()
{
_clearQuickFix(nullptr, nullptr);
}
void TermControl::_PasteCommandHandler(const IInspectable& /*sender*/,
const IInspectable& /*args*/)
{

View File

@@ -75,6 +75,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
float SnapDimensionToGrid(const bool widthOrHeight, const float dimension);
Windows::Foundation::Point CursorPositionInDips();
double CalculateQuickFixButtonWidth();
double CalculateQuickFixButtonCollapsedWidth();
void WindowVisibilityChanged(const bool showOrHide);
@@ -167,6 +169,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
hstring ReadEntireBuffer() const;
Control::CommandHistoryContext CommandHistory() const;
void UpdateWinGetSuggestions(Windows::Foundation::Collections::IVector<hstring> suggestions);
winrt::Microsoft::Terminal::Core::Scheme ColorScheme() const noexcept;
void ColorScheme(const winrt::Microsoft::Terminal::Core::Scheme& scheme) const noexcept;
@@ -178,6 +181,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void RawWriteString(const winrt::hstring& text);
void ShowContextMenu();
void ShowQuickFixMenu();
void ClearQuickFix();
void Detach();
@@ -202,17 +207,18 @@ namespace winrt::Microsoft::Terminal::Control::implementation
til::typed_event<IInspectable, Control::KeySentEventArgs> KeySent;
til::typed_event<IInspectable, Control::CharSentEventArgs> CharSent;
til::typed_event<IInspectable, Control::StringSentEventArgs> StringSent;
til::typed_event<IInspectable, Control::SearchMissingCommandEventArgs> SearchMissingCommand;
// UNDER NO CIRCUMSTANCES SHOULD YOU ADD A (PROJECTED_)FORWARDED_TYPED_EVENT HERE
// Those attach the handler to the core directly, and will explode if
// the core ever gets detached & reattached to another window.
BUBBLED_FORWARDED_TYPED_EVENT(TitleChanged, IInspectable, Control::TitleChangedEventArgs);
BUBBLED_FORWARDED_TYPED_EVENT(TabColorChanged, IInspectable, IInspectable);
BUBBLED_FORWARDED_TYPED_EVENT(SetTaskbarProgress, IInspectable, IInspectable);
BUBBLED_FORWARDED_TYPED_EVENT(ConnectionStateChanged, IInspectable, IInspectable);
BUBBLED_FORWARDED_TYPED_EVENT(ShowWindowChanged, IInspectable, Control::ShowWindowArgs);
BUBBLED_FORWARDED_TYPED_EVENT(CloseTerminalRequested, IInspectable, IInspectable);
BUBBLED_FORWARDED_TYPED_EVENT(CompletionsChanged, IInspectable, Control::CompletionsChangedEventArgs);
BUBBLED_FORWARDED_TYPED_EVENT(TitleChanged, IInspectable, Control::TitleChangedEventArgs);
BUBBLED_FORWARDED_TYPED_EVENT(TabColorChanged, IInspectable, IInspectable);
BUBBLED_FORWARDED_TYPED_EVENT(SetTaskbarProgress, IInspectable, IInspectable);
BUBBLED_FORWARDED_TYPED_EVENT(ConnectionStateChanged, IInspectable, IInspectable);
BUBBLED_FORWARDED_TYPED_EVENT(ShowWindowChanged, IInspectable, Control::ShowWindowArgs);
BUBBLED_FORWARDED_TYPED_EVENT(CloseTerminalRequested, IInspectable, IInspectable);
BUBBLED_FORWARDED_TYPED_EVENT(CompletionsChanged, IInspectable, Control::CompletionsChangedEventArgs);
BUBBLED_FORWARDED_TYPED_EVENT(RestartTerminalRequested, IInspectable, IInspectable);
BUBBLED_FORWARDED_TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs);
@@ -242,6 +248,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
bool _closing{ false };
bool _focused{ false };
bool _initializedTerminal{ false };
bool _quickFixButtonCollapsible{ false };
bool _quickFixesAvailable{ false };
std::shared_ptr<ThrottledFuncLeading> _playWarningBell;
@@ -332,6 +340,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void _MouseWheelHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e);
void _ScrollbarChangeHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Controls::Primitives::RangeBaseValueChangedEventArgs& e);
void QuickFixButton_PointerEntered(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e);
void QuickFixButton_PointerExited(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e);
void _GotFocusHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e);
void _LostFocusHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e);
@@ -394,6 +405,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void _contextMenuHandler(IInspectable sender, Control::ContextMenuRequestedEventArgs args);
void _showContextMenuAt(const til::point& controlRelativePos);
void _bubbleSearchMissingCommand(const IInspectable& sender, const Control::SearchMissingCommandEventArgs& args);
void _clearQuickFix(const IInspectable& sender, const IInspectable& args);
void _PasteCommandHandler(const IInspectable& sender, const IInspectable& args);
void _CopyCommandHandler(const IInspectable& sender, const IInspectable& args);
void _SearchCommandHandler(const IInspectable& sender, const IInspectable& args);
@@ -423,6 +437,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
Control::ControlCore::CloseTerminalRequested_revoker CloseTerminalRequested;
Control::ControlCore::CompletionsChanged_revoker CompletionsChanged;
Control::ControlCore::RestartTerminalRequested_revoker RestartTerminalRequested;
Control::ControlCore::SearchMissingCommand_revoker SearchMissingCommand;
Control::ControlCore::ClearQuickFix_revoker ClearQuickFix;
// These are set up in _InitializeTerminal
Control::ControlCore::RendererWarning_revoker RendererWarning;

View File

@@ -69,10 +69,12 @@ namespace Microsoft.Terminal.Control
event Windows.Foundation.TypedEventHandler<Object, KeySentEventArgs> KeySent;
event Windows.Foundation.TypedEventHandler<Object, CharSentEventArgs> CharSent;
event Windows.Foundation.TypedEventHandler<Object, StringSentEventArgs> StringSent;
event Windows.Foundation.TypedEventHandler<Object, SearchMissingCommandEventArgs> SearchMissingCommand;
Microsoft.UI.Xaml.Controls.CommandBarFlyout ContextMenu { get; };
Microsoft.UI.Xaml.Controls.CommandBarFlyout SelectionContextMenu { get; };
Windows.UI.Xaml.Controls.MenuFlyout QuickFixMenu { get; };
event Windows.Foundation.TypedEventHandler<TermControl, Windows.UI.Xaml.RoutedEventArgs> Initialized;
// This is an event handler forwarder for the underlying connection.
@@ -125,6 +127,7 @@ namespace Microsoft.Terminal.Control
String ReadEntireBuffer();
CommandHistoryContext CommandHistory();
void UpdateWinGetSuggestions(Windows.Foundation.Collections.IVector<String> suggestions);
void AdjustOpacity(Single Opacity, Boolean relative);
@@ -141,8 +144,12 @@ namespace Microsoft.Terminal.Control
void ColorSelection(SelectionColor fg, SelectionColor bg, Microsoft.Terminal.Core.MatchMode matchMode);
Windows.Foundation.Point CursorPositionInDips { get; };
Double CalculateQuickFixButtonWidth { get; };
Double CalculateQuickFixButtonCollapsedWidth { get; };
void ShowContextMenu();
void ShowQuickFixMenu();
void ClearQuickFix();
void Detach();
}

View File

@@ -1279,6 +1279,28 @@
</ToolTip>
</ToolTipService.ToolTip>
</Border>
<Button x:Name="QuickFixButton"
x:Uid="QuickFixButton"
Width="{x:Bind CalculateQuickFixButtonWidth}"
Padding="0"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
AllowFocusOnInteraction="False"
CornerRadius="0,5,5,0"
Style="{StaticResource AccentButtonStyle}"
Visibility="Collapsed">
<Button.Content>
<FontIcon x:Name="QuickFixIcon"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
Glyph="&#xEA80;" />
</Button.Content>
<Button.Flyout>
<MenuFlyout x:Name="QuickFixMenu" />
</Button.Flyout>
</Button>
</Canvas>
<Canvas x:Name="SelectionCanvas"
@@ -1353,6 +1375,18 @@
</Border>
</Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="QuickFixButtonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Collapsed">
<VisualState.Setters>
<Setter Target="QuickFixButton.Width" Value="{x:Bind CalculateQuickFixButtonCollapsedWidth}" />
<Setter Target="QuickFixButton.Background" Value="{ThemeResource SystemAccentColor}" />
<Setter Target="QuickFixIcon.Visibility" Value="Collapsed" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</UserControl>

View File

@@ -171,6 +171,13 @@
<Private>false</Private>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Reference Include="$(OpenConsoleDir)src\cascadia\TerminalControl\Microsoft.Management.Deployment.winmd">
<IsWinMDFile>true</IsWinMDFile>
<Private>false</Private>
<CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
</Reference>
</ItemGroup>
<!-- ====================== Compiler & Linker Flags ===================== -->
<ItemDefinitionGroup>
<ClCompile>

View File

@@ -699,34 +699,44 @@ TerminalInput::OutputType Terminal::SendCharEvent(const wchar_t ch, const WORD s
vkey = _VirtualKeyFromCharacter(ch);
}
// GH#1527: When the user has auto mark prompts enabled, we're going to try
// and heuristically detect if this was the line the prompt was on.
// * If the key was an Enter keypress (Terminal.app also marks ^C keypresses
// as prompts. That's omitted for now.)
// * AND we're not in the alt buffer
//
// Then treat this line like it's a prompt mark.
if (_autoMarkPrompts && vkey == VK_RETURN && !_inAltBuffer())
if (vkey == VK_RETURN && !_inAltBuffer())
{
// We need to be a little tricky here, to try and support folks that are
// auto-marking prompts, but don't necessarily have the rest of shell
// integration enabled.
//
// We'll set the current attributes to Output, so that the output after
// here is marked as the command output. But we also need to make sure
// that a mark was started.
// We can't just check if the current row has a mark - there could be a
// multiline prompt.
//
// (TextBuffer::_createPromptMarkIfNeeded does that work for us)
const bool createdMark = _activeBuffer().StartOutput();
if (createdMark)
// Treat VK_RETURN as a new prompt,
// so we should clear the quick fix UI if it's visible.
if (_pfnClearQuickFix)
{
_activeBuffer().ManuallyMarkRowAsPrompt(_activeBuffer().GetCursor().GetPosition().y);
_pfnClearQuickFix();
}
// This changed the scrollbar marks - raise a notification to update them
_NotifyScrollEvent();
// GH#1527: When the user has auto mark prompts enabled, we're going to try
// and heuristically detect if this was the line the prompt was on.
// * If the key was an Enter keypress (Terminal.app also marks ^C keypresses
// as prompts. That's omitted for now.)
// * AND we're not in the alt buffer
//
// Then treat this line like it's a prompt mark.
if (_autoMarkPrompts)
{
// We need to be a little tricky here, to try and support folks that are
// auto-marking prompts, but don't necessarily have the rest of shell
// integration enabled.
//
// We'll set the current attributes to Output, so that the output after
// here is marked as the command output. But we also need to make sure
// that a mark was started.
// We can't just check if the current row has a mark - there could be a
// multiline prompt.
//
// (TextBuffer::_createPromptMarkIfNeeded does that work for us)
const bool createdMark = _activeBuffer().StartOutput();
if (createdMark)
{
_activeBuffer().ManuallyMarkRowAsPrompt(_activeBuffer().GetCursor().GetPosition().y);
// This changed the scrollbar marks - raise a notification to update them
_NotifyScrollEvent();
}
}
}
@@ -961,7 +971,7 @@ Viewport Terminal::_GetMutableViewport() const noexcept
// GH#3493: if we're in the alt buffer, then it's possible that the mutable
// viewport's size hasn't been updated yet. In that case, use the
// temporarily stashed _altBufferSize instead.
return _inAltBuffer() ? Viewport::FromDimensions(_altBufferSize) :
return _inAltBuffer() ? Viewport::FromDimensions({}, _altBufferSize) :
_mutableViewport;
}
@@ -1227,6 +1237,16 @@ void Microsoft::Terminal::Core::Terminal::CompletionsChangedCallback(std::functi
_pfnCompletionsChanged.swap(pfn);
}
void Microsoft::Terminal::Core::Terminal::SetSearchMissingCommandCallback(std::function<void(std::wstring_view)> pfn) noexcept
{
_pfnSearchMissingCommand.swap(pfn);
}
void Microsoft::Terminal::Core::Terminal::SetClearQuickFixCallback(std::function<void()> pfn) noexcept
{
_pfnClearQuickFix.swap(pfn);
}
// Method Description:
// - Stores the search highlighted regions in the terminal
void Terminal::SetSearchHighlights(const std::vector<til::point_span>& highlights) noexcept

View File

@@ -157,6 +157,8 @@ public:
void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) override;
void SearchMissingCommand(const std::wstring_view command) override;
#pragma endregion
void ClearMark();
@@ -229,6 +231,8 @@ public:
void SetShowWindowCallback(std::function<void(bool)> pfn) noexcept;
void SetPlayMidiNoteCallback(std::function<void(const int, const int, const std::chrono::microseconds)> pfn) noexcept;
void CompletionsChangedCallback(std::function<void(std::wstring_view, unsigned int)> pfn) noexcept;
void SetSearchMissingCommandCallback(std::function<void(std::wstring_view)> pfn) noexcept;
void SetClearQuickFixCallback(std::function<void()> pfn) noexcept;
void SetSearchHighlights(const std::vector<til::point_span>& highlights) noexcept;
void SetSearchHighlightFocused(const size_t focusedIdx);
@@ -338,6 +342,8 @@ private:
std::function<void(bool)> _pfnShowWindowChanged;
std::function<void(const int, const int, const std::chrono::microseconds)> _pfnPlayMidiNote;
std::function<void(std::wstring_view, unsigned int)> _pfnCompletionsChanged;
std::function<void(std::wstring_view)> _pfnSearchMissingCommand;
std::function<void()> _pfnClearQuickFix;
RenderSettings _renderSettings;
std::unique_ptr<::Microsoft::Console::VirtualTerminal::StateMachine> _stateMachine;

View File

@@ -333,6 +333,14 @@ void Terminal::InvokeCompletions(std::wstring_view menuJson, unsigned int replac
}
}
void Terminal::SearchMissingCommand(const std::wstring_view command)
{
if (_pfnSearchMissingCommand)
{
_pfnSearchMissingCommand(command);
}
}
void Terminal::NotifyBufferRotation(const int delta)
{
// Update our selection, so it doesn't move as the buffer is cycled

View File

@@ -495,7 +495,7 @@ void Terminal::SelectHyperlink(const SearchDirection dir)
searchEnd = { bufferSize.RightInclusive(), searchStart.y - 1 };
searchStart = { bufferSize.Left(), std::max(searchStart.y - viewportHeight, bufferSize.Top()) };
}
searchArea = Viewport::FromDimensions(searchStart, searchEnd.x + 1, searchEnd.y + 1);
searchArea = Viewport::FromDimensions(searchStart, { searchEnd.x + 1, searchEnd.y + 1 });
const til::point bufferStart{ bufferSize.Origin() };
const til::point bufferEnd{ bufferSize.RightInclusive(), ViewEndIndex() };
@@ -516,7 +516,7 @@ void Terminal::SelectHyperlink(const SearchDirection dir)
searchEnd.y -= 1;
searchStart.y = std::max(searchEnd.y - viewportHeight, bufferSize.Top());
}
searchArea = Viewport::FromDimensions(searchStart, searchEnd.x + 1, searchEnd.y + 1);
searchArea = Viewport::FromDimensions(searchStart, { searchEnd.x + 1, searchEnd.y + 1 });
}
}

View File

@@ -699,17 +699,23 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
}
}
if (!isMicaAvailable)
{
return;
}
const auto& theme = _settingsSource.GlobalSettings().CurrentTheme();
const auto& requestedTheme = _settingsSource.GlobalSettings().CurrentTheme().RequestedTheme();
const bool hasThemeForSettings{ theme.Settings() != nullptr };
const auto& appTheme = theme.RequestedTheme();
const auto& requestedTheme = (hasThemeForSettings) ? theme.Settings().RequestedTheme() : appTheme;
RequestedTheme(requestedTheme);
const auto bgKey = (theme.Window() != nullptr && theme.Window().UseMica()) ?
// Mica gets it's appearance from the app's theme, not necessarily the
// Page's theme. In the case of dark app, light settings, mica will be a
// dark color, and the text will also be dark, making the UI _very_ hard
// to read. (and similarly in the inverse situation).
//
// To mitigate this, don't set the transparent background in the case
// that our theme is different than the app's.
const bool actuallyUseMica = isMicaAvailable && (appTheme == requestedTheme);
const auto bgKey = (theme.Window() != nullptr && theme.Window().UseMica()) && actuallyUseMica ?
L"SettingsPageMicaBackground" :
L"SettingsPageBackground";

View File

@@ -41,5 +41,13 @@
<ToggleSwitch IsOn="{x:Bind ViewModel.SoftwareRendering, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingContainer>
<local:SettingContainer x:Uid="Globals_TextMeasurement">
<ComboBox AutomationProperties.AccessibilityView="Content"
ItemTemplate="{StaticResource EnumComboBoxTemplate}"
ItemsSource="{x:Bind ViewModel.TextMeasurementList}"
SelectedItem="{x:Bind ViewModel.CurrentTextMeasurement, Mode=TwoWay}"
Style="{StaticResource ComboBoxSettingStyle}" />
</local:SettingContainer>
</StackPanel>
</Page>

View File

@@ -17,5 +17,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
_settings{ std::move(settings) }
{
INITIALIZE_BINDABLE_ENUM_SETTING(GraphicsAPI, GraphicsAPI, winrt::Microsoft::Terminal::Control::GraphicsAPI, L"Globals_GraphicsAPI_", L"Text");
INITIALIZE_BINDABLE_ENUM_SETTING(TextMeasurement, TextMeasurement, winrt::Microsoft::Terminal::Control::TextMeasurement, L"Globals_TextMeasurement_", L"Text");
}
}

View File

@@ -16,6 +16,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
GETSET_BINDABLE_ENUM_SETTING(GraphicsAPI, winrt::Microsoft::Terminal::Control::GraphicsAPI, _settings.GlobalSettings().GraphicsAPI);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_settings.GlobalSettings(), DisablePartialInvalidation);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_settings.GlobalSettings(), SoftwareRendering);
GETSET_BINDABLE_ENUM_SETTING(TextMeasurement, winrt::Microsoft::Terminal::Control::TextMeasurement, _settings.GlobalSettings().TextMeasurement);
private:
Model::CascadiaSettings _settings{ nullptr };

View File

@@ -15,5 +15,7 @@ namespace Microsoft.Terminal.Settings.Editor
Windows.Foundation.Collections.IObservableVector<Microsoft.Terminal.Settings.Editor.EnumEntry> GraphicsAPIList { get; };
PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, DisablePartialInvalidation);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, SoftwareRendering);
IInspectable CurrentTextMeasurement;
Windows.Foundation.Collections.IObservableVector<Microsoft.Terminal.Settings.Editor.EnumEntry> TextMeasurementList { get; };
}
}

View File

@@ -342,6 +342,24 @@
<value>When enabled, the terminal will use a software rasterizer (WARP). This setting should be left disabled under almost all circumstances.</value>
<comment>{Locked="WARP"} WARP is the "Windows Advanced Rasterization Platform".</comment>
</data>
<data name="Globals_TextMeasurement.Header" xml:space="preserve">
<value>Text measurement mode</value>
<comment>This text is shown next to a list of choices.</comment>
</data>
<data name="Globals_TextMeasurement.HelpText" xml:space="preserve">
<value>This changes the way incoming text is grouped into cells. The "Grapheme clusters" option is the most modern and Unicode-correct way to do so, while "wcswidth" is a common approach on UNIX, and "Windows Console" replicates the way it used to work on Windows. Changing this setting requires a restart of Windows Terminal and it only applies to applications launched from within it.</value>
</data>
<data name="Globals_TextMeasurement_Graphemes.Text" xml:space="preserve">
<value>Grapheme clusters</value>
<comment>The default choice between multiple graphics APIs.</comment>
</data>
<data name="Globals_TextMeasurement_Wcswidth.Text" xml:space="preserve">
<value>wcswidth</value>
<comment>{Locked="wcswidth"}</comment>
</data>
<data name="Globals_TextMeasurement_Console.Text" xml:space="preserve">
<value>Windows Console</value>
</data>
<data name="Globals_InitialCols.Text" xml:space="preserve">
<value>Columns</value>
<comment>Header for a control to choose the number of columns in the terminal's text grid.</comment>

View File

@@ -52,6 +52,7 @@ static constexpr std::string_view SwitchToTabKey{ "switchToTab" };
static constexpr std::string_view TabSearchKey{ "tabSearch" };
static constexpr std::string_view ToggleAlwaysOnTopKey{ "toggleAlwaysOnTop" };
static constexpr std::string_view ToggleCommandPaletteKey{ "commandPalette" };
static constexpr std::string_view SaveTaskKey{ "experimental.saveTask" };
static constexpr std::string_view SuggestionsKey{ "showSuggestions" };
static constexpr std::string_view ToggleFocusModeKey{ "toggleFocusMode" };
static constexpr std::string_view SetFocusModeKey{ "setFocusMode" };
@@ -389,6 +390,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{ ShortcutAction::TabSearch, RS_(L"TabSearchCommandKey") },
{ ShortcutAction::ToggleAlwaysOnTop, RS_(L"ToggleAlwaysOnTopCommandKey") },
{ ShortcutAction::ToggleCommandPalette, MustGenerate },
{ ShortcutAction::SaveTask, MustGenerate },
{ ShortcutAction::Suggestions, MustGenerate },
{ ShortcutAction::ToggleFocusMode, RS_(L"ToggleFocusModeCommandKey") },
{ ShortcutAction::SetFocusMode, MustGenerate },

View File

@@ -33,6 +33,7 @@
#include "ScrollToMarkArgs.g.cpp"
#include "AddMarkArgs.g.cpp"
#include "FindMatchArgs.g.cpp"
#include "SaveTaskArgs.g.cpp"
#include "ToggleCommandPaletteArgs.g.cpp"
#include "SuggestionsArgs.g.cpp"
#include "NewWindowArgs.g.cpp"
@@ -947,6 +948,29 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
}
}
winrt::hstring SaveTaskArgs::GenerateName() const
{
if (Feature_SaveTask::IsEnabled())
{
std::wstringstream ss;
ss << RS_(L"SaveActionNamePrefix").c_str() << L" commandline: " << Commandline().c_str();
if (!Name().empty())
{
ss << L", name: " << Name().c_str();
}
if (!KeyChord().empty())
{
ss << L", keyChord " << KeyChord().c_str();
}
return winrt::hstring{ ss.str() };
}
return L"";
}
static winrt::hstring _FormatColorString(const Control::SelectionColor& selectionColor)
{
if (!selectionColor)

View File

@@ -34,6 +34,7 @@
#include "ScrollToMarkArgs.g.h"
#include "AddMarkArgs.g.h"
#include "MoveTabArgs.g.h"
#include "SaveTaskArgs.g.h"
#include "ToggleCommandPaletteArgs.g.h"
#include "SuggestionsArgs.g.h"
#include "FindMatchArgs.g.h"
@@ -215,6 +216,12 @@ protected: \
#define TOGGLE_COMMAND_PALETTE_ARGS(X) \
X(CommandPaletteLaunchMode, LaunchMode, "launchMode", false, CommandPaletteLaunchMode::Action)
////////////////////////////////////////////////////////////////////////////////
#define SAVE_TASK_ARGS(X) \
X(winrt::hstring, Name, "name", false, L"") \
X(winrt::hstring, Commandline, "commandline", args->Commandline().empty(), L"") \
X(winrt::hstring, KeyChord, "keyChord", false, L"")
////////////////////////////////////////////////////////////////////////////////
#define SUGGESTIONS_ARGS(X) \
X(SuggestionsSource, Source, "source", false, SuggestionsSource::Tasks) \
@@ -819,6 +826,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
ACTION_ARGS_STRUCT(ToggleCommandPaletteArgs, TOGGLE_COMMAND_PALETTE_ARGS);
ACTION_ARGS_STRUCT(SaveTaskArgs, SAVE_TASK_ARGS);
ACTION_ARGS_STRUCT(SuggestionsArgs, SUGGESTIONS_ARGS);
ACTION_ARGS_STRUCT(FindMatchArgs, FIND_MATCH_ARGS);
@@ -928,6 +937,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);
@@ -940,6 +950,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
BASIC_FACTORY(CloseTabArgs);
BASIC_FACTORY(MoveTabArgs);
BASIC_FACTORY(OpenSettingsArgs);
BASIC_FACTORY(SaveTaskArgs);
BASIC_FACTORY(FindMatchArgs);
BASIC_FACTORY(NewWindowArgs);
BASIC_FACTORY(FocusPaneArgs);

View File

@@ -119,6 +119,7 @@ namespace Microsoft.Terminal.Settings.Model
Tasks = 0x1,
CommandHistory = 0x2,
DirectoryHistory = 0x4,
QuickFixes = 0x8,
All = 0xffffffff,
};
@@ -225,6 +226,8 @@ namespace Microsoft.Terminal.Settings.Model
[default_interface] runtimeclass SendInputArgs : IActionArgs
{
SendInputArgs(String input);
String Input { get; };
};
@@ -354,6 +357,15 @@ namespace Microsoft.Terminal.Settings.Model
FindMatchDirection Direction { get; };
};
[default_interface] runtimeclass SaveTaskArgs : IActionArgs
{
SaveTaskArgs();
SaveTaskArgs(String Name, String Commandline, String KeyChord);
String Name;
String Commandline;
String KeyChord;
};
[default_interface] runtimeclass NewWindowArgs : IActionArgs
{
NewWindowArgs(INewContentArgs contentArgs);

View File

@@ -0,0 +1,36 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "ActionEntry.h"
#include "JsonUtils.h"
#include "ActionEntry.g.cpp"
using namespace Microsoft::Terminal::Settings::Model;
using namespace winrt::Microsoft::Terminal::Settings::Model::implementation;
static constexpr std::string_view ActionIdKey{ "actionId" };
ActionEntry::ActionEntry() noexcept :
ActionEntryT<ActionEntry, NewTabMenuEntry>(NewTabMenuEntryType::Action)
{
}
Json::Value ActionEntry::ToJson() const
{
auto json = NewTabMenuEntry::ToJson();
JsonUtils::SetValueForKey(json, ActionIdKey, _ActionId);
return json;
}
winrt::com_ptr<NewTabMenuEntry> ActionEntry::FromJson(const Json::Value& json)
{
auto entry = winrt::make_self<ActionEntry>();
JsonUtils::GetValueForKey(json, ActionIdKey, entry->_ActionId);
return entry;
}

View File

@@ -0,0 +1,37 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- ActionEntry.h
Abstract:
- An action entry in the "new tab" dropdown menu
Author(s):
- Pankaj Bhojwani - May 2024
--*/
#pragma once
#include "NewTabMenuEntry.h"
#include "ActionEntry.g.h"
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
struct ActionEntry : ActionEntryT<ActionEntry, NewTabMenuEntry>
{
public:
ActionEntry() noexcept;
Json::Value ToJson() const override;
static com_ptr<NewTabMenuEntry> FromJson(const Json::Value& json);
WINRT_PROPERTY(winrt::hstring, ActionId);
};
}
namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
{
BASIC_FACTORY(ActionEntry);
}

View File

@@ -60,31 +60,84 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
}
// Method Description:
// - Retrieves the Command in the current layer, if it's valid
// - We internally store invalid commands as full commands.
// This helper function returns nullptr when we get an invalid
// command. This allows us to simply check for null when we
// want a valid command.
// - Detects if any of the user's actions are identical to the inbox actions,
// and if so, deletes them and redirects their keybindings to the inbox actions
// - We have to do this here instead of when loading since we don't actually have
// any parents while loading the user settings, the parents are added after
void ActionMap::_FinalizeInheritance()
{
// first, gather the inbox actions from the relevant parent
std::unordered_map<InternalActionID, Model::Command> inboxActions;
winrt::com_ptr<implementation::ActionMap> foundParent{ nullptr };
for (const auto& parent : _parents)
{
const auto parentMap = parent->_ActionMap;
if (parentMap.begin() != parentMap.end() && parentMap.begin()->second.Origin() == OriginTag::InBox)
{
// only one parent contains all the inbox actions and that parent contains only inbox actions,
// so if we found an inbox action we know this is the parent we are looking for
foundParent = parent;
break;
}
}
if (foundParent)
{
for (const auto& [_, cmd] : foundParent->_ActionMap)
{
inboxActions.emplace(Hash(cmd.ActionAndArgs()), cmd);
}
}
std::unordered_map<KeyChord, winrt::hstring, KeyChordHash, KeyChordEquality> keysToReassign;
// now, look through our _ActionMap for commands that
// - had an ID generated for them
// - do not have a name/icon path
// - have a hash that matches a command in the inbox actions
std::erase_if(_ActionMap, [&](const auto& pair) {
const auto userCmdImpl{ get_self<Command>(pair.second) };
if (userCmdImpl->IDWasGenerated() && !userCmdImpl->HasName() && userCmdImpl->IconPath().empty())
{
const auto userActionHash = Hash(userCmdImpl->ActionAndArgs());
if (const auto inboxCmd = inboxActions.find(userActionHash); inboxCmd != inboxActions.end())
{
for (const auto& [key, cmdID] : _KeyMap)
{
// for any of our keys that point to the user action, point them to the inbox action instead
if (cmdID == pair.first)
{
keysToReassign.insert_or_assign(key, inboxCmd->second.ID());
}
}
// remove this pair
return true;
}
}
return false;
});
for (const auto [key, cmdID] : keysToReassign)
{
_KeyMap.insert_or_assign(key, cmdID);
}
}
bool ActionMap::FixupsAppliedDuringLoad() const
{
return _fixupsAppliedDuringLoad;
}
// Method Description:
// - Retrieves the Command referred to be the given ID
// - Will recurse through parents if we don't find it in this layer
// Arguments:
// - actionID: the internal ID associated with a Command
// Return Value:
// - If the command is valid, the command itself.
// - If the command is explicitly unbound, nullptr.
// - If the command cannot be found in this layer, nullopt.
std::optional<Model::Command> ActionMap::_GetActionByID(const InternalActionID actionID) const
// - The command if it exists in this layer, otherwise nullptr
Model::Command ActionMap::_GetActionByID(const winrt::hstring& actionID) const
{
// Check the masking actions
const auto maskingPair{ _MaskingActions.find(actionID) };
if (maskingPair != _MaskingActions.end())
{
// ActionMap should never point to nullptr
FAIL_FAST_IF_NULL(maskingPair->second);
// masking actions cannot contain nested or invalid commands,
// so we can just return it directly.
return maskingPair->second;
}
// Check current layer
const auto actionMapPair{ _ActionMap.find(actionID) };
if (actionMapPair != _ActionMap.end())
@@ -94,13 +147,19 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
// ActionMap should never point to nullptr
FAIL_FAST_IF_NULL(cmd);
return !cmd.HasNestedCommands() && cmd.ActionAndArgs().Action() == ShortcutAction::Invalid ?
nullptr : // explicitly unbound
cmd;
return cmd;
}
for (const auto& parent : _parents)
{
if (const auto inheritedCmd = parent->_GetActionByID(actionID))
{
return inheritedCmd;
}
}
// We don't have an answer
return std::nullopt;
return nullptr;
}
static void RegisterShortcutAction(ShortcutAction shortcutAction, std::unordered_map<hstring, Model::ActionAndArgs>& list, std::unordered_set<InternalActionID>& visited)
@@ -143,24 +202,22 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
void ActionMap::_PopulateAvailableActionsWithStandardCommands(std::unordered_map<hstring, Model::ActionAndArgs>& availableActions, std::unordered_set<InternalActionID>& visitedActionIDs) const
{
// Update AvailableActions and visitedActionIDs with our current layer
for (const auto& [actionID, cmd] : _ActionMap)
for (const auto& [_, cmd] : _ActionMap)
{
if (cmd.ActionAndArgs().Action() != ShortcutAction::Invalid)
// Only populate AvailableActions with actions that haven't been visited already.
const auto actionID = Hash(cmd.ActionAndArgs());
if (!visitedActionIDs.contains(actionID))
{
// Only populate AvailableActions with actions that haven't been visited already.
if (visitedActionIDs.find(actionID) == visitedActionIDs.end())
const auto name{ cmd.Name() };
if (!name.empty())
{
const auto& name{ cmd.Name() };
if (!name.empty())
{
// Update AvailableActions.
const auto actionAndArgsImpl{ get_self<ActionAndArgs>(cmd.ActionAndArgs()) };
availableActions.insert_or_assign(name, *actionAndArgsImpl->Copy());
}
// Record that we already handled adding this action to the NameMap.
visitedActionIDs.insert(actionID);
// Update AvailableActions.
const auto actionAndArgsImpl{ get_self<ActionAndArgs>(cmd.ActionAndArgs()) };
availableActions.insert_or_assign(name, *actionAndArgsImpl->Copy());
}
// Record that we already handled adding this action to the NameMap.
visitedActionIDs.insert(actionID);
}
}
@@ -179,6 +236,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
if (!_NameMapCache)
{
if (!_CumulativeActionMapCache)
{
_RefreshKeyBindingCaches();
}
// populate _NameMapCache
std::unordered_map<hstring, Model::Command> nameMap{};
_PopulateNameMapWithSpecialCommands(nameMap);
@@ -231,59 +292,66 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
// - Populates the provided nameMap with all of our actions and our parents actions
// while omitting the actions that were already added before
// Arguments:
// - nameMap: the nameMap we're populating. This maps the name (hstring) of a command to the command itself.
// There should only ever by one of each command (identified by the actionID) in the nameMap.
// - nameMap: the nameMap we're populating, this maps the name (hstring) of a command to the command itself
void ActionMap::_PopulateNameMapWithStandardCommands(std::unordered_map<hstring, Model::Command>& nameMap) const
{
std::unordered_set<InternalActionID> visitedActionIDs;
for (const auto& cmd : _GetCumulativeActions())
for (const auto& [_, cmd] : _CumulativeActionMapCache)
{
// only populate with valid commands
if (cmd.ActionAndArgs().Action() != ShortcutAction::Invalid)
const auto& name{ cmd.Name() };
if (!name.empty())
{
// Only populate NameMap with actions that haven't been visited already.
const auto actionID{ Hash(cmd.ActionAndArgs()) };
if (visitedActionIDs.find(actionID) == visitedActionIDs.end())
// there might be a collision here, where there could be 2 different commands with the same name
// in this case, prioritize the user's action
// TODO GH #17166: we should no longer use Command.Name to identify commands anywhere
if (!nameMap.contains(name) || cmd.Origin() == OriginTag::User)
{
const auto& name{ cmd.Name() };
if (!name.empty())
{
// Update NameMap.
nameMap.insert_or_assign(name, cmd);
}
// Record that we already handled adding this action to the NameMap.
visitedActionIDs.emplace(actionID);
// either a command with this name does not exist, or this is a user-defined command with a name
// in either case, update the name map with the command (if this is a user-defined command with
// the same name as an existing command, the existing one will get overwritten)
nameMap.insert_or_assign(name, cmd);
}
}
}
}
// Method Description:
// - Provides an accumulated list of actions that are exposed. The accumulated list includes actions added in this layer, followed by actions added by our parents.
std::vector<Model::Command> ActionMap::_GetCumulativeActions() const noexcept
// - Recursively populate keyBindingsMap with ours and our parents' key -> id pairs
// - This is a bottom-up approach
// - Keybindings of the parents are overridden by the children
void ActionMap::_PopulateCumulativeKeyMap(std::unordered_map<Control::KeyChord, winrt::hstring, KeyChordHash, KeyChordEquality>& keyBindingsMap)
{
// First, add actions from our current layer
std::vector<Model::Command> cumulativeActions;
cumulativeActions.reserve(_MaskingActions.size() + _ActionMap.size());
// masking actions have priority. Actions here are constructed from consolidating an inherited action with changes we've found when populating this layer.
std::transform(_MaskingActions.begin(), _MaskingActions.end(), std::back_inserter(cumulativeActions), [](std::pair<InternalActionID, Model::Command> actionPair) {
return actionPair.second;
});
std::transform(_ActionMap.begin(), _ActionMap.end(), std::back_inserter(cumulativeActions), [](std::pair<InternalActionID, Model::Command> actionPair) {
return actionPair.second;
});
// Now, add the accumulated actions from our parents
for (const auto& parent : _parents)
for (const auto& [keys, cmdID] : _KeyMap)
{
const auto parentActions{ parent->_GetCumulativeActions() };
cumulativeActions.reserve(cumulativeActions.size() + parentActions.size());
cumulativeActions.insert(cumulativeActions.end(), parentActions.begin(), parentActions.end());
if (!keyBindingsMap.contains(keys))
{
keyBindingsMap.emplace(keys, cmdID);
}
}
return cumulativeActions;
for (const auto& parent : _parents)
{
parent->_PopulateCumulativeKeyMap(keyBindingsMap);
}
}
// Method Description:
// - Recursively populate actionMap with ours and our parents' id -> command pairs
// - This is a bottom-up approach
// - Actions of the parents are overridden by the children
void ActionMap::_PopulateCumulativeActionMap(std::unordered_map<hstring, Model::Command>& actionMap)
{
for (const auto& [cmdID, cmd] : _ActionMap)
{
if (!actionMap.contains(cmdID))
{
actionMap.emplace(cmdID, cmd);
}
}
for (const auto& parent : _parents)
{
parent->_PopulateCumulativeActionMap(actionMap);
}
}
IMapView<Control::KeyChord, Model::Command> ActionMap::GlobalHotkeys()
@@ -297,81 +365,42 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
IMapView<Control::KeyChord, Model::Command> ActionMap::KeyBindings()
{
if (!_KeyBindingMapCache)
if (!_ResolvedKeyActionMapCache)
{
_RefreshKeyBindingCaches();
}
return _KeyBindingMapCache.GetView();
return _ResolvedKeyActionMapCache.GetView();
}
void ActionMap::_RefreshKeyBindingCaches()
{
std::unordered_map<KeyChord, Model::Command, KeyChordHash, KeyChordEquality> keyBindingsMap;
std::unordered_map<KeyChord, Model::Command, KeyChordHash, KeyChordEquality> globalHotkeys;
std::unordered_set<Control::KeyChord, KeyChordHash, KeyChordEquality> unboundKeys;
std::unordered_map<KeyChord, winrt::hstring, KeyChordHash, KeyChordEquality> accumulatedKeybindingsMap;
std::unordered_map<winrt::hstring, Model::Command> accumulatedActionsMap;
std::unordered_map<KeyChord, Model::Command, KeyChordHash, KeyChordEquality> resolvedKeyActionMap;
_PopulateKeyBindingMapWithStandardCommands(keyBindingsMap, unboundKeys);
_PopulateCumulativeKeyMap(accumulatedKeybindingsMap);
_PopulateCumulativeActionMap(accumulatedActionsMap);
for (const auto& [keys, cmd] : keyBindingsMap)
for (const auto& [keys, cmdID] : accumulatedKeybindingsMap)
{
// Only populate GlobalHotkeys with actions whose
// ShortcutAction is GlobalSummon or QuakeMode
if (cmd.ActionAndArgs().Action() == ShortcutAction::GlobalSummon || cmd.ActionAndArgs().Action() == ShortcutAction::QuakeMode)
if (const auto idCmdPair = accumulatedActionsMap.find(cmdID); idCmdPair != accumulatedActionsMap.end())
{
globalHotkeys.emplace(keys, cmd);
}
}
resolvedKeyActionMap.emplace(keys, idCmdPair->second);
_KeyBindingMapCache = single_threaded_map(std::move(keyBindingsMap));
_GlobalHotkeysCache = single_threaded_map(std::move(globalHotkeys));
}
// Method Description:
// - Populates the provided keyBindingsMap with all of our actions and our parents actions
// while omitting the key bindings that were already added before.
// - This needs to be a bottom up approach to ensure that we only add each key chord once.
// Arguments:
// - keyBindingsMap: the keyBindingsMap we're populating. This maps the key chord of a command to the command itself.
// - unboundKeys: a set of keys that are explicitly unbound
void ActionMap::_PopulateKeyBindingMapWithStandardCommands(std::unordered_map<Control::KeyChord, Model::Command, KeyChordHash, KeyChordEquality>& keyBindingsMap, std::unordered_set<Control::KeyChord, KeyChordHash, KeyChordEquality>& unboundKeys) const
{
// Update KeyBindingsMap with our current layer
for (const auto& [keys, actionID] : _KeyMap)
{
// Get the action our KeyMap maps to.
// This _cannot_ be nullopt because KeyMap can only map to
// actions in this layer.
// This _can_ be nullptr because nullptr means it was
// explicitly unbound ( "command": "unbound", "keys": "ctrl+c" ).
const auto cmd{ _GetActionByID(actionID).value() };
if (cmd)
{
// iterate over all of the action's bound keys
const auto cmdImpl{ get_self<Command>(cmd) };
for (const auto& keys : cmdImpl->KeyMappings())
// Only populate GlobalHotkeys with actions whose
// ShortcutAction is GlobalSummon or QuakeMode
if (idCmdPair->second.ActionAndArgs().Action() == ShortcutAction::GlobalSummon || idCmdPair->second.ActionAndArgs().Action() == ShortcutAction::QuakeMode)
{
// Only populate KeyBindingsMap with actions that...
// (1) haven't been visited already
// (2) aren't explicitly unbound
if (keyBindingsMap.find(keys) == keyBindingsMap.end() && unboundKeys.find(keys) == unboundKeys.end())
{
keyBindingsMap.emplace(keys, cmd);
}
globalHotkeys.emplace(keys, idCmdPair->second);
}
}
else
{
// record any keys that are explicitly unbound,
// but don't add them to the list of key bindings
unboundKeys.emplace(keys);
}
}
// Update keyBindingsMap and unboundKeys with our parents
for (const auto& parent : _parents)
{
parent->_PopulateKeyBindingMapWithStandardCommands(keyBindingsMap, unboundKeys);
}
_CumulativeKeyMapCache = single_threaded_map(std::move(accumulatedKeybindingsMap));
_CumulativeActionMapCache = single_threaded_map(std::move(accumulatedActionsMap));
_ResolvedKeyActionMapCache = single_threaded_map(std::move(resolvedKeyActionMap));
_GlobalHotkeysCache = single_threaded_map(std::move(globalHotkeys));
}
com_ptr<ActionMap> ActionMap::Copy() const
@@ -388,13 +417,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
actionMap->_ActionMap.emplace(actionID, *winrt::get_self<Command>(cmd)->Copy());
}
// ID --> Command
actionMap->_MaskingActions.reserve(_MaskingActions.size());
for (const auto& [actionID, cmd] : _MaskingActions)
{
actionMap->_MaskingActions.emplace(actionID, *winrt::get_self<Command>(cmd)->Copy());
}
// Name --> Command
actionMap->_NestedCommands.reserve(_NestedCommands.size());
for (const auto& [name, cmd] : _NestedCommands)
@@ -421,7 +443,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
// - Adds a command to the ActionMap
// Arguments:
// - cmd: the command we're adding
void ActionMap::AddAction(const Model::Command& cmd)
void ActionMap::AddAction(const Model::Command& cmd, const Control::KeyChord& keys)
{
// _Never_ add null to the ActionMap
if (!cmd)
@@ -432,7 +454,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
// invalidate caches
_NameMapCache = nullptr;
_GlobalHotkeysCache = nullptr;
_KeyBindingMapCache = nullptr;
_CumulativeKeyMapCache = nullptr;
_CumulativeActionMapCache = nullptr;
_ResolvedKeyActionMapCache = nullptr;
// Handle nested commands
const auto cmdImpl{ get_self<Command>(cmd) };
@@ -455,236 +479,89 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
}
// General Case:
// Add the new command to the KeyMap.
// This map directs you to an entry in the ActionMap.
// Add the new command to the _ActionMap
// Add the new keybinding to the _KeyMap
// Removing Actions from the Command Palette:
// cmd.Name and cmd.Action have a one-to-one relationship.
// If cmd.Name is empty, we must retrieve the old name and remove it.
// Removing Key Bindings:
// cmd.Keys and cmd.Action have a many-to-one relationship.
// If cmd.Keys is empty, we don't care.
// If action is "unbound"/"invalid", you're explicitly unbinding the provided cmd.keys.
// NOTE: If we're unbinding a command from a different layer, we must use maskingActions
// to keep track of what key mappings are still valid.
// _TryUpdateActionMap may update oldCmd and maskingCmd
Model::Command oldCmd{ nullptr };
Model::Command maskingCmd{ nullptr };
_TryUpdateActionMap(cmd, oldCmd, maskingCmd);
_TryUpdateName(cmd, oldCmd, maskingCmd);
_TryUpdateKeyChord(cmd, oldCmd, maskingCmd);
_TryUpdateActionMap(cmd);
_TryUpdateKeyChord(cmd, keys);
}
// Method Description:
// - Try to add the new command to _ActionMap.
// - If the command was added previously in this layer, populate oldCmd.
// - If the command was added previously in another layer, populate maskingCmd.
// - Try to add the new command to _ActionMap
// Arguments:
// - cmd: the action we're trying to register
// - oldCmd: the action found in _ActionMap, if one already exists
// - maskingAction: the action found in a parent layer, if one already exists
void ActionMap::_TryUpdateActionMap(const Model::Command& cmd, Model::Command& oldCmd, Model::Command& maskingCmd)
void ActionMap::_TryUpdateActionMap(const Model::Command& cmd)
{
// Example:
// { "command": "copy", "keys": "ctrl+c" } --> add the action in for the first time
// { "command": "copy", "keys": "ctrl+shift+c" } --> update oldCmd
const auto actionID{ Hash(cmd.ActionAndArgs()) };
const auto& actionPair{ _ActionMap.find(actionID) };
if (actionPair == _ActionMap.end())
// if the shortcut action is invalid, then this is for unbinding and _TryUpdateKeyChord will handle that
if (cmd.ActionAndArgs().Action() != ShortcutAction::Invalid)
{
// add this action in for the first time
_ActionMap.emplace(actionID, cmd);
}
else
{
// We're adding an action that already exists in our layer.
// Record it so that we update it with any new information.
oldCmd = actionPair->second;
}
// Masking Actions
//
// Example:
// parent: { "command": "copy", "keys": "ctrl+c" } --> add the action to parent._ActionMap
// current: { "command": "copy", "keys": "ctrl+shift+c" } --> look through parents for the "ctrl+c" binding, add it to _MaskingActions
// { "command": "copy", "keys": "ctrl+ins" } --> this should already be in _MaskingActions
// Now check if this action was introduced in another layer.
const auto& maskingActionPair{ _MaskingActions.find(actionID) };
if (maskingActionPair == _MaskingActions.end())
{
// Check if we need to add this to our list of masking commands.
for (const auto& parent : _parents)
const auto cmdImpl{ get_self<implementation::Command>(cmd) };
if (cmd.Origin() == OriginTag::User && cmd.ID().empty())
{
// NOTE: This only checks the layer above us, but that's ok.
// If we had to find one from a layer above that, parent->_MaskingActions
// would have found it, so we inherit it for free!
const auto& inheritedCmd{ parent->_GetActionByID(actionID) };
if (inheritedCmd && *inheritedCmd)
// the user did not define an ID for their non-nested, non-iterable, valid command - generate one for them
cmdImpl->GenerateID();
}
// only add to the _ActionMap if there is an ID
if (auto cmdID = cmd.ID(); !cmdID.empty())
{
// in the legacy scenario, a user might have several of the same action but only one of them has defined an icon or a name
// eg. { "command": "paste", "name": "myPaste", "keys":"ctrl+a" }
// { "command": "paste", "keys": "ctrl+b" }
// once they port over to the new implementation, we will reduce it to just one Command object with a generated ID
// but several key binding entries, like so
// { "command": "newTab", "id": "User.paste" } -> in the actions map
// { "keys": "ctrl+a", "id": "User.paste" } -> in the keybindings map
// { "keys": "ctrl+b", "id": "User.paste" } -> in the keybindings map
// however, we have to make sure that we preserve the icon/name that might have been there in one of the command objects
// to do that, we check if this command we're adding had an ID that was generated
// if so, we check if there already exists a command with that generated ID, and if there is we port over any name/icon there might be
// (this may cause us to overwrite in scenarios where the user has an existing command that has the same generated ID but
// performs a different action or has different args, but that falls under "play stupid games")
if (cmdImpl->IDWasGenerated())
{
const auto& inheritedCmdImpl{ get_self<Command>(*inheritedCmd) };
maskingCmd = *inheritedCmdImpl->Copy();
_MaskingActions.emplace(actionID, maskingCmd);
if (const auto foundCmd{ _GetActionByID(cmdID) })
{
const auto foundCmdImpl{ get_self<implementation::Command>(foundCmd) };
if (foundCmdImpl->HasName() && !cmdImpl->HasName())
{
cmdImpl->Name(foundCmdImpl->Name());
}
if (!foundCmdImpl->IconPath().empty() && cmdImpl->IconPath().empty())
{
cmdImpl->IconPath(foundCmdImpl->IconPath());
}
}
}
_ActionMap.insert_or_assign(cmdID, cmd);
}
}
else
{
// This is an action that we already have a mutable "masking" record for.
// Record it so that we update it with any new information.
maskingCmd = maskingActionPair->second;
}
}
// Method Description:
// - Update our internal state with the name of the newly registered action
// Arguments:
// - cmd: the action we're trying to register
// - oldCmd: the action that already exists in our internal state. May be null.
// - maskingCmd: the masking action that already exists in our internal state. May be null.
void ActionMap::_TryUpdateName(const Model::Command& cmd, const Model::Command& oldCmd, const Model::Command& maskingCmd)
{
// Example:
// { "name": "foo", "command": "copy" } --> we are setting a name, update oldCmd and maskingCmd
// { "command": "copy" } --> no change to name, exit early
const auto cmdImpl{ get_self<Command>(cmd) };
if (!cmdImpl->HasName())
{
// the user is not trying to update the name.
return;
}
// Update oldCmd:
// If we have a Command in our _ActionMap that we're trying to update,
// update it.
const auto newName{ cmd.Name() };
if (oldCmd)
{
// This command has a name, check if it's new.
if (newName != oldCmd.Name())
{
// The new name differs from the old name,
// update our name.
auto oldCmdImpl{ get_self<Command>(oldCmd) };
oldCmdImpl->Name(newName);
}
}
// Update maskingCmd:
// We have a Command that is masking one from a parent layer.
// We need to ensure that this has the correct name. That way,
// we can return an accumulated view of a Command at this layer.
// This differs from oldCmd which is mainly used for serialization
// by recording the delta of the Command in this layer.
if (maskingCmd)
{
// This command has a name, check if it's new.
if (newName != maskingCmd.Name())
{
// The new name differs from the old name,
// update our name.
auto maskingCmdImpl{ get_self<Command>(maskingCmd) };
maskingCmdImpl->Name(newName);
}
}
// Handle a collision with NestedCommands
_NestedCommands.erase(newName);
}
// Method Description:
// - Update our internal state with the key chord of the newly registered action
// Arguments:
// - cmd: the action we're trying to register
// - oldCmd: the action that already exists in our internal state. May be null.
// - maskingCmd: the masking action that already exists in our internal state. May be null.
void ActionMap::_TryUpdateKeyChord(const Model::Command& cmd, const Model::Command& oldCmd, const Model::Command& maskingCmd)
void ActionMap::_TryUpdateKeyChord(const Model::Command& cmd, const Control::KeyChord& keys)
{
// Example:
// { "command": "copy", "keys": "ctrl+c" } --> we are registering a new key chord, update oldCmd and maskingCmd
// Example (this is a legacy case, where the keys are provided in the same block as the command):
// { "command": "copy", "keys": "ctrl+c" } --> we are registering a new key chord
// { "name": "foo", "command": "copy" } --> no change to keys, exit early
const auto keys{ cmd.Keys() };
if (!keys)
{
// the user is not trying to update the keys.
return;
}
// Handle collisions
const auto oldKeyPair{ _KeyMap.find(keys) };
if (oldKeyPair != _KeyMap.end())
{
// Collision: The key chord was already in use.
//
// Example:
// { "command": "copy", "keys": "ctrl+c" } --> register "ctrl+c" (different branch)
// { "command": "paste", "keys": "ctrl+c" } --> Collision! (this branch)
//
// Remove the old one. (unbind "copy" in the example above)
const auto actionPair{ _ActionMap.find(oldKeyPair->second) };
const auto conflictingCmd{ actionPair->second };
const auto conflictingCmdImpl{ get_self<implementation::Command>(conflictingCmd) };
conflictingCmdImpl->EraseKey(keys);
}
else if (const auto& conflictingCmd{ GetActionByKeyChord(keys) })
{
// Collision with ancestor: The key chord was already in use, but by an action in another layer
//
// Example:
// parent: { "command": "copy", "keys": "ctrl+c" } --> register "ctrl+c" (different branch)
// current: { "command": "paste", "keys": "ctrl+c" } --> Collision with ancestor! (this branch, sub-branch 1)
// { "command": "unbound", "keys": "ctrl+c" } --> Collision with masking action! (this branch, sub-branch 2)
const auto conflictingActionID{ Hash(conflictingCmd.ActionAndArgs()) };
const auto maskingCmdPair{ _MaskingActions.find(conflictingActionID) };
if (maskingCmdPair == _MaskingActions.end())
{
// This is the first time we're colliding with an action from a different layer,
// so let's add this action to _MaskingActions and update it appropriately.
// Create a copy of the conflicting action,
// and erase the conflicting key chord from the copy.
const auto conflictingCmdImpl{ get_self<implementation::Command>(conflictingCmd) };
const auto conflictingCmdCopy{ conflictingCmdImpl->Copy() };
conflictingCmdCopy->EraseKey(keys);
_MaskingActions.emplace(conflictingActionID, *conflictingCmdCopy);
}
else
{
// We've collided with this action before. Let's resolve a collision with a masking action.
const auto maskingCmdImpl{ get_self<implementation::Command>(maskingCmdPair->second) };
maskingCmdImpl->EraseKey(keys);
}
}
// Assign the new action in the _KeyMap.
const auto actionID{ Hash(cmd.ActionAndArgs()) };
_KeyMap.insert_or_assign(keys, actionID);
// Additive operation:
// Register the new key chord with oldCmd (an existing _ActionMap entry)
// Example:
// { "command": "copy", "keys": "ctrl+c" } --> register "ctrl+c" (section above)
// { "command": "copy", "keys": "ctrl+shift+c" } --> also register "ctrl+shift+c" to the same Command (oldCmd)
if (oldCmd)
{
// Update inner Command with new key chord
auto oldCmdImpl{ get_self<Command>(oldCmd) };
oldCmdImpl->RegisterKey(keys);
}
// Additive operation:
// Register the new key chord with maskingCmd (an existing _maskingAction entry)
// Example:
// parent: { "command": "copy", "keys": "ctrl+c" } --> register "ctrl+c" to parent._ActionMap (different branch in a different layer)
// current: { "command": "copy", "keys": "ctrl+shift+c" } --> also register "ctrl+shift+c" to the same Command (maskingCmd)
if (maskingCmd)
{
// Update inner Command with new key chord
auto maskingCmdImpl{ get_self<Command>(maskingCmd) };
maskingCmdImpl->RegisterKey(keys);
}
// Assign the new action in the _KeyMap
// However, there's a strange edge case here - since we're parsing a legacy or modern block,
// the user might have { "command": null, "id": "someID", "keys": "ctrl+c" }
// i.e. they provided an ID for a null command (which they really shouldn't, there's no purpose)
// in this case, we do _not_ want to use the id they provided, we want to use an empty id
// (empty id in the _KeyMap indicates the keychord was explicitly unbound)
const auto action = cmd.ActionAndArgs().Action();
const auto id = action == ShortcutAction::Invalid ? hstring{} : cmd.ID();
_KeyMap.insert_or_assign(keys, id);
}
// Method Description:
@@ -714,6 +591,41 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
return _GetActionByKeyChordInternal(keys).value_or(nullptr);
}
Model::Command ActionMap::GetActionById(const winrt::hstring& cmdID) const
{
return _GetActionByID(cmdID);
}
// Method Description:
// - Retrieves the assigned command ID with the given key chord.
// - Can return nullopt to differentiate explicit unbinding vs lack of binding.
// Arguments:
// - keys: the key chord of the command to search for
// Return Value:
// - the command ID with the given key chord
// - an empty string if the key chord is explicitly unbound
// - nullopt if it is not bound
std::optional<winrt::hstring> ActionMap::_GetActionIdByKeyChordInternal(const Control::KeyChord& keys) const
{
if (const auto keyIDPair = _KeyMap.find(keys); keyIDPair != _KeyMap.end())
{
// the keychord is defined in this layer, return the ID
return keyIDPair->second;
}
// search through our parents
for (const auto& parent : _parents)
{
if (const auto foundCmdID = parent->_GetActionIdByKeyChordInternal(keys))
{
return foundCmdID;
}
}
// we did not find the keychord anywhere, it's not bound and not explicitly unbound either
return std::nullopt;
}
// Method Description:
// - Retrieves the assigned command with the given key chord.
// - Can return nullopt to differentiate explicit unbinding vs lack of binding.
@@ -722,73 +634,49 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
// Return Value:
// - the command with the given key chord
// - nullptr if the key chord is explicitly unbound
// - nullopt if it was not bound in this layer
// - nullopt if it is not bound
std::optional<Model::Command> ActionMap::_GetActionByKeyChordInternal(const Control::KeyChord& keys) const
{
// Check the current layer
if (const auto actionIDPair = _KeyMap.find(keys); actionIDPair != _KeyMap.end())
if (const auto actionIDOptional = _GetActionIdByKeyChordInternal(keys))
{
// the command was explicitly bound,
// return what we found (invalid commands exposed as nullptr)
return _GetActionByID(actionIDPair->second);
}
// the command was not bound in this layer,
// ask my parents
for (const auto& parent : _parents)
{
const auto& inheritedCmd{ parent->_GetActionByKeyChordInternal(keys) };
if (inheritedCmd)
if (!actionIDOptional->empty())
{
return *inheritedCmd;
// there is an ID associated with these keys, find the command
if (const auto foundCmd = _GetActionByID(*actionIDOptional))
{
return foundCmd;
}
}
// the ID is an empty string, these keys are explicitly unbound
return nullptr;
}
// This action is not explicitly bound
return std::nullopt;
}
// Method Description:
// - Retrieves the key chord for the provided action
// Arguments:
// - action: the shortcut action (an action type) we're looking for
// - cmdID: the ID of the command we're looking for
// Return Value:
// - the key chord that executes the given action
// - nullptr if the action is not bound to a key chord
Control::KeyChord ActionMap::GetKeyBindingForAction(const ShortcutAction& action) const
Control::KeyChord ActionMap::GetKeyBindingForAction(const winrt::hstring& cmdID)
{
return GetKeyBindingForAction(action, nullptr);
}
// Method Description:
// - Retrieves the key chord for the provided action
// Arguments:
// - action: the shortcut action (an action type) we're looking for
// - myArgs: the action args for the action we're looking for
// Return Value:
// - the key chord that executes the given action
// - nullptr if the action is not bound to a key chord
Control::KeyChord ActionMap::GetKeyBindingForAction(const ShortcutAction& myAction, const IActionArgs& myArgs) const
{
if (myAction == ShortcutAction::Invalid)
if (!_ResolvedKeyActionMapCache)
{
return nullptr;
_RefreshKeyBindingCaches();
}
// Check our internal state.
const auto actionAndArgs = winrt::make<ActionAndArgs>(myAction, myArgs);
const auto hash{ Hash(actionAndArgs) };
if (const auto& cmd{ _GetActionByID(hash) })
// I dislike that we have to do an O(n) lookup every time we want to get the keybinding for an action -
// an alternative is having the key->action map be a bi-map (would require a dependency), or store another map that is just
// the reverse direction (action->key) which would be mean storing the same data twice but getting faster lookup
for (const auto [key, action] : _ResolvedKeyActionMapCache)
{
return cmd->Keys();
}
// Check our parents
for (const auto& parent : _parents)
{
if (const auto& keys{ parent->GetKeyBindingForAction(myAction, myArgs) })
if (action.ID() == cmdID)
{
return keys;
// if there are multiple keys bound to this action, we will just return the first one we find
return key;
}
}
@@ -796,25 +684,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
return nullptr;
}
bool ActionMap::GenerateIDsForActions()
{
bool fixedUp{ false };
for (auto actionPair : _ActionMap)
{
auto cmdImpl{ winrt::get_self<Command>(actionPair.second) };
// Note: this function should ONLY be called for the action map in the user's settings file
// this debug assert should verify that for debug builds
assert(cmdImpl->Origin() == OriginTag::User);
if (cmdImpl->ID().empty())
{
fixedUp = cmdImpl->GenerateID() || fixedUp;
}
}
return fixedUp;
}
// Method Description:
// - Rebinds a key binding to a new key chord
// Arguments:
@@ -824,24 +693,26 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
// - true, if successful. False, otherwise.
bool ActionMap::RebindKeys(const Control::KeyChord& oldKeys, const Control::KeyChord& newKeys)
{
const auto& cmd{ GetActionByKeyChord(oldKeys) };
const auto cmd{ GetActionByKeyChord(oldKeys) };
if (!cmd)
{
// oldKeys must be bound. Otherwise, we don't know what action to bind.
return false;
}
if (newKeys)
if (auto oldKeyPair = _KeyMap.find(oldKeys); oldKeyPair != _KeyMap.end())
{
// Bind newKeys
const auto newCmd{ make_self<Command>() };
newCmd->ActionAndArgs(cmd.ActionAndArgs());
newCmd->RegisterKey(newKeys);
AddAction(*newCmd);
// oldKeys is bound in our layer, replace it with newKeys
_KeyMap.insert_or_assign(newKeys, cmd.ID());
_KeyMap.erase(oldKeyPair);
}
else
{
// oldKeys is bound in some other layer, set newKeys to cmd in this layer, and oldKeys to unbound in this layer
_KeyMap.insert_or_assign(newKeys, cmd.ID());
_KeyMap.insert_or_assign(oldKeys, L"");
}
// unbind oldKeys
DeleteKeyBinding(oldKeys);
return true;
}
@@ -853,12 +724,19 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
// - <none>
void ActionMap::DeleteKeyBinding(const KeyChord& keys)
{
// create an "unbound" command
// { "command": "unbound", "keys": <keys> }
const auto cmd{ make_self<Command>() };
cmd->ActionAndArgs(make<ActionAndArgs>());
cmd->RegisterKey(keys);
AddAction(*cmd);
if (auto keyPair = _KeyMap.find(keys); keyPair != _KeyMap.end())
{
// this keychord is bound in our layer, delete it
_KeyMap.erase(keyPair);
}
// either the keychord was never in this layer or we just deleted it above,
// if GetActionByKeyChord still returns a command that means the keychord is bound in another layer
if (GetActionByKeyChord(keys))
{
// set to unbound in this layer
_KeyMap.emplace(keys, L"");
}
}
// Method Description:
@@ -872,32 +750,25 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
void ActionMap::RegisterKeyBinding(Control::KeyChord keys, Model::ActionAndArgs action)
{
auto cmd{ make_self<Command>() };
cmd->RegisterKey(keys);
cmd->ActionAndArgs(action);
AddAction(*cmd);
cmd->GenerateID();
AddAction(*cmd, keys);
}
void ActionMap::_recursiveUpdateCommandKeybindingLabels()
void ActionMap::AddSendInputAction(winrt::hstring name, winrt::hstring input, const Control::KeyChord keys)
{
const auto& commands{ _ExpandedCommandsCache };
for (const auto& command : commands)
auto newAction = winrt::make<ActionAndArgs>();
newAction.Action(ShortcutAction::SendInput);
auto sendInputArgs = winrt::make<SendInputArgs>(input);
newAction.Args(sendInputArgs);
auto cmd{ make_self<Command>() };
cmd->ActionAndArgs(newAction);
if (!name.empty())
{
if (command.HasNestedCommands())
{
_recursiveUpdateCommandKeybindingLabels();
}
else
{
// If there's a keybinding that's bound to exactly this command,
// then get the keychord and display it as a
// part of the command in the UI.
// We specifically need to do this for nested commands.
const auto keyChord{ GetKeyBindingForAction(command.ActionAndArgs().Action(),
command.ActionAndArgs().Args()) };
command.RegisterKey(keyChord);
}
cmd->Name(name);
}
cmd->GenerateID();
AddAction(*cmd, keys);
}
// This is a helper to aid in sorting commands by their `Name`s, alphabetically.
@@ -965,6 +836,7 @@ 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{ inArgs ? inArgs.Input() : L"" };
auto args = winrt::make_self<SendInputArgs>(
winrt::hstring{ fmt::format(FMT_COMPILE(L"{:\x7f^{}}{}"),

View File

@@ -49,6 +49,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
struct ActionMap : ActionMapT<ActionMap>, IInheritable<ActionMap>
{
void _FinalizeInheritance() override;
// views
Windows::Foundation::Collections::IMapView<hstring, Model::ActionAndArgs> AvailableActions();
Windows::Foundation::Collections::IMapView<hstring, Model::Command> NameMap();
@@ -58,23 +60,25 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
// queries
Model::Command GetActionByKeyChord(const Control::KeyChord& keys) const;
Model::Command GetActionById(const winrt::hstring& cmdID) const;
bool IsKeyChordExplicitlyUnbound(const Control::KeyChord& keys) const;
Control::KeyChord GetKeyBindingForAction(const ShortcutAction& action) const;
Control::KeyChord GetKeyBindingForAction(const ShortcutAction& action, const IActionArgs& actionArgs) const;
Control::KeyChord GetKeyBindingForAction(const winrt::hstring& cmdID);
// population
void AddAction(const Model::Command& cmd);
void AddAction(const Model::Command& cmd, const Control::KeyChord& keys);
// JSON
static com_ptr<ActionMap> FromJson(const Json::Value& json, const OriginTag origin = OriginTag::None);
std::vector<SettingsLoadWarnings> LayerJson(const Json::Value& json, const OriginTag origin, const bool withKeybindings = true);
Json::Value ToJson() const;
Json::Value KeyBindingsToJson() const;
bool FixupsAppliedDuringLoad() const;
// modification
bool GenerateIDsForActions();
bool RebindKeys(const Control::KeyChord& oldKeys, const Control::KeyChord& newKeys);
void DeleteKeyBinding(const Control::KeyChord& keys);
void RegisterKeyBinding(Control::KeyChord keys, Model::ActionAndArgs action);
void AddSendInputAction(winrt::hstring name, winrt::hstring input, const Control::KeyChord keys);
Windows::Foundation::Collections::IVector<Model::Command> ExpandedCommands();
void ExpandCommands(const Windows::Foundation::Collections::IVectorView<Model::Profile>& profiles,
@@ -83,46 +87,47 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
winrt::Windows::Foundation::Collections::IVector<Model::Command> FilterToSendInput(winrt::hstring currentCommandline);
private:
std::optional<Model::Command> _GetActionByID(const InternalActionID actionID) const;
Model::Command _GetActionByID(const winrt::hstring& actionID) const;
std::optional<winrt::hstring> _GetActionIdByKeyChordInternal(const Control::KeyChord& keys) const;
std::optional<Model::Command> _GetActionByKeyChordInternal(const Control::KeyChord& keys) const;
void _RefreshKeyBindingCaches();
void _PopulateAvailableActionsWithStandardCommands(std::unordered_map<hstring, Model::ActionAndArgs>& availableActions, std::unordered_set<InternalActionID>& visitedActionIDs) const;
void _PopulateNameMapWithSpecialCommands(std::unordered_map<hstring, Model::Command>& nameMap) const;
void _PopulateNameMapWithStandardCommands(std::unordered_map<hstring, Model::Command>& nameMap) const;
void _PopulateKeyBindingMapWithStandardCommands(std::unordered_map<Control::KeyChord, Model::Command, KeyChordHash, KeyChordEquality>& keyBindingsMap, std::unordered_set<Control::KeyChord, KeyChordHash, KeyChordEquality>& unboundKeys) const;
std::vector<Model::Command> _GetCumulativeActions() const noexcept;
void _TryUpdateActionMap(const Model::Command& cmd, Model::Command& oldCmd, Model::Command& consolidatedCmd);
void _TryUpdateName(const Model::Command& cmd, const Model::Command& oldCmd, const Model::Command& consolidatedCmd);
void _TryUpdateKeyChord(const Model::Command& cmd, const Model::Command& oldCmd, const Model::Command& consolidatedCmd);
void _PopulateCumulativeKeyMap(std::unordered_map<Control::KeyChord, winrt::hstring, KeyChordHash, KeyChordEquality>& keyBindingsMap);
void _PopulateCumulativeActionMap(std::unordered_map<hstring, Model::Command>& actionMap);
void _recursiveUpdateCommandKeybindingLabels();
void _TryUpdateActionMap(const Model::Command& cmd);
void _TryUpdateKeyChord(const Model::Command& cmd, const Control::KeyChord& keys);
Windows::Foundation::Collections::IMap<hstring, Model::ActionAndArgs> _AvailableActionsCache{ nullptr };
Windows::Foundation::Collections::IMap<hstring, Model::Command> _NameMapCache{ nullptr };
Windows::Foundation::Collections::IMap<Control::KeyChord, Model::Command> _GlobalHotkeysCache{ nullptr };
Windows::Foundation::Collections::IMap<Control::KeyChord, Model::Command> _KeyBindingMapCache{ nullptr };
Windows::Foundation::Collections::IVector<Model::Command> _ExpandedCommandsCache{ nullptr };
std::unordered_map<winrt::hstring, Model::Command> _NestedCommands;
std::vector<Model::Command> _IterableCommands;
std::unordered_map<Control::KeyChord, InternalActionID, KeyChordHash, KeyChordEquality> _KeyMap;
std::unordered_map<InternalActionID, Model::Command> _ActionMap;
// Masking Actions:
// These are actions that were introduced in an ancestor,
// but were edited (or unbound) in the current layer.
// _ActionMap shows a Command with keys that were added in this layer,
// whereas _MaskingActions provides a view that encompasses all of
// the valid associated key chords.
// Maintaining this map allows us to return a valid Command
// in GetKeyBindingForAction.
// Additionally, these commands to not need to be serialized,
// whereas those in _ActionMap do. These actions provide more data
// than is necessary to be serialized.
std::unordered_map<InternalActionID, Model::Command> _MaskingActions;
bool _fixupsAppliedDuringLoad{ false };
// _KeyMap is the map of key chords -> action IDs defined in this layer
// _ActionMap is the map of action IDs -> commands defined in this layer
// These maps are the ones that we deserialize into when parsing the user json and vice-versa
std::unordered_map<Control::KeyChord, winrt::hstring, KeyChordHash, KeyChordEquality> _KeyMap;
std::unordered_map<winrt::hstring, Model::Command> _ActionMap;
// _CumulativeKeyMapCache is the map of key chords -> action IDs defined in all layers, with child layers overriding parent layers
Windows::Foundation::Collections::IMap<Control::KeyChord, winrt::hstring> _CumulativeKeyMapCache{ nullptr };
// _CumulativeActionMapCache is the map of action IDs -> commands defined in all layers, with child layers overriding parent layers
Windows::Foundation::Collections::IMap<winrt::hstring, Model::Command> _CumulativeActionMapCache{ nullptr };
// _ResolvedKeyActionMapCache is the map of key chords -> commands defined in all layers, with child layers overriding parent layers
// This is effectively a combination of _CumulativeKeyMapCache and _CumulativeActionMapCache and its purpose is so that
// we can give the SUI a view of the key chords and the commands they map to
Windows::Foundation::Collections::IMap<Control::KeyChord, Model::Command> _ResolvedKeyActionMapCache{ nullptr };
friend class SettingsModelUnitTests::KeyBindingsTests;
friend class SettingsModelUnitTests::DeserializationTests;

View File

@@ -11,9 +11,8 @@ namespace Microsoft.Terminal.Settings.Model
Boolean IsKeyChordExplicitlyUnbound(Microsoft.Terminal.Control.KeyChord keys);
Command GetActionByKeyChord(Microsoft.Terminal.Control.KeyChord keys);
Microsoft.Terminal.Control.KeyChord GetKeyBindingForAction(ShortcutAction action);
[method_name("GetKeyBindingForActionWithArgs")] Microsoft.Terminal.Control.KeyChord GetKeyBindingForAction(ShortcutAction action, IActionArgs actionArgs);
Command GetActionById(String cmdID);
Microsoft.Terminal.Control.KeyChord GetKeyBindingForAction(String cmdID);
Windows.Foundation.Collections.IMapView<String, ActionAndArgs> AvailableActions { get; };
@@ -32,5 +31,6 @@ namespace Microsoft.Terminal.Settings.Model
void DeleteKeyBinding(Microsoft.Terminal.Control.KeyChord keys);
void RegisterKeyBinding(Microsoft.Terminal.Control.KeyChord keys, ActionAndArgs action);
void AddSendInputAction(String name, String input, Microsoft.Terminal.Control.KeyChord keys);
}
}

View File

@@ -27,12 +27,12 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
}
// Method Description:
// - Deserialize an ActionMap from the array `json`. The json array should contain
// an array of serialized `Command` objects.
// - These actions are added to the `ActionMap`, where we automatically handle
// overwriting and unbinding actions.
// - Deserialize an ActionMap from the array `json`
// - The json array either contains an array of serialized `Command` objects,
// or an array of keybindings
// - The actions are added to _ActionMap and the keybindings are added to _KeyMap
// Arguments:
// - json: an array of Json::Value's to deserialize into our ActionMap.
// - json: an array of Json::Value's to deserialize into our _ActionMap and _KeyMap
// Return value:
// - a list of warnings encountered while deserializing the json
std::vector<SettingsLoadWarnings> ActionMap::LayerJson(const Json::Value& json, const OriginTag origin, const bool withKeybindings)
@@ -43,14 +43,69 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
// settings phase, so we'll collect them now.
std::vector<SettingsLoadWarnings> warnings;
for (const auto& cmdJson : json)
for (const auto& jsonBlock : json)
{
if (!cmdJson.isObject())
if (!jsonBlock.isObject())
{
continue;
}
AddAction(*Command::FromJson(cmdJson, warnings, origin, withKeybindings));
// the json block may be 1 of 3 things:
// - the legacy style command block, that has the action, args and keys in it
// - the modern style command block, that has the action, args and an ID
// - the modern style keys block, that has the keys and an ID
// if the block contains a "command" field, it is either a legacy or modern style command block
// and we can call Command::FromJson on it (Command::FromJson can handle parsing both legacy or modern)
// if there is no "command" field, then it is a modern style keys block
// if there are keys, extract them first
Control::KeyChord keys{ nullptr };
if (withKeybindings && jsonBlock.isMember(JsonKey(KeysKey)))
{
const auto keysJson{ jsonBlock[JsonKey(KeysKey)] };
if (keysJson.isArray() && keysJson.size() > 1)
{
warnings.push_back(SettingsLoadWarnings::TooManyKeysForChord);
}
else
{
JsonUtils::GetValueForKey(jsonBlock, KeysKey, keys);
}
}
// Now check if this is a command block
if (jsonBlock.isMember(JsonKey(CommandsKey)) || jsonBlock.isMember(JsonKey(ActionKey)))
{
AddAction(*Command::FromJson(jsonBlock, warnings, origin), keys);
if (jsonBlock.isMember(JsonKey(KeysKey)))
{
// there are keys in this command block meaning this is the legacy style -
// inform the loader that fixups are needed
_fixupsAppliedDuringLoad = true;
}
if (jsonBlock.isMember(JsonKey(ActionKey)) && !jsonBlock.isMember(JsonKey(IterateOnKey)) && origin == OriginTag::User && !jsonBlock.isMember(JsonKey(IDKey)))
{
// for non-nested non-iterable commands,
// if there's no ID in the command block we will generate one for the user -
// inform the loader that the ID needs to be written into the json
_fixupsAppliedDuringLoad = true;
}
}
else if (keys)
{
// this is not a command block, so it is a keybinding block
// if the "id" field doesn't exist in the json, then idJson will be an empty string which is fine
winrt::hstring idJson;
JsonUtils::GetValueForKey(jsonBlock, IDKey, idJson);
// any existing keybinding with the same keychord in this layer will get overwritten
_KeyMap.insert_or_assign(keys, idJson);
}
}
return warnings;
@@ -60,23 +115,12 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
Json::Value actionList{ Json::ValueType::arrayValue };
// Command serializes to an array of JSON objects.
// This is because a Command may have multiple key chords associated with it.
// The name and icon are only serialized in the first object.
// Example:
// { "name": "Custom Copy", "command": "copy", "keys": "ctrl+c" }
// { "command": "copy", "keys": "ctrl+shift+c" }
// { "command": "copy", "keys": "ctrl+ins" }
auto toJson = [&actionList](const Model::Command& cmd) {
const auto cmdImpl{ winrt::get_self<implementation::Command>(cmd) };
const auto& cmdJsonArray{ cmdImpl->ToJson() };
for (const auto& cmdJson : cmdJsonArray)
{
actionList.append(cmdJson);
}
const auto& cmdJson{ cmdImpl->ToJson() };
actionList.append(cmdJson);
};
// Serialize all standard Command objects in the current layer
for (const auto& [_, cmd] : _ActionMap)
{
toJson(cmd);
@@ -96,4 +140,20 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
return actionList;
}
Json::Value ActionMap::KeyBindingsToJson() const
{
Json::Value keybindingsList{ Json::ValueType::arrayValue };
// Serialize all standard keybinding objects in the current layer
for (const auto& [keys, cmdID] : _KeyMap)
{
Json::Value keyIDPair{ Json::ValueType::objectValue };
JsonUtils::SetValueForKey(keyIDPair, KeysKey, keys);
JsonUtils::SetValueForKey(keyIDPair, IDKey, cmdID);
keybindingsList.append(keyIDPair);
}
return keybindingsList;
}
}

View File

@@ -75,6 +75,7 @@
ON_ALL_ACTIONS(CloseTabsAfter) \
ON_ALL_ACTIONS(TabSearch) \
ON_ALL_ACTIONS(MoveTab) \
ON_ALL_ACTIONS(SaveTask) \
ON_ALL_ACTIONS(BreakIntoDebugger) \
ON_ALL_ACTIONS(TogglePaneReadOnly) \
ON_ALL_ACTIONS(EnablePaneReadOnly) \
@@ -148,6 +149,7 @@
ON_ALL_ACTIONS_WITH_ARGS(SplitPane) \
ON_ALL_ACTIONS_WITH_ARGS(SwitchToTab) \
ON_ALL_ACTIONS_WITH_ARGS(ToggleCommandPalette) \
ON_ALL_ACTIONS_WITH_ARGS(SaveTask) \
ON_ALL_ACTIONS_WITH_ARGS(FocusPane) \
ON_ALL_ACTIONS_WITH_ARGS(ExportBuffer) \
ON_ALL_ACTIONS_WITH_ARGS(ClearBuffer) \

View File

@@ -461,6 +461,7 @@ bool SettingsLoader::FixupUserSettings()
};
auto fixedUp = userSettings.fixupsAppliedDuringLoad;
fixedUp = userSettings.globals->FixupsAppliedDuringLoad() || fixedUp;
fixedUp = RemapColorSchemeForProfile(userSettings.baseLayerProfile) || fixedUp;
for (const auto& profile : userSettings.profiles)
@@ -504,10 +505,6 @@ bool SettingsLoader::FixupUserSettings()
fixedUp = true;
}
// we need to generate an ID for a command in the user settings if it doesn't already have one
auto actionMap{ winrt::get_self<ActionMap>(userSettings.globals->ActionMap()) };
actionMap->GenerateIDsForActions();
return fixedUp;
}

View File

@@ -20,14 +20,6 @@ namespace winrt
namespace WUX = Windows::UI::Xaml;
}
static constexpr std::string_view NameKey{ "name" };
static constexpr std::string_view IDKey{ "id" };
static constexpr std::string_view IconKey{ "icon" };
static constexpr std::string_view ActionKey{ "command" };
static constexpr std::string_view IterateOnKey{ "iterateOn" };
static constexpr std::string_view CommandsKey{ "commands" };
static constexpr std::string_view KeysKey{ "keys" };
static constexpr std::string_view ProfileNameToken{ "${profile.name}" };
static constexpr std::string_view ProfileIconToken{ "${profile.icon}" };
static constexpr std::string_view SchemeNameToken{ "${scheme.name}" };
@@ -43,7 +35,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
command->_Origin = _Origin;
command->_ID = _ID;
command->_ActionAndArgs = *get_self<implementation::ActionAndArgs>(_ActionAndArgs)->Copy();
command->_keyMappings = _keyMappings;
command->_iconPath = _iconPath;
command->_IterateOn = _IterateOn;
@@ -121,7 +112,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
return hstring{ _ID };
}
bool Command::GenerateID()
void Command::GenerateID()
{
if (_ActionAndArgs)
{
@@ -130,10 +121,13 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
_ID = generatedID;
_IDWasGenerated = true;
return true;
}
}
return false;
}
bool Command::IDWasGenerated()
{
return _IDWasGenerated;
}
void Command::Name(const hstring& value)
@@ -144,70 +138,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
}
}
std::vector<Control::KeyChord> Command::KeyMappings() const noexcept
{
return _keyMappings;
}
// Function Description:
// - Add the key chord to the command's list of key mappings.
// - If the key chord was already registered, move it to the back
// of the line, and dispatch a notification that Command::Keys changed.
// Arguments:
// - keys: the new key chord that we are registering this command to
// Return Value:
// - <none>
void Command::RegisterKey(const Control::KeyChord& keys)
{
if (!keys)
{
return;
}
// Remove the KeyChord and add it to the back of the line.
// This makes it so that the main key chord associated with this
// command is updated.
EraseKey(keys);
_keyMappings.push_back(keys);
}
// Function Description:
// - Remove the key chord from the command's list of key mappings.
// Arguments:
// - keys: the key chord that we are unregistering
// Return Value:
// - <none>
void Command::EraseKey(const Control::KeyChord& keys)
{
_keyMappings.erase(std::remove_if(_keyMappings.begin(), _keyMappings.end(), [&keys](const Control::KeyChord& iterKey) {
return keys.Modifiers() == iterKey.Modifiers() && keys.Vkey() == iterKey.Vkey();
}),
_keyMappings.end());
}
// Function Description:
// - Keys is the Command's identifying KeyChord. The command may have multiple keys associated
// with it, but we'll only ever display the most recently added one externally. To do this,
// _keyMappings stores all of the associated key chords, but ensures that the last entry
// is the most recently added one.
// Arguments:
// - <none>
// Return Value:
// - the primary key chord associated with this Command
Control::KeyChord Command::Keys() const noexcept
{
if (_keyMappings.empty())
{
return nullptr;
}
return _keyMappings.back();
}
hstring Command::KeyChordText() const noexcept
{
return KeyChordSerialization::ToString(Keys());
}
hstring Command::IconPath() const noexcept
{
if (_iconPath.has_value())
@@ -281,8 +211,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
// - the newly constructed Command object.
winrt::com_ptr<Command> Command::FromJson(const Json::Value& json,
std::vector<SettingsLoadWarnings>& warnings,
const OriginTag origin,
const bool parseKeys)
const OriginTag origin)
{
auto result = winrt::make_self<Command>();
result->_Origin = origin;
@@ -338,26 +267,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
// create an "invalid" ActionAndArgs
result->_ActionAndArgs = make<implementation::ActionAndArgs>();
}
if (parseKeys)
{
// GH#4239 - If the user provided more than one key
// chord to a "keys" array, warn the user here.
// TODO: GH#1334 - remove this check.
const auto keysJson{ json[JsonKey(KeysKey)] };
if (keysJson.isArray() && keysJson.size() > 1)
{
warnings.push_back(SettingsLoadWarnings::TooManyKeysForChord);
}
else
{
Control::KeyChord keys{ nullptr };
if (JsonUtils::GetValueForKey(json, KeysKey, keys))
{
result->RegisterKey(keys);
}
}
}
}
// If an iterable command doesn't have a name set, we'll still just
@@ -423,14 +332,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
}
// Function Description:
// - Serialize the Command into an array of json actions
// - Serialize the Command into a json value
// Arguments:
// - <none>
// Return Value:
// - an array of serialized actions
// - a serialized command
Json::Value Command::ToJson() const
{
Json::Value cmdList{ Json::ValueType::arrayValue };
Json::Value cmdJson{ Json::ValueType::objectValue };
if (_nestedCommand || _IterateOn != ExpandCommandType::None)
{
@@ -438,15 +347,13 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
// For these, we can trust _originalJson to be correct.
// In fact, we _need_ to use it here because we don't actually deserialize `iterateOn`
// until we expand the command.
cmdList.append(_originalJson);
cmdJson = _originalJson;
}
else if (_keyMappings.empty())
else
{
// only write out one command
Json::Value cmdJson{ Json::ValueType::objectValue };
JsonUtils::SetValueForKey(cmdJson, IconKey, _iconPath);
JsonUtils::SetValueForKey(cmdJson, NameKey, _name);
if (!_ID.empty() && !_IDWasGenerated)
if (!_ID.empty())
{
JsonUtils::SetValueForKey(cmdJson, IDKey, _ID);
}
@@ -455,38 +362,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
cmdJson[JsonKey(ActionKey)] = ActionAndArgs::ToJson(_ActionAndArgs);
}
cmdList.append(cmdJson);
}
else
{
// we'll write out one command per key mapping
for (auto keys{ _keyMappings.begin() }; keys != _keyMappings.end(); ++keys)
{
Json::Value cmdJson{ Json::ValueType::objectValue };
if (keys == _keyMappings.begin())
{
// First iteration also writes icon and name
JsonUtils::SetValueForKey(cmdJson, IconKey, _iconPath);
JsonUtils::SetValueForKey(cmdJson, NameKey, _name);
if (!_ID.empty())
{
JsonUtils::SetValueForKey(cmdJson, IDKey, _ID);
}
}
if (_ActionAndArgs)
{
cmdJson[JsonKey(ActionKey)] = ActionAndArgs::ToJson(_ActionAndArgs);
}
JsonUtils::SetValueForKey(cmdJson, KeysKey, *keys);
cmdList.append(cmdJson);
}
}
return cmdList;
return cmdJson;
}
// Function Description:
@@ -787,7 +665,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
// the command will be run as a directory change instead.
IVector<Model::Command> Command::HistoryToCommands(IVector<winrt::hstring> history,
winrt::hstring currentCommandline,
bool directories)
bool directories,
hstring iconPath)
{
std::wstring cdText = directories ? L"cd " : L"";
auto result = std::vector<Model::Command>();
@@ -819,9 +698,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
auto command = winrt::make_self<Command>();
command->_ActionAndArgs = actionAndArgs;
command->_name = winrt::hstring{ line };
command->_iconPath = directories ?
L"\ue8da" : // OpenLocal (a folder with an arrow pointing up)
L"\ue81c"; // History icon
command->_iconPath = iconPath;
result.push_back(*command);
foundCommands[line] = true;
}

View File

@@ -31,6 +31,14 @@ namespace SettingsModelUnitTests
class CommandTests;
};
static constexpr std::string_view NameKey{ "name" };
static constexpr std::string_view IDKey{ "id" };
static constexpr std::string_view IconKey{ "icon" };
static constexpr std::string_view ActionKey{ "command" };
static constexpr std::string_view IterateOnKey{ "iterateOn" };
static constexpr std::string_view CommandsKey{ "commands" };
static constexpr std::string_view KeysKey{ "keys" };
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
struct Command : CommandT<Command>
@@ -40,8 +48,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
static winrt::com_ptr<Command> FromJson(const Json::Value& json,
std::vector<SettingsLoadWarnings>& warnings,
const OriginTag origin,
const bool parseKeys = true);
const OriginTag origin);
static void ExpandCommands(Windows::Foundation::Collections::IMap<winrt::hstring, Model::Command>& commands,
Windows::Foundation::Collections::IVectorView<Model::Profile> profiles,
@@ -62,13 +69,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
void Name(const hstring& name);
hstring ID() const noexcept;
bool GenerateID();
Control::KeyChord Keys() const noexcept;
hstring KeyChordText() const noexcept;
std::vector<Control::KeyChord> KeyMappings() const noexcept;
void RegisterKey(const Control::KeyChord& keys);
void EraseKey(const Control::KeyChord& keys);
void GenerateID();
bool IDWasGenerated();
hstring IconPath() const noexcept;
void IconPath(const hstring& val);
@@ -76,7 +78,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
static Windows::Foundation::Collections::IVector<Model::Command> ParsePowerShellMenuComplete(winrt::hstring json, int32_t replaceLength);
static Windows::Foundation::Collections::IVector<Model::Command> HistoryToCommands(Windows::Foundation::Collections::IVector<winrt::hstring> history,
winrt::hstring currentCommandline,
bool directories);
bool directories,
hstring iconPath);
WINRT_PROPERTY(ExpandCommandType, IterateOn, ExpandCommandType::None);
WINRT_PROPERTY(Model::ActionAndArgs, ActionAndArgs);
@@ -85,7 +88,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
private:
Json::Value _originalJson;
Windows::Foundation::Collections::IMap<winrt::hstring, Model::Command> _subcommands{ nullptr };
std::vector<Control::KeyChord> _keyMappings;
std::optional<std::wstring> _name;
std::wstring _ID;
bool _IDWasGenerated{ false };

View File

@@ -38,9 +38,6 @@ namespace Microsoft.Terminal.Settings.Model
String Name { get; };
String ID { get; };
ActionAndArgs ActionAndArgs { get; };
Microsoft.Terminal.Control.KeyChord Keys { get; };
void RegisterKey(Microsoft.Terminal.Control.KeyChord keys);
String KeyChordText { get; };
String IconPath;
@@ -48,7 +45,7 @@ namespace Microsoft.Terminal.Settings.Model
Windows.Foundation.Collections.IMapView<String, Command> NestedCommands { get; };
static IVector<Command> ParsePowerShellMenuComplete(String json, Int32 replaceLength);
static IVector<Command> HistoryToCommands(IVector<String> commandHistory, String commandline, Boolean directories);
static IVector<Command> HistoryToCommands(IVector<String> commandHistory, String commandline, Boolean directories, String iconPath);
}
}

View File

@@ -40,6 +40,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
DEFINE_ENUM_MAP(Model::WindowingMode, WindowingMode);
DEFINE_ENUM_MAP(Microsoft::Terminal::Core::MatchMode, MatchMode);
DEFINE_ENUM_MAP(Microsoft::Terminal::Control::GraphicsAPI, GraphicsAPI);
DEFINE_ENUM_MAP(Microsoft::Terminal::Control::TextMeasurement, TextMeasurement);
// Profile Settings
DEFINE_ENUM_MAP(Model::CloseOnExitMode, CloseOnExitMode);

View File

@@ -36,6 +36,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, WindowingMode> WindowingMode();
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Microsoft::Terminal::Core::MatchMode> MatchMode();
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Microsoft::Terminal::Control::GraphicsAPI> GraphicsAPI();
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Microsoft::Terminal::Control::TextMeasurement> TextMeasurement();
// Profile Settings
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, CloseOnExitMode> CloseOnExitMode();

View File

@@ -18,6 +18,7 @@ namespace Microsoft.Terminal.Settings.Model
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Settings.Model.WindowingMode> WindowingMode { get; };
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Core.MatchMode> MatchMode { get; };
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Control.GraphicsAPI> GraphicsAPI { get; };
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Control.TextMeasurement> TextMeasurement { get; };
// Profile Settings
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Settings.Model.CloseOnExitMode> CloseOnExitMode { get; };

View File

@@ -17,7 +17,7 @@ using namespace winrt::Windows::UI::Xaml;
using namespace ::Microsoft::Console;
using namespace winrt::Microsoft::UI::Xaml::Controls;
static constexpr std::string_view LegacyKeybindingsKey{ "keybindings" };
static constexpr std::string_view KeybindingsKey{ "keybindings" };
static constexpr std::string_view ActionsKey{ "actions" };
static constexpr std::string_view ThemeKey{ "theme" };
static constexpr std::string_view DefaultProfileKey{ "defaultProfile" };
@@ -45,6 +45,7 @@ void GlobalAppSettings::_FinalizeInheritance()
}
}
}
_actionMap->_FinalizeInheritance();
}
winrt::com_ptr<GlobalAppSettings> GlobalAppSettings::Copy() const
@@ -155,7 +156,9 @@ void GlobalAppSettings::LayerJson(const Json::Value& json, const OriginTag origi
void GlobalAppSettings::LayerActionsFrom(const Json::Value& json, const OriginTag origin, const bool withKeybindings)
{
static constexpr std::array bindingsKeys{ LegacyKeybindingsKey, ActionsKey };
// we want to do the keybindings map after the actions map so that we overwrite any leftover keybindings
// that might have existed in the first pass, in case the user did a partial update from legacy to modern
static constexpr std::array bindingsKeys{ ActionsKey, KeybindingsKey };
for (const auto& jsonKey : bindingsKeys)
{
if (auto bindings{ json[JsonKey(jsonKey)] })
@@ -240,6 +243,11 @@ Json::Value GlobalAppSettings::ToJson()
{
_GraphicsAPI.reset();
}
if (_TextMeasurement == Control::TextMeasurement::Graphemes)
{
_TextMeasurement.reset();
}
if (_DisablePartialInvalidation == false)
{
_DisablePartialInvalidation.reset();
@@ -259,9 +267,16 @@ Json::Value GlobalAppSettings::ToJson()
#undef GLOBAL_SETTINGS_TO_JSON
json[JsonKey(ActionsKey)] = _actionMap->ToJson();
json[JsonKey(KeybindingsKey)] = _actionMap->KeyBindingsToJson();
return json;
}
bool GlobalAppSettings::FixupsAppliedDuringLoad()
{
return _actionMap->FixupsAppliedDuringLoad();
}
winrt::Microsoft::Terminal::Settings::Model::Theme GlobalAppSettings::CurrentTheme() noexcept
{
auto requestedTheme = Model::Theme::IsSystemInDarkTheme() ?

View File

@@ -53,6 +53,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
void LayerActionsFrom(const Json::Value& json, const OriginTag origin, const bool withKeybindings = true);
Json::Value ToJson();
bool FixupsAppliedDuringLoad();
const std::vector<SettingsLoadWarnings>& KeybindingsWarnings() const;

View File

@@ -79,6 +79,7 @@ namespace Microsoft.Terminal.Settings.Model
INHERITABLE_SETTING(Microsoft.Terminal.Control.GraphicsAPI, GraphicsAPI);
INHERITABLE_SETTING(Boolean, DisablePartialInvalidation);
INHERITABLE_SETTING(Boolean, SoftwareRendering);
INHERITABLE_SETTING(Microsoft.Terminal.Control.TextMeasurement, TextMeasurement);
INHERITABLE_SETTING(Boolean, UseBackgroundImageForWindow);
INHERITABLE_SETTING(Boolean, ForceVTInput);
INHERITABLE_SETTING(Boolean, DebugFeaturesEnabled);

View File

@@ -27,6 +27,7 @@ Author(s):
X(winrt::Microsoft::Terminal::Control::GraphicsAPI, GraphicsAPI, "rendering.graphicsAPI") \
X(bool, DisablePartialInvalidation, "rendering.disablePartialInvalidation", false) \
X(bool, SoftwareRendering, "rendering.software", false) \
X(winrt::Microsoft::Terminal::Control::TextMeasurement, TextMeasurement, "compatibility.textMeasurement") \
X(bool, UseBackgroundImageForWindow, "experimental.useBackgroundImageForWindow", false) \
X(bool, ForceVTInput, "experimental.input.forceVT", false) \
X(bool, TrimBlockSelection, "trimBlockSelection", true) \
@@ -136,9 +137,10 @@ Author(s):
// * ForegroundKey, BackgroundKey, SelectionBackgroundKey, CursorColorKey: all optional colors
// * Opacity: needs special parsing
#define MTSM_THEME_SETTINGS(X) \
X(winrt::Microsoft::Terminal::Settings::Model::WindowTheme, Window, "window", nullptr) \
X(winrt::Microsoft::Terminal::Settings::Model::TabRowTheme, TabRow, "tabRow", nullptr) \
#define MTSM_THEME_SETTINGS(X) \
X(winrt::Microsoft::Terminal::Settings::Model::WindowTheme, Window, "window", nullptr) \
X(winrt::Microsoft::Terminal::Settings::Model::SettingsTheme, Settings, "settings", nullptr) \
X(winrt::Microsoft::Terminal::Settings::Model::TabRowTheme, TabRow, "tabRow", nullptr) \
X(winrt::Microsoft::Terminal::Settings::Model::TabTheme, Tab, "tab", nullptr)
#define MTSM_THEME_WINDOW_SETTINGS(X) \
@@ -148,6 +150,9 @@ Author(s):
X(bool, RainbowFrame, "experimental.rainbowFrame", false) \
X(bool, UseMica, "useMica", false)
#define MTSM_THEME_SETTINGS_SETTINGS(X) \
X(winrt::Windows::UI::Xaml::ElementTheme, RequestedTheme, "theme", winrt::Windows::UI::Xaml::ElementTheme::Default)
#define MTSM_THEME_TABROW_SETTINGS(X) \
X(winrt::Microsoft::Terminal::Settings::Model::ThemeColor, Background, "background", nullptr) \
X(winrt::Microsoft::Terminal::Settings::Model::ThemeColor, UnfocusedBackground, "unfocusedBackground", nullptr)

View File

@@ -28,6 +28,9 @@
<ClInclude Include="SeparatorEntry.h">
<DependentUpon>NewTabMenuEntry.idl</DependentUpon>
</ClInclude>
<ClInclude Include="ActionEntry.h">
<DependentUpon>NewTabMenuEntry.idl</DependentUpon>
</ClInclude>
<ClInclude Include="FolderEntry.h">
<DependentUpon>NewTabMenuEntry.idl</DependentUpon>
</ClInclude>
@@ -185,6 +188,9 @@
<ClCompile Include="SeparatorEntry.cpp">
<DependentUpon>NewTabMenuEntry.idl</DependentUpon>
</ClCompile>
<ClCompile Include="ActionEntry.cpp">
<DependentUpon>NewTabMenuEntry.idl</DependentUpon>
</ClCompile>
<ClCompile Include="FolderEntry.cpp">
<DependentUpon>NewTabMenuEntry.idl</DependentUpon>
</ClCompile>

View File

@@ -8,6 +8,7 @@
#include "SeparatorEntry.h"
#include "FolderEntry.h"
#include "ProfileEntry.h"
#include "ActionEntry.h"
#include "RemainingProfilesEntry.h"
#include "MatchProfilesEntry.h"
@@ -52,6 +53,8 @@ winrt::com_ptr<NewTabMenuEntry> NewTabMenuEntry::FromJson(const Json::Value& jso
return RemainingProfilesEntry::FromJson(json);
case NewTabMenuEntryType::MatchProfiles:
return MatchProfilesEntry::FromJson(json);
case NewTabMenuEntryType::Action:
return ActionEntry::FromJson(json);
default:
return nullptr;
}

View File

@@ -12,7 +12,8 @@ namespace Microsoft.Terminal.Settings.Model
Separator,
Folder,
RemainingProfiles,
MatchProfiles
MatchProfiles,
Action
};
[default_interface] unsealed runtimeclass NewTabMenuEntry
@@ -34,6 +35,13 @@ namespace Microsoft.Terminal.Settings.Model
Int32 ProfileIndex;
}
[default_interface] runtimeclass ActionEntry : NewTabMenuEntry
{
ActionEntry();
String ActionId;
}
enum FolderEntryInlining
{
Never = 0,

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.
-->
@@ -126,6 +126,9 @@
<data name="SplitPaneParentCommandName" xml:space="preserve">
<value>Split pane</value>
</data>
<data name="SnippetsPaneCommandName" xml:space="preserve">
<value>Open snippets pane</value>
</data>
<data name="ApplicationDisplayNamePortable" xml:space="preserve">
<value>Terminal (Portable)</value>
<comment>This display name is used when the Terminal application is running in a "portable" mode, where settings are not stored in a shared location.</comment>
@@ -727,4 +730,11 @@
<value>Open about dialog</value>
<comment>This will open the "about" dialog, to display version info and other documentation</comment>
</data>
</root>
<data name="SaveActionNamePrefix" xml:space="preserve">
<value>Save Task</value>
</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>
</root>

View File

@@ -367,6 +367,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
_GraphicsAPI = globalSettings.GraphicsAPI();
_DisablePartialInvalidation = globalSettings.DisablePartialInvalidation();
_SoftwareRendering = globalSettings.SoftwareRendering();
_TextMeasurement = globalSettings.TextMeasurement();
_UseBackgroundImageForWindow = globalSettings.UseBackgroundImageForWindow();
_ForceVTInput = globalSettings.ForceVTInput();
_TrimBlockSelection = globalSettings.TrimBlockSelection();

View File

@@ -158,6 +158,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
INHERITABLE_SETTING(Model::TerminalSettings, Microsoft::Terminal::Control::GraphicsAPI, GraphicsAPI);
INHERITABLE_SETTING(Model::TerminalSettings, bool, DisablePartialInvalidation, false);
INHERITABLE_SETTING(Model::TerminalSettings, bool, SoftwareRendering, false);
INHERITABLE_SETTING(Model::TerminalSettings, Microsoft::Terminal::Control::TextMeasurement, TextMeasurement);
INHERITABLE_SETTING(Model::TerminalSettings, bool, UseBackgroundImageForWindow, false);
INHERITABLE_SETTING(Model::TerminalSettings, bool, ForceVTInput, false);

View File

@@ -501,11 +501,12 @@ JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::FindMatchDirecti
JSON_FLAG_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::SuggestionsSource)
{
static constexpr std::array<pair_type, 5> mappings = {
static constexpr std::array<pair_type, 6> mappings = {
pair_type{ "none", AllClear },
pair_type{ "tasks", ValueType::Tasks },
pair_type{ "commandHistory", ValueType::CommandHistory },
pair_type{ "directoryHistory", ValueType::DirectoryHistory },
pair_type{ "quickFix", ValueType::QuickFixes },
pair_type{ "all", AllSet },
};
};
@@ -678,8 +679,9 @@ JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Control::ScrollToMarkDirection)
// Possible NewTabMenuEntryType values
JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::NewTabMenuEntryType)
{
JSON_MAPPINGS(5) = {
JSON_MAPPINGS(6) = {
pair_type{ "profile", ValueType::Profile },
pair_type{ "action", ValueType::Action },
pair_type{ "separator", ValueType::Separator },
pair_type{ "folder", ValueType::Folder },
pair_type{ "remainingProfiles", ValueType::RemainingProfiles },
@@ -770,3 +772,12 @@ JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Control::GraphicsAPI)
pair_type{ "direct3d11", ValueType::Direct3D11 },
};
};
JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Control::TextMeasurement)
{
JSON_MAPPINGS(3) = {
pair_type{ "graphemes", ValueType::Graphemes },
pair_type{ "wcswidth", ValueType::Wcswidth },
pair_type{ "console", ValueType::Console },
};
};

View File

@@ -9,6 +9,7 @@
#include "JsonUtils.h"
#include "TerminalSettingsSerializationHelpers.h"
#include "SettingsTheme.g.h"
#include "ThemeColor.g.cpp"
#include "WindowTheme.g.cpp"
#include "TabRowTheme.g.cpp"
@@ -56,6 +57,7 @@ static constexpr wchar_t RegKeyAccentColor[] = L"AccentColor";
}
THEME_OBJECT(WindowTheme, MTSM_THEME_WINDOW_SETTINGS);
THEME_OBJECT(SettingsTheme, MTSM_THEME_SETTINGS_SETTINGS);
THEME_OBJECT(TabRowTheme, MTSM_THEME_TABROW_SETTINGS);
THEME_OBJECT(TabTheme, MTSM_THEME_TAB_SETTINGS);
@@ -219,6 +221,7 @@ uint8_t ThemeColor::UnfocusedTabOpacity() const noexcept
};
THEME_OBJECT_CONVERTER(winrt::Microsoft::Terminal::Settings::Model, WindowTheme, MTSM_THEME_WINDOW_SETTINGS);
THEME_OBJECT_CONVERTER(winrt::Microsoft::Terminal::Settings::Model, SettingsTheme, MTSM_THEME_SETTINGS_SETTINGS);
THEME_OBJECT_CONVERTER(winrt::Microsoft::Terminal::Settings::Model, TabRowTheme, MTSM_THEME_TABROW_SETTINGS);
THEME_OBJECT_CONVERTER(winrt::Microsoft::Terminal::Settings::Model, TabTheme, MTSM_THEME_TAB_SETTINGS);
@@ -251,6 +254,10 @@ winrt::com_ptr<Theme> Theme::Copy() const
{
theme->_Tab = *winrt::get_self<implementation::TabTheme>(_Tab)->Copy();
}
if (_Settings)
{
theme->_Settings = *winrt::get_self<implementation::SettingsTheme>(_Settings)->Copy();
}
return theme;
}

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