Add up, down, enter, esc keybinding for Dropdown.
This commit is contained in:
parent
720672b0e2
commit
63076725f2
4 changed files with 70 additions and 15 deletions
|
|
@ -1,18 +1,30 @@
|
|||
use std::rc::Rc;
|
||||
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder as _, px, AppContext, Div, ElementId, FocusHandle, FocusableView,
|
||||
InteractiveElement, IntoElement, ParentElement as _, Render, RenderOnce, SharedString,
|
||||
Stateful, StatefulInteractiveElement as _, Styled as _, View, ViewContext, VisualContext as _,
|
||||
WeakView,
|
||||
actions, div, prelude::FluentBuilder as _, px, AppContext, Div, ElementId, FocusHandle,
|
||||
FocusableView, InteractiveElement, IntoElement, KeyBinding, ParentElement as _, Render,
|
||||
RenderOnce, SharedString, Stateful, StatefulInteractiveElement as _, Styled as _, View,
|
||||
ViewContext, VisualContext as _, WeakView,
|
||||
};
|
||||
|
||||
actions!(dropdown, [Up, Down, Enter, Escape]);
|
||||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
let context = Some("Dropdown");
|
||||
cx.bind_keys([
|
||||
KeyBinding::new("up", Up, context),
|
||||
KeyBinding::new("down", Down, context),
|
||||
KeyBinding::new("enter", Enter, context),
|
||||
KeyBinding::new("escape", Escape, context),
|
||||
])
|
||||
}
|
||||
|
||||
use crate::{
|
||||
h_flex,
|
||||
list::ListItem,
|
||||
picker::{Picker, PickerDelegate},
|
||||
picker::{self, Picker, PickerDelegate},
|
||||
theme::ActiveTheme,
|
||||
v_flex, IconName, StyledExt,
|
||||
v_flex, IconName,
|
||||
};
|
||||
|
||||
/// A trait for items that can be displayed in a dropdown.
|
||||
|
|
@ -98,6 +110,7 @@ where
|
|||
view.value = Some(item.value().to_string().into());
|
||||
}
|
||||
view.open = false;
|
||||
view.focus_handle.focus(cx);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -139,6 +152,35 @@ where
|
|||
self.value = Some(value.into());
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn up(&mut self, _: &Up, cx: &mut ViewContext<Self>) {
|
||||
if !self.open {
|
||||
return;
|
||||
}
|
||||
self.picker.focus_handle(cx).focus(cx);
|
||||
cx.dispatch_action(Box::new(picker::SelectPrev));
|
||||
}
|
||||
|
||||
fn down(&mut self, _: &Down, cx: &mut ViewContext<Self>) {
|
||||
if !self.open {
|
||||
self.open = true;
|
||||
}
|
||||
|
||||
self.picker.focus_handle(cx).focus(cx);
|
||||
cx.dispatch_action(Box::new(picker::SelectNext));
|
||||
}
|
||||
|
||||
fn enter(&mut self, _: &Enter, cx: &mut ViewContext<Self>) {
|
||||
if !self.open {
|
||||
self.open = true;
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
|
||||
fn escape(&mut self, _: &Escape, cx: &mut ViewContext<Self>) {
|
||||
self.open = false;
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> FocusableView for Dropdown<D>
|
||||
|
|
@ -155,11 +197,18 @@ where
|
|||
D: DropdownDelegate + 'static,
|
||||
{
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
let group_id = format!("dropdown-group:{}", self.id);
|
||||
let title = self.title.clone().unwrap_or_else(|| "Select...".into());
|
||||
let is_focused = self.focus_handle.is_focused(cx);
|
||||
let focused = self.focus_handle.is_focused(cx);
|
||||
|
||||
div()
|
||||
.key_context("Dropdown")
|
||||
.group(group_id.clone())
|
||||
.track_focus(&self.focus_handle)
|
||||
.on_action(cx.listener(Self::up))
|
||||
.on_action(cx.listener(Self::down))
|
||||
.on_action(cx.listener(Self::enter))
|
||||
.on_action(cx.listener(Self::escape))
|
||||
.size_full()
|
||||
.relative()
|
||||
.child(
|
||||
|
|
@ -177,7 +226,7 @@ where
|
|||
.shadow_sm()
|
||||
.px_3()
|
||||
.py_2()
|
||||
.when(is_focused, |this| this.border_color(cx.theme().ring))
|
||||
.when(focused, |this| this.border_color(cx.theme().ring))
|
||||
.on_click(cx.listener(|this, _, cx| {
|
||||
this.open = !this.open;
|
||||
cx.notify();
|
||||
|
|
@ -204,7 +253,11 @@ where
|
|||
.rounded(px(cx.theme().radius))
|
||||
.shadow_md()
|
||||
.track_focus(&self.picker.focus_handle(cx))
|
||||
.child(self.picker.clone()),
|
||||
.child(self.picker.clone())
|
||||
.on_mouse_down_out(cx.listener(|view, _, cx| {
|
||||
view.open = false;
|
||||
cx.notify();
|
||||
})),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -541,7 +541,7 @@ impl Element for TextElement {
|
|||
cx: &mut WindowContext,
|
||||
) {
|
||||
let focus_handle = self.input.read(cx).focus_handle.clone();
|
||||
let is_focused = focus_handle.is_focused(cx);
|
||||
let focused = focus_handle.is_focused(cx);
|
||||
|
||||
cx.handle_input(
|
||||
&focus_handle,
|
||||
|
|
@ -553,7 +553,7 @@ impl Element for TextElement {
|
|||
let line = prepaint.line.take().unwrap();
|
||||
line.paint(bounds.origin, cx.line_height(), cx).unwrap();
|
||||
|
||||
if is_focused {
|
||||
if focused {
|
||||
if let Some(cursor) = prepaint.cursor.take() {
|
||||
cx.paint_quad(cursor);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,4 +35,5 @@ pub use stock::*;
|
|||
pub fn init(cx: &mut gpui::AppContext) {
|
||||
input::init(cx);
|
||||
picker::init(cx);
|
||||
dropdown::init(cx);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,11 +24,12 @@ actions!(
|
|||
);
|
||||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
let context = Some("Picker");
|
||||
cx.bind_keys([
|
||||
KeyBinding::new("enter", Confirm, None),
|
||||
KeyBinding::new("escape", Cancel, None),
|
||||
KeyBinding::new("up", SelectPrev, None),
|
||||
KeyBinding::new("down", SelectNext, None),
|
||||
KeyBinding::new("enter", Confirm, context),
|
||||
KeyBinding::new("escape", Cancel, context),
|
||||
KeyBinding::new("up", SelectPrev, context),
|
||||
KeyBinding::new("down", SelectNext, context),
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue