use crate::schema::MessageDuration;
use std::cell::{Cell, RefCell};
use std::collections::VecDeque;
pub(crate) struct PendingMessage {
pub(crate) handler_id: u64,
pub(crate) text: String,
pub(crate) action: String,
pub(crate) duration: MessageDuration,
}
const ACTION_HANDLER_BASE: u64 = 1 << 63;
const LIVE_ACTIONS: usize = 16;
pub(crate) type Action = Box<dyn FnMut()>;
thread_local! {
static QUEUE: RefCell<Vec<PendingMessage>> = const { RefCell::new(Vec::new()) };
static ACTIONS: RefCell<VecDeque<(u64, Action)>> = const {
RefCell::new(VecDeque::new())
};
static NEXT_ACTION_ID: Cell<u64> = const { Cell::new(ACTION_HANDLER_BASE) };
}
pub struct Message {
text: String,
action: String,
on_action: Option<Action>,
duration: MessageDuration,
}
impl Message {
pub fn new(text: impl Into<String>) -> Self {
Self {
text: text.into(),
action: String::new(),
on_action: None,
duration: MessageDuration::Short,
}
}
pub fn with_action(
mut self,
label: impl Into<String>,
mut on_action: impl FnMut(()) + 'static,
) -> Self {
self.action = label.into();
self.on_action = Some(Box::new(move || on_action(())));
self
}
pub fn with_duration(mut self, duration: MessageDuration) -> Self {
self.duration = duration;
self
}
pub fn show(self) {
let handler_id = match self.on_action {
Some(callback) => {
let id = NEXT_ACTION_ID.with(|next| {
let id = next.get();
next.set(id.wrapping_add(1).max(ACTION_HANDLER_BASE));
id
});
ACTIONS.with_borrow_mut(|actions| {
while actions.len() >= LIVE_ACTIONS {
actions.pop_front();
}
actions.push_back((id, callback));
});
id
}
None => 0,
};
QUEUE.with_borrow_mut(|queue| {
queue.push(PendingMessage {
handler_id,
text: self.text,
action: self.action,
duration: self.duration,
});
});
}
}
pub fn show_message(text: impl Into<String>) {
Message::new(text).show();
}
pub(crate) fn drain(mut emit: impl FnMut(&PendingMessage)) {
let mut taken = QUEUE.with_borrow_mut(std::mem::take);
if taken.is_empty() {
return;
}
for message in &taken {
emit(message);
}
taken.clear();
QUEUE.with_borrow_mut(|queue| {
if queue.is_empty() {
*queue = taken;
}
});
}
pub(crate) fn take_action(handler_id: u64) -> Option<Action> {
if handler_id < ACTION_HANDLER_BASE {
return None;
}
ACTIONS.with_borrow_mut(|actions| {
let position = actions.iter().position(|(id, _)| *id == handler_id)?;
actions.remove(position).map(|(_, callback)| callback)
})
}
#[doc(hidden)]
pub fn reset_messages() {
QUEUE.with_borrow_mut(Vec::clear);
ACTIONS.with_borrow_mut(VecDeque::clear);
NEXT_ACTION_ID.with(|next| next.set(ACTION_HANDLER_BASE));
}