Skip to content

Commit ba90c58

Browse files
committed
PRE-MERGE #20014 Add settings for "notifying" on "activity"
2 parents b6f9706 + 90fdd9b commit ba90c58

38 files changed

+869
-94
lines changed

doc/cascadia/profiles.schema.json

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2860,6 +2860,78 @@
28602860
"description": "Sets the sound played when the application emits a BEL. When set to an array, the terminal will pick one of those sounds at random.",
28612861
"$ref": "#/$defs/BellSound"
28622862
},
2863+
"notifyOnInactiveOutput": {
2864+
"oneOf": [
2865+
{
2866+
"type": "boolean"
2867+
},
2868+
{
2869+
"type": "array",
2870+
"items": {
2871+
"type": "string",
2872+
"enum": [
2873+
"taskbar",
2874+
"audible",
2875+
"tab",
2876+
"notification"
2877+
]
2878+
}
2879+
},
2880+
{
2881+
"type": "string",
2882+
"enum": [
2883+
"taskbar",
2884+
"audible",
2885+
"tab",
2886+
"notification",
2887+
"all",
2888+
"none"
2889+
]
2890+
}
2891+
],
2892+
"description": "Controls how you are notified when an inactive tab produces new output."
2893+
},
2894+
"notifyOnNextPrompt": {
2895+
"oneOf": [
2896+
{
2897+
"type": "boolean"
2898+
},
2899+
{
2900+
"type": "array",
2901+
"items": {
2902+
"type": "string",
2903+
"enum": [
2904+
"taskbar",
2905+
"audible",
2906+
"tab",
2907+
"notification"
2908+
]
2909+
}
2910+
},
2911+
{
2912+
"type": "string",
2913+
"enum": [
2914+
"taskbar",
2915+
"audible",
2916+
"tab",
2917+
"notification",
2918+
"all",
2919+
"none"
2920+
]
2921+
}
2922+
],
2923+
"description": "Controls how you are notified when a new shell prompt is detected. Requires shell integration."
2924+
},
2925+
"autoDetectRunningCommand": {
2926+
"default": "disabled",
2927+
"description": "Automatically detect when a command is running and show a progress indicator in the tab and taskbar.",
2928+
"enum": [
2929+
"disabled",
2930+
"automatic",
2931+
"progress"
2932+
],
2933+
"type": "string"
2934+
},
28632935
"closeOnExit": {
28642936
"default": "automatic",
28652937
"description": "Sets how the profile reacts to termination or failure to launch. Possible values:\n -\"graceful\" (close when exit is typed or the process exits normally)\n -\"always\" (always close)\n -\"automatic\" (behave as \"graceful\" only for processes launched by terminal, behave as \"always\" otherwise)\n -\"never\" (never close).\ntrue and false are accepted as synonyms for \"graceful\" and \"never\" respectively.",

src/cascadia/TerminalApp/IPaneContent.idl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ namespace TerminalApp
1818

1919
runtimeclass NotificationEventArgs
2020
{
21+
Microsoft.Terminal.Control.OutputNotificationStyle Style { get; };
22+
Boolean OnlyWhenInactive { get; };
2123
String Title { get; };
2224
String Body { get; };
2325
};

src/cascadia/TerminalApp/Tab.cpp

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,12 @@ namespace winrt::TerminalApp::implementation
123123
_bellIndicatorTimer.Stop();
124124
}
125125

126+
void Tab::_ActivityIndicatorTimerTick(const Windows::Foundation::IInspectable& /*sender*/, const Windows::Foundation::IInspectable& /*e*/)
127+
{
128+
ShowActivityIndicator(false);
129+
_activityIndicatorTimer.Stop();
130+
}
131+
126132
// Method Description:
127133
// - Initializes a TabViewItem for this Tab instance.
128134
// Arguments:
@@ -329,6 +335,10 @@ namespace winrt::TerminalApp::implementation
329335
{
330336
ShowBellIndicator(false);
331337
}
338+
if (_tabStatus.ActivityIndicator())
339+
{
340+
ShowActivityIndicator(false);
341+
}
332342
}
333343
}
334344

@@ -459,6 +469,26 @@ namespace winrt::TerminalApp::implementation
459469
_bellIndicatorTimer.Start();
460470
}
461471

472+
void Tab::ShowActivityIndicator(const bool show)
473+
{
474+
ASSERT_UI_THREAD();
475+
476+
_tabStatus.ActivityIndicator(show);
477+
}
478+
479+
void Tab::ActivateActivityIndicatorTimer()
480+
{
481+
ASSERT_UI_THREAD();
482+
483+
if (!_activityIndicatorTimer)
484+
{
485+
_activityIndicatorTimer.Interval(std::chrono::milliseconds(2000));
486+
_activityIndicatorTimer.Tick({ get_weak(), &Tab::_ActivityIndicatorTimerTick });
487+
}
488+
489+
_activityIndicatorTimer.Start();
490+
}
491+
462492
// Method Description:
463493
// - Gets the title string of the last focused terminal control in our tree.
464494
// Returns the empty string if there is no such control.
@@ -1068,6 +1098,7 @@ namespace winrt::TerminalApp::implementation
10681098
dispatcher,
10691099
til::throttled_func_options{
10701100
.delay = std::chrono::milliseconds{ 200 },
1101+
.leading = true,
10711102
.trailing = true,
10721103
},
10731104
[weakThis]() {
@@ -1168,13 +1199,50 @@ namespace winrt::TerminalApp::implementation
11681199

11691200
events.NotificationRequested = content.NotificationRequested(
11701201
winrt::auto_revoke,
1171-
[dispatcher, weakThis](TerminalApp::IPaneContent /*sender*/, auto notifArgs) -> safe_void_coroutine {
1202+
[dispatcher, weakThis](TerminalApp::IPaneContent sender, auto notifArgs) -> safe_void_coroutine {
11721203
const auto weakThisCopy = weakThis;
11731204
co_await wil::resume_foreground(dispatcher);
11741205
if (const auto tab{ weakThisCopy.get() })
11751206
{
1176-
const auto title = notifArgs.Title().empty() ? tab->Title() : notifArgs.Title();
1177-
tab->TabToastNotificationRequested.raise(title, notifArgs.Body());
1207+
const auto activeContent = tab->GetActiveContent();
1208+
const auto isActivePaneContent = activeContent && activeContent == sender;
1209+
1210+
if (notifArgs.OnlyWhenInactive() && isActivePaneContent &&
1211+
tab->_focusState != WUX::FocusState::Unfocused)
1212+
{
1213+
co_return;
1214+
}
1215+
1216+
const auto style = notifArgs.Style();
1217+
1218+
if (WI_IsFlagSet(style, OutputNotificationStyle::Taskbar))
1219+
{
1220+
tab->TabRaiseVisualBell.raise();
1221+
}
1222+
1223+
if (WI_IsFlagSet(style, winrt::Microsoft::Terminal::Control::OutputNotificationStyle::Audible))
1224+
{
1225+
if (const auto termContent{ sender.try_as<TerminalApp::TerminalPaneContent>() })
1226+
{
1227+
termContent.PlayNotificationSound();
1228+
}
1229+
}
1230+
1231+
if (WI_IsFlagSet(style, winrt::Microsoft::Terminal::Control::OutputNotificationStyle::Tab))
1232+
{
1233+
tab->ShowActivityIndicator(true);
1234+
1235+
if (tab->_focusState != WUX::FocusState::Unfocused)
1236+
{
1237+
tab->ActivateActivityIndicatorTimer();
1238+
}
1239+
}
1240+
1241+
if (WI_IsFlagSet(style, winrt::Microsoft::Terminal::Control::OutputNotificationStyle::Notification))
1242+
{
1243+
const auto title = notifArgs.Title().empty() ? tab->Title() : notifArgs.Title();
1244+
tab->TabToastNotificationRequested.raise(title, notifArgs.Body());
1245+
}
11781246
}
11791247
});
11801248

@@ -1414,6 +1482,10 @@ namespace winrt::TerminalApp::implementation
14141482
{
14151483
tab->ShowBellIndicator(false);
14161484
}
1485+
if (tab->_tabStatus.ActivityIndicator())
1486+
{
1487+
tab->ShowActivityIndicator(false);
1488+
}
14171489
}
14181490
});
14191491

src/cascadia/TerminalApp/Tab.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ namespace winrt::TerminalApp::implementation
4848
void ShowBellIndicator(const bool show);
4949
void ActivateBellIndicatorTimer();
5050

51+
void ShowActivityIndicator(const bool show);
52+
void ActivateActivityIndicatorTimer();
53+
5154
float CalcSnappedDimension(const bool widthOrHeight, const float dimension) const;
5255
std::optional<winrt::Microsoft::Terminal::Settings::Model::SplitDirection> PreCalculateCanSplit(winrt::Microsoft::Terminal::Settings::Model::SplitDirection splitType,
5356
const float splitSize,
@@ -215,6 +218,9 @@ namespace winrt::TerminalApp::implementation
215218
SafeDispatcherTimer _bellIndicatorTimer;
216219
void _BellIndicatorTimerTick(const Windows::Foundation::IInspectable& sender, const Windows::Foundation::IInspectable& e);
217220

221+
SafeDispatcherTimer _activityIndicatorTimer;
222+
void _ActivityIndicatorTimerTick(const Windows::Foundation::IInspectable& sender, const Windows::Foundation::IInspectable& e);
223+
218224
void _UpdateHeaderControlMaxWidth();
219225

220226
void _CreateContextMenu();

src/cascadia/TerminalApp/TabHeaderControl.xaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@
3232
FontSize="12"
3333
Glyph="&#xEA8F;"
3434
Visibility="{x:Bind TabStatus.BellIndicator, Mode=OneWay}" />
35+
<FontIcon x:Name="HeaderActivityIndicator"
36+
Margin="0,0,8,0"
37+
FontFamily="{ThemeResource SymbolThemeFontFamily}"
38+
FontSize="8"
39+
Glyph="&#xF127;"
40+
Visibility="{x:Bind TabStatus.ActivityIndicator, Mode=OneWay}" />
3541
<FontIcon x:Name="HeaderZoomIcon"
3642
Margin="0,0,8,0"
3743
FontFamily="{ThemeResource SymbolThemeFontFamily}"

src/cascadia/TerminalApp/TabManagement.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1341,8 +1341,8 @@ namespace winrt::TerminalApp::implementation
13411341
{
13421342
// The toast Activated callback runs on a background thread.
13431343
// Marshal to the UI thread for tab focus and window summon.
1344-
page->Dispatcher().RunAsync(winrt::Windows::UI::Core::CoreDispatcherPriority::Normal, [weakPage{ page->get_weak() }, weakTab]() {
1345-
if (const auto p{ weakPage.get() })
1344+
page->Dispatcher().RunAsync(winrt::Windows::UI::Core::CoreDispatcherPriority::Normal, [weakThis, weakTab]() {
1345+
if (const auto p{ weakThis.get() })
13461346
{
13471347
if (const auto t{ weakTab.get() })
13481348
{

0 commit comments

Comments
 (0)