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
This commit is contained in:
@@ -616,12 +616,11 @@ impl DebugPanel {
|
||||
})
|
||||
.tooltip({
|
||||
let focus_handle = focus_handle.clone();
|
||||
move |window, cx| {
|
||||
move |_window, cx| {
|
||||
Tooltip::for_action_in(
|
||||
"Start Debug Session",
|
||||
&crate::Start,
|
||||
&focus_handle,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
@@ -694,12 +693,11 @@ impl DebugPanel {
|
||||
))
|
||||
.tooltip({
|
||||
let focus_handle = focus_handle.clone();
|
||||
move |window, cx| {
|
||||
move |_window, cx| {
|
||||
Tooltip::for_action_in(
|
||||
"Pause Program",
|
||||
&Pause,
|
||||
&focus_handle,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
@@ -719,12 +717,11 @@ impl DebugPanel {
|
||||
.disabled(thread_status != ThreadStatus::Stopped)
|
||||
.tooltip({
|
||||
let focus_handle = focus_handle.clone();
|
||||
move |window, cx| {
|
||||
move |_window, cx| {
|
||||
Tooltip::for_action_in(
|
||||
"Continue Program",
|
||||
&Continue,
|
||||
&focus_handle,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
@@ -744,12 +741,11 @@ impl DebugPanel {
|
||||
.disabled(thread_status != ThreadStatus::Stopped)
|
||||
.tooltip({
|
||||
let focus_handle = focus_handle.clone();
|
||||
move |window, cx| {
|
||||
move |_window, cx| {
|
||||
Tooltip::for_action_in(
|
||||
"Step Over",
|
||||
&StepOver,
|
||||
&focus_handle,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
@@ -770,12 +766,11 @@ impl DebugPanel {
|
||||
.disabled(thread_status != ThreadStatus::Stopped)
|
||||
.tooltip({
|
||||
let focus_handle = focus_handle.clone();
|
||||
move |window, cx| {
|
||||
move |_window, cx| {
|
||||
Tooltip::for_action_in(
|
||||
"Step In",
|
||||
&StepInto,
|
||||
&focus_handle,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
@@ -793,12 +788,11 @@ impl DebugPanel {
|
||||
.disabled(thread_status != ThreadStatus::Stopped)
|
||||
.tooltip({
|
||||
let focus_handle = focus_handle.clone();
|
||||
move |window, cx| {
|
||||
move |_window, cx| {
|
||||
Tooltip::for_action_in(
|
||||
"Step Out",
|
||||
&StepOut,
|
||||
&focus_handle,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
@@ -816,12 +810,11 @@ impl DebugPanel {
|
||||
))
|
||||
.tooltip({
|
||||
let focus_handle = focus_handle.clone();
|
||||
move |window, cx| {
|
||||
move |_window, cx| {
|
||||
Tooltip::for_action_in(
|
||||
"Rerun Session",
|
||||
&RerunSession,
|
||||
&focus_handle,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
@@ -861,12 +854,11 @@ impl DebugPanel {
|
||||
} else {
|
||||
"Terminate All Threads"
|
||||
};
|
||||
move |window, cx| {
|
||||
move |_window, cx| {
|
||||
Tooltip::for_action_in(
|
||||
label,
|
||||
&Stop,
|
||||
&focus_handle,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
@@ -893,12 +885,11 @@ impl DebugPanel {
|
||||
))
|
||||
.tooltip({
|
||||
let focus_handle = focus_handle.clone();
|
||||
move |window, cx| {
|
||||
move |_window, cx| {
|
||||
Tooltip::for_action_in(
|
||||
"Detach",
|
||||
&Detach,
|
||||
&focus_handle,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -745,22 +745,15 @@ impl Render for NewProcessModal {
|
||||
== 0;
|
||||
let secondary_action = menu::SecondaryConfirm.boxed_clone();
|
||||
container
|
||||
.child(div().children(
|
||||
KeyBinding::for_action(&*secondary_action, window, cx).map(
|
||||
|keybind| {
|
||||
Button::new("edit-attach-task", "Edit in debug.json")
|
||||
.label_size(LabelSize::Small)
|
||||
.key_binding(keybind)
|
||||
.on_click(move |_, window, cx| {
|
||||
window.dispatch_action(
|
||||
secondary_action.boxed_clone(),
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.disabled(disabled)
|
||||
},
|
||||
),
|
||||
))
|
||||
.child(div().child({
|
||||
Button::new("edit-attach-task", "Edit in debug.json")
|
||||
.label_size(LabelSize::Small)
|
||||
.key_binding(KeyBinding::for_action(&*secondary_action, cx))
|
||||
.on_click(move |_, window, cx| {
|
||||
window.dispatch_action(secondary_action.boxed_clone(), cx)
|
||||
})
|
||||
.disabled(disabled)
|
||||
}))
|
||||
.child(
|
||||
h_flex()
|
||||
.child(div().child(self.adapter_drop_down_menu(window, cx))),
|
||||
@@ -1447,56 +1440,48 @@ impl PickerDelegate for DebugDelegate {
|
||||
.justify_between()
|
||||
.border_t_1()
|
||||
.border_color(cx.theme().colors().border_variant)
|
||||
.children({
|
||||
.child({
|
||||
let action = menu::SecondaryConfirm.boxed_clone();
|
||||
if self.matches.is_empty() {
|
||||
Some(
|
||||
Button::new("edit-debug-json", "Edit debug.json")
|
||||
.label_size(LabelSize::Small)
|
||||
.on_click(cx.listener(|_picker, _, window, cx| {
|
||||
window.dispatch_action(
|
||||
zed_actions::OpenProjectDebugTasks.boxed_clone(),
|
||||
cx,
|
||||
);
|
||||
cx.emit(DismissEvent);
|
||||
})),
|
||||
)
|
||||
Button::new("edit-debug-json", "Edit debug.json")
|
||||
.label_size(LabelSize::Small)
|
||||
.on_click(cx.listener(|_picker, _, window, cx| {
|
||||
window.dispatch_action(
|
||||
zed_actions::OpenProjectDebugTasks.boxed_clone(),
|
||||
cx,
|
||||
);
|
||||
cx.emit(DismissEvent);
|
||||
}))
|
||||
} else {
|
||||
KeyBinding::for_action(&*action, window, cx).map(|keybind| {
|
||||
Button::new("edit-debug-task", "Edit in debug.json")
|
||||
.label_size(LabelSize::Small)
|
||||
.key_binding(keybind)
|
||||
.on_click(move |_, window, cx| {
|
||||
window.dispatch_action(action.boxed_clone(), cx)
|
||||
})
|
||||
})
|
||||
Button::new("edit-debug-task", "Edit in debug.json")
|
||||
.label_size(LabelSize::Small)
|
||||
.key_binding(KeyBinding::for_action(&*action, cx))
|
||||
.on_click(move |_, window, cx| {
|
||||
window.dispatch_action(action.boxed_clone(), cx)
|
||||
})
|
||||
}
|
||||
})
|
||||
.map(|this| {
|
||||
if (current_modifiers.alt || self.matches.is_empty()) && !self.prompt.is_empty() {
|
||||
let action = picker::ConfirmInput { secondary: false }.boxed_clone();
|
||||
this.children(KeyBinding::for_action(&*action, window, cx).map(|keybind| {
|
||||
this.child({
|
||||
Button::new("launch-custom", "Launch Custom")
|
||||
.key_binding(keybind)
|
||||
.key_binding(KeyBinding::for_action(&*action, cx))
|
||||
.on_click(move |_, window, cx| {
|
||||
window.dispatch_action(action.boxed_clone(), cx)
|
||||
})
|
||||
}))
|
||||
})
|
||||
} else {
|
||||
this.children(KeyBinding::for_action(&menu::Confirm, window, cx).map(
|
||||
|keybind| {
|
||||
let is_recent_selected =
|
||||
self.divider_index >= Some(self.selected_index);
|
||||
let run_entry_label =
|
||||
if is_recent_selected { "Rerun" } else { "Spawn" };
|
||||
this.child({
|
||||
let is_recent_selected = self.divider_index >= Some(self.selected_index);
|
||||
let run_entry_label = if is_recent_selected { "Rerun" } else { "Spawn" };
|
||||
|
||||
Button::new("spawn", run_entry_label)
|
||||
.key_binding(keybind)
|
||||
.on_click(|_, window, cx| {
|
||||
window.dispatch_action(menu::Confirm.boxed_clone(), cx);
|
||||
})
|
||||
},
|
||||
))
|
||||
Button::new("spawn", run_entry_label)
|
||||
.key_binding(KeyBinding::for_action(&menu::Confirm, cx))
|
||||
.on_click(|_, window, cx| {
|
||||
window.dispatch_action(menu::Confirm.boxed_clone(), cx);
|
||||
})
|
||||
})
|
||||
}
|
||||
});
|
||||
Some(footer.into_any_element())
|
||||
|
||||
@@ -566,14 +566,13 @@ pub(crate) fn new_debugger_pane(
|
||||
}))
|
||||
.tooltip({
|
||||
let focus_handle = focus_handle.clone();
|
||||
move |window, cx| {
|
||||
move |_window, cx| {
|
||||
let zoomed_text =
|
||||
if zoomed { "Minimize" } else { "Expand" };
|
||||
Tooltip::for_action_in(
|
||||
zoomed_text,
|
||||
&ToggleExpandItem,
|
||||
&focus_handle,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -607,13 +607,12 @@ impl BreakpointList {
|
||||
.when_some(toggle_label, |this, (label, meta)| {
|
||||
this.tooltip({
|
||||
let focus_handle = focus_handle.clone();
|
||||
move |window, cx| {
|
||||
move |_window, cx| {
|
||||
Tooltip::with_meta_in(
|
||||
label,
|
||||
Some(&ToggleEnableBreakpoint),
|
||||
meta,
|
||||
&focus_handle,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
@@ -634,13 +633,12 @@ impl BreakpointList {
|
||||
.when_some(remove_breakpoint_tooltip, |this, tooltip| {
|
||||
this.tooltip({
|
||||
let focus_handle = focus_handle.clone();
|
||||
move |window, cx| {
|
||||
move |_window, cx| {
|
||||
Tooltip::with_meta_in(
|
||||
"Remove Breakpoint",
|
||||
Some(&UnsetBreakpoint),
|
||||
tooltip,
|
||||
&focus_handle,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
@@ -819,7 +817,7 @@ impl LineBreakpoint {
|
||||
)
|
||||
.tooltip({
|
||||
let focus_handle = focus_handle.clone();
|
||||
move |window, cx| {
|
||||
move |_window, cx| {
|
||||
Tooltip::for_action_in(
|
||||
if is_enabled {
|
||||
"Disable Breakpoint"
|
||||
@@ -828,7 +826,6 @@ impl LineBreakpoint {
|
||||
},
|
||||
&ToggleEnableBreakpoint,
|
||||
&focus_handle,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
@@ -980,7 +977,7 @@ impl DataBreakpoint {
|
||||
)
|
||||
.tooltip({
|
||||
let focus_handle = focus_handle.clone();
|
||||
move |window, cx| {
|
||||
move |_window, cx| {
|
||||
Tooltip::for_action_in(
|
||||
if is_enabled {
|
||||
"Disable Data Breakpoint"
|
||||
@@ -989,7 +986,6 @@ impl DataBreakpoint {
|
||||
},
|
||||
&ToggleEnableBreakpoint,
|
||||
&focus_handle,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
@@ -1085,7 +1081,7 @@ impl ExceptionBreakpoint {
|
||||
)
|
||||
.tooltip({
|
||||
let focus_handle = focus_handle.clone();
|
||||
move |window, cx| {
|
||||
move |_window, cx| {
|
||||
Tooltip::for_action_in(
|
||||
if is_enabled {
|
||||
"Disable Exception Breakpoint"
|
||||
@@ -1094,7 +1090,6 @@ impl ExceptionBreakpoint {
|
||||
},
|
||||
&ToggleEnableBreakpoint,
|
||||
&focus_handle,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
@@ -1402,12 +1397,11 @@ impl RenderOnce for BreakpointOptionsStrip {
|
||||
.disabled(!supports_logs)
|
||||
.toggle_state(self.is_toggled(ActiveBreakpointStripMode::Log))
|
||||
.on_click(self.on_click_callback(ActiveBreakpointStripMode::Log))
|
||||
.tooltip(|window, cx| {
|
||||
.tooltip(|_window, cx| {
|
||||
Tooltip::with_meta(
|
||||
"Set Log Message",
|
||||
None,
|
||||
"Set log message to display (instead of stopping) when a breakpoint is hit.",
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
}),
|
||||
@@ -1438,12 +1432,11 @@ impl RenderOnce for BreakpointOptionsStrip {
|
||||
.disabled(!supports_condition)
|
||||
.toggle_state(self.is_toggled(ActiveBreakpointStripMode::Condition))
|
||||
.on_click(self.on_click_callback(ActiveBreakpointStripMode::Condition))
|
||||
.tooltip(|window, cx| {
|
||||
.tooltip(|_window, cx| {
|
||||
Tooltip::with_meta(
|
||||
"Set Condition",
|
||||
None,
|
||||
"Set condition to evaluate when a breakpoint is hit. Program execution will stop only when the condition is met.",
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
}),
|
||||
@@ -1474,12 +1467,11 @@ impl RenderOnce for BreakpointOptionsStrip {
|
||||
.disabled(!supports_hit_condition)
|
||||
.toggle_state(self.is_toggled(ActiveBreakpointStripMode::HitCondition))
|
||||
.on_click(self.on_click_callback(ActiveBreakpointStripMode::HitCondition))
|
||||
.tooltip(|window, cx| {
|
||||
.tooltip(|_window, cx| {
|
||||
Tooltip::with_meta(
|
||||
"Set Hit Condition",
|
||||
None,
|
||||
"Set expression that controls how many hits of the breakpoint are ignored.",
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
}),
|
||||
|
||||
@@ -484,12 +484,11 @@ impl Render for Console {
|
||||
.tooltip({
|
||||
let query_focus_handle = query_focus_handle.clone();
|
||||
|
||||
move |window, cx| {
|
||||
move |_window, cx| {
|
||||
Tooltip::for_action_in(
|
||||
"Evaluate",
|
||||
&Confirm,
|
||||
&query_focus_handle,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -872,8 +872,8 @@ impl StackFrameList {
|
||||
"filter-by-visible-worktree-stack-frame-list",
|
||||
IconName::ListFilter,
|
||||
)
|
||||
.tooltip(move |window, cx| {
|
||||
Tooltip::for_action(tooltip_title, &ToggleUserFrames, window, cx)
|
||||
.tooltip(move |_window, cx| {
|
||||
Tooltip::for_action(tooltip_title, &ToggleUserFrames, cx)
|
||||
})
|
||||
.toggle_state(self.list_filter == StackFrameFilter::OnlyUserFrames)
|
||||
.icon_size(IconSize::Small)
|
||||
|
||||
@@ -1306,14 +1306,8 @@ impl VariableList {
|
||||
.ok();
|
||||
}
|
||||
})
|
||||
.tooltip(move |window, cx| {
|
||||
Tooltip::for_action_in(
|
||||
"Remove Watch",
|
||||
&RemoveWatch,
|
||||
&focus_handle,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
.tooltip(move |_window, cx| {
|
||||
Tooltip::for_action_in("Remove Watch", &RemoveWatch, &focus_handle, cx)
|
||||
})
|
||||
.icon_size(ui::IconSize::Indicator),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user