From 903176d8ff24ff679755745269996f5cd47fea50 Mon Sep 17 00:00:00 2001 From: Mikayla Date: Thu, 18 Jan 2024 15:31:31 -0800 Subject: [PATCH] Pull out fluent builder helpers into re-usable trait --- crates/collab_ui/src/face_pile.rs | 1 + crates/gpui/src/element.rs | 38 ++------------ crates/gpui/src/prelude.rs | 6 +-- crates/gpui/src/util.rs | 52 +++++++++++++++++++ .../src/active_buffer_language.rs | 2 +- crates/project_panel/src/project_panel.rs | 24 ++++----- crates/ui/src/components/context_menu.rs | 30 +---------- crates/ui/src/components/popover_menu.rs | 6 +-- 8 files changed, 77 insertions(+), 82 deletions(-) diff --git a/crates/collab_ui/src/face_pile.rs b/crates/collab_ui/src/face_pile.rs index fb6c59cc80..b12b11f63f 100644 --- a/crates/collab_ui/src/face_pile.rs +++ b/crates/collab_ui/src/face_pile.rs @@ -1,5 +1,6 @@ use gpui::{div, AnyElement, IntoElement, ParentElement, RenderOnce, Styled, WindowContext}; use smallvec::SmallVec; +use ui::FluentBuilder; #[derive(Default, IntoElement)] pub struct FacePile { diff --git a/crates/gpui/src/element.rs b/crates/gpui/src/element.rs index 3022f9f30a..47fb7241ab 100644 --- a/crates/gpui/src/element.rs +++ b/crates/gpui/src/element.rs @@ -1,6 +1,6 @@ use crate::{ - ArenaBox, AvailableSpace, BorrowWindow, Bounds, ElementId, LayoutId, Pixels, Point, Size, - ViewContext, WindowContext, ELEMENT_ARENA, + util::FluentBuilder, ArenaBox, AvailableSpace, BorrowWindow, Bounds, ElementId, LayoutId, + Pixels, Point, Size, ViewContext, WindowContext, ELEMENT_ARENA, }; use derive_more::{Deref, DerefMut}; pub(crate) use smallvec::SmallVec; @@ -77,40 +77,10 @@ pub trait IntoElement: Sized { }) } } - - /// Convert self to another type by calling the given closure. Useful in rendering code. - fn map(self, f: impl FnOnce(Self) -> U) -> U - where - Self: Sized, - U: IntoElement, - { - f(self) - } - - /// Conditionally chain onto self with the given closure. Useful in rendering code. - fn when(self, condition: bool, then: impl FnOnce(Self) -> Self) -> Self - where - Self: Sized, - { - self.map(|this| if condition { then(this) } else { this }) - } - - /// Conditionally chain onto self with the given closure if the given option is Some. - /// The contents of the option are provided to the closure. - fn when_some(self, option: Option, then: impl FnOnce(Self, T) -> Self) -> Self - where - Self: Sized, - { - self.map(|this| { - if let Some(value) = option { - then(this, value) - } else { - this - } - }) - } } +impl FluentBuilder for T {} + pub trait Render: 'static + Sized { fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement; } diff --git a/crates/gpui/src/prelude.rs b/crates/gpui/src/prelude.rs index 90d09b3fc5..218cbd0e28 100644 --- a/crates/gpui/src/prelude.rs +++ b/crates/gpui/src/prelude.rs @@ -1,5 +1,5 @@ pub use crate::{ - BorrowAppContext, BorrowWindow, Context, Element, FocusableElement, InteractiveElement, - IntoElement, ParentElement, Refineable, Render, RenderOnce, StatefulInteractiveElement, Styled, - VisualContext, + util::FluentBuilder, BorrowAppContext, BorrowWindow, Context, Element, FocusableElement, + InteractiveElement, IntoElement, ParentElement, Refineable, Render, RenderOnce, + StatefulInteractiveElement, Styled, VisualContext, }; diff --git a/crates/gpui/src/util.rs b/crates/gpui/src/util.rs index cba7ed84b5..4bff3da740 100644 --- a/crates/gpui/src/util.rs +++ b/crates/gpui/src/util.rs @@ -9,6 +9,58 @@ use smol::future::FutureExt; pub use util::*; +/// A helper trait for building complex objects with imperative conditionals in a fluent style. +pub trait FluentBuilder { + /// Imperatively modify self with the given closure. + fn map(self, f: impl FnOnce(Self) -> U) -> U + where + Self: Sized, + { + f(self) + } + + /// Conditionally modify self with the given closure. + fn when(self, condition: bool, then: impl FnOnce(Self) -> Self) -> Self + where + Self: Sized, + { + self.map(|this| if condition { then(this) } else { this }) + } + + /// Conditionally unwrap and modify self with the given closure, if the given option is Some. + fn when_some(self, option: Option, then: impl FnOnce(Self, T) -> Self) -> Self + where + Self: Sized, + { + self.map(|this| { + if let Some(value) = option { + then(this, value) + } else { + this + } + }) + } + + /// Conditionally modify self with one closure or another + fn when_else( + self, + condition: bool, + then: impl FnOnce(Self) -> Self, + otherwise: impl FnOnce(Self) -> Self, + ) -> Self + where + Self: Sized, + { + self.map(|this| { + if condition { + then(this) + } else { + otherwise(this) + } + }) + } +} + #[cfg(any(test, feature = "test-support"))] pub async fn timeout(timeout: Duration, f: F) -> Result where diff --git a/crates/language_selector/src/active_buffer_language.rs b/crates/language_selector/src/active_buffer_language.rs index d5f177f7d6..647ff93b81 100644 --- a/crates/language_selector/src/active_buffer_language.rs +++ b/crates/language_selector/src/active_buffer_language.rs @@ -1,7 +1,7 @@ use editor::Editor; use gpui::{div, IntoElement, ParentElement, Render, Subscription, View, ViewContext, WeakView}; use std::sync::Arc; -use ui::{Button, ButtonCommon, Clickable, LabelSize, Tooltip}; +use ui::{Button, ButtonCommon, Clickable, FluentBuilder, LabelSize, Tooltip}; use workspace::{item::ItemHandle, StatusItemView, Workspace}; use crate::LanguageSelector; diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index eef3921a45..abfc2d5818 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -382,21 +382,21 @@ impl ProjectPanel { let is_read_only = project.is_read_only(); let context_menu = ContextMenu::build(cx, |menu, cx| { - menu.context(self.focus_handle.clone()).then_if_else( + menu.context(self.focus_handle.clone()).when_else( is_read_only, |menu| { menu.action("Copy Relative Path", Box::new(CopyRelativePath)) - .then_if(is_dir, |menu| { + .when(is_dir, |menu| { menu.action("Search Inside", Box::new(NewSearchInDirectory)) }) }, |menu| { - menu.then_if(is_local, |menu| { + menu.when(is_local, |menu| { menu.action( "Add Folder to Project", Box::new(workspace::AddFolderToProject), ) - .then_if(is_root, |menu| { + .when(is_root, |menu| { menu.entry( "Remove from Project", None, @@ -413,8 +413,8 @@ impl ProjectPanel { .separator() .action("Cut", Box::new(Cut)) .action("Copy", Box::new(Copy)) - .if_some(self.clipboard_entry, |menu, entry| { - menu.then_if(entry.worktree_id() == worktree_id, |menu| { + .when_some(self.clipboard_entry, |menu, entry| { + menu.when(entry.worktree_id() == worktree_id, |menu| { menu.action("Paste", Box::new(Paste)) }) }) @@ -423,15 +423,13 @@ impl ProjectPanel { .action("Copy Relative Path", Box::new(CopyRelativePath)) .separator() .action("Reveal in Finder", Box::new(RevealInFinder)) - .then_if(is_dir, |menu| { - menu - .action("Open in Terminal", Box::new(OpenInTerminal)) + .when(is_dir, |menu| { + menu.action("Open in Terminal", Box::new(OpenInTerminal)) .action("Search Inside", Box::new(NewSearchInDirectory)) }) - .separator().action("Rename", Box::new(Rename)) - .then_if(!is_root, |menu| { - menu.action("Delete", Box::new(Delete)) - }) + .separator() + .action("Rename", Box::new(Rename)) + .when(!is_root, |menu| menu.action("Delete", Box::new(Delete))) }, ) }); diff --git a/crates/ui/src/components/context_menu.rs b/crates/ui/src/components/context_menu.rs index dbc0394a5b..e3a7aeefda 100644 --- a/crates/ui/src/components/context_menu.rs +++ b/crates/ui/src/components/context_menu.rs @@ -42,6 +42,8 @@ impl FocusableView for ContextMenu { impl EventEmitter for ContextMenu {} +impl FluentBuilder for ContextMenu {} + impl ContextMenu { pub fn build( cx: &mut WindowContext, @@ -68,34 +70,6 @@ impl ContextMenu { }) } - pub fn if_some(self, condition: Option, f: impl FnOnce(Self, T) -> Self) -> Self { - if let Some(t) = condition { - f(self, t) - } else { - self - } - } - - pub fn then_if_else(self, condition: bool, then: impl FnOnce(Self) -> Self, otherwise: impl FnOnce(Self) -> Self) -> Self { - if condition { - then(self) - } else { - otherwise(self) - } - } - - pub fn then_if(self, condition: bool, f: impl FnOnce(Self) -> Self) -> Self { - if condition { - f(self) - } else { - self - } - } - - pub fn map(self, f: impl FnOnce(Self) -> Self) -> Self { - f(self) - } - pub fn context(mut self, focus: FocusHandle) -> Self { self.action_context = Some(focus); self diff --git a/crates/ui/src/components/popover_menu.rs b/crates/ui/src/components/popover_menu.rs index 39202bf7ef..73384321cb 100644 --- a/crates/ui/src/components/popover_menu.rs +++ b/crates/ui/src/components/popover_menu.rs @@ -1,9 +1,9 @@ use std::{cell::RefCell, rc::Rc}; use gpui::{ - overlay, point, px, rems, AnchorCorner, AnyElement, Bounds, DismissEvent, DispatchPhase, - Element, ElementId, InteractiveBounds, IntoElement, LayoutId, ManagedView, MouseDownEvent, - ParentElement, Pixels, Point, View, VisualContext, WindowContext, + overlay, point, prelude::FluentBuilder, px, rems, AnchorCorner, AnyElement, Bounds, + DismissEvent, DispatchPhase, Element, ElementId, InteractiveBounds, IntoElement, LayoutId, + ManagedView, MouseDownEvent, ParentElement, Pixels, Point, View, VisualContext, WindowContext, }; use crate::{Clickable, Selectable};