Skip to content

Commit a6af0d5

Browse files
committed
PRE-MERGE #20014 Add settings for "notifying" on "activity"
2 parents 0d8a5d0 + 98a4ccb commit a6af0d5

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
@@ -2867,6 +2867,78 @@
28672867
"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.",
28682868
"$ref": "#/$defs/BellSound"
28692869
},
2870+
"notifyOnInactiveOutput": {
2871+
"oneOf": [
2872+
{
2873+
"type": "boolean"
2874+
},
2875+
{
2876+
"type": "array",
2877+
"items": {
2878+
"type": "string",
2879+
"enum": [
2880+
"taskbar",
2881+
"audible",
2882+
"tab",
2883+
"notification"
2884+
]
2885+
}
2886+
},
2887+
{
2888+
"type": "string",
2889+
"enum": [
2890+
"taskbar",
2891+
"audible",
2892+
"tab",
2893+
"notification",
2894+
"all",
2895+
"none"
2896+
]
2897+
}
2898+
],
2899+
"description": "Controls how you are notified when an inactive tab produces new output."
2900+
},
2901+
"notifyOnNextPrompt": {
2902+
"oneOf": [
2903+
{
2904+
"type": "boolean"
2905+
},
2906+
{
2907+
"type": "array",
2908+
"items": {
2909+
"type": "string",
2910+
"enum": [
2911+
"taskbar",
2912+
"audible",
2913+
"tab",
2914+
"notification"
2915+
]
2916+
}
2917+
},
2918+
{
2919+
"type": "string",
2920+
"enum": [
2921+
"taskbar",
2922+
"audible",
2923+
"tab",
2924+
"notification",
2925+
"all",
2926+
"none"
2927+
]
2928+
}
2929+
],
2930+
"description": "Controls how you are notified when a new shell prompt is detected. Requires shell integration."
2931+
},
2932+
"autoDetectRunningCommand": {
2933+
"default": "disabled",
2934+
"description": "Automatically detect when a command is running and show a progress indicator in the tab and taskbar.",
2935+
"enum": [
2936+
"disabled",
2937+
"automatic",
2938+
"progress"
2939+
],
2940+
"type": "string"
2941+
},
28702942
"closeOnExit": {
28712943
"default": "automatic",
28722944
"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
@@ -19,6 +19,8 @@ namespace TerminalApp
1919

2020
runtimeclass NotificationEventArgs
2121
{
22+
Microsoft.Terminal.Control.OutputNotificationStyle Style { get; };
23+
Boolean OnlyWhenInactive { get; };
2224
String Title { get; };
2325
String Body { get; };
2426
};

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]() {
@@ -1176,13 +1207,50 @@ namespace winrt::TerminalApp::implementation
11761207

11771208
events.NotificationRequested = content.NotificationRequested(
11781209
winrt::auto_revoke,
1179-
[dispatcher, weakThis](TerminalApp::IPaneContent /*sender*/, auto notifArgs) -> safe_void_coroutine {
1210+
[dispatcher, weakThis](TerminalApp::IPaneContent sender, auto notifArgs) -> safe_void_coroutine {
11801211
const auto weakThisCopy = weakThis;
11811212
co_await wil::resume_foreground(dispatcher);
11821213
if (const auto tab{ weakThisCopy.get() })
11831214
{
1184-
const auto title = notifArgs.Title().empty() ? tab->Title() : notifArgs.Title();
1185-
tab->TabToastNotificationRequested.raise(title, notifArgs.Body());
1215+
const auto activeContent = tab->GetActiveContent();
1216+
const auto isActivePaneContent = activeContent && activeContent == sender;
1217+
1218+
if (notifArgs.OnlyWhenInactive() && isActivePaneContent &&
1219+
tab->_focusState != WUX::FocusState::Unfocused)
1220+
{
1221+
co_return;
1222+
}
1223+
1224+
const auto style = notifArgs.Style();
1225+
1226+
if (WI_IsFlagSet(style, OutputNotificationStyle::Taskbar))
1227+
{
1228+
tab->TabRaiseVisualBell.raise();
1229+
}
1230+
1231+
if (WI_IsFlagSet(style, winrt::Microsoft::Terminal::Control::OutputNotificationStyle::Audible))
1232+
{
1233+
if (const auto termContent{ sender.try_as<TerminalApp::TerminalPaneContent>() })
1234+
{
1235+
termContent.PlayNotificationSound();
1236+
}
1237+
}
1238+
1239+
if (WI_IsFlagSet(style, winrt::Microsoft::Terminal::Control::OutputNotificationStyle::Tab))
1240+
{
1241+
tab->ShowActivityIndicator(true);
1242+
1243+
if (tab->_focusState != WUX::FocusState::Unfocused)
1244+
{
1245+
tab->ActivateActivityIndicatorTimer();
1246+
}
1247+
}
1248+
1249+
if (WI_IsFlagSet(style, winrt::Microsoft::Terminal::Control::OutputNotificationStyle::Notification))
1250+
{
1251+
const auto title = notifArgs.Title().empty() ? tab->Title() : notifArgs.Title();
1252+
tab->TabToastNotificationRequested.raise(title, notifArgs.Body());
1253+
}
11861254
}
11871255
});
11881256

@@ -1422,6 +1490,10 @@ namespace winrt::TerminalApp::implementation
14221490
{
14231491
tab->ShowBellIndicator(false);
14241492
}
1493+
if (tab->_tabStatus.ActivityIndicator())
1494+
{
1495+
tab->ShowActivityIndicator(false);
1496+
}
14251497
}
14261498
});
14271499

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)