-
Notifications
You must be signed in to change notification settings - Fork 8.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a setting to flash the pane when BEL is emitted #9270
Conversation
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have no idea why this doesn't work. This should work - Terminal::SetScreenMode
already does call TriggerRedrawAll()
.
Maybe InvertScreenColors
/_InvertTimerTick
need to take the write lock?
That seems to have fixed it, tested by repeatedly sending a bunch of BELs and the screen reverted back properly |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You have activated my trap card!
Since you updated the mapping for a setting, you are now forced to update the Settings UI. Otherwise, we'll crash when attempting to find the resw entry for these new values.
You also need to update the schema and the docs.
I see the gif in your PR description, but I don't understand what the difference between window
and visual
is. Also, taskbar
is added, but not mentioned in the PR description above.
src/cascadia/TerminalApp/Pane.cpp
Outdated
WI_IsFlagSet(paneProfile.BellStyle(), winrt::Microsoft::Terminal::Settings::Model::BellStyle::Taskbar); | ||
|
||
// raise the event with the bool value corresponding to the taskbar or visual flag | ||
_PaneRaiseBellHandlers(nullptr, flashTaskbar); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, the settings now look like this?
window
: flash terminaltaskbar
: add bell icon to tabvisual
=[ "window", "taskbar" ]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
taskbar
flashes the taskbar icon at the toolbar (if the terminal window is not focused)! The others are correct
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I need to have a think about this :)
## Summary of the Pull Request This replaces `ThrottledFunc` with two variants: * `ThrottledFuncLeading` invokes the callback immediately and blocks further calls for the given duration * `ThrottledFuncTrailing` blocks calls for the given duration and then invokes the callback ## References * #9270 - `ThrottledFuncLeading` will allow the pane to flash immediately for a BEL, but block further BELs until the animation finished ## PR Checklist * [x] I work here * [ ] Tests added/passed ## Validation Steps Performed * [x] Ensured scrolling still works
_PaneRaiseBellHandlers(nullptr, WI_IsFlagSet(paneProfile.BellStyle(), winrt::Microsoft::Terminal::Settings::Model::BellStyle::Visual)); | ||
if (WI_IsFlagSet(paneProfile.BellStyle(), winrt::Microsoft::Terminal::Settings::Model::BellStyle::Window)) | ||
{ | ||
_control.BellLightOn(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this seems like the wrong abstraction-- the app should not tell the control to blink after the control tells the app that there was a bell.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your comment about the visual bell moving up into the application resonates with me. Thank you.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
abstraction concern. if you'd rather keep it this way, let me know!
I think I like the idea of letting the app handle the flash. Though I think it'll be much smoother to do that after the pane refactor goes in - that way we can attach the xaml light to the pane instead of the control's grid. Thoughts? If we want the control to handle the flash, 1. it'll have to know if |
{ | ||
// Stop the timer and switch off the light | ||
_bellLightTimer->Stop(); | ||
_bellLightTimer.reset(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Theoretically, once the timer's been initialized, we don't need to keep creating and destroying it each time. We could just re-use it, but I'm not gonna block over that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good notes for cleanup later. 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, I have nits & questions, but nothing to block over
|
||
// Switch on the light and animate the intensity to fade out | ||
VisualBellLight::SetIsTarget(RootGrid(), true); | ||
BellLight().CompositionLight().StartAnimation(L"Intensity", _bellLightAnimation); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to be clear, the animation runs from Intensity=2.0->1.0, then is switched off entirely at the end of the time. That's neat
@@ -164,8 +167,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation | |||
Windows::UI::Xaml::DispatcherTimer _autoScrollTimer; | |||
std::optional<std::chrono::high_resolution_clock::time_point> _lastAutoScrollUpdateTime; | |||
|
|||
winrt::Windows::UI::Composition::ScalarKeyFrameAnimation _bellLightAnimation; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
meh that seems fine to me. In the future, we could always init this as
winrt::Windows::UI::Composition::ScalarKeyFrameAnimation _bellLightAnimation{ nullptr };
and only ctor it when actually requested in the settings, but again, meh
@@ -627,6 +626,32 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation | |||
_State.Profile().ColorSchemeName(val.Name()); | |||
} | |||
|
|||
bool Profiles::IsBellStyleFlagSet(const uint32_t flag) | |||
{ | |||
return (WI_EnumValue(_State.Profile().BellStyle()) & flag) == flag; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wait uh, what exactly is happening here? Is this just WI_IsFlagSet
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or WI_AreAllFlagsSet
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
explained offline -- this is because the WI macros require a compile-time constant. We have to do it manually.
@@ -630,7 +630,8 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils | |||
for (const auto& pair : TBase::mappings) | |||
{ | |||
if (pair.second != AllClear && | |||
(val & pair.second) == pair.second) | |||
(val & pair.second) == pair.second && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This again looks like a WI_IsFlagSet
, which I think also does WI_IsSingleFlagSet
|
||
void VisualBellLight::OnIsTargetChanged(DependencyObject const& d, DependencyPropertyChangedEventArgs const& e) | ||
{ | ||
const auto uielem{ d.try_as<UIElement>() }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
kinda surprised that uielem
resolved as a word with spellcheck
return VisualBellLight::GetIdStatic(); | ||
} | ||
|
||
void VisualBellLight::OnIsTargetChanged(DependencyObject const& d, DependencyPropertyChangedEventArgs const& e) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think I totally understand what's going on with this method. This is a static function that's called when IsTarget
changes on a VisualBellLight
? And then d
is either a UIElement
, or a Brush
... When is this ever a brush?
Or is this just forward thinking? All this seems like handy boilerplate around XamlLight
, I'm kinda shocked (but also not surprised) that we need this much boilerplate just to add a light.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So yeah I think this is some cool stuff -- he's created an "attached property" (like Grid.Row
-- a property that's about one object, stored in global storage, attached to another object). It looks like this one can be attached to either a brush or a UI Element, seamlessly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep! I've mostly just adopted the code from the xaml light docs. Lights can only target a Brush
or a UIElement
, and yeah in our case we never use it for a brush but I feel like its probably a good idea to have that check anyway
@carlos-zamora can you dismiss or re-review? |
_PaneRaiseBellHandlers(nullptr, WI_IsFlagSet(paneProfile.BellStyle(), winrt::Microsoft::Terminal::Settings::Model::BellStyle::Visual)); | ||
if (WI_IsFlagSet(paneProfile.BellStyle(), winrt::Microsoft::Terminal::Settings::Model::BellStyle::Window)) | ||
{ | ||
_control.BellLightOn(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your comment about the visual bell moving up into the application resonates with me. Thank you.
{ | ||
// Stop the timer and switch off the light | ||
_bellLightTimer->Stop(); | ||
_bellLightTimer.reset(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good notes for cleanup later. 😄
return VisualBellLight::GetIdStatic(); | ||
} | ||
|
||
void VisualBellLight::OnIsTargetChanged(DependencyObject const& d, DependencyPropertyChangedEventArgs const& e) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So yeah I think this is some cool stuff -- he's created an "attached property" (like Grid.Row
-- a property that's about one object, stored in global storage, attached to another object). It looks like this one can be attached to either a brush or a UI Element, seamlessly.
@@ -627,6 +626,32 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation | |||
_State.Profile().ColorSchemeName(val.Name()); | |||
} | |||
|
|||
bool Profiles::IsBellStyleFlagSet(const uint32_t flag) | |||
{ | |||
return (WI_EnumValue(_State.Profile().BellStyle()) & flag) == flag; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
explained offline -- this is because the WI macros require a compile-time constant. We have to do it manually.
src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw
Outdated
Show resolved
Hide resolved
This replaces `ThrottledFunc` with two variants: * `ThrottledFuncLeading` invokes the callback immediately and blocks further calls for the given duration * `ThrottledFuncTrailing` blocks calls for the given duration and then invokes the callback * #9270 - `ThrottledFuncLeading` will allow the pane to flash immediately for a BEL, but block further BELs until the animation finished * [x] I work here * [ ] Tests added/passed * [x] Ensured scrolling still works (cherry picked from commit 13f0b8e)
Hello @DHowett! Because this pull request has the p.s. you can customize the way I help with merging this pull request, such as holding this pull request until a specific person approves. Simply @mention me (
|
🎉 Handy links: |
Just come cleanup I did not manage to get to before #9270 merged. Specifically: - We only initialize the animation and timer if we need them - We don't repeatedly destroy/create the timer ## Validation Steps Performed It still works
Adds a variable `_isBackgroundLight` that is updated when the background color is changed. When it is `true`, the BEL indicator flash will darken the screen instead of brightening. `_isColorLight(bg)` returns `true` if the average of `r`, `g`, and `b` is >127 I was unsure of an appropriate way to change the color of the `CompositionLight` based on the background, so I changed it to always be gray and adjusted the intensity values of the original animation to have roughly the same visual effect as the white. ## Validation Steps Performed * Tested the two flashes on the default color schemes and some custom background colors to see if they look consistent * Used tracepoints and visual to check that the right animation is used (including multiple tabs, split windows with different themes, and changing settings while window is open) References #9270 Closes #13450
Summary of the Pull Request
Adds a new bellStyle called
window
. Whenwindow
is set and a BEL is emitted, we flash the pane that emitted it.Additionally, changes bellStyle in the SUI to a list of checkboxes instead of radio buttons, to match bellStyle being a flag-enum. Deprecates 'BellStyle::Visual' in the schema, but still allows it to be set in the json (it maps to
Window | Taskbar
)References
#6700
PR Checklist
Validation Steps Performed
GIF in Teams