list: Add secondary confirm support to list. (#769)
This commit is contained in:
parent
c91dce3139
commit
a60acd7e4d
18 changed files with 187 additions and 134 deletions
|
|
@ -249,7 +249,7 @@ impl InputStory {
|
|||
) {
|
||||
match event {
|
||||
InputEvent::Change(text) => println!("Change: {}", text),
|
||||
InputEvent::PressEnter => println!("PressEnter"),
|
||||
InputEvent::PressEnter { secondary } => println!("PressEnter secondary: {}", secondary),
|
||||
InputEvent::Focus => println!("Focus"),
|
||||
InputEvent::Blur => println!("Blur"),
|
||||
};
|
||||
|
|
@ -270,7 +270,9 @@ impl InputStory {
|
|||
}
|
||||
println!("Change: {}", text);
|
||||
}
|
||||
InputEvent::PressEnter => println!("PressEnter"),
|
||||
InputEvent::PressEnter { secondary } => {
|
||||
println!("PressEnter secondary: {}", secondary)
|
||||
}
|
||||
InputEvent::Focus => println!("Focus"),
|
||||
InputEvent::Blur => println!("Blur"),
|
||||
},
|
||||
|
|
@ -306,7 +308,9 @@ impl InputStory {
|
|||
}
|
||||
println!("Change: {}", text);
|
||||
}
|
||||
InputEvent::PressEnter => println!("PressEnter"),
|
||||
InputEvent::PressEnter { secondary } => {
|
||||
println!("PressEnter secondary: {}", secondary);
|
||||
}
|
||||
InputEvent::Focus => println!("Focus"),
|
||||
InputEvent::Blur => println!("Blur"),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -174,8 +174,8 @@ impl ListDelegate for CompanyListDelegate {
|
|||
Task::ready(())
|
||||
}
|
||||
|
||||
fn confirm(&mut self, ix: usize, window: &mut Window, cx: &mut Context<List<Self>>) {
|
||||
self.confirmed_index = Some(ix);
|
||||
fn confirm(&mut self, secondary: bool, window: &mut Window, cx: &mut Context<List<Self>>) {
|
||||
println!("Confirmed with secondary: {}", secondary);
|
||||
window.dispatch_action(Box::new(SelectedCompany), cx);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -368,7 +368,6 @@ impl StoryWorkspace {
|
|||
Arc::new(StoryContainer::panel::<AccordionStory>(window, cx)),
|
||||
Arc::new(StoryContainer::panel::<SidebarStory>(window, cx)),
|
||||
Arc::new(StoryContainer::panel::<FormStory>(window, cx)),
|
||||
Arc::new(StoryContainer::panel::<WebViewStory>(window, cx)),
|
||||
],
|
||||
None,
|
||||
&dock_area,
|
||||
|
|
|
|||
|
|
@ -128,11 +128,13 @@ impl ListDelegate for ListItemDeletegate {
|
|||
});
|
||||
}
|
||||
|
||||
fn confirm(&mut self, ix: usize, window: &mut Window, cx: &mut Context<List<Self>>) {
|
||||
fn confirm(&mut self, _secondary: bool, window: &mut Window, cx: &mut Context<List<Self>>) {
|
||||
_ = self.story.update(cx, |this, cx| {
|
||||
self.confirmed_index = Some(ix);
|
||||
if let Some(item) = self.matches.get(ix) {
|
||||
this.selected_value = Some(SharedString::from(item.to_string()));
|
||||
self.confirmed_index = self.selected_index;
|
||||
if let Some(ix) = self.confirmed_index {
|
||||
if let Some(item) = self.matches.get(ix) {
|
||||
this.selected_value = Some(SharedString::from(item.to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
window.close_drawer(cx);
|
||||
|
|
|
|||
|
|
@ -660,7 +660,7 @@ impl TableStory {
|
|||
) {
|
||||
match event {
|
||||
// Update when the user presses Enter or the input loses focus
|
||||
InputEvent::PressEnter | InputEvent::Blur => {
|
||||
InputEvent::PressEnter { .. } | InputEvent::Blur => {
|
||||
let text = self.num_stocks_input.read(cx).text().to_string();
|
||||
if let Ok(num) = text.parse::<usize>() {
|
||||
self.table.update(cx, |table, _| {
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ impl WebViewStory {
|
|||
cx.subscribe(
|
||||
&address_input,
|
||||
|this: &mut Self, input, event: &InputEvent, cx| match event {
|
||||
InputEvent::PressEnter => {
|
||||
InputEvent::PressEnter { .. } => {
|
||||
let url = input.read(cx).text();
|
||||
this.webview.update(cx, |view, _| {
|
||||
view.load_url(&url);
|
||||
|
|
|
|||
11
crates/ui/src/actions.rs
Normal file
11
crates/ui/src/actions.rs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
use gpui::{actions, impl_internal_actions};
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Deserialize)]
|
||||
pub struct Confirm {
|
||||
/// Is confirm with secondary.
|
||||
pub secondary: bool,
|
||||
}
|
||||
|
||||
actions!(list, [Cancel, SelectPrev, SelectNext]);
|
||||
impl_internal_actions!(list, [Confirm]);
|
||||
|
|
@ -6,19 +6,19 @@ use gpui::{
|
|||
};
|
||||
|
||||
use crate::{
|
||||
actions::Cancel,
|
||||
button::{Button, ButtonVariants},
|
||||
divider::Divider,
|
||||
h_flex,
|
||||
input::{InputEvent, TextInput},
|
||||
popover::Escape,
|
||||
tooltip::Tooltip,
|
||||
v_flex, ActiveTheme as _, Colorize as _, Icon, Selectable as _, Sizable, Size, StyleSized,
|
||||
};
|
||||
|
||||
const KEY_CONTEXT: &'static str = "ColorPicker";
|
||||
const CONTEXT: &'static str = "ColorPicker";
|
||||
|
||||
pub fn init(cx: &mut App) {
|
||||
cx.bind_keys([KeyBinding::new("escape", Escape, Some(KEY_CONTEXT))])
|
||||
cx.bind_keys([KeyBinding::new("escape", Cancel, Some(CONTEXT))])
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
|
@ -73,7 +73,7 @@ pub struct ColorPicker {
|
|||
|
||||
impl ColorPicker {
|
||||
pub fn new(id: impl Into<ElementId>, window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||
let color_input = cx.new(|cx| TextInput::new(window, cx).xsmall());
|
||||
let color_input = cx.new(|cx| TextInput::new(window, cx).small());
|
||||
|
||||
let _subscriptions = vec![cx.subscribe_in(
|
||||
&color_input,
|
||||
|
|
@ -85,7 +85,7 @@ impl ColorPicker {
|
|||
this.hovered_color = Some(color);
|
||||
}
|
||||
}
|
||||
InputEvent::PressEnter => {
|
||||
InputEvent::PressEnter { .. } => {
|
||||
let val = this.color_input.read(cx).text();
|
||||
if let Ok(color) = Hsla::parse_hex(&val) {
|
||||
this.open = false;
|
||||
|
|
@ -170,8 +170,11 @@ impl ColorPicker {
|
|||
self
|
||||
}
|
||||
|
||||
fn on_escape(&mut self, _: &Escape, _: &mut Window, cx: &mut Context<Self>) {
|
||||
cx.propagate();
|
||||
fn on_escape(&mut self, _: &Cancel, _: &mut Window, cx: &mut Context<Self>) {
|
||||
if !self.open {
|
||||
cx.propagate();
|
||||
}
|
||||
|
||||
self.open = false;
|
||||
cx.notify();
|
||||
}
|
||||
|
|
@ -314,7 +317,7 @@ impl Render for ColorPicker {
|
|||
|
||||
div()
|
||||
.id(self.id.clone())
|
||||
.key_context(KEY_CONTEXT)
|
||||
.key_context(CONTEXT)
|
||||
.track_focus(&self.focus_handle)
|
||||
.on_action(cx.listener(Self::on_escape))
|
||||
.child(
|
||||
|
|
@ -358,10 +361,6 @@ impl Render for ColorPicker {
|
|||
})
|
||||
.when_some(self.label.clone(), |this, label| this.child(label))
|
||||
.on_click(cx.listener(Self::toggle_picker))
|
||||
.on_mouse_up_out(
|
||||
MouseButton::Left,
|
||||
cx.listener(|view, _, window, cx| view.on_escape(&Escape, window, cx)),
|
||||
)
|
||||
.child(
|
||||
canvas(
|
||||
move |bounds, _, cx| view.update(cx, |r, _| r.bounds = bounds),
|
||||
|
|
@ -394,7 +393,13 @@ impl Render for ColorPicker {
|
|||
.shadow_lg()
|
||||
.rounded(cx.theme().radius)
|
||||
.bg(cx.theme().background)
|
||||
.child(self.render_colors(window, cx)),
|
||||
.child(self.render_colors(window, cx))
|
||||
.on_mouse_up_out(
|
||||
MouseButton::Left,
|
||||
cx.listener(|view, _, window, cx| {
|
||||
view.on_escape(&Cancel, window, cx)
|
||||
}),
|
||||
),
|
||||
),
|
||||
)
|
||||
.with_priority(1),
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
use std::{rc::Rc, time::Duration};
|
||||
|
||||
use gpui::{
|
||||
actions, anchored, div, point, prelude::FluentBuilder as _, px, Animation, AnimationExt as _,
|
||||
anchored, div, point, prelude::FluentBuilder as _, px, Animation, AnimationExt as _,
|
||||
AnyElement, App, ClickEvent, DefiniteLength, DismissEvent, Div, EventEmitter, FocusHandle,
|
||||
InteractiveElement as _, IntoElement, KeyBinding, MouseButton, ParentElement, Pixels,
|
||||
RenderOnce, Styled, Window,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
actions::Cancel,
|
||||
button::{Button, ButtonVariants as _},
|
||||
h_flex,
|
||||
modal::overlay_color,
|
||||
|
|
@ -17,11 +18,9 @@ use crate::{
|
|||
v_flex, ActiveTheme, IconName, Placement, Sizable, StyledExt as _,
|
||||
};
|
||||
|
||||
actions!(drawer, [Escape]);
|
||||
|
||||
const CONTEXT: &str = "Drawer";
|
||||
pub fn init(cx: &mut App) {
|
||||
cx.bind_keys([KeyBinding::new("escape", Escape, Some(CONTEXT))])
|
||||
cx.bind_keys([KeyBinding::new("escape", Cancel, Some(CONTEXT))])
|
||||
}
|
||||
|
||||
#[derive(IntoElement)]
|
||||
|
|
@ -154,7 +153,9 @@ impl RenderOnce for Drawer {
|
|||
.track_focus(&self.focus_handle)
|
||||
.on_action({
|
||||
let on_close = self.on_close.clone();
|
||||
move |_: &Escape, window, cx| {
|
||||
move |_: &Cancel, window, cx| {
|
||||
cx.propagate();
|
||||
|
||||
on_close(&ClickEvent::default(), window, cx);
|
||||
window.close_drawer(cx);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,19 @@
|
|||
use gpui::{
|
||||
actions, anchored, canvas, deferred, div, prelude::FluentBuilder, px, rems, AnyElement, App,
|
||||
AppContext, Bounds, ClickEvent, Context, DismissEvent, ElementId, Entity, EventEmitter,
|
||||
FocusHandle, Focusable, InteractiveElement, IntoElement, KeyBinding, Length, ParentElement,
|
||||
Pixels, Render, SharedString, StatefulInteractiveElement, Styled, Subscription, Task,
|
||||
WeakEntity, Window,
|
||||
anchored, canvas, deferred, div, prelude::FluentBuilder, px, rems, AnyElement, App, AppContext,
|
||||
Bounds, ClickEvent, Context, DismissEvent, ElementId, Entity, EventEmitter, FocusHandle,
|
||||
Focusable, InteractiveElement, IntoElement, KeyBinding, Length, ParentElement, Pixels, Render,
|
||||
SharedString, StatefulInteractiveElement, Styled, Subscription, Task, WeakEntity, Window,
|
||||
};
|
||||
use rust_i18n::t;
|
||||
|
||||
use crate::{
|
||||
actions::{Cancel, Confirm, SelectNext, SelectPrev},
|
||||
h_flex,
|
||||
input::clear_button,
|
||||
list::{self, List, ListDelegate, ListItem},
|
||||
list::{List, ListDelegate, ListItem},
|
||||
v_flex, ActiveTheme, Disableable as _, Icon, IconName, Sizable, Size, StyleSized, StyledExt,
|
||||
};
|
||||
|
||||
actions!(dropdown, [Up, Down, Enter, Escape]);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum ListEvent {
|
||||
/// Single click or move to selected row.
|
||||
|
|
@ -29,10 +27,15 @@ pub enum ListEvent {
|
|||
const CONTEXT: &str = "Dropdown";
|
||||
pub fn init(cx: &mut App) {
|
||||
cx.bind_keys([
|
||||
KeyBinding::new("up", Up, Some(CONTEXT)),
|
||||
KeyBinding::new("down", Down, Some(CONTEXT)),
|
||||
KeyBinding::new("enter", Enter, Some(CONTEXT)),
|
||||
KeyBinding::new("escape", Escape, Some(CONTEXT)),
|
||||
KeyBinding::new("up", SelectPrev, Some(CONTEXT)),
|
||||
KeyBinding::new("down", SelectNext, Some(CONTEXT)),
|
||||
KeyBinding::new("enter", Confirm { secondary: false }, Some(CONTEXT)),
|
||||
KeyBinding::new(
|
||||
"secondary-enter",
|
||||
Confirm { secondary: true },
|
||||
Some(CONTEXT),
|
||||
),
|
||||
KeyBinding::new("escape", Cancel, Some(CONTEXT)),
|
||||
])
|
||||
}
|
||||
|
||||
|
|
@ -173,9 +176,7 @@ where
|
|||
});
|
||||
}
|
||||
|
||||
fn confirm(&mut self, ix: usize, window: &mut Window, cx: &mut Context<List<Self>>) {
|
||||
self.selected_index = Some(ix);
|
||||
|
||||
fn confirm(&mut self, _secondary: bool, window: &mut Window, cx: &mut Context<List<Self>>) {
|
||||
let selected_value = self
|
||||
.selected_index
|
||||
.and_then(|ix| self.delegate.get(ix))
|
||||
|
|
@ -495,24 +496,25 @@ where
|
|||
cx.notify();
|
||||
}
|
||||
|
||||
fn up(&mut self, _: &Up, window: &mut Window, cx: &mut Context<Self>) {
|
||||
fn up(&mut self, _: &SelectPrev, window: &mut Window, cx: &mut Context<Self>) {
|
||||
if !self.open {
|
||||
return;
|
||||
}
|
||||
|
||||
self.list.focus_handle(cx).focus(window);
|
||||
cx.dispatch_action(&list::SelectPrev);
|
||||
cx.propagate();
|
||||
}
|
||||
|
||||
fn down(&mut self, _: &Down, window: &mut Window, cx: &mut Context<Self>) {
|
||||
fn down(&mut self, _: &SelectNext, window: &mut Window, cx: &mut Context<Self>) {
|
||||
if !self.open {
|
||||
self.open = true;
|
||||
}
|
||||
|
||||
self.list.focus_handle(cx).focus(window);
|
||||
cx.dispatch_action(&list::SelectNext);
|
||||
cx.propagate();
|
||||
}
|
||||
|
||||
fn enter(&mut self, _: &Enter, window: &mut Window, cx: &mut Context<Self>) {
|
||||
fn enter(&mut self, _: &Confirm, window: &mut Window, cx: &mut Context<Self>) {
|
||||
// Propagate the event to the parent view, for example to the Modal to support ENTER to confirm.
|
||||
cx.propagate();
|
||||
|
||||
|
|
@ -521,7 +523,6 @@ where
|
|||
cx.notify();
|
||||
} else {
|
||||
self.list.focus_handle(cx).focus(window);
|
||||
cx.dispatch_action(&list::Confirm);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -535,9 +536,10 @@ where
|
|||
cx.notify();
|
||||
}
|
||||
|
||||
fn escape(&mut self, _: &Escape, _: &mut Window, cx: &mut Context<Self>) {
|
||||
// Propagate the event to the parent view, for example to the Modal to support ESC to close.
|
||||
cx.propagate();
|
||||
fn escape(&mut self, _: &Cancel, _: &mut Window, cx: &mut Context<Self>) {
|
||||
if !self.open {
|
||||
cx.propagate();
|
||||
}
|
||||
|
||||
self.open = false;
|
||||
cx.notify();
|
||||
|
|
@ -734,13 +736,10 @@ where
|
|||
.border_color(cx.theme().border)
|
||||
.rounded(popup_radius)
|
||||
.shadow_md()
|
||||
.on_mouse_down_out(|_, _, cx| {
|
||||
cx.dispatch_action(&Escape);
|
||||
})
|
||||
.child(self.list.clone()),
|
||||
)
|
||||
.on_mouse_down_out(cx.listener(|this, _, window, cx| {
|
||||
this.escape(&Escape, window, cx);
|
||||
this.escape(&Cancel, window, cx);
|
||||
})),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
//! Based on the `Input` example from the `gpui` crate.
|
||||
//! https://github.com/zed-industries/zed/blob/main/crates/gpui/examples/input.rs
|
||||
|
||||
use serde::Deserialize;
|
||||
use smallvec::SmallVec;
|
||||
use std::cell::Cell;
|
||||
use std::ops::Range;
|
||||
|
|
@ -11,12 +12,12 @@ use unicode_segmentation::*;
|
|||
|
||||
use gpui::prelude::FluentBuilder as _;
|
||||
use gpui::{
|
||||
actions, div, point, px, relative, AnyElement, App, AppContext, Bounds, ClickEvent,
|
||||
ClipboardItem, Context, DefiniteLength, Entity, EntityInputHandler, EventEmitter, FocusHandle,
|
||||
Focusable, InteractiveElement as _, IntoElement, KeyBinding, KeyDownEvent, MouseButton,
|
||||
MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement as _, Pixels, Point, Rems, Render,
|
||||
ScrollHandle, ScrollWheelEvent, SharedString, Styled as _, Subscription, UTF16Selection,
|
||||
Window, WrappedLine,
|
||||
actions, div, impl_internal_actions, point, px, relative, AnyElement, App, AppContext, Bounds,
|
||||
ClickEvent, ClipboardItem, Context, DefiniteLength, Entity, EntityInputHandler, EventEmitter,
|
||||
FocusHandle, Focusable, InteractiveElement as _, IntoElement, KeyBinding, KeyDownEvent,
|
||||
MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement as _, Pixels, Point,
|
||||
Rems, Render, ScrollHandle, ScrollWheelEvent, SharedString, Styled as _, Subscription,
|
||||
UTF16Selection, Window, WrappedLine,
|
||||
};
|
||||
|
||||
// TODO:
|
||||
|
|
@ -37,6 +38,14 @@ use crate::{ActiveTheme, Root};
|
|||
use crate::{IconName, Size};
|
||||
use crate::{Sizable, StyleSized};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Deserialize)]
|
||||
pub struct Enter {
|
||||
/// Is confirm with secondary.
|
||||
pub secondary: bool,
|
||||
}
|
||||
|
||||
impl_internal_actions!(input, [Enter]);
|
||||
|
||||
actions!(
|
||||
input,
|
||||
[
|
||||
|
|
@ -46,7 +55,6 @@ actions!(
|
|||
DeleteToEndOfLine,
|
||||
DeleteToPreviousWordStart,
|
||||
DeleteToNextWordEnd,
|
||||
Enter,
|
||||
Up,
|
||||
Down,
|
||||
Left,
|
||||
|
|
@ -83,7 +91,7 @@ actions!(
|
|||
#[derive(Clone)]
|
||||
pub enum InputEvent {
|
||||
Change(SharedString),
|
||||
PressEnter,
|
||||
PressEnter { secondary: bool },
|
||||
Focus,
|
||||
Blur,
|
||||
}
|
||||
|
|
@ -106,7 +114,8 @@ pub fn init(cx: &mut App) {
|
|||
KeyBinding::new("alt-delete", DeleteToNextWordEnd, Some(CONTEXT)),
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
KeyBinding::new("ctrl-delete", DeleteToNextWordEnd, Some(CONTEXT)),
|
||||
KeyBinding::new("enter", Enter, Some(CONTEXT)),
|
||||
KeyBinding::new("enter", Enter { secondary: false }, Some(CONTEXT)),
|
||||
KeyBinding::new("secondary-enter", Enter { secondary: true }, Some(CONTEXT)),
|
||||
KeyBinding::new("up", Up, Some(CONTEXT)),
|
||||
KeyBinding::new("down", Down, Some(CONTEXT)),
|
||||
KeyBinding::new("left", Left, Some(CONTEXT)),
|
||||
|
|
@ -1004,7 +1013,7 @@ impl TextInput {
|
|||
self.pause_blink_cursor(cx);
|
||||
}
|
||||
|
||||
fn enter(&mut self, _: &Enter, window: &mut Window, cx: &mut Context<Self>) {
|
||||
fn enter(&mut self, action: &Enter, window: &mut Window, cx: &mut Context<Self>) {
|
||||
if self.is_multi_line() {
|
||||
let is_eof = self.selected_range.end == self.text.len();
|
||||
self.replace_text_in_range(None, "\n", window, cx);
|
||||
|
|
@ -1017,7 +1026,9 @@ impl TextInput {
|
|||
self.move_to(new_offset, window, cx);
|
||||
}
|
||||
|
||||
cx.emit(InputEvent::PressEnter);
|
||||
cx.emit(InputEvent::PressEnter {
|
||||
secondary: action.secondary,
|
||||
});
|
||||
}
|
||||
|
||||
fn check_to_auto_grow(&mut self, _: &mut Window, cx: &mut Context<Self>) {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ mod title_bar;
|
|||
mod virtual_list;
|
||||
mod window_border;
|
||||
|
||||
pub(crate) mod actions;
|
||||
|
||||
pub mod accordion;
|
||||
pub mod alert;
|
||||
pub mod animation;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use std::time::Duration;
|
||||
use std::{cell::Cell, rc::Rc};
|
||||
|
||||
use crate::actions::{Cancel, Confirm, SelectNext, SelectPrev};
|
||||
use crate::Icon;
|
||||
use crate::{
|
||||
input::{InputEvent, TextInput},
|
||||
|
|
@ -8,23 +9,22 @@ use crate::{
|
|||
v_flex, ActiveTheme, IconName, Size,
|
||||
};
|
||||
use gpui::{
|
||||
actions, div, prelude::FluentBuilder, uniform_list, AnyElement, AppContext, Entity,
|
||||
FocusHandle, Focusable, InteractiveElement, IntoElement, KeyBinding, Length,
|
||||
ListSizingBehavior, MouseButton, ParentElement, Render, SharedString, Styled, Task,
|
||||
UniformListScrollHandle, Window,
|
||||
div, prelude::FluentBuilder, uniform_list, AnyElement, AppContext, Entity, FocusHandle,
|
||||
Focusable, InteractiveElement, IntoElement, KeyBinding, Length, ListSizingBehavior,
|
||||
MouseButton, ParentElement, Render, SharedString, Styled, Task, UniformListScrollHandle,
|
||||
Window,
|
||||
};
|
||||
use gpui::{px, App, Context, EventEmitter, ScrollStrategy, Subscription};
|
||||
use gpui::{px, App, Context, EventEmitter, MouseDownEvent, ScrollStrategy, Subscription};
|
||||
use smol::Timer;
|
||||
|
||||
use super::loading::Loading;
|
||||
|
||||
actions!(list, [Cancel, Confirm, SelectPrev, SelectNext]);
|
||||
|
||||
pub fn init(cx: &mut App) {
|
||||
let context: Option<&str> = Some("List");
|
||||
cx.bind_keys([
|
||||
KeyBinding::new("escape", Cancel, context),
|
||||
KeyBinding::new("enter", Confirm, context),
|
||||
KeyBinding::new("enter", Confirm { secondary: false }, context),
|
||||
KeyBinding::new("secondary-enter", Confirm { secondary: true }, context),
|
||||
KeyBinding::new("up", SelectPrev, context),
|
||||
KeyBinding::new("down", SelectNext, context),
|
||||
]);
|
||||
|
|
@ -112,7 +112,9 @@ pub trait ListDelegate: Sized + 'static {
|
|||
);
|
||||
|
||||
/// Set the confirm and give the selected index, this is means user have clicked the item or pressed Enter.
|
||||
fn confirm(&mut self, ix: usize, window: &mut Window, cx: &mut Context<List<Self>>) {}
|
||||
///
|
||||
/// This will always to `set_selected_index` before confirm.
|
||||
fn confirm(&mut self, secondary: bool, window: &mut Window, cx: &mut Context<List<Self>>) {}
|
||||
|
||||
/// Cancel the selection, e.g.: Pressed ESC.
|
||||
fn cancel(&mut self, window: &mut Window, cx: &mut Context<List<Self>>) {}
|
||||
|
|
@ -347,7 +349,13 @@ where
|
|||
});
|
||||
});
|
||||
}
|
||||
InputEvent::PressEnter => self.on_action_confirm(&Confirm, window, cx),
|
||||
InputEvent::PressEnter { secondary } => self.on_action_confirm(
|
||||
&Confirm {
|
||||
secondary: *secondary,
|
||||
},
|
||||
window,
|
||||
cx,
|
||||
),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
@ -389,15 +397,25 @@ where
|
|||
}
|
||||
|
||||
fn on_action_cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
|
||||
if self.selected_index.is_none() {
|
||||
cx.propagate();
|
||||
}
|
||||
|
||||
if self.reset_on_cancel {
|
||||
self.set_selected_index(None, window, cx);
|
||||
}
|
||||
|
||||
self.delegate.cancel(window, cx);
|
||||
cx.emit(ListEvent::Cancel);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn on_action_confirm(&mut self, _: &Confirm, window: &mut Window, cx: &mut Context<Self>) {
|
||||
fn on_action_confirm(
|
||||
&mut self,
|
||||
confirm: &Confirm,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
if self.delegate.items_count(cx) == 0 {
|
||||
return;
|
||||
}
|
||||
|
|
@ -406,7 +424,9 @@ where
|
|||
return;
|
||||
};
|
||||
|
||||
self.delegate.confirm(ix, window, cx);
|
||||
self.delegate
|
||||
.set_selected_index(self.selected_index, window, cx);
|
||||
self.delegate.confirm(confirm.secondary, window, cx);
|
||||
cx.emit(ListEvent::Confirm(ix));
|
||||
cx.notify();
|
||||
}
|
||||
|
|
@ -496,10 +516,16 @@ where
|
|||
})
|
||||
.on_mouse_down(
|
||||
MouseButton::Left,
|
||||
cx.listener(move |this, _, window, cx| {
|
||||
cx.listener(move |this, ev: &MouseDownEvent, window, cx| {
|
||||
this.right_clicked_index = None;
|
||||
this.selected_index = Some(ix);
|
||||
this.on_action_confirm(&Confirm, window, cx);
|
||||
this.on_action_confirm(
|
||||
&Confirm {
|
||||
secondary: ev.modifiers.secondary(),
|
||||
},
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
}),
|
||||
)
|
||||
.on_mouse_down(
|
||||
|
|
|
|||
|
|
@ -1,26 +1,25 @@
|
|||
use std::{rc::Rc, time::Duration};
|
||||
|
||||
use gpui::{
|
||||
actions, anchored, div, hsla, point, prelude::FluentBuilder, px, relative, Animation,
|
||||
AnimationExt as _, AnyElement, App, Bounds, ClickEvent, Div, FocusHandle, Hsla,
|
||||
InteractiveElement, IntoElement, KeyBinding, MouseButton, ParentElement, Pixels, Point,
|
||||
RenderOnce, SharedString, Styled, Window,
|
||||
anchored, div, hsla, point, prelude::FluentBuilder, px, relative, Animation, AnimationExt as _,
|
||||
AnyElement, App, Bounds, ClickEvent, Div, FocusHandle, Hsla, InteractiveElement, IntoElement,
|
||||
KeyBinding, MouseButton, ParentElement, Pixels, Point, RenderOnce, SharedString, Styled,
|
||||
Window,
|
||||
};
|
||||
use rust_i18n::t;
|
||||
|
||||
use crate::{
|
||||
actions::{Cancel, Confirm},
|
||||
animation::cubic_bezier,
|
||||
button::{Button, ButtonVariant, ButtonVariants as _},
|
||||
h_flex, v_flex, ActiveTheme as _, ContextModal, IconName, Sizable as _, StyledExt,
|
||||
};
|
||||
|
||||
actions!(modal, [Escape, Enter]);
|
||||
|
||||
const CONTEXT: &str = "Modal";
|
||||
pub fn init(cx: &mut App) {
|
||||
cx.bind_keys([
|
||||
KeyBinding::new("escape", Escape, Some(CONTEXT)),
|
||||
KeyBinding::new("enter", Enter, Some(CONTEXT)),
|
||||
KeyBinding::new("escape", Cancel, Some(CONTEXT)),
|
||||
KeyBinding::new("enter", Confirm { secondary: false }, Some(CONTEXT)),
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
@ -382,7 +381,7 @@ impl RenderOnce for Modal {
|
|||
this.on_action({
|
||||
let on_cancel = on_cancel.clone();
|
||||
let on_close = on_close.clone();
|
||||
move |_: &Escape, window, cx| {
|
||||
move |_: &Cancel, window, cx| {
|
||||
// FIXME:
|
||||
//
|
||||
// Here some Modal have no focus_handle, so it will not work will Escape key.
|
||||
|
|
@ -395,7 +394,7 @@ impl RenderOnce for Modal {
|
|||
.on_action({
|
||||
let on_ok = on_ok.clone();
|
||||
let on_close = on_close.clone();
|
||||
move |_: &Enter, window, cx| {
|
||||
move |_: &Confirm, window, cx| {
|
||||
if on_ok(&ClickEvent::default(), window, cx) {
|
||||
on_close(&ClickEvent::default(), window, cx);
|
||||
window.close_modal(cx);
|
||||
|
|
|
|||
|
|
@ -1,20 +1,18 @@
|
|||
use gpui::{
|
||||
actions, anchored, deferred, div, prelude::FluentBuilder as _, px, AnyElement, App, Bounds,
|
||||
Context, Corner, DismissEvent, DispatchPhase, Element, ElementId, Entity, EventEmitter,
|
||||
FocusHandle, Focusable, GlobalElementId, Hitbox, InteractiveElement as _, IntoElement,
|
||||
KeyBinding, LayoutId, ManagedView, MouseButton, MouseDownEvent, ParentElement, Pixels, Point,
|
||||
Render, Style, StyleRefinement, Styled, Window,
|
||||
anchored, deferred, div, prelude::FluentBuilder as _, px, AnyElement, App, Bounds, Context,
|
||||
Corner, DismissEvent, DispatchPhase, Element, ElementId, Entity, EventEmitter, FocusHandle,
|
||||
Focusable, GlobalElementId, Hitbox, InteractiveElement as _, IntoElement, KeyBinding, LayoutId,
|
||||
ManagedView, MouseButton, MouseDownEvent, ParentElement, Pixels, Point, Render, Style,
|
||||
StyleRefinement, Styled, Window,
|
||||
};
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use crate::{Selectable, StyledExt as _};
|
||||
use crate::{actions::Cancel, Selectable, StyledExt as _};
|
||||
|
||||
const CONTEXT: &str = "Popover";
|
||||
|
||||
actions!(popover, [Escape]);
|
||||
|
||||
pub fn init(cx: &mut App) {
|
||||
cx.bind_keys([KeyBinding::new("escape", Escape, Some(CONTEXT))])
|
||||
cx.bind_keys([KeyBinding::new("escape", Cancel, Some(CONTEXT))])
|
||||
}
|
||||
|
||||
pub struct PopoverContent {
|
||||
|
|
@ -55,7 +53,10 @@ impl Render for PopoverContent {
|
|||
div()
|
||||
.track_focus(&self.focus_handle)
|
||||
.key_context(CONTEXT)
|
||||
.on_action(cx.listener(|_, _: &Escape, _, cx| cx.emit(DismissEvent)))
|
||||
.on_action(cx.listener(|_, _: &Cancel, _, cx| {
|
||||
cx.propagate();
|
||||
cx.emit(DismissEvent);
|
||||
}))
|
||||
.p_2()
|
||||
.when_some(self.max_width, |this, v| this.max_w(v))
|
||||
.child(self.content.clone()(window, cx))
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use crate::actions::{Cancel, Confirm, SelectNext, SelectPrev};
|
||||
use crate::scroll::{Scrollbar, ScrollbarState};
|
||||
use crate::{
|
||||
button::Button, h_flex, list::ListItem, popover::Popover, v_flex, ActiveTheme, Icon, IconName,
|
||||
|
|
@ -6,24 +7,22 @@ use crate::{
|
|||
use crate::{Kbd, StyledExt};
|
||||
use gpui::Subscription;
|
||||
use gpui::{
|
||||
actions, anchored, canvas, div, prelude::FluentBuilder, px, rems, Action, AnyElement, App,
|
||||
AppContext, Bounds, Context, Corner, DismissEvent, Edges, Entity, EventEmitter, FocusHandle,
|
||||
Focusable, InteractiveElement, IntoElement, KeyBinding, ParentElement, Pixels, Render,
|
||||
ScrollHandle, SharedString, StatefulInteractiveElement, Styled, WeakEntity, Window,
|
||||
anchored, canvas, div, prelude::FluentBuilder, px, rems, Action, AnyElement, App, AppContext,
|
||||
Bounds, Context, Corner, DismissEvent, Edges, Entity, EventEmitter, FocusHandle, Focusable,
|
||||
InteractiveElement, IntoElement, KeyBinding, ParentElement, Pixels, Render, ScrollHandle,
|
||||
SharedString, StatefulInteractiveElement, Styled, WeakEntity, Window,
|
||||
};
|
||||
use std::cell::Cell;
|
||||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
|
||||
actions!(menu, [Confirm, Dismiss, SelectNext, SelectPrev]);
|
||||
|
||||
const ITEM_HEIGHT: Pixels = px(26.);
|
||||
|
||||
pub fn init(cx: &mut App) {
|
||||
let context = Some("PopupMenu");
|
||||
cx.bind_keys([
|
||||
KeyBinding::new("enter", Confirm, context),
|
||||
KeyBinding::new("escape", Dismiss, context),
|
||||
KeyBinding::new("enter", Confirm { secondary: false }, context),
|
||||
KeyBinding::new("escape", Cancel, context),
|
||||
KeyBinding::new("up", SelectPrev, context),
|
||||
KeyBinding::new("down", SelectNext, context),
|
||||
]);
|
||||
|
|
@ -127,7 +126,7 @@ impl PopupMenu {
|
|||
let _subscriptions =
|
||||
vec![
|
||||
cx.on_blur(&focus_handle, window, |this: &mut PopupMenu, window, cx| {
|
||||
this.dismiss(&Dismiss, window, cx)
|
||||
this.dismiss(&Cancel, window, cx)
|
||||
}),
|
||||
];
|
||||
|
||||
|
|
@ -544,7 +543,7 @@ impl PopupMenu {
|
|||
cx.stop_propagation();
|
||||
window.prevent_default();
|
||||
self.selected_index = Some(ix);
|
||||
self.confirm(&Confirm, window, cx);
|
||||
self.confirm(&Confirm { secondary: false }, window, cx);
|
||||
}
|
||||
|
||||
fn confirm(&mut self, _: &Confirm, window: &mut Window, cx: &mut Context<Self>) {
|
||||
|
|
@ -554,11 +553,11 @@ impl PopupMenu {
|
|||
match item {
|
||||
Some(PopupMenuItem::Item { handler, .. }) => {
|
||||
handler(window, cx);
|
||||
self.dismiss(&Dismiss, window, cx)
|
||||
self.dismiss(&Cancel, window, cx)
|
||||
}
|
||||
Some(PopupMenuItem::ElementItem { handler, .. }) => {
|
||||
handler(window, cx);
|
||||
self.dismiss(&Dismiss, window, cx)
|
||||
self.dismiss(&Cancel, window, cx)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
@ -601,7 +600,7 @@ impl PopupMenu {
|
|||
}
|
||||
}
|
||||
|
||||
fn dismiss(&mut self, _: &Dismiss, window: &mut Window, cx: &mut Context<Self>) {
|
||||
fn dismiss(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
|
||||
if self.active_submenu().is_some() {
|
||||
return;
|
||||
}
|
||||
|
|
@ -620,7 +619,7 @@ impl PopupMenu {
|
|||
// Dismiss parent menu, when this menu is dismissed
|
||||
_ = parent_menu.update(cx, |view, cx| {
|
||||
view.hovered_menu_ix = None;
|
||||
view.dismiss(&Dismiss, window, cx);
|
||||
view.dismiss(&Cancel, window, cx);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -883,9 +882,7 @@ impl Render for PopupMenu {
|
|||
.on_action(cx.listener(Self::select_prev))
|
||||
.on_action(cx.listener(Self::confirm))
|
||||
.on_action(cx.listener(Self::dismiss))
|
||||
.on_mouse_down_out(
|
||||
cx.listener(|this, _, window, cx| this.dismiss(&Dismiss, window, cx)),
|
||||
)
|
||||
.on_mouse_down_out(cx.listener(|this, _, window, cx| this.dismiss(&Cancel, window, cx)))
|
||||
.popover_style(cx)
|
||||
.text_color(cx.theme().popover_foreground)
|
||||
.relative()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use std::{cell::Cell, ops::Range, rc::Rc, time::Duration};
|
||||
|
||||
use crate::{
|
||||
actions::{Cancel, SelectNext, SelectPrev},
|
||||
context_menu::ContextMenuExt,
|
||||
h_flex,
|
||||
popup_menu::PopupMenu,
|
||||
|
|
@ -18,16 +19,7 @@ use gpui::{
|
|||
|
||||
mod loading;
|
||||
|
||||
actions!(
|
||||
table,
|
||||
[
|
||||
Cancel,
|
||||
SelectPrev,
|
||||
SelectNext,
|
||||
SelectPrevColumn,
|
||||
SelectNextColumn
|
||||
]
|
||||
);
|
||||
actions!(table, [SelectPrevColumn, SelectNextColumn]);
|
||||
|
||||
pub fn init(cx: &mut App) {
|
||||
let context = Some("Table");
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ use gpui::{
|
|||
use rust_i18n::t;
|
||||
|
||||
use crate::{
|
||||
actions::Cancel,
|
||||
button::{Button, ButtonVariants as _},
|
||||
dropdown::Escape,
|
||||
h_flex,
|
||||
input::clear_button,
|
||||
v_flex, ActiveTheme, Icon, IconName, Sizable, Size, StyleSized as _, StyledExt as _,
|
||||
|
|
@ -19,7 +19,7 @@ use super::calendar::{Calendar, CalendarEvent, Date, Matcher};
|
|||
|
||||
pub fn init(cx: &mut App) {
|
||||
let context = Some("DatePicker");
|
||||
cx.bind_keys([KeyBinding::new("escape", Escape, context)])
|
||||
cx.bind_keys([KeyBinding::new("escape", Cancel, context)])
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
|
@ -208,7 +208,11 @@ impl DatePicker {
|
|||
cx.notify();
|
||||
}
|
||||
|
||||
fn escape(&mut self, _: &Escape, window: &mut Window, cx: &mut Context<Self>) {
|
||||
fn escape(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
|
||||
if !self.open {
|
||||
cx.propagate();
|
||||
}
|
||||
|
||||
self.focus_back_if_need(window, cx);
|
||||
self.open = false;
|
||||
|
||||
|
|
@ -364,7 +368,7 @@ impl Render for DatePicker {
|
|||
.on_mouse_up_out(
|
||||
MouseButton::Left,
|
||||
cx.listener(|view, _, window, cx| {
|
||||
view.escape(&Escape, window, cx);
|
||||
view.escape(&Cancel, window, cx);
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
|
|
|
|||
Loading…
Reference in a new issue