Fix shared element state across component instances
This commit is contained in:
@@ -510,8 +510,12 @@ where
|
||||
if state.highlight.is_some() {
|
||||
state.highlight = None;
|
||||
} else {
|
||||
let query = state.query.read(cx).value().to_string();
|
||||
state.refilter(&query);
|
||||
if state.selected.is_some() {
|
||||
state.refilter("");
|
||||
} else {
|
||||
let query = state.query.read(cx).value().to_string();
|
||||
state.refilter(&query);
|
||||
}
|
||||
state.highlight = (!state.visible.is_empty()).then_some(0usize);
|
||||
}
|
||||
cx.notify();
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
//! The command palette: a modal query field over the application's actions.
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use gpui::prelude::FluentBuilder as _;
|
||||
use gpui::{
|
||||
AnyElement, App, Bounds, Context, ElementId, Entity, InteractiveElement as _, IntoElement,
|
||||
KeyBinding, MouseButton, MouseDownEvent, ParentElement as _, Pixels, Render, Role,
|
||||
ScrollHandle, SharedString, StatefulInteractiveElement as _, Styled as _, Subscription, Window,
|
||||
actions, canvas, div, px, relative,
|
||||
AnyElement, App, Context, ElementId, Entity, InteractiveElement as _, IntoElement, KeyBinding,
|
||||
MouseButton, ParentElement as _, Render, Role, ScrollHandle, SharedString,
|
||||
StatefulInteractiveElement as _, Styled as _, Subscription, Window, actions, canvas, deferred,
|
||||
div, px, relative,
|
||||
};
|
||||
use gpui_native_icons::{IconRole, icon};
|
||||
use gpui_native_theme::{ActiveTheme as _, Size};
|
||||
@@ -322,6 +321,7 @@ impl Render for CommandPaletteState {
|
||||
|
||||
let id = self.id.clone();
|
||||
let state = cx.entity();
|
||||
let query_focus = self.query.read(cx).focus_handle();
|
||||
|
||||
let rows: Vec<AnyElement> = self
|
||||
.matches
|
||||
@@ -406,9 +406,6 @@ impl Render for CommandPaletteState {
|
||||
.into_any_element()
|
||||
};
|
||||
|
||||
let panel_bounds: Rc<RefCell<Option<Bounds<Pixels>>>> = Rc::new(RefCell::new(None));
|
||||
let query_focus = self.query.read(cx).focus_handle();
|
||||
|
||||
let panel = v_flex()
|
||||
.id((id.clone(), "panel"))
|
||||
.debug_selector({
|
||||
@@ -460,20 +457,14 @@ impl Render for CommandPaletteState {
|
||||
)
|
||||
.child(rows)
|
||||
.child(
|
||||
canvas(
|
||||
{
|
||||
let cell = panel_bounds.clone();
|
||||
move |bounds, _window, _cx| *cell.borrow_mut() = Some(bounds)
|
||||
},
|
||||
{
|
||||
let focus = query_focus.clone();
|
||||
move |_, _, window, cx| {
|
||||
if !focus.contains_focused(window, cx) {
|
||||
window.focus(&focus, cx);
|
||||
}
|
||||
canvas(|_, _, _| {}, {
|
||||
let focus = query_focus.clone();
|
||||
move |_, _, window, cx| {
|
||||
if !focus.contains_focused(window, cx) {
|
||||
window.focus(&focus, cx);
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
})
|
||||
.absolute()
|
||||
.inset_0(),
|
||||
);
|
||||
@@ -485,28 +476,23 @@ impl Render for CommandPaletteState {
|
||||
.occlude()
|
||||
.on_mouse_down(MouseButton::Left, {
|
||||
let state = state.clone();
|
||||
let panel_bounds = panel_bounds.clone();
|
||||
move |event: &MouseDownEvent, _window, cx| {
|
||||
let on_panel = panel_bounds
|
||||
.borrow()
|
||||
.is_some_and(|bounds| bounds.contains(&event.position));
|
||||
if on_panel {
|
||||
return;
|
||||
}
|
||||
move |_, _window, cx| {
|
||||
state.update(cx, |state, cx| state.close(cx));
|
||||
cx.stop_propagation();
|
||||
}
|
||||
});
|
||||
|
||||
div()
|
||||
.absolute()
|
||||
.inset_0()
|
||||
.flex()
|
||||
.justify_center()
|
||||
.items_start()
|
||||
.pt_10()
|
||||
.child(scrim)
|
||||
.child(panel)
|
||||
.into_any_element()
|
||||
deferred(
|
||||
div()
|
||||
.absolute()
|
||||
.inset_0()
|
||||
.flex()
|
||||
.justify_center()
|
||||
.items_start()
|
||||
.pt_10()
|
||||
.child(scrim)
|
||||
.child(panel),
|
||||
)
|
||||
.into_any_element()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,7 +207,8 @@ impl RenderOnce for DropdownMenu {
|
||||
let a11y_label = self.a11y_label.or_else(|| self.label.clone());
|
||||
let focus = focus_handle(&self.id, window, cx).tab_stop(self.tab_stop);
|
||||
// How the menu was last opened, read by the frame that mounts the panel.
|
||||
let keyboard_open = window.use_keyed_state("dropdown-open-origin", cx, |_, _| false);
|
||||
let keyboard_open =
|
||||
window.use_keyed_state((self.id.clone(), "dropdown-open-origin"), cx, |_, _| false);
|
||||
|
||||
let on_open: ChangeHandler = {
|
||||
let focus = focus.clone();
|
||||
|
||||
@@ -1799,7 +1799,9 @@ impl EntityInputHandler for InputState {
|
||||
) -> Option<usize> {
|
||||
let layout = self.layout.borrow();
|
||||
let layout = layout.as_ref()?;
|
||||
Some(real_index(&layout.mask_map, layout.index_at(window_point)).min(self.text.len()))
|
||||
let index =
|
||||
real_index(&layout.mask_map, layout.index_at(window_point)).min(self.text.len());
|
||||
Some(utf8_range_to_utf16(&self.text, index..index).start)
|
||||
}
|
||||
|
||||
fn text_length_utf16(
|
||||
@@ -2516,7 +2518,9 @@ fn utf16_to_utf8_offset(text: &str, utf16_offset: usize) -> usize {
|
||||
|
||||
/// Converts a UTF-8 byte range to a UTF-16 range.
|
||||
fn utf8_range_to_utf16(text: &str, range: Range<usize>) -> Range<usize> {
|
||||
utf16_to_utf8_offset(text, range.start)..utf16_to_utf8_offset(text, range.end)
|
||||
let start = text[..range.start].encode_utf16().count();
|
||||
let end = start + text[range.start..range.end].encode_utf16().count();
|
||||
start..end
|
||||
}
|
||||
|
||||
/// Converts a UTF-16 range to a UTF-8 byte range.
|
||||
|
||||
@@ -382,7 +382,7 @@ pub(crate) fn anchored_menu_panel(
|
||||
chrome: &MenuChrome,
|
||||
enter_duration: std::time::Duration,
|
||||
) -> Option<AnyElement> {
|
||||
let was_open = window.use_keyed_state("menu-was-open", cx, |_, _| false);
|
||||
let was_open = window.use_keyed_state((id.clone(), "menu-was-open"), cx, |_, _| false);
|
||||
|
||||
if !open {
|
||||
was_open.update(cx, |was_open, _| *was_open = false);
|
||||
@@ -392,8 +392,9 @@ pub(crate) fn anchored_menu_panel(
|
||||
let on_change = on_change.clone();
|
||||
// Keyboard focus rides the panel across frames
|
||||
let focus = gpui_native_utils::focus_handle(&(id.clone(), "panel").into(), window, cx);
|
||||
let scroll = window.use_keyed_state("menu-scroll", cx, |_, _| ScrollHandle::new());
|
||||
let selection = window.use_keyed_state("menu-selection", cx, |_, _| None);
|
||||
let scroll =
|
||||
window.use_keyed_state((id.clone(), "menu-scroll"), cx, |_, _| ScrollHandle::new());
|
||||
let selection = window.use_keyed_state((id.clone(), "menu-selection"), cx, |_, _| None);
|
||||
|
||||
let selected = if *was_open.read(cx) {
|
||||
*selection.read(cx)
|
||||
|
||||
@@ -128,33 +128,35 @@ pub struct Scrollbar {
|
||||
axis: Axis,
|
||||
handle: Rc<dyn ScrollbarHandle>,
|
||||
visibility: ScrollbarVisibility,
|
||||
key: ElementId,
|
||||
id: ElementId,
|
||||
}
|
||||
|
||||
impl Scrollbar {
|
||||
/// A vertical scrollbar driven by `handle`.
|
||||
pub fn vertical<H>(handle: H) -> Self
|
||||
/// A vertical scrollbar named by `id`, driven by `handle`.
|
||||
pub fn vertical<I, H>(id: I, handle: H) -> Self
|
||||
where
|
||||
I: Into<ElementId>,
|
||||
H: ScrollbarHandle,
|
||||
{
|
||||
Self {
|
||||
axis: Axis::Vertical,
|
||||
handle: Rc::new(handle),
|
||||
visibility: ScrollbarVisibility::default(),
|
||||
key: ElementId::Name("scrollbar".into()),
|
||||
id: id.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// A horizontal scrollbar driven by `handle`.
|
||||
pub fn horizontal<H>(handle: H) -> Self
|
||||
/// A horizontal scrollbar named by `id`, driven by `handle`.
|
||||
pub fn horizontal<I, H>(id: I, handle: H) -> Self
|
||||
where
|
||||
I: Into<ElementId>,
|
||||
H: ScrollbarHandle,
|
||||
{
|
||||
Self {
|
||||
axis: Axis::Horizontal,
|
||||
handle: Rc::new(handle),
|
||||
visibility: ScrollbarVisibility::default(),
|
||||
key: ElementId::Name("scrollbar".into()),
|
||||
id: id.into(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,12 +165,6 @@ impl Scrollbar {
|
||||
self.visibility = visibility;
|
||||
self
|
||||
}
|
||||
|
||||
/// Names this scrollbar's state.
|
||||
pub fn key(mut self, key: impl Into<ElementId>) -> Self {
|
||||
self.key = key.into();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for Scrollbar {
|
||||
@@ -177,8 +173,8 @@ impl RenderOnce for Scrollbar {
|
||||
let handle = self.handle;
|
||||
let axis = self.axis;
|
||||
let visibility = self.visibility;
|
||||
let key = self.key;
|
||||
move |_, _, window, cx| paint_scrollbar(&handle, axis, visibility, &key, window, cx)
|
||||
let id = self.id;
|
||||
move |_, _, window, cx| paint_scrollbar(&handle, axis, visibility, &id, window, cx)
|
||||
})
|
||||
.absolute()
|
||||
.inset_0()
|
||||
@@ -192,7 +188,7 @@ fn paint_scrollbar(
|
||||
handle: &Rc<dyn ScrollbarHandle>,
|
||||
axis: Axis,
|
||||
visibility: ScrollbarVisibility,
|
||||
key: &ElementId,
|
||||
id: &ElementId,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) {
|
||||
@@ -217,12 +213,10 @@ fn paint_scrollbar(
|
||||
ScrollbarVisibility::Always => false,
|
||||
ScrollbarVisibility::Autohide => true,
|
||||
};
|
||||
|
||||
let autohide_state =
|
||||
autohide.then(|| Autohide::new(scrollbar_state_key("autohide", key), window, cx));
|
||||
autohide.then(|| Autohide::new(scrollbar_state_key("autohide", id), window, cx));
|
||||
|
||||
let track_length = viewport_along - inset * 2.0;
|
||||
|
||||
let (thumb_length, thumb_travel) = thumb_geometry(
|
||||
viewport_along,
|
||||
viewport_along + max_offset,
|
||||
@@ -267,9 +261,7 @@ fn paint_scrollbar(
|
||||
}
|
||||
};
|
||||
|
||||
let drag = window.use_keyed_state(scrollbar_state_key("thumb-drag", key), cx, |_, _| {
|
||||
Drag::None
|
||||
});
|
||||
let drag = window.use_keyed_state(scrollbar_state_key("thumb-drag", id), cx, |_, _| Drag::None);
|
||||
let dragging = drag.read(cx).is_some();
|
||||
|
||||
let alpha = match &autohide_state {
|
||||
|
||||
@@ -135,16 +135,12 @@ impl RenderOnce for Slider {
|
||||
0.0
|
||||
};
|
||||
|
||||
// The track bounds, recorded by the overlay canvas each frame,
|
||||
// turn pointer positions into values inside the listeners.
|
||||
let bounds_cell = Rc::new(Cell::new(None::<Bounds<Pixels>>));
|
||||
// Dragging crosses frames, so it lives in keyed element state,
|
||||
// a per-render flag would reset when the owner's update repaints.
|
||||
let dragging = window.use_keyed_state("slider-dragging", cx, |_, _| false);
|
||||
// A held handle gives the arrow-key listener something to check focus against,
|
||||
// and the click-to-focus behavior a target.
|
||||
let focus_handle = gpui_native_utils::focus_handle(&self.id, window, cx);
|
||||
|
||||
let dragging =
|
||||
window.use_keyed_state((self.id.clone(), "slider-dragging"), cx, |_, _| false);
|
||||
|
||||
let set_value: ValueSetter = {
|
||||
let bounds_cell = bounds_cell.clone();
|
||||
let on_change = self.on_change.clone();
|
||||
|
||||
Reference in New Issue
Block a user