384 lines
16 KiB
Rust
384 lines
16 KiB
Rust
use gpui::{
|
|
actions, div, impl_internal_actions, px, App, AppContext, Context, Corner, DismissEvent,
|
|
Element, Entity, EventEmitter, FocusHandle, Focusable, InteractiveElement, IntoElement,
|
|
KeyBinding, MouseButton, ParentElement as _, Render, SharedString, Styled as _, Window,
|
|
};
|
|
use gpui_component::{
|
|
button::{Button, ButtonVariants as _},
|
|
context_menu::ContextMenuExt,
|
|
divider::Divider,
|
|
h_flex,
|
|
input::TextInput,
|
|
popover::{Popover, PopoverContent},
|
|
popup_menu::PopupMenuExt,
|
|
switch::Switch,
|
|
v_flex, ActiveTheme as _, ContextModal, IconName, Sizable,
|
|
};
|
|
use serde::Deserialize;
|
|
|
|
#[derive(Clone, PartialEq, Deserialize)]
|
|
struct Info(usize);
|
|
|
|
actions!(
|
|
popover_story,
|
|
[Copy, Paste, Cut, SearchAll, ToggleWindowMode]
|
|
);
|
|
impl_internal_actions!(popover_story, [Info]);
|
|
|
|
pub fn init(cx: &mut App) {
|
|
cx.bind_keys([
|
|
#[cfg(target_os = "macos")]
|
|
KeyBinding::new("cmd-c", Copy, None),
|
|
#[cfg(not(target_os = "macos"))]
|
|
KeyBinding::new("ctrl-c", Copy, None),
|
|
#[cfg(target_os = "macos")]
|
|
KeyBinding::new("cmd-v", Paste, None),
|
|
#[cfg(not(target_os = "macos"))]
|
|
KeyBinding::new("ctrl-v", Paste, None),
|
|
#[cfg(target_os = "macos")]
|
|
KeyBinding::new("cmd-x", Cut, None),
|
|
#[cfg(not(target_os = "macos"))]
|
|
KeyBinding::new("ctrl-x", Cut, None),
|
|
#[cfg(target_os = "macos")]
|
|
KeyBinding::new("cmd-shift-f", SearchAll, None),
|
|
#[cfg(not(target_os = "macos"))]
|
|
KeyBinding::new("ctrl-shift-f", SearchAll, None),
|
|
])
|
|
}
|
|
|
|
struct Form {
|
|
input1: Entity<TextInput>,
|
|
}
|
|
|
|
impl Form {
|
|
fn new(window: &mut Window, cx: &mut App) -> Entity<Self> {
|
|
cx.new(|cx| Self {
|
|
input1: cx.new(|cx| TextInput::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(self.input1.clone())
|
|
.child(
|
|
Button::new("submit")
|
|
.label("Submit")
|
|
.primary()
|
|
.on_click(cx.listener(|_, _, _, cx| cx.emit(DismissEvent))),
|
|
)
|
|
}
|
|
}
|
|
|
|
pub struct PopupStory {
|
|
focus_handle: FocusHandle,
|
|
form: Entity<Form>,
|
|
message: String,
|
|
window_mode: bool,
|
|
}
|
|
|
|
impl super::Story for PopupStory {
|
|
fn title() -> &'static str {
|
|
"Popup"
|
|
}
|
|
|
|
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 PopupStory {
|
|
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);
|
|
Self {
|
|
form,
|
|
focus_handle: cx.focus_handle(),
|
|
message: "".to_string(),
|
|
window_mode: false,
|
|
}
|
|
}
|
|
|
|
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_toggle_window_mode(
|
|
&mut self,
|
|
_: &ToggleWindowMode,
|
|
_: &mut Window,
|
|
cx: &mut Context<Self>,
|
|
) {
|
|
self.window_mode = !self.window_mode;
|
|
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()
|
|
}
|
|
}
|
|
|
|
impl Focusable for PopupStory {
|
|
fn focus_handle(&self, _cx: &App) -> FocusHandle {
|
|
self.focus_handle.clone()
|
|
}
|
|
}
|
|
|
|
impl Render for PopupStory {
|
|
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
|
let form = self.form.clone();
|
|
let window_mode = self.window_mode;
|
|
|
|
v_flex()
|
|
.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_toggle_window_mode))
|
|
.on_action(cx.listener(Self::on_action_info))
|
|
.p_4()
|
|
.mb_5()
|
|
.size_full()
|
|
.min_h(px(400.))
|
|
.context_menu({
|
|
move |this, window, cx| {
|
|
this.separator()
|
|
.menu("Cut", Box::new(Cut))
|
|
.menu("Copy", Box::new(Copy))
|
|
.menu("Paste", Box::new(Paste))
|
|
.separator()
|
|
.separator()
|
|
.submenu("Settings", window, cx, move |menu, _, _| {
|
|
menu.menu_with_check(
|
|
"Toggle Window Mode",
|
|
window_mode,
|
|
Box::new(ToggleWindowMode),
|
|
)
|
|
.separator()
|
|
.menu("Info 0", Box::new(Info(0)))
|
|
.menu("Item 1", Box::new(Info(1)))
|
|
.menu("Item 2", Box::new(Info(2)))
|
|
})
|
|
.separator()
|
|
.menu("Search All", Box::new(SearchAll))
|
|
.separator()
|
|
}
|
|
})
|
|
.gap_6()
|
|
.child(
|
|
Switch::new("switch-window-mode")
|
|
.checked(window_mode)
|
|
.label("Use Window Popover")
|
|
.on_click(cx.listener(|this, checked, _, _| {
|
|
this.window_mode = *checked;
|
|
})),
|
|
)
|
|
.child(
|
|
h_flex()
|
|
.items_center()
|
|
.justify_between()
|
|
.child(
|
|
v_flex().gap_4().child(
|
|
Popover::new("info-top-left")
|
|
.trigger(Button::new("info-top-left").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")
|
|
.label("Yes")
|
|
.w(px(80.))
|
|
.small(),
|
|
)
|
|
.into_any()
|
|
})
|
|
.max_w(px(600.))
|
|
})
|
|
}),
|
|
),
|
|
)
|
|
.child(
|
|
Popover::new("info-top-right")
|
|
.anchor(Corner::TopRight)
|
|
.trigger(Button::new("info-top-right").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")
|
|
.label("Yes")
|
|
.w(px(80.))
|
|
.small(),
|
|
)
|
|
.into_any()
|
|
})
|
|
})
|
|
}),
|
|
),
|
|
)
|
|
.child(
|
|
h_flex()
|
|
.gap_3()
|
|
.child(
|
|
Button::new("popup-menu-1")
|
|
.icon(IconName::Ellipsis)
|
|
.popup_menu(move |this, window, cx| {
|
|
this.menu("Copy", Box::new(Copy))
|
|
.menu("Cut", Box::new(Cut))
|
|
.menu("Paste", Box::new(Paste))
|
|
.separator()
|
|
.menu_with_icon("Search", IconName::Search, Box::new(SearchAll))
|
|
.separator()
|
|
.menu_with_check(
|
|
"Window Mode",
|
|
window_mode,
|
|
Box::new(ToggleWindowMode),
|
|
)
|
|
.separator()
|
|
.menu_with_element(
|
|
|_, cx| {
|
|
v_flex().gap_1().child("Custom Element").child(
|
|
div()
|
|
.text_sm()
|
|
.text_color(cx.theme().muted_foreground)
|
|
.child("THis is sub-title"),
|
|
)
|
|
},
|
|
Box::new(Info(0)),
|
|
)
|
|
.separator()
|
|
.submenu("Links", window, cx, |menu, _, _| {
|
|
menu.link_with_icon(
|
|
"GitHub Repository",
|
|
IconName::GitHub,
|
|
"https://github.com/longbridge/gpui-component",
|
|
)
|
|
.separator()
|
|
.link("GPUI", "https://gpui.rs")
|
|
.link("Zed", "https://zed.dev")
|
|
})
|
|
}),
|
|
)
|
|
.child(
|
|
Button::new("popup-menu-11112")
|
|
.label("Scrollable Menu")
|
|
.popup_menu_with_anchor(Corner::TopRight, move |this, _, _| {
|
|
let mut this = this.scrollable().max_h(px(300.));
|
|
for i in 0..100 {
|
|
this = this.menu(
|
|
SharedString::from(format!("Item {}", i)),
|
|
Box::new(Info(i)),
|
|
)
|
|
}
|
|
this
|
|
}),
|
|
)
|
|
.child(self.message.clone()),
|
|
)
|
|
.child("Right click to open ContextMenu")
|
|
.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").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").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()
|
|
.child(
|
|
Button::new("info1")
|
|
.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()
|
|
})
|
|
})
|
|
}),
|
|
),
|
|
),
|
|
)
|
|
}
|
|
}
|