From 80689264e2f89cd1bdf2a0bbf72fd3f92ec13bed Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Thu, 6 Nov 2025 15:19:10 +0800 Subject: [PATCH] sheet: Rename `Drawer` to `Sheet`. (#1527) ## Break Change - Renamed `Drawer` to `Sheet`, also renamed relative method contains `drawer` to `sheet`. - Renamed `ContextModal` to `WindowExt`. ```diff - use gpui_component::drawer::Drawer + use gpui_component::sheet::Sheet - use gpui_component::ContextModal + use gpui_component::WindowExt ``` --- crates/story/examples/dock.rs | 4 +- crates/story/examples/editor.rs | 2 +- crates/story/examples/large-text.rs | 2 +- crates/story/examples/tiles.rs | 4 +- crates/story/src/clipboard_story.rs | 2 +- crates/story/src/lib.rs | 10 +- crates/story/src/main.rs | 4 +- crates/story/src/modal_story.rs | 2 +- crates/story/src/notification_story.rs | 3 +- crates/story/src/popover_story.rs | 2 +- .../src/{drawer_story.rs => sheet_story.rs} | 122 +++++------- crates/story/src/slider_story.rs | 7 +- crates/story/src/title_bar.rs | 2 +- crates/ui/src/lib.rs | 6 +- crates/ui/src/modal.rs | 2 +- crates/ui/src/root.rs | 95 +++++----- crates/ui/src/{drawer.rs => sheet.rs} | 49 +++-- docs/docs/components/index.md | 2 +- docs/docs/components/modal.md | 2 +- docs/docs/components/notification.md | 2 +- docs/docs/components/{drawer.md => sheet.md} | 174 +++++++++--------- docs/docs/root.md | 4 +- examples/modal_overlay/src/main.rs | 4 +- 23 files changed, 234 insertions(+), 272 deletions(-) rename crates/story/src/{drawer_story.rs => sheet_story.rs} (79%) rename crates/ui/src/{drawer.rs => sheet.rs} (87%) rename docs/docs/components/{drawer.md => sheet.md} (63%) diff --git a/crates/story/examples/dock.rs b/crates/story/examples/dock.rs index c57fc0b8..0d0242f8 100644 --- a/crates/story/examples/dock.rs +++ b/crates/story/examples/dock.rs @@ -506,7 +506,7 @@ pub fn open_new( impl Render for StoryWorkspace { fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { - let drawer_layer = Root::render_drawer_layer(window, cx); + let sheet_layer = Root::render_sheet_layer(window, cx); let modal_layer = Root::render_modal_layer(window, cx); let notification_layer = Root::render_notification_layer(window, cx); @@ -521,7 +521,7 @@ impl Render for StoryWorkspace { .flex_col() .child(self.title_bar.clone()) .child(self.dock_area.clone()) - .children(drawer_layer) + .children(sheet_layer) .children(modal_layer) .children(notification_layer) } diff --git a/crates/story/examples/editor.rs b/crates/story/examples/editor.rs index 48cb0baf..ff347d17 100644 --- a/crates/story/examples/editor.rs +++ b/crates/story/examples/editor.rs @@ -10,7 +10,7 @@ use std::{ use autocorrect::ignorer::Ignorer; use gpui::{prelude::FluentBuilder, *}; use gpui_component::{ - ActiveTheme, ContextModal, IconName, Sizable, + ActiveTheme, IconName, Sizable, WindowExt, button::{Button, ButtonVariants as _}, h_flex, highlighter::{Diagnostic, DiagnosticSeverity, Language, LanguageConfig, LanguageRegistry}, diff --git a/crates/story/examples/large-text.rs b/crates/story/examples/large-text.rs index 8f50e425..03ab7a82 100644 --- a/crates/story/examples/large-text.rs +++ b/crates/story/examples/large-text.rs @@ -1,6 +1,6 @@ use gpui::*; use gpui_component::{ - ActiveTheme, ContextModal, Selectable, Sizable, + ActiveTheme, Selectable, Sizable, WindowExt, button::{Button, ButtonVariants as _}, h_flex, input::{self, Input, InputEvent, InputState, TabSize}, diff --git a/crates/story/examples/tiles.rs b/crates/story/examples/tiles.rs index 45ad95ff..4d90f22a 100644 --- a/crates/story/examples/tiles.rs +++ b/crates/story/examples/tiles.rs @@ -405,7 +405,7 @@ pub fn open_new( impl Render for StoryTiles { fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { - let drawer_layer = Root::render_drawer_layer(window, cx); + let sheet_layer = Root::render_sheet_layer(window, cx); let modal_layer = Root::render_modal_layer(window, cx); let notification_layer = Root::render_notification_layer(window, cx); @@ -419,7 +419,7 @@ impl Render for StoryTiles { .text_color(cx.theme().foreground) .child(TitleBar::new().child(div().flex().items_center().child("Story Tiles"))) .child(self.dock_area.clone()) - .children(drawer_layer) + .children(sheet_layer) .children(modal_layer) .children(notification_layer) } diff --git a/crates/story/src/clipboard_story.rs b/crates/story/src/clipboard_story.rs index dbd0caa4..0e41a52c 100644 --- a/crates/story/src/clipboard_story.rs +++ b/crates/story/src/clipboard_story.rs @@ -4,7 +4,7 @@ use gpui::{ }; use gpui_component::{ - ContextModal, + WindowExt, clipboard::Clipboard, h_flex, input::{Input, InputState}, diff --git a/crates/story/src/lib.rs b/crates/story/src/lib.rs index a7f72ca8..d685e999 100644 --- a/crates/story/src/lib.rs +++ b/crates/story/src/lib.rs @@ -13,7 +13,6 @@ mod collapsible_story; mod color_picker_story; mod date_picker_story; mod description_list_story; -mod drawer_story; mod form_story; mod group_box_story; mod icon_story; @@ -33,6 +32,7 @@ mod radio_story; mod resizable_story; mod scrollable_story; mod select_story; +mod sheet_story; mod sidebar_story; mod skeleton_story; mod slider_story; @@ -73,7 +73,6 @@ pub use collapsible_story::CollapsibleStory; pub use color_picker_story::ColorPickerStory; pub use date_picker_story::DatePickerStory; pub use description_list_story::DescriptionListStory; -pub use drawer_story::DrawerStory; pub use form_story::FormStory; pub use group_box_story::GroupBoxStory; pub use icon_story::IconStory; @@ -94,6 +93,7 @@ pub use resizable_story::ResizableStory; pub use scrollable_story::ScrollableStory; pub use select_story::SelectStory; use serde::{Deserialize, Serialize}; +pub use sheet_story::SheetStory; pub use sidebar_story::SidebarStory; pub use skeleton_story::SkeletonStory; pub use slider_story::SliderStory; @@ -112,7 +112,7 @@ pub use webview_story::WebViewStory; pub use welcome_story::WelcomeStory; use gpui_component::{ - ActiveTheme, ContextModal, IconName, Root, TitleBar, + ActiveTheme, IconName, Root, TitleBar, WindowExt, button::Button, dock::{Panel, PanelControl, PanelEvent, PanelInfo, PanelState, TitleStyle, register_panel}, group_box::GroupBox, @@ -262,7 +262,7 @@ impl StoryRoot { impl Render for StoryRoot { fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { - let drawer_layer = Root::render_drawer_layer(window, cx); + let sheet_layer = Root::render_sheet_layer(window, cx); let modal_layer = Root::render_modal_layer(window, cx); let notification_layer = Root::render_notification_layer(window, cx); @@ -274,7 +274,7 @@ impl Render for StoryRoot { .child(self.title_bar.clone()) .child(div().flex_1().overflow_hidden().child(self.view.clone())), ) - .children(drawer_layer) + .children(sheet_layer) .children(modal_layer) .children(notification_layer) } diff --git a/crates/story/src/main.rs b/crates/story/src/main.rs index 47bad9f0..413fb54d 100644 --- a/crates/story/src/main.rs +++ b/crates/story/src/main.rs @@ -49,12 +49,10 @@ impl Gallery { StoryContainer::panel::(window, cx), StoryContainer::panel::(window, cx), StoryContainer::panel::(window, cx), - StoryContainer::panel::(window, cx), StoryContainer::panel::(window, cx), StoryContainer::panel::(window, cx), StoryContainer::panel::(window, cx), StoryContainer::panel::(window, cx), - StoryContainer::panel::(window, cx), StoryContainer::panel::(window, cx), StoryContainer::panel::(window, cx), StoryContainer::panel::(window, cx), @@ -70,9 +68,11 @@ impl Gallery { StoryContainer::panel::(window, cx), StoryContainer::panel::(window, cx), StoryContainer::panel::(window, cx), + StoryContainer::panel::(window, cx), StoryContainer::panel::(window, cx), StoryContainer::panel::(window, cx), StoryContainer::panel::(window, cx), + StoryContainer::panel::(window, cx), StoryContainer::panel::(window, cx), StoryContainer::panel::(window, cx), StoryContainer::panel::(window, cx), diff --git a/crates/story/src/modal_story.rs b/crates/story/src/modal_story.rs index 604da081..4afef210 100644 --- a/crates/story/src/modal_story.rs +++ b/crates/story/src/modal_story.rs @@ -4,7 +4,7 @@ use gpui::{ }; use gpui_component::{ - ActiveTheme, ContextModal as _, Icon, IconName, + ActiveTheme, Icon, IconName, WindowExt as _, button::{Button, ButtonVariant, ButtonVariants as _}, checkbox::Checkbox, date_picker::{DatePicker, DatePickerState}, diff --git a/crates/story/src/notification_story.rs b/crates/story/src/notification_story.rs index d7ca7092..c780df92 100644 --- a/crates/story/src/notification_story.rs +++ b/crates/story/src/notification_story.rs @@ -4,10 +4,11 @@ use gpui::{ }; use gpui_component::{ + WindowExt as _, button::{Button, ButtonVariants}, notification::{Notification, NotificationType}, text::TextView, - v_flex, ContextModal as _, + v_flex, }; use crate::section; diff --git a/crates/story/src/popover_story.rs b/crates/story/src/popover_story.rs index 71ca2c55..3cba6ab8 100644 --- a/crates/story/src/popover_story.rs +++ b/crates/story/src/popover_story.rs @@ -4,7 +4,7 @@ use gpui::{ ParentElement as _, Render, Styled as _, Window, actions, div, px, }; use gpui_component::{ - ContextModal, Sizable, + Sizable, WindowExt, button::{Button, ButtonVariants as _}, divider::Divider, h_flex, diff --git a/crates/story/src/drawer_story.rs b/crates/story/src/sheet_story.rs similarity index 79% rename from crates/story/src/drawer_story.rs rename to crates/story/src/sheet_story.rs index b22c72aa..95ab4d8f 100644 --- a/crates/story/src/drawer_story.rs +++ b/crates/story/src/sheet_story.rs @@ -9,7 +9,7 @@ use gpui::{ use raw_window_handle::HasWindowHandle; use gpui_component::{ - ActiveTheme as _, ContextModal as _, Icon, IconName, IndexPath, Placement, + ActiveTheme as _, Icon, IconName, IndexPath, Placement, WindowExt as _, button::{Button, ButtonVariant, ButtonVariants as _}, checkbox::Checkbox, date_picker::{DatePicker, DatePickerState}, @@ -25,7 +25,7 @@ use crate::TestAction; use crate::{Story, section}; pub struct ListItemDeletegate { - story: WeakEntity, + story: WeakEntity, confirmed_index: Option, selected_index: Option, items: Vec>, @@ -115,7 +115,7 @@ impl ListDelegate for ListItemDeletegate { fn cancel(&mut self, window: &mut Window, cx: &mut Context>) { _ = self.story.update(cx, |this, cx| { - this.close_drawer(window, cx); + this.close_sheet(window, cx); }); } @@ -144,28 +144,25 @@ impl ListDelegate for ListItemDeletegate { } } -pub struct DrawerStory { +pub struct SheetStory { focus_handle: FocusHandle, - drawer_placement: Option, + placement: Option, selected_value: Option, list: Entity>, input1: Entity, input2: Entity, date: Entity, - modal_overlay: bool, - model_show_close: bool, - model_padding: bool, - model_keyboard: bool, + overlay: bool, overlay_closable: bool, } -impl Story for DrawerStory { +impl Story for SheetStory { fn title() -> &'static str { - "Drawer" + "Sheet" } fn description() -> &'static str { - "Drawer for open a popup in the edge of the window" + "Sheet for open a popup in the edge of the window" } fn new_view(window: &mut Window, cx: &mut App) -> Entity { @@ -173,7 +170,7 @@ impl Story for DrawerStory { } } -impl DrawerStory { +impl SheetStory { pub fn view(window: &mut Window, cx: &mut App) -> Entity { cx.new(|cx| Self::new(window, cx)) } @@ -256,26 +253,18 @@ impl DrawerStory { Self { focus_handle: cx.focus_handle(), - drawer_placement: None, + placement: None, selected_value: None, list, input1, input2, date, - modal_overlay: true, - model_show_close: true, - model_padding: true, - model_keyboard: true, + overlay: true, overlay_closable: true, } } - fn open_drawer_at( - &mut self, - placement: Placement, - window: &mut Window, - cx: &mut Context, - ) { + fn open_sheet_at(&mut self, placement: Placement, window: &mut Window, cx: &mut Context) { let list = self.list.clone(); let list_h = match placement { @@ -283,15 +272,15 @@ impl DrawerStory { Placement::Top | Placement::Bottom => px(160.), }; - let overlay = self.modal_overlay; + let overlay = self.overlay; let overlay_closable = self.overlay_closable; let input1 = self.input1.clone(); let date = self.date.clone(); - window.open_drawer_at(placement, cx, move |this, _, cx| { + window.open_sheet_at(placement, cx, move |this, _, cx| { this.overlay(overlay) .overlay_closable(overlay_closable) .size(px(400.)) - .title("Drawer Title") + .title("Sheet Title") .gap_4() .child(Input::new(&input1)) .child(DatePicker::new(&date).placeholder("Date of Birth")) @@ -299,7 +288,7 @@ impl DrawerStory { Button::new("send-notification") .child("Test Notification") .on_click(|_, window, cx| { - window.push_notification("Hello this is message from Drawer.", cx) + window.push_notification("Hello this is message from Sheet.", cx) }), ) .child( @@ -317,22 +306,22 @@ impl DrawerStory { .items_center() .child(Button::new("confirm").primary().label("Confirm").on_click( |_, window, cx| { - window.close_drawer(cx); + window.close_sheet(cx); }, )) .child( Button::new("cancel") .label("Cancel") .on_click(|_, window, cx| { - window.close_drawer(cx); + window.close_sheet(cx); }), ), ) }); } - fn close_drawer(&mut self, _: &mut Window, cx: &mut Context) { - self.drawer_placement = None; + fn close_sheet(&mut self, _: &mut Window, cx: &mut Context) { + self.placement = None; cx.notify(); } @@ -346,16 +335,16 @@ impl DrawerStory { } } -impl Focusable for DrawerStory { +impl Focusable for SheetStory { fn focus_handle(&self, _cx: &gpui::App) -> FocusHandle { self.focus_handle.clone() } } -impl Render for DrawerStory { +impl Render for SheetStory { fn render(&mut self, _: &mut Window, cx: &mut Context) -> impl IntoElement { div() - .id("drawer-story") + .id("sheet-story") .track_focus(&self.focus_handle) .on_action(cx.listener(Self::on_action_test_action)) .size_full() @@ -370,9 +359,9 @@ impl Render for DrawerStory { .child( Checkbox::new("overlay") .label("Overlay") - .checked(self.modal_overlay) + .checked(self.overlay) .on_click(cx.listener(|view, _, _, cx| { - view.modal_overlay = !view.modal_overlay; + view.overlay = !view.overlay; cx.notify(); })), ) @@ -384,67 +373,40 @@ impl Render for DrawerStory { view.overlay_closable = !view.overlay_closable; cx.notify(); })), - ) - .child( - Checkbox::new("show-close") - .label("Close Button") - .checked(self.model_show_close) - .on_click(cx.listener(|view, _, _, cx| { - view.model_show_close = !view.model_show_close; - cx.notify(); - })), - ) - .child( - Checkbox::new("padding") - .label("Inner Padding") - .checked(self.model_padding) - .on_click(cx.listener(|view, _, _, cx| { - view.model_padding = !view.model_padding; - cx.notify(); - })), - ) - .child( - Checkbox::new("keyboard") - .label("Keyboard") - .checked(self.model_keyboard) - .on_click(cx.listener(|view, _, _, cx| { - view.model_keyboard = !view.model_keyboard; - cx.notify(); - })), ), ) .child( - section("Normal Drawer") + section("Normal Sheet") .child( - Button::new("show-drawer-left") + Button::new("show-sheet-left") .outline() - .label("Left Drawer...") + .label("Left Sheet...") .on_click(cx.listener(|this, _, window, cx| { - this.open_drawer_at(Placement::Left, window, cx) + this.open_sheet_at(Placement::Left, window, cx) })), ) .child( - Button::new("show-drawer-top") + Button::new("show-sheet-top") .outline() - .label("Top Drawer...") + .label("Top Sheet...") .on_click(cx.listener(|this, _, window, cx| { - this.open_drawer_at(Placement::Top, window, cx) + this.open_sheet_at(Placement::Top, window, cx) })), ) .child( - Button::new("show-drawer-right") + Button::new("show-sheet-right") .outline() - .label("Right Drawer...") + .label("Right Sheet...") .on_click(cx.listener(|this, _, window, cx| { - this.open_drawer_at(Placement::Right, window, cx) + this.open_sheet_at(Placement::Right, window, cx) })), ) .child( - Button::new("show-drawer-bottom") + Button::new("show-sheet-bottom") .outline() - .label("Bottom Drawer...") + .label("Bottom Sheet...") .on_click(cx.listener(|this, _, window, cx| { - this.open_drawer_at(Placement::Bottom, window, cx) + this.open_sheet_at(Placement::Bottom, window, cx) })), ), ) @@ -468,7 +430,7 @@ impl Render for DrawerStory { ), ) .child( - section("WebView in Drawer").child( + section("WebView in Sheet").child( Button::new("webview") .outline() .label("Open WebView") @@ -485,12 +447,12 @@ impl Render for DrawerStory { webview.update(cx, |webview, _| { webview.load_url("https://github.com/explore"); }); - window.open_drawer(cx, move |drawer, window, cx| { + window.open_sheet(cx, move |sheet, window, cx| { let height = window.window_bounds().get_bounds().size.height; let webview_bounds = webview.read(cx).bounds(); - drawer.title("WebView Title").p_0().child( + sheet.title("WebView Title").p_0().child( div() .h(height - webview_bounds.origin.y) .child(webview.clone()), diff --git a/crates/story/src/slider_story.rs b/crates/story/src/slider_story.rs index 3918b315..27c48589 100644 --- a/crates/story/src/slider_story.rs +++ b/crates/story/src/slider_story.rs @@ -1,13 +1,14 @@ use gpui::{ - hsla, px, App, AppContext, Context, Entity, Focusable, Hsla, IntoElement, ParentElement, - Render, SharedString, Styled, Subscription, Window, + App, AppContext, Context, Entity, Focusable, Hsla, IntoElement, ParentElement, Render, + SharedString, Styled, Subscription, Window, hsla, px, }; use gpui_component::{ + ActiveTheme, Colorize as _, StyledExt, WindowExt, checkbox::Checkbox, clipboard::Clipboard, h_flex, slider::{Slider, SliderEvent, SliderState}, - v_flex, ActiveTheme, Colorize as _, ContextModal, StyledExt, + v_flex, }; use crate::section; diff --git a/crates/story/src/title_bar.rs b/crates/story/src/title_bar.rs index 5b905337..5d65f1f4 100644 --- a/crates/story/src/title_bar.rs +++ b/crates/story/src/title_bar.rs @@ -6,7 +6,7 @@ use gpui::{ Window, div, px, }; use gpui_component::{ - ActiveTheme as _, ContextModal as _, IconName, PixelsExt, Sizable as _, Theme, TitleBar, + ActiveTheme as _, IconName, PixelsExt, Sizable as _, Theme, TitleBar, WindowExt as _, badge::Badge, button::{Button, ButtonVariants as _}, menu::AppMenuBar, diff --git a/crates/ui/src/lib.rs b/crates/ui/src/lib.rs index 3ee25585..5c53c338 100644 --- a/crates/ui/src/lib.rs +++ b/crates/ui/src/lib.rs @@ -31,7 +31,6 @@ pub mod color_picker; pub mod description_list; pub mod divider; pub mod dock; -pub mod drawer; pub mod form; pub mod group_box; pub mod highlighter; @@ -51,6 +50,7 @@ pub mod radio; pub mod resizable; pub mod scroll; pub mod select; +pub mod sheet; pub mod sidebar; pub mod skeleton; pub mod slider; @@ -79,7 +79,7 @@ pub use index_path::IndexPath; pub use input::{Rope, RopeExt, RopeLines}; #[cfg(any(feature = "inspector", debug_assertions))] pub use inspector::*; -pub use root::{ContextModal, Root}; +pub use root::{Root, WindowExt}; pub use styled::*; pub use theme::*; pub use title_bar::*; @@ -100,7 +100,7 @@ pub fn init(cx: &mut App) { date_picker::init(cx); color_picker::init(cx); dock::init(cx); - drawer::init(cx); + sheet::init(cx); select::init(cx); input::init(cx); list::init(cx); diff --git a/crates/ui/src/modal.rs b/crates/ui/src/modal.rs index e63fbd60..4c2061f6 100644 --- a/crates/ui/src/modal.rs +++ b/crates/ui/src/modal.rs @@ -12,7 +12,7 @@ use crate::{ actions::{Cancel, Confirm}, animation::cubic_bezier, button::{Button, ButtonVariant, ButtonVariants as _}, - h_flex, v_flex, ActiveTheme as _, ContextModal, IconName, Root, Sizable as _, StyledExt, + h_flex, v_flex, ActiveTheme as _, IconName, Root, Sizable as _, StyledExt, WindowExt as _, }; const CONTEXT: &str = "Modal"; diff --git a/crates/ui/src/root.rs b/crates/ui/src/root.rs index fd5c61a9..3bda5e6c 100644 --- a/crates/ui/src/root.rs +++ b/crates/ui/src/root.rs @@ -1,8 +1,8 @@ use crate::{ - drawer::Drawer, input::InputState, modal::Modal, notification::{Notification, NotificationList}, + sheet::Sheet, window_border, ActiveTheme, Placement, }; use gpui::{ @@ -15,7 +15,6 @@ use std::{any::TypeId, rc::Rc}; actions!(root, [Tab, TabPrev]); const CONTEXT: &str = "Root"; - pub(crate) fn init(cx: &mut App) { cx.bind_keys([ KeyBinding::new("tab", Tab, Some(CONTEXT)), @@ -23,23 +22,23 @@ pub(crate) fn init(cx: &mut App) { ]); } -/// Extension trait for [`WindowContext`] and [`ViewContext`] to add drawer functionality. -pub trait ContextModal: Sized { - /// Opens a Drawer at right placement. - fn open_drawer(&mut self, cx: &mut App, build: F) +/// Extension trait for [`Window`] to add modal, sheet .. functionality. +pub trait WindowExt: Sized { + /// Opens a Sheet at right placement. + fn open_sheet(&mut self, cx: &mut App, build: F) where - F: Fn(Drawer, &mut Window, &mut App) -> Drawer + 'static; + F: Fn(Sheet, &mut Window, &mut App) -> Sheet + 'static; - /// Opens a Drawer at the given placement. - fn open_drawer_at(&mut self, placement: Placement, cx: &mut App, build: F) + /// Opens a Sheet at the given placement. + fn open_sheet_at(&mut self, placement: Placement, cx: &mut App, build: F) where - F: Fn(Drawer, &mut Window, &mut App) -> Drawer + 'static; + F: Fn(Sheet, &mut Window, &mut App) -> Sheet + 'static; - /// Return true, if there is an active Drawer. - fn has_active_drawer(&mut self, cx: &mut App) -> bool; + /// Return true, if there is an active Sheet. + fn has_active_sheet(&mut self, cx: &mut App) -> bool; - /// Closes the active Drawer. - fn close_drawer(&mut self, cx: &mut App); + /// Closes the active Sheet. + fn close_sheet(&mut self, cx: &mut App); /// Opens a Modal. fn open_modal(&mut self, cx: &mut App, build: F) @@ -73,27 +72,27 @@ pub trait ContextModal: Sized { fn has_focused_input(&mut self, cx: &mut App) -> bool; } -impl ContextModal for Window { - fn open_drawer(&mut self, cx: &mut App, build: F) +impl WindowExt for Window { + fn open_sheet(&mut self, cx: &mut App, build: F) where - F: Fn(Drawer, &mut Window, &mut App) -> Drawer + 'static, + F: Fn(Sheet, &mut Window, &mut App) -> Sheet + 'static, { - self.open_drawer_at(Placement::Right, cx, build) + self.open_sheet_at(Placement::Right, cx, build) } - fn open_drawer_at(&mut self, placement: Placement, cx: &mut App, build: F) + fn open_sheet_at(&mut self, placement: Placement, cx: &mut App, build: F) where - F: Fn(Drawer, &mut Window, &mut App) -> Drawer + 'static, + F: Fn(Sheet, &mut Window, &mut App) -> Sheet + 'static, { Root::update(self, cx, move |root, window, cx| { - if root.active_drawer.is_none() { + if root.active_sheet.is_none() { root.previous_focus_handle = window.focused(cx); } let focus_handle = cx.focus_handle(); focus_handle.focus(window); - root.active_drawer = Some(ActiveDrawer { + root.active_sheet = Some(ActiveSheet { focus_handle, placement, builder: Rc::new(build), @@ -102,14 +101,14 @@ impl ContextModal for Window { }) } - fn has_active_drawer(&mut self, cx: &mut App) -> bool { - Root::read(self, cx).active_drawer.is_some() + fn has_active_sheet(&mut self, cx: &mut App) -> bool { + Root::read(self, cx).active_sheet.is_some() } - fn close_drawer(&mut self, cx: &mut App) { + fn close_sheet(&mut self, cx: &mut App) { Root::update(self, cx, |root, window, cx| { root.focused_input = None; - root.active_drawer = None; + root.active_sheet = None; root.focus_back(window, cx); cx.notify(); }) @@ -209,24 +208,24 @@ impl ContextModal for Window { /// Root is a view for the App window for as the top level view (Must be the first view in the window). /// -/// It is used to manage the Drawer, Modal, and Notification. +/// It is used to manage the Sheet, Modal, and Notification. pub struct Root { /// Used to store the focus handle of the previous view. - /// When the Modal, Drawer closes, we will focus back to the previous view. + /// When the Modal, Sheet closes, we will focus back to the previous view. previous_focus_handle: Option, - active_drawer: Option, + active_sheet: Option, pub(crate) active_modals: Vec, pub(super) focused_input: Option>, pub notification: Entity, - drawer_size: Option, + sheet_size: Option, view: AnyView, } #[derive(Clone)] -struct ActiveDrawer { +struct ActiveSheet { focus_handle: FocusHandle, placement: Placement, - builder: Rc Drawer + 'static>, + builder: Rc Sheet + 'static>, } #[derive(Clone)] @@ -239,11 +238,11 @@ impl Root { pub fn new(view: AnyView, window: &mut Window, cx: &mut Context) -> Self { Self { previous_focus_handle: None, - active_drawer: None, + active_sheet: None, active_modals: Vec::new(), focused_input: None, notification: cx.new(|cx| NotificationList::new(window, cx)), - drawer_size: None, + sheet_size: None, view, } } @@ -281,11 +280,11 @@ impl Root { ) -> Option { let root = window.root::()??; - let active_drawer_placement = root.read(cx).active_drawer.clone().map(|d| d.placement); + let active_sheet_placement = root.read(cx).active_sheet.clone().map(|d| d.placement); - let (mt, mr) = match active_drawer_placement { - Some(Placement::Right) => (None, root.read(cx).drawer_size), - Some(Placement::Top) => (root.read(cx).drawer_size, None), + let (mt, mr) = match active_sheet_placement { + Some(Placement::Right) => (None, root.read(cx).sheet_size), + Some(Placement::Top) => (root.read(cx).sheet_size, None), _ => (None, None), }; @@ -300,22 +299,22 @@ impl Root { ) } - /// Render the Drawer layer. - pub fn render_drawer_layer(window: &mut Window, cx: &mut App) -> Option { + /// Render the Sheet layer. + pub fn render_sheet_layer(window: &mut Window, cx: &mut App) -> Option { let root = window.root::()??; - if let Some(active_drawer) = root.read(cx).active_drawer.clone() { - let mut drawer = Drawer::new(window, cx); - drawer = (active_drawer.builder)(drawer, window, cx); - drawer.focus_handle = active_drawer.focus_handle.clone(); - drawer.placement = active_drawer.placement; + if let Some(active_sheet) = root.read(cx).active_sheet.clone() { + let mut sheet = Sheet::new(window, cx); + sheet = (active_sheet.builder)(sheet, window, cx); + sheet.focus_handle = active_sheet.focus_handle.clone(); + sheet.placement = active_sheet.placement; - let drawer_size = drawer.size; + let size = sheet.size; return Some( - div().relative().child(drawer).child( + div().relative().child(sheet).child( canvas( - move |_, _, cx| root.update(cx, |r, _| r.drawer_size = Some(drawer_size)), + move |_, _, cx| root.update(cx, |r, _| r.sheet_size = Some(size)), |_, _, _, _| {}, ) .absolute() diff --git a/crates/ui/src/drawer.rs b/crates/ui/src/sheet.rs similarity index 87% rename from crates/ui/src/drawer.rs rename to crates/ui/src/sheet.rs index 0b3ad184..d6ebb326 100644 --- a/crates/ui/src/drawer.rs +++ b/crates/ui/src/sheet.rs @@ -12,19 +12,18 @@ use crate::{ button::{Button, ButtonVariants as _}, h_flex, modal::overlay_color, - root::ContextModal as _, title_bar::TITLE_BAR_HEIGHT, - v_flex, ActiveTheme, IconName, Placement, Sizable, StyledExt as _, + v_flex, ActiveTheme, IconName, Placement, Sizable, StyledExt as _, WindowExt as _, }; -const CONTEXT: &str = "Drawer"; +const CONTEXT: &str = "Sheet"; pub(crate) fn init(cx: &mut App) { cx.bind_keys([KeyBinding::new("escape", Cancel, Some(CONTEXT))]) } -/// A drawer component that slides in from the side of the window. +/// Sheet component that slides in from the side of the window. #[derive(IntoElement)] -pub struct Drawer { +pub struct Sheet { pub(crate) focus_handle: FocusHandle, pub(crate) placement: Placement, pub(crate) size: DefiniteLength, @@ -38,8 +37,8 @@ pub struct Drawer { overlay_closable: bool, } -impl Drawer { - /// Creates a new drawer. +impl Sheet { + /// Creates a new Sheet. pub fn new(_: &mut Window, cx: &mut App) -> Self { Self { focus_handle: cx.focus_handle(), @@ -56,51 +55,51 @@ impl Drawer { } } - /// Sets the title of the drawer. + /// Sets the title of the sheet. pub fn title(mut self, title: impl IntoElement) -> Self { self.title = Some(title.into_any_element()); self } - /// Set the footer of the drawer. + /// Set the footer of the sheet. pub fn footer(mut self, footer: impl IntoElement) -> Self { self.footer = Some(footer.into_any_element()); self } - /// Sets the size of the drawer, default is 350px. + /// Sets the size of the sheet, default is 350px. pub fn size(mut self, size: impl Into) -> Self { self.size = size.into(); self } - /// Sets the margin top of the drawer, default is 0px. + /// Sets the margin top of the sheet, default is 0px. /// - /// This is used to let Drawer be placed below a Windows Title, you can give the height of the title bar. + /// This is used to let Sheet be placed below a Windows Title, you can give the height of the title bar. pub fn margin_top(mut self, top: Pixels) -> Self { self.margin_top = top; self } - /// Sets whether the drawer is resizable, default is `true`. + /// Sets whether the sheet is resizable, default is `true`. pub fn resizable(mut self, resizable: bool) -> Self { self.resizable = resizable; self } - /// Set whether the drawer should have an overlay, default is `true`. + /// Set whether the sheet should have an overlay, default is `true`. pub fn overlay(mut self, overlay: bool) -> Self { self.overlay = overlay; self } - /// Set whether the drawer should be closable by clicking the overlay, default is `true`. + /// Set whether the sheet should be closable by clicking the overlay, default is `true`. pub fn overlay_closable(mut self, overlay_closable: bool) -> Self { self.overlay_closable = overlay_closable; self } - /// Listen to the close event of the drawer. + /// Listen to the close event of the sheet. pub fn on_close( mut self, on_close: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static, @@ -110,19 +109,19 @@ impl Drawer { } } -impl EventEmitter for Drawer {} -impl ParentElement for Drawer { +impl EventEmitter for Sheet {} +impl ParentElement for Sheet { fn extend(&mut self, elements: impl IntoIterator) { self.content.extend(elements); } } -impl Styled for Drawer { +impl Styled for Sheet { fn style(&mut self) -> &mut gpui::StyleRefinement { self.content.style() } } -impl RenderOnce for Drawer { +impl RenderOnce for Sheet { fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement { let placement = self.placement; let titlebar_height = self.margin_top; @@ -154,14 +153,14 @@ impl RenderOnce for Drawer { if self.overlay_closable && event.button == MouseButton::Left { on_close(&ClickEvent::default(), window, cx); - window.close_drawer(cx); + window.close_sheet(cx); } } }) }) .child( v_flex() - .id("drawer") + .id("sheet") .tab_group() .key_context(CONTEXT) .track_focus(&self.focus_handle) @@ -171,7 +170,7 @@ impl RenderOnce for Drawer { cx.propagate(); on_close(&ClickEvent::default(), window, cx); - window.close_drawer(cx); + window.close_sheet(cx); } }) .absolute() @@ -180,7 +179,7 @@ impl RenderOnce for Drawer { .border_color(cx.theme().border) .shadow_xl() .map(|this| { - // Set the size of the drawer. + // Set the size of the sheet. if placement.is_horizontal() { this.h_full().w(self.size) } else { @@ -212,7 +211,7 @@ impl RenderOnce for Drawer { .icon(IconName::Close) .on_click(move |_, window, cx| { on_close(&ClickEvent::default(), window, cx); - window.close_drawer(cx); + window.close_sheet(cx); }), ), ) diff --git a/docs/docs/components/index.md b/docs/docs/components/index.md index 14c2afb2..c5f5b1e3 100644 --- a/docs/docs/components/index.md +++ b/docs/docs/components/index.md @@ -43,13 +43,13 @@ collapsed: false ### Layout Components - [DescriptionList](description-list) - Key-value pair display -- [Drawer](drawer) - Slide-in panel from edges - [GroupBox](group-box) - Grouped content with borders - [Modal](modal) - Dialog and modal windows - [Notification](notification) - Toast notifications - [Popover](popover) - Floating content display - [Resizable](resizable) - Resizable panels and containers - [Scrollable](scrollable) - Scrollable containers +- [Sheet](sheet) - Slide-in panel from edges - [Sidebar](sidebar) - Navigation sidebar ### Advanced Components diff --git a/docs/docs/components/modal.md b/docs/docs/components/modal.md index 49d1f0b5..1d7f4514 100644 --- a/docs/docs/components/modal.md +++ b/docs/docs/components/modal.md @@ -11,7 +11,7 @@ Modal component for creating dialogs, confirmations, and alerts. Supports overla ```rust use gpui_component::modal::ModalButtonProps; -use gpui_component::ContextModal; +use gpui_component::WindowExt; ``` ## Usage diff --git a/docs/docs/components/notification.md b/docs/docs/components/notification.md index e74c9bb8..c8508b4e 100644 --- a/docs/docs/components/notification.md +++ b/docs/docs/components/notification.md @@ -11,7 +11,7 @@ A toast notification system for displaying temporary messages to users. Notifica ```rust use gpui_component::notification::{Notification, NotificationType}; -use gpui_component::ContextModal; +use gpui_component::WindowExt; ``` ## Usage diff --git a/docs/docs/components/drawer.md b/docs/docs/components/sheet.md similarity index 63% rename from docs/docs/components/drawer.md rename to docs/docs/components/sheet.md index 19e13add..33ed57eb 100644 --- a/docs/docs/components/drawer.md +++ b/docs/docs/components/sheet.md @@ -1,26 +1,26 @@ --- -title: Drawer +title: Sheet description: A sliding panel that appears from the edges of the screen for displaying content. --- -# Drawer +# Sheet -A Drawer (also known as a sidebar or slide-out panel) is a navigation component that slides in from the edges of the screen. It provides additional space for content without taking up the main view, and can be used for navigation menus, forms, or any supplementary content. +A Sheet (also known as a sidebar or slide-out panel) is a navigation component that slides in from the edges of the screen. It provides additional space for content without taking up the main view, and can be used for navigation menus, forms, or any supplementary content. ## Import ```rust -use gpui_component::ContextModal; +use gpui_component::WindowExt; use gpui_component::Placement; ``` ## Usage -### Setup application root view for display of drawers +### Setup application root view for display of sheets -You need to set up your application's root view to render the drawer layer. This is typically done in your main application struct's render method. +You need to set up your application's root view to render the sheet layer. This is typically done in your main application struct's render method. -The [Root::render_drawer_layer](https://docs.rs/gpui-component/latest/gpui_component/struct.Root.html#method.render_drawer_layer) function handles rendering any active modals on top of your app content. +The [Root::render_sheet_layer](https://docs.rs/gpui-component/latest/gpui_component/struct.Root.html#method.render_sheet_layer) function handles rendering any active modals on top of your app content. ```rust use gpui_component::TitleBar; @@ -31,7 +31,7 @@ struct MyApp { impl Render for MyApp { fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { - let drawer_layer = Root::render_drawer_layer(window, cx); + let sheet_layer = Root::render_sheet_layer(window, cx); div() .size_full() @@ -41,65 +41,65 @@ impl Render for MyApp { .child(TitleBar::new()) .child(div().flex_1().overflow_hidden().child(self.view.clone())), ) - // Render the drawer layer on top of the app content - .children(drawer_layer) + // Render the sheet layer on top of the app content + .children(sheet_layer) } } ``` -### Basic Drawer +### Basic Sheet ```rust -window.open_drawer(cx, |drawer, _, _| { - drawer +window.open_sheet(cx, |sheet, _, _| { + sheet .title("Navigation") - .child("Drawer content goes here") + .child("Sheet content goes here") }) ``` -### Drawer with Placement +### Sheet with Placement ```rust -// Left drawer (default) -window.open_drawer_at(Placement::Left, cx, |drawer, _, _| { - drawer.title("Left Drawer") +// Left sheet (default) +window.open_sheet_at(Placement::Left, cx, |sheet, _, _| { + sheet.title("Left Sheet") }) -// Right drawer -window.open_drawer_at(Placement::Right, cx, |drawer, _, _| { - drawer.title("Right Drawer") +// Right sheet +window.open_sheet_at(Placement::Right, cx, |sheet, _, _| { + sheet.title("Right Sheet") }) -// Top drawer -window.open_drawer_at(Placement::Top, cx, |drawer, _, _| { - drawer.title("Top Drawer") +// Top sheet +window.open_sheet_at(Placement::Top, cx, |sheet, _, _| { + sheet.title("Top Sheet") }) -// Bottom drawer -window.open_drawer_at(Placement::Bottom, cx, |drawer, _, _| { - drawer.title("Bottom Drawer") +// Bottom sheet +window.open_sheet_at(Placement::Bottom, cx, |sheet, _, _| { + sheet.title("Bottom Sheet") }) ``` -### Drawer with Custom Size +### Sheet with Custom Size ```rust -window.open_drawer(cx, |drawer, _, _| { - drawer - .title("Wide Drawer") +window.open_sheet(cx, |sheet, _, _| { + sheet + .title("Wide Sheet") .size(px(500.)) // Custom width for left/right, height for top/bottom - .child("This drawer is 500px wide") + .child("This sheet is 500px wide") }) ``` -### Drawer with Form Content +### Sheet with Form Content ```rust let input = cx.new(|cx| InputState::new(window, cx)); let date = cx.new(|cx| DatePickerState::new(window, cx)); -window.open_drawer(cx, |drawer, _, _| { - drawer +window.open_sheet(cx, |sheet, _, _| { + sheet .title("User Profile") .child( v_flex() @@ -120,54 +120,54 @@ window.open_drawer(cx, |drawer, _, _| { ### Overlay Options ```rust -window.open_drawer(cx, |drawer, _, _| { - drawer +window.open_sheet(cx, |sheet, _, _| { + sheet .title("Settings") .overlay(true) // Show overlay background (default: true) .overlay_closable(true) // Click overlay to close (default: true) - .child("Drawer settings content") + .child("Sheet settings content") }) // No overlay -window.open_drawer(cx, |drawer, _, _| { - drawer +window.open_sheet(cx, |sheet, _, _| { + sheet .title("Side Panel") .overlay(false) // No overlay background - .child("This drawer has no overlay") + .child("This sheet has no overlay") }) ``` -### Resizable Drawer +### Resizable Sheet ```rust -window.open_drawer(cx, |drawer, _, _| { - drawer +window.open_sheet(cx, |sheet, _, _| { + sheet .title("Resizable Panel") .resizable(true) // Allow user to resize (default: true) .size(px(300.)) - .child("You can resize this drawer by dragging the edge") + .child("You can resize this sheet by dragging the edge") }) ``` ### Custom Margin and Positioning ```rust -window.open_drawer(cx, |drawer, _, _| { - drawer +window.open_sheet(cx, |sheet, _, _| { + sheet .title("Below Title Bar") .margin_top(px(32.)) // Space for window title bar - .child("This drawer appears below the title bar") + .child("This sheet appears below the title bar") }) ``` -### Drawer with List +### Sheet with List ```rust let delegate = ListDelegate::new(items); let list = cx.new(|cx| List::new(delegate, window, cx)); -window.open_drawer_at(Placement::Left, cx, |drawer, _, _| { - drawer +window.open_sheet_at(Placement::Left, cx, |sheet, _, _| { + sheet .title("File Explorer") .size(px(400.)) .child( @@ -184,21 +184,21 @@ window.open_drawer_at(Placement::Left, cx, |drawer, _, _| { ### Close Event Handling ```rust -window.open_drawer(cx, |drawer, _, _| { - drawer - .title("Drawer with Handler") - .child("This drawer has a custom close handler") +window.open_sheet(cx, |sheet, _, _| { + sheet + .title("Sheet with Handler") + .child("This sheet has a custom close handler") .on_close(|_, window, cx| { - window.push_notification("Drawer was closed", cx); + window.push_notification("Sheet was closed", cx); }) }) ``` -### Navigation Drawer +### Navigation Sheet ```rust -window.open_drawer_at(Placement::Left, cx, |drawer, _, _| { - drawer +window.open_sheet_at(Placement::Left, cx, |sheet, _, _| { + sheet .title("Navigation") .size(px(280.)) .child( @@ -215,48 +215,48 @@ window.open_drawer_at(Placement::Left, cx, |drawer, _, _| { ### Custom Styling ```rust -window.open_drawer(cx, |drawer, _, cx| { - drawer - .title("Styled Drawer") +window.open_sheet(cx, |sheet, _, cx| { + sheet + .title("Styled Sheet") .bg(cx.theme().accent) .text_color(cx.theme().accent_foreground) .border_color(cx.theme().primary) - .child("Custom styled drawer content") + .child("Custom styled sheet content") }) ``` ### Programmatic Close ```rust -// Close drawer from inside +// Close sheet from inside Button::new("close") - .label("Close Drawer") + .label("Close Sheet") .on_click(|_, window, cx| { - window.close_drawer(cx); + window.close_sheet(cx); }) -// Close drawer from outside -window.close_drawer(cx); +// Close sheet from outside +window.close_sheet(cx); ``` ## API Reference ### Window Extensions -| Method | Description | -| ----------------------------------- | ------------------------------------------ | -| `open_drawer(cx, fn)` | Open drawer with default placement (Right) | -| `open_drawer_at(placement, cx, fn)` | Open drawer at specific placement | -| `close_drawer(cx)` | Close current drawer | +| Method | Description | +| ---------------------------------- | ----------------------------------------- | +| `open_sheet(cx, fn)` | Open sheet with default placement (Right) | +| `open_sheet_at(placement, cx, fn)` | Open sheet at specific placement | +| `close_sheet(cx)` | Close current sheet | -### Drawer Builder +### Sheet Builder | Method | Description | | ------------------------ | --------------------------------------- | -| `title(str)` | Set drawer title | -| `child(el)` | Add content to drawer body | +| `title(str)` | Set sheet title | +| `child(el)` | Add content to sheet body | | `footer(el)` | Set footer content | -| `size(px)` | Set drawer size (width or height) | +| `size(px)` | Set sheet size (width or height) | | `margin_top(px)` | Set top margin (for title bars) | | `resizable(bool)` | Allow resizing (default: true) | | `overlay(bool)` | Show overlay background (default: true) | @@ -287,8 +287,8 @@ window.close_drawer(cx); ### Settings Panel ```rust -window.open_drawer_at(Placement::Right, cx, |drawer, _, _| { - drawer +window.open_sheet_at(Placement::Right, cx, |sheet, _, _| { + sheet .title("Settings") .size(px(350.)) .child( @@ -313,8 +313,8 @@ window.open_drawer_at(Placement::Right, cx, |drawer, _, _| { ### File Browser ```rust -window.open_drawer_at(Placement::Left, cx, |drawer, _, _| { - drawer +window.open_sheet_at(Placement::Left, cx, |sheet, _, _| { + sheet .title("Files") .size(px(300.)) .child( @@ -340,8 +340,8 @@ window.open_drawer_at(Placement::Left, cx, |drawer, _, _| { ### Help Panel ```rust -window.open_drawer_at(Placement::Bottom, cx, |drawer, _, _| { - drawer +window.open_sheet_at(Placement::Bottom, cx, |sheet, _, _| { + sheet .title("Help & Documentation") .size(px(200.)) .child( @@ -359,9 +359,9 @@ window.open_drawer_at(Placement::Bottom, cx, |drawer, _, _| { ## Best Practices 1. **Appropriate Placement**: Use left/right for navigation, top/bottom for temporary content -2. **Consistent Sizing**: Maintain consistent drawer sizes across your application +2. **Consistent Sizing**: Maintain consistent sheet sizes across your application 3. **Clear Headers**: Always provide descriptive titles 4. **Close Options**: Provide multiple ways to close (ESC, overlay click, close button) -5. **Content Organization**: Use proper spacing and grouping for drawer content -6. **Responsive Design**: Consider drawer behavior on smaller screens -7. **Performance**: Lazy load drawer content when possible for better performance +5. **Content Organization**: Use proper spacing and grouping for sheet content +6. **Responsive Design**: Consider sheet behavior on smaller screens +7. **Performance**: Lazy load sheet content when possible for better performance diff --git a/docs/docs/root.md b/docs/docs/root.md index fc4b353a..44215658 100644 --- a/docs/docs/root.md +++ b/docs/docs/root.md @@ -35,7 +35,7 @@ fn main() { We have modals, drawers, notifications, we need placement for them to show, so [Root] provides methods to render these overlays: - [Root::render_modal_layer](https://docs.rs/gpui-component/latest/gpui_component/struct.Root.html#method.render_modal_layer) - Render the current opened modals. -- [Root::render_drawer_layer](https://docs.rs/gpui-component/latest/gpui_component/struct.Root.html#method.render_drawer_layer) - Render the current opened drawers. +- [Root::render_sheet_layer](https://docs.rs/gpui-component/latest/gpui_component/struct.Root.html#method.render_sheet_layer) - Render the current opened drawers. - [Root::render_notification_layer](https://docs.rs/gpui-component/latest/gpui_component/struct.Root.html#method.render_notification_layer) - Render the notification list. We can put these layers in the `render` method your first level view (Root > YourFirstView): @@ -49,7 +49,7 @@ impl Render for MyApp { .size_full() .child("My App Content") .children(Root::render_modal_layer(cx)) - .children(Root::render_drawer_layer(cx)) + .children(Root::render_sheet_layer(cx)) .children(Root::render_notification_layer(cx)) } } diff --git a/examples/modal_overlay/src/main.rs b/examples/modal_overlay/src/main.rs index fcf2c188..830cd26d 100644 --- a/examples/modal_overlay/src/main.rs +++ b/examples/modal_overlay/src/main.rs @@ -13,7 +13,7 @@ impl HelloWorld { } fn show_drawer(&mut self, _: &ClickEvent, window: &mut Window, cx: &mut Context) { - window.open_drawer(cx, move |drawer, _, _| { + window.open_sheet(cx, move |drawer, _, _| { drawer.title("Test Drawer").child("Hello from Drawer!") }); } @@ -73,7 +73,7 @@ impl Render for HelloWorld { ), ) .children(Root::render_modal_layer(window, cx)) - .children(Root::render_drawer_layer(window, cx)) + .children(Root::render_sheet_layer(window, cx)) } }