305 lines
12 KiB
Rust
305 lines
12 KiB
Rust
use gpui::{
|
|
actions, div, px, Action, App, AppContext, Context, Corner, DismissEvent, Element, Entity,
|
|
EventEmitter, FocusHandle, Focusable, InteractiveElement, IntoElement, KeyBinding, MouseButton,
|
|
ParentElement as _, Render, Styled as _, Window,
|
|
};
|
|
use gpui_component::{
|
|
button::{Button, ButtonVariants as _},
|
|
divider::Divider,
|
|
h_flex,
|
|
input::{InputState, TextInput},
|
|
popover::{Popover, PopoverContent},
|
|
v_flex, ContextModal, Sizable,
|
|
};
|
|
use serde::Deserialize;
|
|
|
|
#[derive(Action, Clone, PartialEq, Eq, Deserialize)]
|
|
#[action(namespace = popover_story, no_json)]
|
|
struct Info(usize);
|
|
|
|
actions!(popover_story, [Copy, Paste, Cut, SearchAll, ToggleCheck]);
|
|
const CONTEXT: &str = "popover-story";
|
|
pub fn init(cx: &mut App) {
|
|
cx.bind_keys([
|
|
#[cfg(target_os = "macos")]
|
|
KeyBinding::new("cmd-c", Copy, Some(CONTEXT)),
|
|
#[cfg(not(target_os = "macos"))]
|
|
KeyBinding::new("ctrl-c", Copy, Some(CONTEXT)),
|
|
#[cfg(target_os = "macos")]
|
|
KeyBinding::new("cmd-v", Paste, Some(CONTEXT)),
|
|
#[cfg(not(target_os = "macos"))]
|
|
KeyBinding::new("ctrl-v", Paste, Some(CONTEXT)),
|
|
#[cfg(target_os = "macos")]
|
|
KeyBinding::new("cmd-x", Cut, Some(CONTEXT)),
|
|
#[cfg(not(target_os = "macos"))]
|
|
KeyBinding::new("ctrl-x", Cut, Some(CONTEXT)),
|
|
#[cfg(target_os = "macos")]
|
|
KeyBinding::new("cmd-shift-f", SearchAll, Some(CONTEXT)),
|
|
#[cfg(not(target_os = "macos"))]
|
|
KeyBinding::new("ctrl-shift-f", SearchAll, Some(CONTEXT)),
|
|
])
|
|
}
|
|
|
|
struct Form {
|
|
input1: Entity<InputState>,
|
|
}
|
|
|
|
impl Form {
|
|
fn new(window: &mut Window, cx: &mut App) -> Entity<Self> {
|
|
cx.new(|cx| Self {
|
|
input1: cx.new(|cx| InputState::new(window, cx)),
|
|
})
|
|
}
|
|
}
|
|
|
|
impl Focusable for Form {
|
|
fn focus_handle(&self, cx: &App) -> FocusHandle {
|
|
self.input1.focus_handle(cx)
|
|
}
|
|
}
|
|
|
|
impl EventEmitter<DismissEvent> for Form {}
|
|
|
|
impl Render for Form {
|
|
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
|
v_flex()
|
|
.gap_4()
|
|
.p_4()
|
|
.size_full()
|
|
.child("This is a form container.")
|
|
.child(TextInput::new(&self.input1))
|
|
.child(
|
|
Button::new("submit")
|
|
.label("Submit")
|
|
.primary()
|
|
.on_click(cx.listener(|_, _, _, cx| cx.emit(DismissEvent))),
|
|
)
|
|
}
|
|
}
|
|
|
|
pub struct PopoverStory {
|
|
focus_handle: FocusHandle,
|
|
form: Entity<Form>,
|
|
checked: bool,
|
|
message: String,
|
|
}
|
|
|
|
impl super::Story for PopoverStory {
|
|
fn title() -> &'static str {
|
|
"Popover"
|
|
}
|
|
|
|
fn description() -> &'static str {
|
|
"A popup displays content on top of the main page."
|
|
}
|
|
|
|
fn new_view(window: &mut Window, cx: &mut App) -> Entity<impl Render + Focusable> {
|
|
Self::view(window, cx)
|
|
}
|
|
}
|
|
|
|
impl PopoverStory {
|
|
pub fn view(window: &mut Window, cx: &mut App) -> Entity<Self> {
|
|
cx.new(|cx| Self::new(window, cx))
|
|
}
|
|
|
|
fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
|
let form = Form::new(window, cx);
|
|
|
|
cx.focus_self(window);
|
|
|
|
Self {
|
|
form,
|
|
checked: true,
|
|
focus_handle: cx.focus_handle(),
|
|
message: "".to_string(),
|
|
}
|
|
}
|
|
|
|
fn on_copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
|
|
self.message = "You have clicked copy".to_string();
|
|
cx.notify()
|
|
}
|
|
|
|
fn on_cut(&mut self, _: &Cut, _: &mut Window, cx: &mut Context<Self>) {
|
|
self.message = "You have clicked cut".to_string();
|
|
cx.notify()
|
|
}
|
|
|
|
fn on_paste(&mut self, _: &Paste, _: &mut Window, cx: &mut Context<Self>) {
|
|
self.message = "You have clicked paste".to_string();
|
|
cx.notify()
|
|
}
|
|
|
|
fn on_search_all(&mut self, _: &SearchAll, _: &mut Window, cx: &mut Context<Self>) {
|
|
self.message = "You have clicked search all".to_string();
|
|
cx.notify()
|
|
}
|
|
|
|
fn on_action_info(&mut self, info: &Info, _: &mut Window, cx: &mut Context<Self>) {
|
|
self.message = format!("You have clicked info: {}", info.0);
|
|
cx.notify()
|
|
}
|
|
|
|
fn on_action_toggle_check(&mut self, _: &ToggleCheck, _: &mut Window, cx: &mut Context<Self>) {
|
|
self.checked = !self.checked;
|
|
self.message = format!("You have clicked toggle check: {}", self.checked);
|
|
cx.notify()
|
|
}
|
|
}
|
|
|
|
impl Focusable for PopoverStory {
|
|
fn focus_handle(&self, _cx: &App) -> FocusHandle {
|
|
self.focus_handle.clone()
|
|
}
|
|
}
|
|
|
|
impl Render for PopoverStory {
|
|
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
|
let form = self.form.clone();
|
|
|
|
v_flex()
|
|
.key_context(CONTEXT)
|
|
.track_focus(&self.focus_handle)
|
|
.on_action(cx.listener(Self::on_copy))
|
|
.on_action(cx.listener(Self::on_cut))
|
|
.on_action(cx.listener(Self::on_paste))
|
|
.on_action(cx.listener(Self::on_search_all))
|
|
.on_action(cx.listener(Self::on_action_info))
|
|
.on_action(cx.listener(Self::on_action_toggle_check))
|
|
.size_full()
|
|
.min_h(px(400.))
|
|
.gap_6()
|
|
.child(
|
|
h_flex()
|
|
.items_center()
|
|
.justify_between()
|
|
.child(
|
|
v_flex().gap_4().child(
|
|
Popover::new("info-top-left")
|
|
.trigger(Button::new("info-top-left").outline().label("Top Left"))
|
|
.content(|window, cx| {
|
|
cx.new(|cx| {
|
|
PopoverContent::new(window, cx, |_, _| {
|
|
v_flex()
|
|
.gap_4()
|
|
.child("Hello, this is a Popover.")
|
|
.w(px(400.))
|
|
.child(Divider::horizontal())
|
|
.child(
|
|
Button::new("info1")
|
|
.primary()
|
|
.label("Ok")
|
|
.w(px(80.))
|
|
.small(),
|
|
)
|
|
.into_any()
|
|
})
|
|
.p_4()
|
|
.max_w(px(600.))
|
|
})
|
|
}),
|
|
),
|
|
)
|
|
.child(
|
|
Popover::new("info-top-right")
|
|
.anchor(Corner::TopRight)
|
|
.trigger(Button::new("info-top-right").outline().label("Top Right"))
|
|
.content(|window, cx| {
|
|
cx.new(|cx| {
|
|
PopoverContent::new(window, cx, |_, _| {
|
|
v_flex()
|
|
.gap_4()
|
|
.w_96()
|
|
.child("Hello, this is a Popover on the Top Right.")
|
|
.child(Divider::horizontal())
|
|
.child(
|
|
Button::new("info1")
|
|
.primary()
|
|
.label("Ok")
|
|
.w(px(80.))
|
|
.small(),
|
|
)
|
|
.into_any()
|
|
})
|
|
.p_4()
|
|
})
|
|
}),
|
|
),
|
|
)
|
|
.child(
|
|
div().absolute().bottom_4().left_0().w_full().h_10().child(
|
|
h_flex()
|
|
.items_center()
|
|
.justify_between()
|
|
.child(
|
|
Popover::new("info-bottom-left")
|
|
.anchor(Corner::BottomLeft)
|
|
.trigger(
|
|
Button::new("pop")
|
|
.outline()
|
|
.label("Popup with Form")
|
|
.w(px(300.)),
|
|
)
|
|
.content(move |_, _| form.clone()),
|
|
)
|
|
.child(
|
|
Popover::new("info-bottom-right")
|
|
.anchor(Corner::BottomRight)
|
|
.mouse_button(MouseButton::Right)
|
|
.trigger(
|
|
Button::new("pop")
|
|
.outline()
|
|
.label("Mouse Right Click")
|
|
.w(px(300.)),
|
|
)
|
|
.content(|window, cx| {
|
|
cx.new(|cx| {
|
|
PopoverContent::new(window, cx, |_, cx| {
|
|
v_flex()
|
|
.gap_2()
|
|
.child(
|
|
"Hello, this is a Popover on the Bottom Right.",
|
|
)
|
|
.child(Divider::horizontal())
|
|
.child(
|
|
h_flex()
|
|
.gap_2()
|
|
.justify_end()
|
|
.child(
|
|
Button::new("info1")
|
|
.primary()
|
|
.label("Ok")
|
|
.w(px(80.))
|
|
.small()
|
|
.on_click(cx.listener(
|
|
|_, _, window, cx| {
|
|
window.push_notification(
|
|
"You have clicked Ok.",
|
|
cx,
|
|
);
|
|
cx.emit(DismissEvent);
|
|
},
|
|
)),
|
|
)
|
|
.child(
|
|
Button::new("close")
|
|
.label("Cancel")
|
|
.small()
|
|
.on_click(cx.listener(
|
|
|_, _, _, cx| {
|
|
cx.emit(DismissEvent);
|
|
},
|
|
)),
|
|
),
|
|
)
|
|
.into_any()
|
|
})
|
|
.p_4()
|
|
})
|
|
}),
|
|
),
|
|
),
|
|
)
|
|
}
|
|
}
|