Pull out fluent builder helpers into re-usable trait
This commit is contained in:
parent
a5084510a1
commit
903176d8ff
8 changed files with 77 additions and 82 deletions
|
@ -1,5 +1,6 @@
|
||||||
use gpui::{div, AnyElement, IntoElement, ParentElement, RenderOnce, Styled, WindowContext};
|
use gpui::{div, AnyElement, IntoElement, ParentElement, RenderOnce, Styled, WindowContext};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
use ui::FluentBuilder;
|
||||||
|
|
||||||
#[derive(Default, IntoElement)]
|
#[derive(Default, IntoElement)]
|
||||||
pub struct FacePile {
|
pub struct FacePile {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
ArenaBox, AvailableSpace, BorrowWindow, Bounds, ElementId, LayoutId, Pixels, Point, Size,
|
util::FluentBuilder, ArenaBox, AvailableSpace, BorrowWindow, Bounds, ElementId, LayoutId,
|
||||||
ViewContext, WindowContext, ELEMENT_ARENA,
|
Pixels, Point, Size, ViewContext, WindowContext, ELEMENT_ARENA,
|
||||||
};
|
};
|
||||||
use derive_more::{Deref, DerefMut};
|
use derive_more::{Deref, DerefMut};
|
||||||
pub(crate) use smallvec::SmallVec;
|
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<U>(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<T>(self, option: Option<T>, then: impl FnOnce(Self, T) -> Self) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.map(|this| {
|
|
||||||
if let Some(value) = option {
|
|
||||||
then(this, value)
|
|
||||||
} else {
|
|
||||||
this
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: IntoElement> FluentBuilder for T {}
|
||||||
|
|
||||||
pub trait Render: 'static + Sized {
|
pub trait Render: 'static + Sized {
|
||||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement;
|
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
BorrowAppContext, BorrowWindow, Context, Element, FocusableElement, InteractiveElement,
|
util::FluentBuilder, BorrowAppContext, BorrowWindow, Context, Element, FocusableElement,
|
||||||
IntoElement, ParentElement, Refineable, Render, RenderOnce, StatefulInteractiveElement, Styled,
|
InteractiveElement, IntoElement, ParentElement, Refineable, Render, RenderOnce,
|
||||||
VisualContext,
|
StatefulInteractiveElement, Styled, VisualContext,
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,6 +9,58 @@ use smol::future::FutureExt;
|
||||||
|
|
||||||
pub use util::*;
|
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<U>(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<T>(self, option: Option<T>, 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"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
pub async fn timeout<F, T>(timeout: Duration, f: F) -> Result<T, ()>
|
pub async fn timeout<F, T>(timeout: Duration, f: F) -> Result<T, ()>
|
||||||
where
|
where
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use editor::Editor;
|
use editor::Editor;
|
||||||
use gpui::{div, IntoElement, ParentElement, Render, Subscription, View, ViewContext, WeakView};
|
use gpui::{div, IntoElement, ParentElement, Render, Subscription, View, ViewContext, WeakView};
|
||||||
use std::sync::Arc;
|
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 workspace::{item::ItemHandle, StatusItemView, Workspace};
|
||||||
|
|
||||||
use crate::LanguageSelector;
|
use crate::LanguageSelector;
|
||||||
|
|
|
@ -382,21 +382,21 @@ impl ProjectPanel {
|
||||||
let is_read_only = project.is_read_only();
|
let is_read_only = project.is_read_only();
|
||||||
|
|
||||||
let context_menu = ContextMenu::build(cx, |menu, cx| {
|
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,
|
is_read_only,
|
||||||
|menu| {
|
|menu| {
|
||||||
menu.action("Copy Relative Path", Box::new(CopyRelativePath))
|
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.action("Search Inside", Box::new(NewSearchInDirectory))
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|menu| {
|
|menu| {
|
||||||
menu.then_if(is_local, |menu| {
|
menu.when(is_local, |menu| {
|
||||||
menu.action(
|
menu.action(
|
||||||
"Add Folder to Project",
|
"Add Folder to Project",
|
||||||
Box::new(workspace::AddFolderToProject),
|
Box::new(workspace::AddFolderToProject),
|
||||||
)
|
)
|
||||||
.then_if(is_root, |menu| {
|
.when(is_root, |menu| {
|
||||||
menu.entry(
|
menu.entry(
|
||||||
"Remove from Project",
|
"Remove from Project",
|
||||||
None,
|
None,
|
||||||
|
@ -413,8 +413,8 @@ impl ProjectPanel {
|
||||||
.separator()
|
.separator()
|
||||||
.action("Cut", Box::new(Cut))
|
.action("Cut", Box::new(Cut))
|
||||||
.action("Copy", Box::new(Copy))
|
.action("Copy", Box::new(Copy))
|
||||||
.if_some(self.clipboard_entry, |menu, entry| {
|
.when_some(self.clipboard_entry, |menu, entry| {
|
||||||
menu.then_if(entry.worktree_id() == worktree_id, |menu| {
|
menu.when(entry.worktree_id() == worktree_id, |menu| {
|
||||||
menu.action("Paste", Box::new(Paste))
|
menu.action("Paste", Box::new(Paste))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -423,15 +423,13 @@ impl ProjectPanel {
|
||||||
.action("Copy Relative Path", Box::new(CopyRelativePath))
|
.action("Copy Relative Path", Box::new(CopyRelativePath))
|
||||||
.separator()
|
.separator()
|
||||||
.action("Reveal in Finder", Box::new(RevealInFinder))
|
.action("Reveal in Finder", Box::new(RevealInFinder))
|
||||||
.then_if(is_dir, |menu| {
|
.when(is_dir, |menu| {
|
||||||
menu
|
menu.action("Open in Terminal", Box::new(OpenInTerminal))
|
||||||
.action("Open in Terminal", Box::new(OpenInTerminal))
|
|
||||||
.action("Search Inside", Box::new(NewSearchInDirectory))
|
.action("Search Inside", Box::new(NewSearchInDirectory))
|
||||||
})
|
})
|
||||||
.separator().action("Rename", Box::new(Rename))
|
.separator()
|
||||||
.then_if(!is_root, |menu| {
|
.action("Rename", Box::new(Rename))
|
||||||
menu.action("Delete", Box::new(Delete))
|
.when(!is_root, |menu| menu.action("Delete", Box::new(Delete)))
|
||||||
})
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
|
@ -42,6 +42,8 @@ impl FocusableView for ContextMenu {
|
||||||
|
|
||||||
impl EventEmitter<DismissEvent> for ContextMenu {}
|
impl EventEmitter<DismissEvent> for ContextMenu {}
|
||||||
|
|
||||||
|
impl FluentBuilder for ContextMenu {}
|
||||||
|
|
||||||
impl ContextMenu {
|
impl ContextMenu {
|
||||||
pub fn build(
|
pub fn build(
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
|
@ -68,34 +70,6 @@ impl ContextMenu {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn if_some<T>(self, condition: Option<T>, 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 {
|
pub fn context(mut self, focus: FocusHandle) -> Self {
|
||||||
self.action_context = Some(focus);
|
self.action_context = Some(focus);
|
||||||
self
|
self
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use std::{cell::RefCell, rc::Rc};
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
overlay, point, px, rems, AnchorCorner, AnyElement, Bounds, DismissEvent, DispatchPhase,
|
overlay, point, prelude::FluentBuilder, px, rems, AnchorCorner, AnyElement, Bounds,
|
||||||
Element, ElementId, InteractiveBounds, IntoElement, LayoutId, ManagedView, MouseDownEvent,
|
DismissEvent, DispatchPhase, Element, ElementId, InteractiveBounds, IntoElement, LayoutId,
|
||||||
ParentElement, Pixels, Point, View, VisualContext, WindowContext,
|
ManagedView, MouseDownEvent, ParentElement, Pixels, Point, View, VisualContext, WindowContext,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{Clickable, Selectable};
|
use crate::{Clickable, Selectable};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue