clipboard: Refactor with Element (#242)

This commit is contained in:
Floyd Wang 2024-09-13 13:53:39 +08:00 committed by GitHub
parent a8d4abea69
commit d8ace3c683
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 105 additions and 27 deletions

View file

@ -1,3 +1,4 @@
{ {
"rust-analyzer.check.command": "clippy" "rust-analyzer.check.command": "clippy",
"cSpell.words": ["xsmall"]
} }

View file

@ -1,6 +1,8 @@
use std::{cell::RefCell, rc::Rc, time::Duration};
use gpui::{ use gpui::{
prelude::FluentBuilder, AnyElement, ClipboardItem, ElementId, IntoElement, ParentElement, prelude::FluentBuilder, AnyElement, ClipboardItem, Element, ElementId, GlobalElementId,
RenderOnce, SharedString, Styled, WindowContext, IntoElement, LayoutId, ParentElement, SharedString, Styled, WindowContext,
}; };
use crate::{ use crate::{
@ -8,12 +10,11 @@ use crate::{
h_flex, IconName, Sizable as _, h_flex, IconName, Sizable as _,
}; };
#[derive(IntoElement)]
pub struct Clipboard { pub struct Clipboard {
id: ElementId, id: ElementId,
value: SharedString, value: SharedString,
content_builder: Option<Box<dyn Fn(&mut WindowContext) -> AnyElement>>, content_builder: Option<Box<dyn Fn(&mut WindowContext) -> AnyElement>>,
copied_callback: Option<Box<dyn Fn(SharedString, &mut WindowContext)>>, copied_callback: Option<Rc<dyn Fn(SharedString, &mut WindowContext)>>,
} }
impl Clipboard { impl Clipboard {
@ -44,32 +45,108 @@ impl Clipboard {
where where
F: Fn(SharedString, &mut WindowContext) + 'static, F: Fn(SharedString, &mut WindowContext) + 'static,
{ {
self.copied_callback = Some(Box::new(handler)); self.copied_callback = Some(Rc::new(handler));
self self
} }
} }
impl RenderOnce for Clipboard { impl IntoElement for Clipboard {
fn render(self, cx: &mut WindowContext) -> impl IntoElement { type Element = Self;
h_flex()
.gap_1()
.items_center()
.when_some(self.content_builder, |this, builder| {
this.child(builder(cx))
})
.child(
Button::new(self.id, cx)
.icon(IconName::Copy)
.ghost()
.xsmall()
.on_click(move |_, cx| {
cx.stop_propagation();
cx.write_to_clipboard(ClipboardItem::new_string(self.value.to_string()));
if let Some(copied) = &self.copied_callback { fn into_element(self) -> Self::Element {
copied(self.value.clone(), cx); self
} }
}), }
)
#[derive(Default)]
pub struct ClipboardState {
copied: Rc<RefCell<bool>>,
}
impl Element for Clipboard {
type RequestLayoutState = AnyElement;
type PrepaintState = ();
fn id(&self) -> Option<ElementId> {
Some(self.id.clone())
}
fn request_layout(
&mut self,
global_id: Option<&GlobalElementId>,
cx: &mut WindowContext,
) -> (LayoutId, Self::RequestLayoutState) {
cx.with_element_state::<ClipboardState, _>(global_id.unwrap(), |state, cx| {
let state = state.unwrap_or_default();
let content_element = self
.content_builder
.as_ref()
.map(|builder| builder(cx).into_any_element());
let value = self.value.clone();
let clipboard_id = self.id.clone();
let copied_callback = self.copied_callback.as_ref().map(|c| c.clone());
let copied = state.copied.clone();
let copide_value = *copied.borrow();
let mut element = h_flex()
.gap_1()
.items_center()
.when_some(content_element, |this, element| this.child(element))
.child(
Button::new(clipboard_id, cx)
.icon(if copide_value {
IconName::Check
} else {
IconName::Copy
})
.ghost()
.xsmall()
.when(!copide_value, |this| {
this.on_click(move |_, cx| {
cx.stop_propagation();
cx.write_to_clipboard(ClipboardItem::new_string(value.to_string()));
*copied.borrow_mut() = true;
let copied = copied.clone();
cx.spawn(|cx| async move {
cx.background_executor().timer(Duration::from_secs(2)).await;
*copied.borrow_mut() = false;
})
.detach();
if let Some(callback) = &copied_callback {
callback(value.clone(), cx);
}
})
}),
)
.into_any_element();
((element.request_layout(cx), element), state)
})
}
fn prepaint(
&mut self,
_: Option<&gpui::GlobalElementId>,
_: gpui::Bounds<gpui::Pixels>,
element: &mut Self::RequestLayoutState,
cx: &mut WindowContext,
) {
element.prepaint(cx);
}
fn paint(
&mut self,
_: Option<&gpui::GlobalElementId>,
_: gpui::Bounds<gpui::Pixels>,
element: &mut Self::RequestLayoutState,
_: &mut Self::PrepaintState,
cx: &mut WindowContext,
) {
element.paint(cx)
} }
} }