audit combobox
This commit is contained in:
+61
-12
@@ -12,13 +12,13 @@ use gpui::prelude::FluentBuilder as _;
|
||||
use gpui::{
|
||||
App, Bounds, Context, ElementId, Entity, EventEmitter, InteractiveElement as _, IntoElement,
|
||||
KeyBinding, MouseButton, MouseDownEvent, ParentElement as _, Pixels, Render, Role,
|
||||
ScrollHandle, SharedString, StatefulInteractiveElement as _, Styled as _, Subscription, Task,
|
||||
Window, actions, canvas, deferred, div, px,
|
||||
ScrollHandle, SharedString, StatefulInteractiveElement as _, StyleRefinement, Styled,
|
||||
Subscription, Task, Window, actions, anchored, canvas, deferred, div, point, px,
|
||||
};
|
||||
use gpui_native_icons::{IconRole, icon};
|
||||
use gpui_native_theme::{ActiveTheme as _, Size};
|
||||
use gpui_native_theme::{ActiveTheme as _, BORDER_WIDTH, Size};
|
||||
|
||||
use crate::common::{Sizable, h_flex, v_flex};
|
||||
use crate::common::{Sizable, StyledExt as _, h_flex, v_flex};
|
||||
use crate::input::{Input, InputState, InputStateEvent};
|
||||
use crate::scroll_area::ScrollArea;
|
||||
|
||||
@@ -81,6 +81,7 @@ where
|
||||
filter: Option<ComboboxFilter<T>>,
|
||||
unmatched: UnmatchedText,
|
||||
size: Size,
|
||||
style: StyleRefinement,
|
||||
}
|
||||
|
||||
impl<T> Combobox<T>
|
||||
@@ -103,6 +104,7 @@ where
|
||||
filter: None,
|
||||
unmatched: UnmatchedText::default(),
|
||||
size: Size::default(),
|
||||
style: StyleRefinement::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,6 +167,16 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Style refinements set the combobox's width.
|
||||
impl<T> Styled for Combobox<T>
|
||||
where
|
||||
T: Clone + PartialEq + 'static,
|
||||
{
|
||||
fn style(&mut self) -> &mut StyleRefinement {
|
||||
&mut self.style
|
||||
}
|
||||
}
|
||||
|
||||
/// Emitted when the combobox's value changes.
|
||||
pub struct ComboboxChanged;
|
||||
|
||||
@@ -189,6 +201,8 @@ where
|
||||
filter: ComboboxFilter<T>,
|
||||
unmatched: UnmatchedText,
|
||||
size: Size,
|
||||
/// Width refinements from the builder, applied to the wrapper each render.
|
||||
style: StyleRefinement,
|
||||
/// Handler reports in flight; held so they die with the state.
|
||||
tasks: Vec<Task<()>>,
|
||||
/// Keeps the query-event subscription tied to the state's lifetime.
|
||||
@@ -225,6 +239,7 @@ where
|
||||
|
||||
let subscription =
|
||||
cx.subscribe_in(&query, window, |state, _, event, window, cx| match event {
|
||||
InputStateEvent::Focused => state.query_focused(window, cx),
|
||||
InputStateEvent::TextChanged => state.text_changed(window, cx),
|
||||
InputStateEvent::Blurred => state.blurred(window, cx),
|
||||
_ => {}
|
||||
@@ -248,6 +263,7 @@ where
|
||||
}),
|
||||
unmatched: combobox.unmatched,
|
||||
size: combobox.size,
|
||||
style: combobox.style,
|
||||
tasks: Vec::new(),
|
||||
_subscription: subscription,
|
||||
}
|
||||
@@ -364,6 +380,16 @@ where
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
/// Gaining focus.
|
||||
fn query_focused(&mut self, _window: &mut Window, cx: &mut Context<Self>) {
|
||||
if self.selected.is_some() {
|
||||
return;
|
||||
}
|
||||
self.refilter("");
|
||||
self.highlight = (!self.visible.is_empty()).then_some(0usize);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
/// Losing focus. [`UnmatchedText`] decides what the text becomes.
|
||||
fn blurred(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
self.highlight = None;
|
||||
@@ -447,11 +473,25 @@ where
|
||||
T: Clone + PartialEq + 'static,
|
||||
{
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let (metrics, colors, radius) = {
|
||||
let (metrics, colors, radius, panel_border, spacing_unit, shadows) = {
|
||||
let theme = cx.theme();
|
||||
let metrics = theme.sizes.resolve(self.size);
|
||||
(metrics, theme.colors, theme.radius)
|
||||
|
||||
let panel_border = if theme.borders.overlays {
|
||||
theme.colors.border
|
||||
} else {
|
||||
theme.colors.surface
|
||||
};
|
||||
(
|
||||
metrics,
|
||||
theme.colors,
|
||||
theme.radius,
|
||||
panel_border,
|
||||
theme.spacing.unit,
|
||||
theme.elevations.popover.shadows(),
|
||||
)
|
||||
};
|
||||
|
||||
let icon_size = metrics.dense_icon_size;
|
||||
let id = self.id.clone();
|
||||
let state = cx.entity();
|
||||
@@ -490,7 +530,9 @@ where
|
||||
.id(id.clone())
|
||||
.key_context(COMBOBOX_CONTEXT)
|
||||
.relative()
|
||||
.w_full()
|
||||
.w(window.rem_size() * 12.0)
|
||||
.min_w(window.rem_size() * 6.0)
|
||||
.refine_style(&self.style)
|
||||
.role(Role::ComboBox)
|
||||
.aria_expanded(open)
|
||||
.aria_label(self.name.clone())
|
||||
@@ -532,13 +574,14 @@ where
|
||||
.child(
|
||||
Input::new((id.clone(), "query"), self.query.clone())
|
||||
.placeholder(self.placeholder.clone())
|
||||
.placeholder_when_focused(true)
|
||||
.with_size(self.size),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.absolute()
|
||||
.right(metrics.dense_padding_x)
|
||||
.top((metrics.input_height - icon_size) / 2.)
|
||||
.top((metrics.input_height + BORDER_WIDTH * 2.0 - icon_size) / 2.)
|
||||
.child(chevron),
|
||||
);
|
||||
|
||||
@@ -626,6 +669,10 @@ where
|
||||
};
|
||||
|
||||
let panel_bounds: Rc<RefCell<Option<Bounds<Pixels>>>> = Rc::new(RefCell::new(None));
|
||||
let popup_width = wrapper_bounds
|
||||
.borrow()
|
||||
.map(|bounds| bounds.size.width)
|
||||
.unwrap_or_else(|| window.rem_size() * 12.0);
|
||||
|
||||
let panel = v_flex()
|
||||
.id((id.clone(), "panel"))
|
||||
@@ -633,14 +680,14 @@ where
|
||||
let name = format!("{id}-panel");
|
||||
move || name
|
||||
})
|
||||
.mt_1()
|
||||
.w_full()
|
||||
.w(popup_width)
|
||||
.p_1()
|
||||
.bg(colors.surface)
|
||||
.text_color(colors.foreground)
|
||||
.rounded(radius.overlay)
|
||||
.border_1()
|
||||
.border_color(colors.border)
|
||||
.border_color(panel_border)
|
||||
.shadow(shadows)
|
||||
.overflow_hidden()
|
||||
.child(rows)
|
||||
.child(
|
||||
@@ -684,7 +731,9 @@ where
|
||||
.inset_0(),
|
||||
);
|
||||
|
||||
wrapper = wrapper.child(deferred(panel));
|
||||
wrapper = wrapper.child(deferred(
|
||||
anchored().offset(point(px(0.), spacing_unit)).child(panel),
|
||||
));
|
||||
}
|
||||
|
||||
wrapper
|
||||
|
||||
Reference in New Issue
Block a user