use crate::{
input_ui::{Input, InputEvent, InputState},
prelude::*,
*,
};
use std::sync::{
Arc,
atomic::{AtomicU64, Ordering},
};
static SENDER_ID: AtomicU64 = AtomicU64::new(0);
pub struct SenderState {
instance: u64,
input: Entity<InputState>,
on_send: Option<Arc<dyn Fn(SharedString, &mut Window, &mut App) + Send + Sync + 'static>>,
pending_send: bool,
}
impl SenderState {
pub fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
let input = cx.new(|cx| InputState::new(window, cx).placeholder("输入消息…"));
cx.subscribe(&input, |this, _input, event, cx| match event {
InputEvent::PressEnter { shift, .. } => {
if !shift {
this.pending_send = true;
cx.notify();
}
}
_ => {}
})
.detach();
Self {
instance: SENDER_ID.fetch_add(1, Ordering::Relaxed),
input,
on_send: None,
pending_send: false,
}
}
pub fn on_send<F>(mut self, f: F) -> Self
where
F: Fn(SharedString, &mut Window, &mut App) + Send + Sync + 'static,
{
self.on_send = Some(Arc::new(f));
self
}
pub fn input(&self) -> &Entity<InputState> {
&self.input
}
}
impl Render for SenderState {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
if self.pending_send {
self.pending_send = false;
let text = self.input.read(cx).text().to_string();
if !text.trim().is_empty() {
let text: SharedString = text.into();
self.input.update(cx, |input, cx| {
input.set_value("", window, cx);
});
if let Some(ref cb) = self.on_send.clone() {
cb(text, window, cx);
}
}
}
let input = self.input.clone();
let panel = cx.entity();
let instance = self.instance;
div()
.flex()
.flex_row()
.items_end()
.gap(px(8.0))
.w_full()
.child(div().flex_1().child(Input::new(&input).w_full()))
.child(
Button::new(SharedString::from(format!("sender-{instance}-send")))
.label("发送")
.on_click(move |_, _, cx| {
panel.update(cx, |this, cx| {
this.pending_send = true;
cx.notify();
});
}),
)
}
}