Files
zed/crates/language_selector/src/active_buffer_language.rs
Finn Evers f393138711 Fix keybind hints flickering in certain scenarios (#40927)
Closes #39172

This refactors when we resolve UI keybindings in an effort to reduce
flickering whilst painting these: Previously, we would always resolve
these upon creating the binding. This could lead to cases where the
corresponding context was not yet available and no binding could be
resolved, even if the binding was then available on the next presented
frame. Following that, on the next rerender of whatever requested this
keybinding, the keybind for that context would then be found, we would
render that and then also win a layout shift in that process, as we went
from nothing rendered to something rendered between these frames.

With these changes, this now happens less often, because we only look
for the keybinding once the context can actually be resolved in the
window.

| Before | After | 
| --- | --- |
|
https://github.com/user-attachments/assets/adebf8ac-217d-4c7f-ae5a-bab3aa0b0ee8
|
https://github.com/user-attachments/assets/70a82b4b-488f-4a9f-94d7-b6d0a49aada9
|

Also reduced cloning in the keymap editor in this process, since that
requiered changing due to this anyway.

Release Notes:

- Fixed some cases where keybinds would appear with a slight delay,
causing a flicker in the process
2025-10-22 19:52:38 +00:00

90 lines
3.0 KiB
Rust

use editor::Editor;
use gpui::{
Context, Entity, IntoElement, ParentElement, Render, Styled, Subscription, WeakEntity, Window,
div,
};
use language::LanguageName;
use settings::Settings as _;
use ui::{Button, ButtonCommon, Clickable, FluentBuilder, LabelSize, Tooltip};
use workspace::{StatusBarSettings, StatusItemView, Workspace, item::ItemHandle};
use crate::{LanguageSelector, Toggle};
pub struct ActiveBufferLanguage {
active_language: Option<Option<LanguageName>>,
workspace: WeakEntity<Workspace>,
_observe_active_editor: Option<Subscription>,
}
impl ActiveBufferLanguage {
pub fn new(workspace: &Workspace) -> Self {
Self {
active_language: None,
workspace: workspace.weak_handle(),
_observe_active_editor: None,
}
}
fn update_language(&mut self, editor: Entity<Editor>, _: &mut Window, cx: &mut Context<Self>) {
self.active_language = Some(None);
let editor = editor.read(cx);
if let Some((_, buffer, _)) = editor.active_excerpt(cx)
&& let Some(language) = buffer.read(cx).language()
{
self.active_language = Some(Some(language.name()));
}
cx.notify();
}
}
impl Render for ActiveBufferLanguage {
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
if !StatusBarSettings::get_global(cx).active_language_button {
return div().hidden();
}
div().when_some(self.active_language.as_ref(), |el, active_language| {
let active_language_text = if let Some(active_language_text) = active_language {
active_language_text.to_string()
} else {
"Unknown".to_string()
};
el.child(
Button::new("change-language", active_language_text)
.label_size(LabelSize::Small)
.on_click(cx.listener(|this, _, window, cx| {
if let Some(workspace) = this.workspace.upgrade() {
workspace.update(cx, |workspace, cx| {
LanguageSelector::toggle(workspace, window, cx)
});
}
}))
.tooltip(|_window, cx| Tooltip::for_action("Select Language", &Toggle, cx)),
)
})
}
}
impl StatusItemView for ActiveBufferLanguage {
fn set_active_pane_item(
&mut self,
active_pane_item: Option<&dyn ItemHandle>,
window: &mut Window,
cx: &mut Context<Self>,
) {
if let Some(editor) = active_pane_item.and_then(|item| item.downcast::<Editor>()) {
self._observe_active_editor =
Some(cx.observe_in(&editor, window, Self::update_language));
self.update_language(editor, window, cx);
} else {
self.active_language = None;
self._observe_active_editor = None;
}
cx.notify();
}
}