Add a setting to flash the taskbar when the terminal emits BEL (#8215)
The terminal taskbar icon can now flash when the BEL sequence is emitted, to let the user know something needs their attention. The `BellStyle` setting can now be set to `audible`, `visual` or both or none. When the pane receives a BEL event and the `bellStyle` includes `visual`, we bubble the event up all the way to `AppHost` to handle flashing the taskbar. Closes #1608
This commit is contained in:
parent
16e8a84cfb
commit
1fbcf34ba8
|
@ -27,11 +27,30 @@
|
|||
"type": "string"
|
||||
},
|
||||
"BellStyle": {
|
||||
"enum": [
|
||||
"none",
|
||||
"audible"
|
||||
],
|
||||
"type": "string"
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"audible",
|
||||
"visual"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"audible",
|
||||
"visual",
|
||||
"all",
|
||||
"none"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"ProfileGuid": {
|
||||
"default": "{}",
|
||||
|
@ -800,7 +819,7 @@
|
|||
},
|
||||
"bellStyle": {
|
||||
"default": "audible",
|
||||
"description": "Controls what happens when the application emits a BEL character. When set to \"audible\", the Terminal will play a sound. When set to \"none\", nothing will happen.",
|
||||
"description": "Controls what happens when the application emits a BEL character. When set to \"all\", the Terminal will play a sound and flash the taskbar icon. An array of specific behaviors can also be used. Supported array values include `audible` and `visual`. When set to \"none\", nothing will happen.",
|
||||
"$ref": "#/definitions/BellStyle"
|
||||
},
|
||||
"closeOnExit": {
|
||||
|
|
|
@ -113,6 +113,7 @@ namespace winrt::TerminalApp::implementation
|
|||
FORWARDED_TYPED_EVENT(FocusModeChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, FocusModeChanged);
|
||||
FORWARDED_TYPED_EVENT(FullscreenChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, FullscreenChanged);
|
||||
FORWARDED_TYPED_EVENT(AlwaysOnTopChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, AlwaysOnTopChanged);
|
||||
FORWARDED_TYPED_EVENT(RaiseVisualBell, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, RaiseVisualBell);
|
||||
FORWARDED_TYPED_EVENT(SetTaskbarProgress, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, SetTaskbarProgress);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -66,6 +66,7 @@ namespace TerminalApp
|
|||
event Windows.Foundation.TypedEventHandler<Object, Object> FocusModeChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> FullscreenChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> AlwaysOnTopChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> RaiseVisualBell;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> SetTaskbarProgress;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -353,6 +353,8 @@ void Pane::_ControlConnectionStateChangedHandler(const TermControl& /*sender*/,
|
|||
// - Plays a warning note when triggered by the BEL control character,
|
||||
// using the sound configured for the "Critical Stop" system event.`
|
||||
// This matches the behavior of the Windows Console host.
|
||||
// - Will also flash the taskbar if the bellStyle setting for this profile
|
||||
// has the 'visual' flag set
|
||||
// Arguments:
|
||||
// - <unused>
|
||||
void Pane::_ControlWarningBellHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/,
|
||||
|
@ -366,11 +368,16 @@ void Pane::_ControlWarningBellHandler(const winrt::Windows::Foundation::IInspect
|
|||
auto paneProfile = settings.FindProfile(_profile.value());
|
||||
if (paneProfile)
|
||||
{
|
||||
if (paneProfile.BellStyle() == winrt::Microsoft::Terminal::Settings::Model::BellStyle::Audible)
|
||||
if (WI_IsFlagSet(paneProfile.BellStyle(), winrt::Microsoft::Terminal::Settings::Model::BellStyle::Audible))
|
||||
{
|
||||
const auto soundAlias = reinterpret_cast<LPCTSTR>(SND_ALIAS_SYSTEMHAND);
|
||||
PlaySound(soundAlias, NULL, SND_ALIAS_ID | SND_ASYNC | SND_SENTRY);
|
||||
}
|
||||
if (WI_IsFlagSet(paneProfile.BellStyle(), winrt::Microsoft::Terminal::Settings::Model::BellStyle::Visual))
|
||||
{
|
||||
// Bubble this event up to app host, starting with bubbling to the hosting tab
|
||||
_PaneRaiseVisualBellHandlers(nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2060,3 +2067,4 @@ std::optional<SplitState> Pane::PreCalculateAutoSplit(const std::shared_ptr<Pane
|
|||
}
|
||||
|
||||
DEFINE_EVENT(Pane, GotFocus, _GotFocusHandlers, winrt::delegate<std::shared_ptr<Pane>>);
|
||||
DEFINE_EVENT(Pane, PaneRaiseVisualBell, _PaneRaiseVisualBellHandlers, winrt::delegate<std::shared_ptr<Pane>>);
|
||||
|
|
|
@ -77,6 +77,7 @@ public:
|
|||
|
||||
WINRT_CALLBACK(Closed, winrt::Windows::Foundation::EventHandler<winrt::Windows::Foundation::IInspectable>);
|
||||
DECLARE_EVENT(GotFocus, _GotFocusHandlers, winrt::delegate<std::shared_ptr<Pane>>);
|
||||
DECLARE_EVENT(PaneRaiseVisualBell, _PaneRaiseVisualBellHandlers, winrt::delegate<std::shared_ptr<Pane>>);
|
||||
|
||||
private:
|
||||
struct SnapSizeResult;
|
||||
|
|
|
@ -701,6 +701,19 @@ namespace winrt::TerminalApp::implementation
|
|||
}
|
||||
});
|
||||
|
||||
// The RaiseVisualBell event has been bubbled up to here from the pane,
|
||||
// the next part of the chain is bubbling up to app logic, which will
|
||||
// forward it to app host.
|
||||
newTabImpl->TabRaiseVisualBell([weakTab, weakThis{ get_weak() }]() {
|
||||
auto page{ weakThis.get() };
|
||||
auto tab{ weakTab.get() };
|
||||
|
||||
if (page && tab)
|
||||
{
|
||||
page->_raiseVisualBellHandlers(nullptr, nullptr);
|
||||
}
|
||||
});
|
||||
|
||||
auto tabViewItem = newTabImpl->TabViewItem();
|
||||
_tabView.TabItems().Append(tabViewItem);
|
||||
_ReapplyCompactTabSize();
|
||||
|
@ -2929,5 +2942,6 @@ namespace winrt::TerminalApp::implementation
|
|||
DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(TerminalPage, FocusModeChanged, _focusModeChangedHandlers, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(TerminalPage, FullscreenChanged, _fullscreenChangedHandlers, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(TerminalPage, AlwaysOnTopChanged, _alwaysOnTopChangedHandlers, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(TerminalPage, RaiseVisualBell, _raiseVisualBellHandlers, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(TerminalPage, SetTaskbarProgress, _setTaskbarProgressHandlers, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
}
|
||||
|
|
|
@ -84,6 +84,7 @@ namespace winrt::TerminalApp::implementation
|
|||
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(FocusModeChanged, _focusModeChangedHandlers, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(FullscreenChanged, _fullscreenChangedHandlers, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(AlwaysOnTopChanged, _alwaysOnTopChangedHandlers, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(RaiseVisualBell, _raiseVisualBellHandlers, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(SetTaskbarProgress, _setTaskbarProgressHandlers, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(Initialized, winrt::Windows::Foundation::IInspectable, winrt::Windows::UI::Xaml::RoutedEventArgs);
|
||||
|
||||
|
|
|
@ -483,6 +483,16 @@ namespace winrt::TerminalApp::implementation
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Add a PaneRaiseVisualBell event handler to the Pane. When the pane emits this event,
|
||||
// we need to bubble it all the way to app host. In this part of the chain we bubble it
|
||||
// from the hosting tab to the page.
|
||||
pane->PaneRaiseVisualBell([weakThis](auto&& /*s*/) {
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
tab->_TabRaiseVisualBellHandlers();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -1022,4 +1032,5 @@ namespace winrt::TerminalApp::implementation
|
|||
DEFINE_EVENT(TerminalTab, ActivePaneChanged, _ActivePaneChangedHandlers, winrt::delegate<>);
|
||||
DEFINE_EVENT(TerminalTab, ColorSelected, _colorSelected, winrt::delegate<winrt::Windows::UI::Color>);
|
||||
DEFINE_EVENT(TerminalTab, ColorCleared, _colorCleared, winrt::delegate<>);
|
||||
DEFINE_EVENT(TerminalTab, TabRaiseVisualBell, _TabRaiseVisualBellHandlers, winrt::delegate<>);
|
||||
}
|
||||
|
|
|
@ -69,6 +69,7 @@ namespace winrt::TerminalApp::implementation
|
|||
DECLARE_EVENT(ActivePaneChanged, _ActivePaneChangedHandlers, winrt::delegate<>);
|
||||
DECLARE_EVENT(ColorSelected, _colorSelected, winrt::delegate<winrt::Windows::UI::Color>);
|
||||
DECLARE_EVENT(ColorCleared, _colorCleared, winrt::delegate<>);
|
||||
DECLARE_EVENT(TabRaiseVisualBell, _TabRaiseVisualBellHandlers, winrt::delegate<>);
|
||||
|
||||
private:
|
||||
std::shared_ptr<Pane> _rootPane{ nullptr };
|
||||
|
|
|
@ -10,10 +10,12 @@ namespace Microsoft.Terminal.Settings.Model
|
|||
Always
|
||||
};
|
||||
|
||||
[flags]
|
||||
enum BellStyle
|
||||
{
|
||||
None,
|
||||
Audible
|
||||
Audible = 0x1,
|
||||
Visual = 0x2,
|
||||
All = 0xffffffff
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass Profile {
|
||||
|
|
|
@ -47,12 +47,33 @@ JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::TerminalControl::ScrollbarState)
|
|||
};
|
||||
};
|
||||
|
||||
JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::BellStyle)
|
||||
JSON_FLAG_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::BellStyle)
|
||||
{
|
||||
static constexpr std::array<pair_type, 2> mappings = {
|
||||
pair_type{ "none", ValueType::None },
|
||||
pair_type{ "audible", ValueType::Audible }
|
||||
static constexpr std::array<pair_type, 4> mappings = {
|
||||
pair_type{ "none", AllClear },
|
||||
pair_type{ "audible", ValueType::Audible },
|
||||
pair_type{ "visual", ValueType::Visual },
|
||||
pair_type{ "all", AllSet },
|
||||
};
|
||||
|
||||
auto FromJson(const Json::Value& json)
|
||||
{
|
||||
if (json.isBool())
|
||||
{
|
||||
return json.asBool() ? AllSet : AllClear;
|
||||
}
|
||||
return BaseFlagMapper::FromJson(json);
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return BaseFlagMapper::CanConvert(json) || json.isBool();
|
||||
}
|
||||
|
||||
Json::Value ToJson(const ::winrt::Microsoft::Terminal::Settings::Model::BellStyle& bellStyle)
|
||||
{
|
||||
return BaseFlagMapper::ToJson(bellStyle);
|
||||
}
|
||||
};
|
||||
|
||||
JSON_ENUM_MAPPER(std::tuple<::winrt::Windows::UI::Xaml::HorizontalAlignment, ::winrt::Windows::UI::Xaml::VerticalAlignment>)
|
||||
|
|
|
@ -183,6 +183,7 @@ void AppHost::Initialize()
|
|||
_logic.FullscreenChanged({ this, &AppHost::_FullscreenChanged });
|
||||
_logic.FocusModeChanged({ this, &AppHost::_FocusModeChanged });
|
||||
_logic.AlwaysOnTopChanged({ this, &AppHost::_AlwaysOnTopChanged });
|
||||
_logic.RaiseVisualBell({ this, &AppHost::_RaiseVisualBell });
|
||||
|
||||
_logic.Create();
|
||||
|
||||
|
@ -397,6 +398,17 @@ void AppHost::_AlwaysOnTopChanged(const winrt::Windows::Foundation::IInspectable
|
|||
_window->SetAlwaysOnTop(_logic.AlwaysOnTop());
|
||||
}
|
||||
|
||||
// Method Description
|
||||
// - Called when the app wants to flash the taskbar, indicating to the user that
|
||||
// something needs their attention
|
||||
// Arguments
|
||||
// - <unused>
|
||||
void AppHost::_RaiseVisualBell(const winrt::Windows::Foundation::IInspectable&,
|
||||
const winrt::Windows::Foundation::IInspectable&)
|
||||
{
|
||||
_window->FlashTaskbar();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Called when the IslandWindow has received a WM_MOUSEWHEEL message. This can
|
||||
// happen on some laptops, where their trackpads won't scroll inactive windows
|
||||
|
|
|
@ -40,5 +40,7 @@ private:
|
|||
const winrt::Windows::Foundation::IInspectable& arg);
|
||||
void _AlwaysOnTopChanged(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const winrt::Windows::Foundation::IInspectable& arg);
|
||||
void _RaiseVisualBell(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const winrt::Windows::Foundation::IInspectable& arg);
|
||||
void _WindowMouseWheeled(const til::point coord, const int32_t delta);
|
||||
};
|
||||
|
|
|
@ -585,6 +585,15 @@ void IslandWindow::SetAlwaysOnTop(const bool alwaysOnTop)
|
|||
}
|
||||
}
|
||||
|
||||
// Method Description
|
||||
// - Flash the taskbar icon, indicating to the user that something needs their attention
|
||||
void IslandWindow::FlashTaskbar()
|
||||
{
|
||||
// Using 'false' as the boolean argument ensures that the taskbar is
|
||||
// only flashed if the app window is not focused
|
||||
FlashWindow(_window.get(), false);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Sets the taskbar progress indicator
|
||||
// - We follow the ConEmu style for the state and progress values,
|
||||
|
|
|
@ -35,6 +35,7 @@ public:
|
|||
void FullscreenChanged(const bool fullscreen);
|
||||
void SetAlwaysOnTop(const bool alwaysOnTop);
|
||||
|
||||
void FlashTaskbar();
|
||||
void SetTaskbarProgress(const size_t state, const size_t progress);
|
||||
|
||||
#pragma endregion
|
||||
|
|
Loading…
Reference in a new issue