#![no_std]
#[macro_use]
extern crate alloc;
#[macro_use]
extern crate sys;
extern crate playdate_menu as menu;
use core::ffi::*;
use alloc::boxed::Box;
use sys::ffi::*;
use menu::*;
const INITIAL_X: u32 = LCD_COLUMNS / 2;
const INITIAL_Y: u32 = (LCD_ROWS - TEXT_HEIGHT) / 2;
const TEXT_HEIGHT: u32 = 16;
const MAX_CLICKS: u32 = 3;
struct State {
first: Option<SimpleMenuItem<u32>>,
second: Option<CheckMenuItem<Option<&'static Self>>>,
third: Option<OptionsMenuItem>,
}
impl State {
const fn new() -> Self {
Self { first: None,
second: None,
third: None }
}
fn update(&mut self) -> Option<()> {
if let Some((_item, value)) = self.first
.as_ref()
.map(|item| item.get_userdata().map(|val| (item, val)))
.flatten()
{
if *value >= MAX_CLICKS {
let item = self.first.take().unwrap();
let value = item.remove();
println!("First item removed on click {value:?}");
}
}
if let Some((_item, value)) = self.third.as_ref().map(|item| (item, item.selected_option())) {
if value != 0 {
self.third.take();
}
}
const LABEL_DEF: &str = "Use System Menu\0";
let cstr = CStr::from_bytes_with_nul(LABEL_DEF.as_bytes()).unwrap();
unsafe {
let graphics = (*sys::API).graphics;
(*graphics).clear?(LCDSolidColor::kColorWhite as LCDColor);
let text_width = (*graphics).getTextWidth?(
core::ptr::null_mut(),
cstr.as_ptr() as *const _,
LABEL_DEF.len(),
PDStringEncoding::kUTF8Encoding,
0,
);
(*graphics).drawText?(
cstr.as_ptr() as *const _,
LABEL_DEF.len(),
PDStringEncoding::kUTF8Encoding,
INITIAL_X as c_int - text_width / 2,
INITIAL_Y.try_into().unwrap(),
);
}
Some(())
}
fn event(&'static mut self, event: PDSystemEvent) -> Option<()> {
match event {
PDSystemEvent::kEventInit => unsafe {
(*(*sys::API).display).setRefreshRate?(20.0);
fn callback(userdata: &mut u32) {
println!("Check menu item clicked {userdata} times.");
*userdata += 1;
}
self.first = SimpleMenuItem::new("Check Me", Some(callback), 0).unwrap().into();
fn change_first(state: &mut Option<&'static State>) {
if let Some(state) = state {
if let Some(item) = state.first.as_ref() {
if let Some(value) = item.get_userdata() {
item.set_title(format!("Clicked: {value}/{MAX_CLICKS}")).unwrap();
} else {
println!("No user-data")
}
} else {
println!("No menu item")
}
} else {
println!("No state")
}
}
self.second = CheckMenuItem::new("Change^", false, Some(change_first), None).unwrap()
.into();
let second = self.second.as_ref().unwrap();
second.set_userdata(Some(self));
self.third = OptionsMenuItem::new("Remove?", ["No", "Yes"], None, ()).unwrap()
.into();
},
_ => {},
}
Some(())
}
}
#[no_mangle]
pub extern "C" fn eventHandlerShim(api: *const PlaydateAPI, event: PDSystemEvent, _arg: u32) -> c_int {
static mut STATE: Option<Box<State>> = None;
match event {
PDSystemEvent::kEventInit => unsafe {
sys::API = api;
if STATE.is_none() {
STATE = Some(Box::new(State::new()));
}
let state = STATE.as_mut().unwrap().as_mut() as *mut State;
let f = (*(*api).system).setUpdateCallback.expect("setUpdateCallback");
f(Some(on_update), state.cast());
},
_ => {},
}
if let Some(state) = unsafe { STATE.as_mut() } {
state.event(event).and(Some(0)).unwrap_or(1)
} else {
1
}
}
unsafe extern "C" fn on_update(state: *mut c_void) -> i32 {
let ptr: *mut State = state.cast();
let state = ptr.as_mut().expect("missed state");
state.update().and(Some(1)).unwrap_or_default()
}
ll_symbols!();