2023-12-27 15:39:53 +01:00
|
|
|
use std::{
|
|
|
|
path::PathBuf,
|
|
|
|
sync::{
|
|
|
|
atomic::{AtomicBool, AtomicU8},
|
|
|
|
mpsc::Sender,
|
|
|
|
Arc, Mutex, RwLock,
|
|
|
|
},
|
|
|
|
time::Duration,
|
|
|
|
};
|
|
|
|
|
|
|
|
use mers_lib::{
|
|
|
|
data::{Data, MersType, Type},
|
|
|
|
errors::CheckError,
|
|
|
|
prelude_compile::CompInfo,
|
|
|
|
};
|
|
|
|
use musicdb_lib::{data::database::Database, server::Command};
|
|
|
|
use speedy2d::{color::Color, dimen::Vec2, shape::Rectangle, window::UserEventSender};
|
|
|
|
|
|
|
|
use crate::{
|
|
|
|
gui::{Gui, GuiAction, GuiConfig, GuiElem, GuiElemCfg, GuiEvent},
|
|
|
|
gui_base::Panel,
|
|
|
|
gui_notif::{NotifInfo, NotifOverlay},
|
|
|
|
gui_text::Label,
|
|
|
|
textcfg::TextBuilder,
|
|
|
|
};
|
|
|
|
|
2024-01-16 20:17:11 +01:00
|
|
|
pub struct OptFunc(pub Option<mers_lib::data::function::Function>);
|
2023-12-27 15:39:53 +01:00
|
|
|
impl OptFunc {
|
|
|
|
pub fn none() -> Self {
|
|
|
|
Self(None)
|
|
|
|
}
|
|
|
|
pub fn some(func: mers_lib::data::function::Function) -> Self {
|
|
|
|
Self(Some(func))
|
|
|
|
}
|
|
|
|
fn run(&self) {
|
|
|
|
if let Some(func) = &self.0 {
|
|
|
|
func.run(Data::empty_tuple());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// mers code must return an object `{}` with hook functions.
|
|
|
|
/// All hook functions will be called with `()` as their argument,
|
|
|
|
/// and their return value will be ignored.
|
|
|
|
///
|
|
|
|
/// Values:
|
|
|
|
/// - `is_playing`
|
|
|
|
/// - `is_idle`
|
|
|
|
/// - `window_size_in_pixels`
|
|
|
|
/// - `idle_screen_cover_aspect_ratio`
|
|
|
|
///
|
|
|
|
/// Functions:
|
|
|
|
/// - `idle_start`
|
|
|
|
/// - `idle_stop`
|
|
|
|
/// - `idle_prevent`
|
|
|
|
/// - `send_notification`
|
|
|
|
/// - `set_idle_screen_cover_pos`
|
|
|
|
/// - `set_idle_screen_artist_image_pos`
|
|
|
|
/// - `set_idle_screen_top_text_pos`
|
|
|
|
/// - `set_idle_screen_side_text_1_pos`
|
|
|
|
/// - `set_idle_screen_side_text_2_pos`
|
|
|
|
/// - `set_statusbar_text_format`
|
|
|
|
/// - `set_idle_screen_top_text_format`
|
|
|
|
/// - `set_idle_screen_side_text_1_format`
|
|
|
|
/// - `set_idle_screen_side_text_2_format`
|
|
|
|
pub struct MersCfg {
|
|
|
|
pub source_file: PathBuf,
|
2024-01-16 20:17:11 +01:00
|
|
|
pub database: Arc<Mutex<Database>>,
|
2023-12-27 15:39:53 +01:00
|
|
|
// - - handler functions - -
|
|
|
|
pub func_before_draw: OptFunc,
|
|
|
|
pub func_library_updated: OptFunc,
|
|
|
|
pub func_queue_updated: OptFunc,
|
|
|
|
// - - globals that aren't functions - -
|
|
|
|
pub var_is_playing: Arc<RwLock<Data>>,
|
|
|
|
pub var_is_idle: Arc<RwLock<Data>>,
|
|
|
|
pub var_window_size_in_pixels: Arc<RwLock<Data>>,
|
|
|
|
pub var_idle_screen_cover_aspect_ratio: Arc<RwLock<Data>>,
|
|
|
|
// - - results from running functions - -
|
2024-01-16 20:17:11 +01:00
|
|
|
pub channel_gui_actions: (
|
|
|
|
std::sync::mpsc::Sender<Command>,
|
|
|
|
std::sync::mpsc::Receiver<Command>,
|
|
|
|
),
|
2023-12-27 15:39:53 +01:00
|
|
|
pub updated_playing_status: Arc<AtomicU8>,
|
|
|
|
pub updated_idle_status: Arc<AtomicU8>,
|
|
|
|
pub updated_idle_screen_cover_pos: Arc<Updatable<Option<Rectangle>>>,
|
|
|
|
pub updated_idle_screen_artist_image_pos: Arc<Updatable<Option<Rectangle>>>,
|
|
|
|
pub updated_idle_screen_top_text_pos: Arc<Updatable<Rectangle>>,
|
|
|
|
pub updated_idle_screen_side_text_1_pos: Arc<Updatable<Rectangle>>,
|
|
|
|
pub updated_idle_screen_side_text_2_pos: Arc<Updatable<Rectangle>>,
|
|
|
|
pub updated_idle_screen_playback_buttons_pos: Arc<Updatable<Rectangle>>,
|
|
|
|
pub updated_statusbar_text_format: Arc<Updatable<TextBuilder>>,
|
|
|
|
pub updated_idle_screen_top_text_format: Arc<Updatable<TextBuilder>>,
|
|
|
|
pub updated_idle_screen_side_text_1_format: Arc<Updatable<TextBuilder>>,
|
|
|
|
pub updated_idle_screen_side_text_2_format: Arc<Updatable<TextBuilder>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl MersCfg {
|
2024-01-16 20:17:11 +01:00
|
|
|
pub fn new(path: PathBuf, database: Arc<Mutex<Database>>) -> Self {
|
2023-12-27 15:39:53 +01:00
|
|
|
Self {
|
|
|
|
source_file: path,
|
2024-01-16 20:17:11 +01:00
|
|
|
database,
|
2023-12-27 15:39:53 +01:00
|
|
|
|
|
|
|
func_before_draw: OptFunc::none(),
|
|
|
|
func_library_updated: OptFunc::none(),
|
|
|
|
func_queue_updated: OptFunc::none(),
|
|
|
|
|
|
|
|
var_is_playing: Arc::new(RwLock::new(Data::new(mers_lib::data::bool::Bool(false)))),
|
|
|
|
var_is_idle: Arc::new(RwLock::new(Data::new(mers_lib::data::bool::Bool(false)))),
|
|
|
|
var_window_size_in_pixels: Arc::new(RwLock::new(Data::new(
|
|
|
|
mers_lib::data::tuple::Tuple(vec![
|
|
|
|
Data::new(mers_lib::data::int::Int(0)),
|
|
|
|
Data::new(mers_lib::data::int::Int(0)),
|
|
|
|
]),
|
|
|
|
))),
|
|
|
|
var_idle_screen_cover_aspect_ratio: Arc::new(RwLock::new(Data::new(
|
|
|
|
mers_lib::data::float::Float(0.0),
|
|
|
|
))),
|
|
|
|
|
2024-01-16 20:17:11 +01:00
|
|
|
channel_gui_actions: std::sync::mpsc::channel(),
|
2023-12-27 15:39:53 +01:00
|
|
|
updated_playing_status: Arc::new(AtomicU8::new(0)),
|
|
|
|
updated_idle_status: Arc::new(AtomicU8::new(0)),
|
|
|
|
updated_idle_screen_cover_pos: Arc::new(Updatable::new()),
|
|
|
|
updated_idle_screen_artist_image_pos: Arc::new(Updatable::new()),
|
|
|
|
updated_idle_screen_top_text_pos: Arc::new(Updatable::new()),
|
|
|
|
updated_idle_screen_side_text_1_pos: Arc::new(Updatable::new()),
|
|
|
|
updated_idle_screen_side_text_2_pos: Arc::new(Updatable::new()),
|
|
|
|
updated_idle_screen_playback_buttons_pos: Arc::new(Updatable::new()),
|
|
|
|
updated_statusbar_text_format: Arc::new(Updatable::new()),
|
|
|
|
updated_idle_screen_top_text_format: Arc::new(Updatable::new()),
|
|
|
|
updated_idle_screen_side_text_1_format: Arc::new(Updatable::new()),
|
|
|
|
updated_idle_screen_side_text_2_format: Arc::new(Updatable::new()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fn custom_globals(
|
|
|
|
&self,
|
|
|
|
cfg: mers_lib::prelude_extend_config::Config,
|
2024-01-16 20:17:11 +01:00
|
|
|
db: &Arc<Mutex<Database>>,
|
2023-12-27 15:39:53 +01:00
|
|
|
event_sender: Arc<UserEventSender<GuiEvent>>,
|
|
|
|
notif_sender: Sender<
|
|
|
|
Box<dyn FnOnce(&NotifOverlay) -> (Box<dyn GuiElem>, NotifInfo) + Send>,
|
|
|
|
>,
|
2024-01-20 01:19:19 +01:00
|
|
|
after_db_cmd: &Arc<Mutex<Option<Box<dyn FnMut(Command) + Send + Sync + 'static>>>>,
|
2023-12-27 15:39:53 +01:00
|
|
|
) -> mers_lib::prelude_extend_config::Config {
|
2024-01-16 20:17:11 +01:00
|
|
|
let cmd_es = event_sender.clone();
|
|
|
|
let cmd_ga = self.channel_gui_actions.0.clone();
|
|
|
|
musicdb_mers::add(cfg, db, &Arc::new(move |cmd| {
|
|
|
|
cmd_ga.send(cmd).unwrap();
|
|
|
|
cmd_es.send_event(GuiEvent::RefreshMers).unwrap();
|
2024-01-20 01:19:19 +01:00
|
|
|
}), after_db_cmd)
|
2024-01-16 20:17:11 +01:00
|
|
|
.add_var_arc(
|
2023-12-27 15:39:53 +01:00
|
|
|
"is_playing".to_owned(),
|
|
|
|
Arc::clone(&self.var_is_playing),
|
|
|
|
self.var_is_playing.read().unwrap().get().as_type(),
|
|
|
|
)
|
|
|
|
.add_var_arc(
|
|
|
|
"is_idle".to_owned(),
|
|
|
|
Arc::clone(&self.var_is_idle),
|
|
|
|
self.var_is_idle.read().unwrap().get().as_type(),
|
|
|
|
)
|
|
|
|
.add_var_arc(
|
|
|
|
"window_size_in_pixels".to_owned(),
|
|
|
|
Arc::clone(&self.var_window_size_in_pixels),
|
|
|
|
self.var_window_size_in_pixels.read().unwrap().get().as_type(),
|
|
|
|
)
|
|
|
|
.add_var_arc(
|
|
|
|
"idle_screen_cover_aspect_ratio".to_owned(),
|
|
|
|
Arc::clone(&self.var_idle_screen_cover_aspect_ratio),
|
|
|
|
self.var_idle_screen_cover_aspect_ratio.read().unwrap().get().as_type(),
|
|
|
|
)
|
|
|
|
.add_var("playback_resume".to_owned(),{
|
|
|
|
let es = event_sender.clone();
|
|
|
|
let v = Arc::clone(&self.updated_playing_status);
|
|
|
|
Data::new(mers_lib::data::function::Function {
|
|
|
|
info: Arc::new(mers_lib::info::Info::neverused()),
|
|
|
|
info_check: Arc::new(Mutex::new(mers_lib::info::Info::neverused())),
|
|
|
|
out: Arc::new(|a, _| {
|
|
|
|
if a.is_zero_tuple() {
|
|
|
|
Ok(Type::empty_tuple())
|
|
|
|
} else {
|
|
|
|
Err(format!("Can't call `playback_resume` with argument of type `{a}` (must be `()`).").into())
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
run: Arc::new(move |_, _| {
|
|
|
|
v.store(1, std::sync::atomic::Ordering::Relaxed);
|
|
|
|
es.send_event(GuiEvent::Refresh).unwrap();
|
|
|
|
Data::empty_tuple()
|
|
|
|
}),
|
|
|
|
inner_statements: None,
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.add_var("playback_pause".to_owned(),{
|
|
|
|
let es = event_sender.clone();
|
|
|
|
let v = Arc::clone(&self.updated_playing_status);
|
|
|
|
Data::new(mers_lib::data::function::Function {
|
|
|
|
info: Arc::new(mers_lib::info::Info::neverused()),
|
|
|
|
info_check: Arc::new(Mutex::new(mers_lib::info::Info::neverused())),
|
|
|
|
out: Arc::new(|a, _| {
|
|
|
|
if a.is_zero_tuple() {
|
|
|
|
Ok(Type::empty_tuple())
|
|
|
|
} else {
|
|
|
|
Err(format!("Can't call `playback_pause` with argument of type `{a}` (must be `()`).").into())
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
run: Arc::new(move |_, _| {
|
|
|
|
v.store(2, std::sync::atomic::Ordering::Relaxed);
|
|
|
|
es.send_event(GuiEvent::Refresh).unwrap();
|
|
|
|
Data::empty_tuple()
|
|
|
|
}),
|
|
|
|
inner_statements: None,
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.add_var("playback_stop".to_owned(),{
|
|
|
|
let es = event_sender.clone();
|
|
|
|
let v = Arc::clone(&self.updated_playing_status);
|
|
|
|
Data::new(mers_lib::data::function::Function {
|
|
|
|
info: Arc::new(mers_lib::info::Info::neverused()),
|
|
|
|
info_check: Arc::new(Mutex::new(mers_lib::info::Info::neverused())),
|
|
|
|
out: Arc::new(|a, _| {
|
|
|
|
if a.is_zero_tuple() {
|
|
|
|
Ok(Type::empty_tuple())
|
|
|
|
} else {
|
|
|
|
Err(format!("Can't call `playback_stop` with argument of type `{a}` (must be `()`).").into())
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
run: Arc::new(move |_, _| {
|
|
|
|
v.store(3, std::sync::atomic::Ordering::Relaxed);
|
|
|
|
es.send_event(GuiEvent::Refresh).unwrap();
|
|
|
|
Data::empty_tuple()
|
|
|
|
}),
|
|
|
|
inner_statements: None,
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.add_var("idle_start".to_owned(),{
|
|
|
|
let es = event_sender.clone();
|
|
|
|
let v = Arc::clone(&self.updated_idle_status);
|
|
|
|
Data::new(mers_lib::data::function::Function {
|
|
|
|
info: Arc::new(mers_lib::info::Info::neverused()),
|
|
|
|
info_check: Arc::new(Mutex::new(mers_lib::info::Info::neverused())),
|
|
|
|
out: Arc::new(|a, _| {
|
|
|
|
if a.is_zero_tuple() {
|
|
|
|
Ok(Type::empty_tuple())
|
|
|
|
} else {
|
|
|
|
Err(format!("Can't call `idle_start` with argument of type `{a}` (must be `()`).").into())
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
run: Arc::new(move |_, _| {
|
|
|
|
v.store(1, std::sync::atomic::Ordering::Relaxed);
|
|
|
|
es.send_event(GuiEvent::Refresh).unwrap();
|
|
|
|
Data::empty_tuple()
|
|
|
|
}),
|
|
|
|
inner_statements: None,
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.add_var("idle_stop".to_owned(),{
|
|
|
|
let es = event_sender.clone();
|
|
|
|
let v = Arc::clone(&self.updated_idle_status);
|
|
|
|
Data::new(mers_lib::data::function::Function {
|
|
|
|
info: Arc::new(mers_lib::info::Info::neverused()),
|
|
|
|
info_check: Arc::new(Mutex::new(mers_lib::info::Info::neverused())),
|
|
|
|
out: Arc::new(|a, _| {
|
|
|
|
if a.is_zero_tuple() {
|
|
|
|
Ok(Type::empty_tuple())
|
|
|
|
} else {
|
|
|
|
Err(format!("Can't call `idle_stop` with argument of type `{a}` (must be `()`).").into())
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
run: Arc::new(move |_, _| {
|
|
|
|
v.store(2, std::sync::atomic::Ordering::Relaxed);
|
|
|
|
es.send_event(GuiEvent::Refresh).unwrap();
|
|
|
|
Data::empty_tuple()
|
|
|
|
}),
|
|
|
|
inner_statements: None,
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.add_var("idle_prevent".to_owned(),{
|
|
|
|
let es = event_sender.clone();
|
|
|
|
let v = Arc::clone(&self.updated_idle_status);
|
|
|
|
Data::new(mers_lib::data::function::Function {
|
|
|
|
info: Arc::new(mers_lib::info::Info::neverused()),
|
|
|
|
info_check: Arc::new(Mutex::new(mers_lib::info::Info::neverused())),
|
|
|
|
out: Arc::new(|a, _| {
|
|
|
|
if a.is_zero_tuple() {
|
|
|
|
Ok(Type::empty_tuple())
|
|
|
|
} else {
|
|
|
|
Err(format!("Can't call `idle_prevent` with argument of type `{a}` (must be `()`).").into())
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
run: Arc::new(move |_, _| {
|
|
|
|
v.store(3, std::sync::atomic::Ordering::Relaxed);
|
|
|
|
es.send_event(GuiEvent::Refresh).unwrap();
|
|
|
|
Data::empty_tuple()
|
|
|
|
}),
|
|
|
|
inner_statements: None,
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.add_var("send_notification".to_owned(),{
|
|
|
|
let es = event_sender.clone();
|
|
|
|
Data::new(mers_lib::data::function::Function {
|
|
|
|
info: Arc::new(mers_lib::info::Info::neverused()),
|
|
|
|
info_check: Arc::new(Mutex::new(mers_lib::info::Info::neverused())),
|
|
|
|
out: Arc::new(|a, _| {
|
|
|
|
if a.is_included_in(&mers_lib::data::tuple::TupleT(vec![
|
|
|
|
mers_lib::data::Type::new(mers_lib::data::string::StringT),
|
|
|
|
mers_lib::data::Type::new(mers_lib::data::string::StringT),
|
|
|
|
mers_lib::data::Type::newm(vec![
|
|
|
|
Arc::new(mers_lib::data::int::IntT),
|
|
|
|
Arc::new(mers_lib::data::float::FloatT)
|
|
|
|
]),
|
|
|
|
])) {
|
|
|
|
Ok(Type::empty_tuple())
|
|
|
|
} else {
|
|
|
|
Err(format!("Can't call `send_notification` with argument of type `{a}` (must be `String`).").into())
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
run: Arc::new(move |a, _| {
|
|
|
|
let a = a.get();
|
|
|
|
let t = &a.as_any().downcast_ref::<mers_lib::data::tuple::Tuple>().unwrap().0;
|
|
|
|
let title = t[0].get().as_any().downcast_ref::<mers_lib::data::string::String>().unwrap().0.clone();
|
|
|
|
let text = t[1].get().as_any().downcast_ref::<mers_lib::data::string::String>().unwrap().0.clone();
|
|
|
|
let t = t[2].get();
|
|
|
|
let duration = t.as_any().downcast_ref::<mers_lib::data::int::Int>().map(|s| Duration::from_secs(s.0.max(0) as _)).unwrap_or_else(|| Duration::from_secs_f64(t.as_any().downcast_ref::<mers_lib::data::float::Float>().unwrap().0));
|
|
|
|
notif_sender
|
|
|
|
.send(Box::new(move |_| {
|
|
|
|
(
|
|
|
|
Box::new(Panel::with_background(
|
|
|
|
GuiElemCfg::default(),
|
|
|
|
(
|
|
|
|
Label::new(
|
|
|
|
GuiElemCfg::at(Rectangle::from_tuples(
|
|
|
|
(0.25, 0.0),
|
|
|
|
(0.75, 0.5),
|
|
|
|
)),
|
|
|
|
title,
|
|
|
|
Color::WHITE,
|
|
|
|
None,
|
|
|
|
Vec2::new(0.5, 0.0),
|
|
|
|
),
|
|
|
|
Label::new(
|
|
|
|
GuiElemCfg::at(Rectangle::from_tuples(
|
|
|
|
(0.0, 0.5),
|
|
|
|
(1.0, 1.0),
|
|
|
|
)),
|
|
|
|
text,
|
|
|
|
Color::WHITE,
|
|
|
|
None,
|
|
|
|
Vec2::new(0.5, 1.0),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
Color::from_rgba(0.0, 0.0, 0.0, 0.8),
|
|
|
|
)),
|
|
|
|
NotifInfo::new(duration),
|
|
|
|
)
|
|
|
|
}))
|
|
|
|
.unwrap();
|
|
|
|
es.send_event(GuiEvent::Refresh).unwrap();
|
|
|
|
Data::empty_tuple()
|
|
|
|
}),
|
|
|
|
inner_statements: None,
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.add_var("set_idle_screen_cover_pos".to_owned(),{
|
|
|
|
let es = event_sender.clone();
|
|
|
|
let update = Arc::clone(&self.updated_idle_screen_cover_pos);
|
|
|
|
Data::new(mers_lib::data::function::Function {
|
|
|
|
info: Arc::new(mers_lib::info::Info::neverused()),
|
|
|
|
info_check: Arc::new(Mutex::new(mers_lib::info::Info::neverused())),
|
|
|
|
out: Arc::new(|a, _| {
|
|
|
|
if a.is_included_in(&mers_lib::data::Type::newm(vec![
|
|
|
|
Arc::new(mers_lib::data::tuple::TupleT(vec![])),
|
|
|
|
Arc::new(mers_lib::data::tuple::TupleT(vec![
|
|
|
|
mers_lib::data::Type::new(mers_lib::data::float::FloatT),
|
|
|
|
mers_lib::data::Type::new(mers_lib::data::float::FloatT),
|
|
|
|
mers_lib::data::Type::new(mers_lib::data::float::FloatT),
|
|
|
|
mers_lib::data::Type::new(mers_lib::data::float::FloatT),
|
|
|
|
]))
|
|
|
|
])) {
|
|
|
|
Ok(Type::empty_tuple())
|
|
|
|
} else {
|
|
|
|
Err(format!("Can't call `set_idle_screen_cover_pos` with argument of type `{a}` (must be `()` or `(Float, Float, Float, Float)`).").into())
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
run: Arc::new(move |a, _| {
|
|
|
|
let a = a.get();
|
|
|
|
let mut vals = a.as_any().downcast_ref::<mers_lib::data::tuple::Tuple>().unwrap().0.iter().map(|v| v.get().as_any().downcast_ref::<mers_lib::data::float::Float>().unwrap().0);
|
|
|
|
update.update(
|
|
|
|
if vals.len() >= 4 {
|
|
|
|
Some(Rectangle::from_tuples((vals.next().unwrap() as _, vals.next().unwrap() as _), (vals.next().unwrap() as _, vals.next().unwrap() as _)))
|
|
|
|
} else { None });
|
|
|
|
es.send_event(GuiEvent::Refresh).unwrap();
|
|
|
|
Data::empty_tuple()
|
|
|
|
}),
|
|
|
|
inner_statements: None,
|
|
|
|
})
|
|
|
|
}).add_var("set_idle_screen_artist_image_pos".to_owned(),{
|
|
|
|
let es = event_sender.clone();
|
|
|
|
let update = Arc::clone(&self.updated_idle_screen_artist_image_pos);
|
|
|
|
Data::new(mers_lib::data::function::Function {
|
|
|
|
info: Arc::new(mers_lib::info::Info::neverused()),
|
|
|
|
info_check: Arc::new(Mutex::new(mers_lib::info::Info::neverused())),
|
|
|
|
out: Arc::new(|a, _| {
|
|
|
|
if a.is_included_in(&mers_lib::data::Type::newm(vec![
|
|
|
|
Arc::new(mers_lib::data::tuple::TupleT(vec![])),
|
|
|
|
Arc::new(mers_lib::data::tuple::TupleT(vec![
|
|
|
|
mers_lib::data::Type::new(mers_lib::data::float::FloatT),
|
|
|
|
mers_lib::data::Type::new(mers_lib::data::float::FloatT),
|
|
|
|
mers_lib::data::Type::new(mers_lib::data::float::FloatT),
|
|
|
|
mers_lib::data::Type::new(mers_lib::data::float::FloatT),
|
|
|
|
]))
|
|
|
|
])) {
|
|
|
|
Ok(Type::empty_tuple())
|
|
|
|
} else {
|
|
|
|
Err(format!("Can't call `set_idle_screen_artist_image_pos` with argument of type `{a}` (must be `()` or `(Float, Float, Float, Float)`).").into())
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
run: Arc::new(move |a, _| {
|
|
|
|
let a = a.get();
|
|
|
|
let mut vals = a.as_any().downcast_ref::<mers_lib::data::tuple::Tuple>().unwrap().0.iter().map(|v| v.get().as_any().downcast_ref::<mers_lib::data::float::Float>().unwrap().0);
|
|
|
|
update.update(
|
|
|
|
if vals.len() >= 4 {
|
|
|
|
Some(Rectangle::from_tuples((vals.next().unwrap() as _, vals.next().unwrap() as _), (vals.next().unwrap() as _, vals.next().unwrap() as _)))
|
|
|
|
} else { None });
|
|
|
|
es.send_event(GuiEvent::Refresh).unwrap();
|
|
|
|
Data::empty_tuple()
|
|
|
|
}),
|
|
|
|
inner_statements: None,
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.add_var("set_idle_screen_top_text_pos".to_owned(), gen_set_pos_func("set_idle_screen_top_text_pos", Arc::clone(&event_sender), Arc::clone(&self.updated_idle_screen_top_text_pos)))
|
|
|
|
.add_var("set_idle_screen_side_text_1_pos".to_owned(), gen_set_pos_func("set_idle_screen_side_text_1_pos", Arc::clone(&event_sender), Arc::clone(&self.updated_idle_screen_side_text_1_pos)))
|
|
|
|
.add_var("set_idle_screen_side_text_2_pos".to_owned(), gen_set_pos_func("set_idle_screen_side_text_2_pos", Arc::clone(&event_sender), Arc::clone(&self.updated_idle_screen_side_text_2_pos)))
|
|
|
|
.add_var("set_idle_screen_playback_buttons_pos".to_owned(), gen_set_pos_func("set_idle_screen_playback_buttons_pos", Arc::clone(&event_sender), Arc::clone(&self.updated_idle_screen_playback_buttons_pos)))
|
|
|
|
|
|
|
|
.add_var("set_statusbar_text_format".to_owned(),{
|
|
|
|
let es = event_sender.clone();
|
|
|
|
let update = Arc::clone(&self.updated_statusbar_text_format);
|
|
|
|
Data::new(mers_lib::data::function::Function {
|
|
|
|
info: Arc::new(mers_lib::info::Info::neverused()),
|
|
|
|
info_check: Arc::new(Mutex::new(mers_lib::info::Info::neverused())),
|
|
|
|
out: Arc::new(|a, _| {
|
|
|
|
if a.is_included_in(&mers_lib::data::string::StringT) {
|
|
|
|
Ok(Type::newm(vec![
|
|
|
|
Arc::new(mers_lib::data::tuple::TupleT(vec![])),
|
|
|
|
Arc::new(mers_lib::data::string::StringT),
|
|
|
|
]))
|
|
|
|
} else {
|
|
|
|
Err(format!("Can't call `set_statusbar_text_format` with argument of type `{a}` (must be `String`).").into())
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
run: Arc::new(move |a, _| {
|
|
|
|
let a = a.get();
|
|
|
|
let o = match a.as_any().downcast_ref::<mers_lib::data::string::String>().unwrap().0.parse() {
|
|
|
|
Ok(v) => {
|
|
|
|
update.update(v);
|
|
|
|
Data::empty_tuple()
|
|
|
|
}
|
|
|
|
Err(e) => mers_lib::data::Data::new(mers_lib::data::string::String(e.to_string())),
|
|
|
|
};
|
|
|
|
es.send_event(GuiEvent::Refresh).unwrap();
|
|
|
|
o
|
|
|
|
}),
|
|
|
|
inner_statements: None,
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.add_var("set_idle_screen_top_text_format".to_owned(),{
|
|
|
|
let es = event_sender.clone();
|
|
|
|
let update = Arc::clone(&self.updated_idle_screen_top_text_format);
|
|
|
|
Data::new(mers_lib::data::function::Function {
|
|
|
|
info: Arc::new(mers_lib::info::Info::neverused()),
|
|
|
|
info_check: Arc::new(Mutex::new(mers_lib::info::Info::neverused())),
|
|
|
|
out: Arc::new(|a, _| {
|
|
|
|
if a.is_included_in(&mers_lib::data::string::StringT) {
|
|
|
|
Ok(Type::newm(vec![
|
|
|
|
Arc::new(mers_lib::data::tuple::TupleT(vec![])),
|
|
|
|
Arc::new(mers_lib::data::string::StringT),
|
|
|
|
]))
|
|
|
|
} else {
|
|
|
|
Err(format!("Can't call `set_idle_screen_top_text_format` with argument of type `{a}` (must be `String`).").into())
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
run: Arc::new(move |a, _| {
|
|
|
|
let a = a.get();
|
|
|
|
let o = match a.as_any().downcast_ref::<mers_lib::data::string::String>().unwrap().0.parse() {
|
|
|
|
Ok(v) => {
|
|
|
|
update.update(v);
|
|
|
|
Data::empty_tuple()
|
|
|
|
}
|
|
|
|
Err(e) => mers_lib::data::Data::new(mers_lib::data::string::String(e.to_string())),
|
|
|
|
};
|
|
|
|
es.send_event(GuiEvent::Refresh).unwrap();
|
|
|
|
o
|
|
|
|
}),
|
|
|
|
inner_statements: None,
|
|
|
|
})
|
|
|
|
}).add_var("set_idle_screen_side_text_1_format".to_owned(),{
|
|
|
|
let es = event_sender.clone();
|
|
|
|
let update = Arc::clone(&self.updated_idle_screen_side_text_1_format);
|
|
|
|
Data::new(mers_lib::data::function::Function {
|
|
|
|
info: Arc::new(mers_lib::info::Info::neverused()),
|
|
|
|
info_check: Arc::new(Mutex::new(mers_lib::info::Info::neverused())),
|
|
|
|
out: Arc::new(|a, _| {
|
|
|
|
if a.is_included_in(&mers_lib::data::string::StringT) {
|
|
|
|
Ok(Type::newm(vec![
|
|
|
|
Arc::new(mers_lib::data::tuple::TupleT(vec![])),
|
|
|
|
Arc::new(mers_lib::data::string::StringT),
|
|
|
|
]))
|
|
|
|
} else {
|
|
|
|
Err(format!("Can't call `set_idle_screen_side_text_1_format` with argument of type `{a}` (must be `String`).").into())
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
run: Arc::new(move |a, _| {
|
|
|
|
let a = a.get();
|
|
|
|
let o = match a.as_any().downcast_ref::<mers_lib::data::string::String>().unwrap().0.parse() {
|
|
|
|
Ok(v) => {
|
|
|
|
update.update(v);
|
|
|
|
Data::empty_tuple()
|
|
|
|
}
|
|
|
|
Err(e) => mers_lib::data::Data::new(mers_lib::data::string::String(e.to_string())),
|
|
|
|
};
|
|
|
|
es.send_event(GuiEvent::Refresh).unwrap();
|
|
|
|
o
|
|
|
|
}),
|
|
|
|
inner_statements: None,
|
|
|
|
})
|
|
|
|
}).add_var("set_idle_screen_side_text_2_format".to_owned(),{
|
|
|
|
let es = event_sender.clone();
|
|
|
|
let update = Arc::clone(&self.updated_idle_screen_side_text_2_format);
|
|
|
|
Data::new(mers_lib::data::function::Function {
|
|
|
|
info: Arc::new(mers_lib::info::Info::neverused()),
|
|
|
|
info_check: Arc::new(Mutex::new(mers_lib::info::Info::neverused())),
|
|
|
|
out: Arc::new(|a, _| {
|
|
|
|
if a.is_included_in(&mers_lib::data::string::StringT) {
|
|
|
|
Ok(Type::newm(vec![
|
|
|
|
Arc::new(mers_lib::data::tuple::TupleT(vec![])),
|
|
|
|
Arc::new(mers_lib::data::string::StringT),
|
|
|
|
]))
|
|
|
|
} else {
|
|
|
|
Err(format!("Can't call `set_idle_screen_side_text_2_format` with argument of type `{a}` (must be `String`).").into())
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
run: Arc::new(move |a, _| {
|
|
|
|
let a = a.get();
|
|
|
|
let o = match a.as_any().downcast_ref::<mers_lib::data::string::String>().unwrap().0.parse() {
|
|
|
|
Ok(v) => {
|
|
|
|
update.update(v);
|
|
|
|
Data::empty_tuple()
|
|
|
|
}
|
|
|
|
Err(e) => mers_lib::data::Data::new(mers_lib::data::string::String(e.to_string())),
|
|
|
|
};
|
|
|
|
es.send_event(GuiEvent::Refresh).unwrap();
|
|
|
|
o
|
|
|
|
}),
|
|
|
|
inner_statements: None,
|
|
|
|
})
|
|
|
|
})
|
|
|
|
// .add_type("Song".to_owned(), Ok(Arc::new(mers_lib::data::object::ObjectT(vec![
|
|
|
|
// ("id".to_owned(), Type::new(mers_lib::data::int::IntT)),
|
|
|
|
// ("title".to_owned(), Type::new(mers_lib::data::string::StringT)),
|
|
|
|
// ("album".to_owned(), Type::new(mers_lib::data::string::StringT)),
|
|
|
|
// ("artist".to_owned(), Type::new(mers_lib::data::string::StringT)),
|
|
|
|
// ]))))
|
|
|
|
}
|
|
|
|
|
2024-01-16 20:17:11 +01:00
|
|
|
pub fn run(gui_cfg: &mut GuiConfig, gui: &mut Gui, run: impl FnOnce(&Self) -> &OptFunc) {
|
|
|
|
{
|
|
|
|
let mut db = gui_cfg.merscfg.database.lock().unwrap();
|
|
|
|
let db = &mut db;
|
|
|
|
// prepare vars
|
2023-12-27 15:39:53 +01:00
|
|
|
*gui_cfg.merscfg.var_is_playing.write().unwrap() =
|
|
|
|
mers_lib::data::Data::new(mers_lib::data::bool::Bool(db.playing));
|
|
|
|
}
|
|
|
|
*gui_cfg.merscfg.var_window_size_in_pixels.write().unwrap() =
|
|
|
|
mers_lib::data::Data::new(mers_lib::data::tuple::Tuple(vec![
|
|
|
|
mers_lib::data::Data::new(mers_lib::data::int::Int(gui.size.x as _)),
|
|
|
|
mers_lib::data::Data::new(mers_lib::data::int::Int(gui.size.y as _)),
|
|
|
|
]));
|
|
|
|
*gui_cfg
|
|
|
|
.merscfg
|
|
|
|
.var_idle_screen_cover_aspect_ratio
|
|
|
|
.write()
|
|
|
|
.unwrap() = mers_lib::data::Data::new(mers_lib::data::float::Float(
|
|
|
|
gui.gui.c_idle_display.cover_aspect_ratio.value as _,
|
|
|
|
));
|
|
|
|
|
|
|
|
// run
|
|
|
|
run(&gui_cfg.merscfg).run();
|
|
|
|
|
2024-01-16 20:17:11 +01:00
|
|
|
loop {
|
|
|
|
if let Ok(a) = gui_cfg.merscfg.channel_gui_actions.1.try_recv() {
|
|
|
|
gui.exec_gui_action(GuiAction::SendToServer(a));
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-27 15:39:53 +01:00
|
|
|
// apply updates
|
|
|
|
|
|
|
|
match gui_cfg
|
|
|
|
.merscfg
|
|
|
|
.updated_playing_status
|
|
|
|
.load(std::sync::atomic::Ordering::Relaxed)
|
|
|
|
{
|
|
|
|
0 => {}
|
|
|
|
v => {
|
|
|
|
match v {
|
|
|
|
1 => gui.exec_gui_action(GuiAction::SendToServer(Command::Resume)),
|
|
|
|
2 => gui.exec_gui_action(GuiAction::SendToServer(Command::Pause)),
|
|
|
|
3 => gui.exec_gui_action(GuiAction::SendToServer(Command::Stop)),
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
gui_cfg
|
|
|
|
.merscfg
|
|
|
|
.updated_playing_status
|
|
|
|
.store(0, std::sync::atomic::Ordering::Relaxed);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
match gui_cfg
|
|
|
|
.merscfg
|
|
|
|
.updated_idle_status
|
|
|
|
.load(std::sync::atomic::Ordering::Relaxed)
|
|
|
|
{
|
|
|
|
0 => {}
|
|
|
|
v => {
|
|
|
|
match v {
|
|
|
|
1 => gui.gui.force_idle(),
|
|
|
|
2 => gui.gui.unidle(),
|
|
|
|
3 => gui.gui.not_idle(),
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
gui_cfg
|
|
|
|
.merscfg
|
|
|
|
.updated_idle_status
|
|
|
|
.store(0, std::sync::atomic::Ordering::Relaxed);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(maybe_rect) = gui_cfg.merscfg.updated_idle_screen_cover_pos.take_val() {
|
|
|
|
gui.gui.c_idle_display.cover_pos = maybe_rect;
|
|
|
|
}
|
|
|
|
if let Some(maybe_rect) = gui_cfg
|
|
|
|
.merscfg
|
|
|
|
.updated_idle_screen_artist_image_pos
|
|
|
|
.take_val()
|
|
|
|
{
|
|
|
|
gui.gui.c_idle_display.artist_image_pos = maybe_rect;
|
|
|
|
}
|
|
|
|
if let Some(maybe_rect) = gui_cfg.merscfg.updated_idle_screen_top_text_pos.take_val() {
|
|
|
|
gui.gui.c_idle_display.c_top_label.config_mut().pos = maybe_rect;
|
|
|
|
}
|
|
|
|
if let Some(maybe_rect) = gui_cfg
|
|
|
|
.merscfg
|
|
|
|
.updated_idle_screen_side_text_1_pos
|
|
|
|
.take_val()
|
|
|
|
{
|
|
|
|
gui.gui.c_idle_display.c_side1_label.config_mut().pos = maybe_rect;
|
|
|
|
}
|
|
|
|
if let Some(maybe_rect) = gui_cfg
|
|
|
|
.merscfg
|
|
|
|
.updated_idle_screen_side_text_2_pos
|
|
|
|
.take_val()
|
|
|
|
{
|
|
|
|
gui.gui.c_idle_display.c_side2_label.config_mut().pos = maybe_rect;
|
|
|
|
}
|
|
|
|
if let Some(maybe_rect) = gui_cfg
|
|
|
|
.merscfg
|
|
|
|
.updated_idle_screen_playback_buttons_pos
|
|
|
|
.take_val()
|
|
|
|
{
|
|
|
|
gui.gui.c_idle_display.c_buttons.config_mut().pos = maybe_rect;
|
|
|
|
gui.gui.c_idle_display.c_buttons_custom_pos = true;
|
|
|
|
}
|
|
|
|
if let Some(fmt) = gui_cfg.merscfg.updated_statusbar_text_format.take_val() {
|
|
|
|
gui_cfg.status_bar_text = fmt;
|
|
|
|
gui.gui.c_status_bar.force_reset_texts = true;
|
|
|
|
}
|
|
|
|
if let Some(fmt) = gui_cfg
|
|
|
|
.merscfg
|
|
|
|
.updated_idle_screen_top_text_format
|
|
|
|
.take_val()
|
|
|
|
{
|
|
|
|
gui_cfg.idle_top_text = fmt;
|
|
|
|
gui.gui.c_idle_display.force_reset_texts = true;
|
|
|
|
}
|
|
|
|
if let Some(fmt) = gui_cfg
|
|
|
|
.merscfg
|
|
|
|
.updated_idle_screen_side_text_1_format
|
|
|
|
.take_val()
|
|
|
|
{
|
|
|
|
gui_cfg.idle_side1_text = fmt;
|
|
|
|
gui.gui.c_idle_display.force_reset_texts = true;
|
|
|
|
}
|
|
|
|
if let Some(fmt) = gui_cfg
|
|
|
|
.merscfg
|
|
|
|
.updated_idle_screen_side_text_2_format
|
|
|
|
.take_val()
|
|
|
|
{
|
|
|
|
gui_cfg.idle_side2_text = fmt;
|
|
|
|
gui.gui.c_idle_display.force_reset_texts = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn load(
|
|
|
|
&mut self,
|
|
|
|
event_sender: Arc<UserEventSender<GuiEvent>>,
|
|
|
|
notif_sender: Sender<
|
|
|
|
Box<dyn FnOnce(&NotifOverlay) -> (Box<dyn GuiElem>, NotifInfo) + Send>,
|
|
|
|
>,
|
2024-01-20 01:19:19 +01:00
|
|
|
after_db_cmd: &Arc<Mutex<Option<Box<dyn FnMut(Command) + Send + Sync + 'static>>>>,
|
2023-12-27 15:39:53 +01:00
|
|
|
) -> std::io::Result<Result<Result<(), (String, Option<CheckError>)>, CheckError>> {
|
|
|
|
let src = mers_lib::prelude_compile::Source::new_from_file(self.source_file.clone())?;
|
2024-01-20 01:19:19 +01:00
|
|
|
Ok(self.load2(src, event_sender, notif_sender, after_db_cmd))
|
2023-12-27 15:39:53 +01:00
|
|
|
}
|
|
|
|
fn load2(
|
|
|
|
&mut self,
|
|
|
|
mut src: mers_lib::prelude_compile::Source,
|
|
|
|
event_sender: Arc<UserEventSender<GuiEvent>>,
|
|
|
|
notif_sender: Sender<
|
|
|
|
Box<dyn FnOnce(&NotifOverlay) -> (Box<dyn GuiElem>, NotifInfo) + Send>,
|
|
|
|
>,
|
2024-01-20 01:19:19 +01:00
|
|
|
after_db_cmd: &Arc<Mutex<Option<Box<dyn FnMut(Command) + Send + Sync + 'static>>>>,
|
2023-12-27 15:39:53 +01:00
|
|
|
) -> Result<Result<(), (String, Option<CheckError>)>, CheckError> {
|
|
|
|
let srca = Arc::new(src.clone());
|
|
|
|
let (mut i1, mut i2, mut i3) = self
|
|
|
|
.custom_globals(
|
|
|
|
mers_lib::prelude_extend_config::Config::new().bundle_std(),
|
2024-01-16 20:17:11 +01:00
|
|
|
&self.database,
|
2023-12-27 15:39:53 +01:00
|
|
|
event_sender,
|
|
|
|
notif_sender,
|
2024-01-20 01:19:19 +01:00
|
|
|
after_db_cmd,
|
2023-12-27 15:39:53 +01:00
|
|
|
)
|
|
|
|
.infos();
|
|
|
|
let compiled = mers_lib::prelude_compile::parse(&mut src, &srca)?
|
|
|
|
.compile(&mut i1, CompInfo::default())?;
|
|
|
|
let _ = compiled.check(&mut i3, None)?;
|
|
|
|
let out = compiled.run(&mut i2);
|
|
|
|
Ok(self.load3(out))
|
|
|
|
}
|
|
|
|
fn load3(&mut self, out: mers_lib::data::Data) -> Result<(), (String, Option<CheckError>)> {
|
|
|
|
if let Some(obj) = out
|
|
|
|
.get()
|
|
|
|
.as_any()
|
|
|
|
.downcast_ref::<mers_lib::data::object::Object>()
|
|
|
|
{
|
|
|
|
for (name, val) in obj.0.iter() {
|
|
|
|
let name = name.as_str();
|
|
|
|
match name {
|
|
|
|
"before_draw" => {
|
|
|
|
self.func_before_draw = OptFunc::some(check_handler(name, val)?);
|
|
|
|
}
|
|
|
|
"library_updated" => {
|
|
|
|
self.func_library_updated = OptFunc::some(check_handler(name, val)?);
|
|
|
|
}
|
|
|
|
"queue_updated" => {
|
|
|
|
self.func_queue_updated = OptFunc::some(check_handler(name, val)?);
|
|
|
|
}
|
|
|
|
name => {
|
|
|
|
eprintln!("merscfg: ignoring unexpected field named '{name}'.")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return Err((format!("mers config file must return an object!"), None));
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn check_handler(
|
|
|
|
name: &str,
|
|
|
|
val: &mers_lib::data::Data,
|
|
|
|
) -> Result<mers_lib::data::function::Function, (String, Option<CheckError>)> {
|
|
|
|
if let Some(func) = val
|
|
|
|
.get()
|
|
|
|
.as_any()
|
|
|
|
.downcast_ref::<mers_lib::data::function::Function>()
|
|
|
|
{
|
|
|
|
match func.check(&Type::empty_tuple()) {
|
|
|
|
Ok(_) => Ok(func.clone()),
|
|
|
|
Err(e) => Err((format!("Function '{name}' causes an error:"), Some(e))),
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Err((format!("Expected a function for field '{name}'!"), None))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn gen_set_pos_func(
|
|
|
|
name: &'static str,
|
|
|
|
es: Arc<UserEventSender<GuiEvent>>,
|
|
|
|
update: Arc<Updatable<Rectangle>>,
|
|
|
|
) -> Data {
|
|
|
|
Data::new(mers_lib::data::function::Function {
|
|
|
|
info: Arc::new(mers_lib::info::Info::neverused()),
|
|
|
|
info_check: Arc::new(Mutex::new(mers_lib::info::Info::neverused())),
|
|
|
|
out: Arc::new(move |a, _| {
|
|
|
|
if a.is_included_in(&mers_lib::data::Type::newm(vec![Arc::new(
|
|
|
|
mers_lib::data::tuple::TupleT(vec![
|
|
|
|
mers_lib::data::Type::new(mers_lib::data::float::FloatT),
|
|
|
|
mers_lib::data::Type::new(mers_lib::data::float::FloatT),
|
|
|
|
mers_lib::data::Type::new(mers_lib::data::float::FloatT),
|
|
|
|
mers_lib::data::Type::new(mers_lib::data::float::FloatT),
|
|
|
|
]),
|
|
|
|
)])) {
|
|
|
|
Ok(Type::empty_tuple())
|
|
|
|
} else {
|
|
|
|
Err(format!("Can't call `{name}` with argument of type `{a}` (must be `(Float, Float, Float, Float)`).").into())
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
run: Arc::new(move |a, _| {
|
|
|
|
let a = a.get();
|
|
|
|
let mut vals = a
|
|
|
|
.as_any()
|
|
|
|
.downcast_ref::<mers_lib::data::tuple::Tuple>()
|
|
|
|
.unwrap()
|
|
|
|
.0
|
|
|
|
.iter()
|
|
|
|
.map(|v| {
|
|
|
|
v.get()
|
|
|
|
.as_any()
|
|
|
|
.downcast_ref::<mers_lib::data::float::Float>()
|
|
|
|
.unwrap()
|
|
|
|
.0
|
|
|
|
});
|
|
|
|
update.update(Rectangle::from_tuples(
|
|
|
|
(vals.next().unwrap() as _, vals.next().unwrap() as _),
|
|
|
|
(vals.next().unwrap() as _, vals.next().unwrap() as _),
|
|
|
|
));
|
|
|
|
es.send_event(GuiEvent::Refresh).unwrap();
|
|
|
|
Data::empty_tuple()
|
|
|
|
}),
|
|
|
|
inner_statements: None,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Updatable<T> {
|
|
|
|
updated: AtomicBool,
|
|
|
|
value: Mutex<Option<T>>,
|
|
|
|
}
|
|
|
|
impl<T> Updatable<T> {
|
|
|
|
pub fn new() -> Self {
|
|
|
|
Self {
|
|
|
|
updated: AtomicBool::new(false),
|
|
|
|
value: Mutex::new(None),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pub fn update(&self, val: T) {
|
|
|
|
self.updated
|
|
|
|
.store(true, std::sync::atomic::Ordering::Relaxed);
|
|
|
|
*self.value.lock().unwrap() = Some(val);
|
|
|
|
}
|
|
|
|
pub fn take_val(&self) -> Option<T> {
|
|
|
|
if self.updated.load(std::sync::atomic::Ordering::Relaxed) {
|
|
|
|
self.updated
|
|
|
|
.store(false, std::sync::atomic::Ordering::Relaxed);
|
|
|
|
self.value.lock().unwrap().take()
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
impl<T> Updatable<T>
|
|
|
|
where
|
|
|
|
T: Default,
|
|
|
|
{
|
|
|
|
pub fn modify<R>(&self, func: impl FnOnce(&mut T) -> R) -> R {
|
|
|
|
self.updated
|
|
|
|
.store(true, std::sync::atomic::Ordering::Relaxed);
|
|
|
|
let mut val = self.value.lock().unwrap();
|
|
|
|
if val.is_none() {
|
|
|
|
*val = Some(Default::default());
|
|
|
|
}
|
|
|
|
func(val.as_mut().unwrap())
|
|
|
|
}
|
|
|
|
}
|