use crate::constants::{COLS, INFO_TEXT_MAX_ITEMS, ROWS}; use crate::state::{BoardState, SharedState, Turn}; use std::cell::Cell; use std::rc::Rc; use yew::prelude::*; pub struct Slot {} pub enum SlotMessage { Press(u8), } #[derive(Clone, PartialEq, Properties)] pub struct SlotProperties { idx: u8, state: Rc>, } impl Component for Slot { type Message = SlotMessage; type Properties = SlotProperties; fn create(_ctx: &Context) -> Self { Self {} } fn view(&self, ctx: &Context) -> Html { let idx = ctx.props().idx; let state = ctx.props().state.as_ref().get(); let idx_copy = idx; let onclick = ctx.link().callback(move |_| SlotMessage::Press(idx_copy)); let col = idx % COLS; let row = idx / COLS; html! { } } fn update(&mut self, ctx: &Context, msg: Self::Message) -> bool { match msg { SlotMessage::Press(idx) => { // notify Wrapper with message let msg = WrapperMsg::Pressed(idx); if let Some(p) = ctx.link().get_parent() { p.clone().downcast::().send_message(msg); } } } true } } pub struct Wrapper {} pub enum WrapperMsg { Pressed(u8), } impl Component for Wrapper { type Message = WrapperMsg; type Properties = (); fn create(_ctx: &Context) -> Self { Self {} } fn view(&self, ctx: &Context) -> Html { let (shared, _) = ctx .link() .context::(Callback::noop()) .expect("state to be set"); html! {
// wrapper } } fn update(&mut self, ctx: &Context, msg: Self::Message) -> bool { let (shared, _) = ctx .link() .context::(Callback::noop()) .expect("state to be set"); let window = web_sys::window().expect("no window exists"); let document = window.document().expect("window should have a document"); match msg { WrapperMsg::Pressed(idx) => { let mut bottom_idx = idx; let mut placed = false; let current_player = shared.turn.get(); // check if clicked on empty slot if shared.board[idx as usize].get() == BoardState::Empty { // get bottom-most empty slot while bottom_idx + COLS < ROWS * COLS && shared.board[(bottom_idx + COLS) as usize].get() == BoardState::Empty { bottom_idx += COLS; } // apply current player's color to bottom-most empty slot shared.board[bottom_idx as usize].replace(match shared.turn.get() { Turn::CyanPlayer => BoardState::Cyan, Turn::MagentaPlayer => BoardState::Magenta, }); let current_board_state = shared.board[bottom_idx as usize].get(); // swap turn shared.turn.replace(match shared.turn.get() { Turn::CyanPlayer => Turn::MagentaPlayer, Turn::MagentaPlayer => Turn::CyanPlayer, }); // get handle to slot if let Some(slot) = shared.slot_refs[bottom_idx as usize].cast::() { // set slot info slot.set_class_name(&format!( "slot {} r{} c{}", current_board_state, bottom_idx / COLS, bottom_idx % COLS )); } placed = true; } // DEBUG //log::info!("{} is {:?}", idx, shared.board[idx as usize].get()); // DEBUG //log::info!("{}", &output_str); // info text below the grid if let Some(info_text) = shared.info_text_ref[0].cast::() { let height = info_text.client_height(); // create the new text to be appended in the output let p = document .create_element("p") .expect("document should be able to create

"); let output_str = match placed { true => format!("{} placed into slot {}", current_player, bottom_idx), false => "Invalid place to insert".into(), }; p.set_text_content(Some(&output_str)); // DEBUG //log::info!( // "pre: scroll top is {}, scroll height is {}", // info_text.scroll_top(), // info_text.scroll_height() //); // check if scrolled to top let at_top: bool = info_text.scroll_top() <= height - info_text.scroll_height(); // append text to output info_text .append_with_node_1(&p) .expect("should be able to append to info_text"); while info_text.child_element_count() > INFO_TEXT_MAX_ITEMS { info_text .remove_child(&info_text.first_child().unwrap()) .expect("should be able to limit items in info_text"); } // DEBUG //log::info!("at_top is {}", if at_top { "true" } else { "false" }); // scroll to top only if at top if at_top { info_text.set_scroll_top(height - info_text.scroll_height()); } // DEBUG //log::info!( // "post: scroll top is {}, scroll height is {}", // info_text.scroll_top(), // info_text.scroll_height() //); } else { log::warn!("Failed to get bottom \"info_text\""); } // info text right of the grid if let Some(info_text) = shared.info_text_ref[1].cast::() { let height = info_text.client_height(); // create the new text to be appended in the output let p = document .create_element("p") .expect("document should be able to create

"); p.set_text_content(Some(&format!("It is {}'s turn", shared.turn.get()))); // check if scrolled to top let at_top: bool = info_text.scroll_top() <= height - info_text.scroll_height(); // append text to output info_text .append_with_node_1(&p) .expect("should be able to append to info_text"); while info_text.child_element_count() > INFO_TEXT_MAX_ITEMS { info_text .remove_child(&info_text.first_child().unwrap()) .expect("should be able to limit items in info_text"); } // scroll to top only if at top if at_top { info_text.set_scroll_top(height - info_text.scroll_height()); } } else { log::warn!("Failed to get side \"info_text\""); } } // WrapperMsg::Pressed(idx) => } // match (msg) true } } #[derive(Clone, Debug, PartialEq, Eq)] pub struct InfoText {} #[derive(Copy, Clone, Debug, PartialEq, Eq, Properties)] pub struct InfoTextProperties { id: usize, } impl Component for InfoText { type Message = (); type Properties = InfoTextProperties; fn create(_ctx: &Context) -> Self { Self {} } fn view(&self, ctx: &Context) -> Html { let (shared, _) = ctx .link() .context::(Callback::noop()) .expect("state to be set"); html! {

{ if ctx.props().id == 1 { "It is CyanPlayer's turn" } else { "Hello" } }
} } }