clipboard: Refactor with Element (#242)
This commit is contained in:
parent
a8d4abea69
commit
d8ace3c683
2 changed files with 105 additions and 27 deletions
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
|
@ -1,3 +1,4 @@
|
||||||
{
|
{
|
||||||
"rust-analyzer.check.command": "clippy"
|
"rust-analyzer.check.command": "clippy",
|
||||||
|
"cSpell.words": ["xsmall"]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue