use gpui::{ actions, div, px, AnchorCorner, AppContext, DismissEvent, Element, EventEmitter, FocusHandle, FocusableView, InteractiveElement, IntoElement, MouseButton, MouseDownEvent, ParentElement as _, Render, Styled as _, View, ViewContext, VisualContext, WindowContext, }; use ui::{ button::Button, context_menu::ContextMenuExt, divider::Divider, h_flex, input::TextInput, popover::{Popover, PopoverContent}, popup_menu::PopupMenuExt, prelude::FluentBuilder, switch::Switch, v_flex, IconName, Sizable, }; actions!( popover_story, [Copy, Paste, Cut, SearchAll, ToggleWindowMode] ); struct Form { input1: View, } impl Form { fn new(cx: &mut WindowContext) -> View { cx.new_view(|cx| Self { input1: cx.new_view(TextInput::new), }) } } impl FocusableView for Form { fn focus_handle(&self, cx: &AppContext) -> FocusHandle { self.input1.focus_handle(cx) } } impl EventEmitter for Form {} impl Render for Form { fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { v_flex() .gap_4() .p_4() .size_full() .child("This is a form container.") .child(self.input1.clone()) .child( Button::new("submit", cx) .label("Submit") .primary() .on_click(cx.listener(|_, _, cx| cx.emit(DismissEvent))), ) } } pub struct PopupStory { focus_handle: FocusHandle, form: View
, message: String, window_mode: bool, } impl PopupStory { pub fn view(cx: &mut WindowContext) -> View { cx.new_view(Self::new) } fn new(cx: &mut ViewContext) -> Self { let form = Form::new(cx); Self { form, focus_handle: cx.focus_handle(), message: "".to_string(), window_mode: false, } } fn on_copy(&mut self, _: &Copy, _: &mut ViewContext) { self.message = "You have clicked copy".to_string(); } fn on_cut(&mut self, _: &Cut, _: &mut ViewContext) { self.message = "You have clicked cut".to_string(); } fn on_paste(&mut self, _: &Paste, _: &mut ViewContext) { self.message = "You have clicked paste".to_string(); } fn on_search_all(&mut self, _: &SearchAll, _: &mut ViewContext) { self.message = "You have clicked search all".to_string(); } } impl FocusableView for PopupStory { fn focus_handle(&self, _cx: &AppContext) -> FocusHandle { self.focus_handle.clone() } } impl Render for PopupStory { fn render(&mut self, cx: &mut ViewContext) -> 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)) .p_4() .mb_5() .size_full() .min_h(px(400.)) .on_any_mouse_down(cx.listener(|this, _: &MouseDownEvent, cx| { cx.focus(&this.focus_handle); })) .context_menu({ move |this, _cx| { this.menu("Cut", Box::new(Cut)) .menu("Copy", Box::new(Copy)) .menu("Paste", Box::new(Paste)) .separator() .menu("About", Box::new(SearchAll)) } }) .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") .when(window_mode, |this| this.window_mode()) .trigger(Button::new("info-top-left", cx).label("Top Left")) .content(|cx| { PopoverContent::new(cx, |cx| { v_flex() .gap_4() .child("Hello, this is a Popover.") .child(Divider::horizontal()) .child( Button::new("info1", cx) .label("Yes") .w(px(80.)) .small(), ) .into_any() }) }), ), ) .child( Popover::new("info-top-right") .when(window_mode, |this| this.window_mode()) .anchor(AnchorCorner::TopRight) .trigger(Button::new("info-top-right", cx).label("Top Right")) .content(|cx| { PopoverContent::new(cx, |cx| { v_flex() .gap_4() .w_96() .child("Hello, this is a Popover on the Top Right.") .child(Divider::horizontal()) .child( Button::new("info1", cx) .label("Yes") .w(px(80.)) .small(), ) .into_any() }) }), ), ) .child( h_flex() .gap_3() .child( Button::new("popup-menu-1", cx) .icon(IconName::Ellipsis) .popup_menu(|this, _| { 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("Check Menu", true, Box::new(SearchAll)) .separator() .link_with_icon( "GitHub Repository", IconName::GitHub, "https://github.com/huacnlee/gpui-component", ) }), ) .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") .when(window_mode, |this| this.window_mode()) .anchor(AnchorCorner::BottomLeft) .trigger( Button::new("pop", cx).label("Popup with Form").w(px(300.)), ) .content(move |_| form.clone()), ) .child( Popover::new("info-bottom-right") .when(window_mode, |this| this.window_mode()) .anchor(AnchorCorner::BottomRight) .mouse_button(MouseButton::Right) .trigger( Button::new("pop", cx) .label("Mouse Right Click") .w(px(300.)), ) .content(|cx| { PopoverContent::new(cx, |cx| { v_flex() .gap_4() .child("Hello, this is a Popover on the Bottom Right.") .child(Divider::horizontal()) .child( Button::new("info1", cx) .label("Yes") .w(px(80.)) .small(), ) .into_any() }) }), ), ), ) } }