From a8e516c535959b9bfededb48f0a9a3b37da4fe4a Mon Sep 17 00:00:00 2001 From: Stephen Seo Date: Tue, 1 Mar 2022 16:02:59 +0900 Subject: [PATCH] Added output-text-box, connected front-end items More work was needed to have separate components of the front-end to communicate with each other. Also added an output-text-box for informative messages. --- front_end/Cargo.lock | 14 +++ front_end/Cargo.toml | 4 + front_end/index.html | 12 ++ front_end/src/main.rs | 269 ++++++++++++++++++++++++++++++---------- spreadsheets/.gitignore | 1 + 5 files changed, 232 insertions(+), 68 deletions(-) create mode 100644 spreadsheets/.gitignore diff --git a/front_end/Cargo.lock b/front_end/Cargo.lock index 0fd8bf2..05c9009 100644 --- a/front_end/Cargo.lock +++ b/front_end/Cargo.lock @@ -40,6 +40,9 @@ dependencies = [ name = "four_line_dropper_frontend" version = "0.1.0" dependencies = [ + "log", + "wasm-logger", + "web-sys", "yew", ] @@ -397,6 +400,17 @@ version = "0.2.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2" +[[package]] +name = "wasm-logger" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "074649a66bb306c8f2068c9016395fa65d8e08d2affcbf95acf3c24c3ab19718" +dependencies = [ + "log", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "web-sys" version = "0.3.56" diff --git a/front_end/Cargo.toml b/front_end/Cargo.toml index 2c42279..9070eaa 100644 --- a/front_end/Cargo.toml +++ b/front_end/Cargo.toml @@ -7,3 +7,7 @@ edition = "2021" [dependencies] yew = "0.19" +log = "0.4.6" +wasm-logger = "0.2.0" +web-sys = { version = "0.3.56", features = ["HtmlParagraphElement", "HtmlBrElement"] } + diff --git a/front_end/index.html b/front_end/index.html index f05bea6..16fc536 100644 --- a/front_end/index.html +++ b/front_end/index.html @@ -11,6 +11,18 @@ grid-auto-rows: 50px; border: 0px; } + div.info_text_wrapper { + grid-row: 9; + grid-column: 1 / 8; + } + div.info_text { + background-color: #DDD; + width: fill; + height: 400px; + overflow-x: hidden; + overflow-y: auto; + word-wrap: break-word; + } button.slot { width: 50px; height: 50px; diff --git a/front_end/src/main.rs b/front_end/src/main.rs index abbdd35..b86a99c 100644 --- a/front_end/src/main.rs +++ b/front_end/src/main.rs @@ -1,10 +1,14 @@ -use std::cell::Cell; +use std::cell::{Cell, RefCell}; +use std::collections::VecDeque; use std::rc::Rc; use yew::prelude::*; //const ROWS: u8 = 8; const COLS: u8 = 7; +const INFO_TEXT_MAX_ITEMS: u32 = 100; +const INFO_TEXT_HEIGHT: i32 = 400; + #[derive(Copy, Clone, Debug, PartialEq, Eq)] enum BoardState { Empty, @@ -19,8 +23,41 @@ impl Default for BoardState { } #[derive(Clone, Debug, PartialEq, Eq)] +struct MessageBus { + queued: VecDeque, +} + +impl Default for MessageBus { + fn default() -> Self { + Self { + queued: VecDeque::new(), + } + } +} + +impl MessageBus { + pub fn get_next_msg(&mut self) -> Option { + self.queued.pop_front() + } + + pub fn push_msg(&mut self, msg: String) -> Result<(), String> { + self.queued.push_back(msg); + Ok(()) + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +enum Turn { + CyanPlayer, + MagentaPlayer, +} + +#[derive(Clone, Debug, PartialEq)] struct SharedState { board: [Rc>; 56], + bus: Rc>, + turn: Turn, + info_text_ref: NodeRef, } impl Default for SharedState { @@ -85,6 +122,9 @@ impl Default for SharedState { Rc::new(Cell::new(BoardState::default())), Rc::new(Cell::new(BoardState::default())), ], + bus: Rc::new(RefCell::new(MessageBus::default())), + turn: Turn::CyanPlayer, + info_text_ref: NodeRef::default(), } } } @@ -99,6 +139,7 @@ enum SlotMessage { struct SlotProperties { idx: u8, state: Rc>, + bus: Rc>, } impl Component for Slot { @@ -133,20 +174,21 @@ impl Component for Slot { .link() .context::(Callback::noop()) .expect("shared to be set"); - let value_at_idx = shared.board[idx as usize].get(); - match value_at_idx { - BoardState::Empty => { - shared.board[idx as usize].replace(BoardState::Cyan); - } - BoardState::Cyan => { - shared.board[idx as usize].replace(BoardState::Magenta); - } - BoardState::Magenta => { - shared.board[idx as usize].replace(BoardState::Empty); - } + + let result = shared.bus.borrow_mut().push_msg(format!("pressed {idx}")); + if let Err(e) = result { + log::error!("Error pushing msg to bus: {}", e); + } else { + log::info!("Pushed \"pressed {idx}\" msg to bus"); } } } + + // notify Wrapper with message + if let Some(p) = ctx.link().get_parent() { + p.clone().downcast::().send_message(()); + } + true } } @@ -168,65 +210,152 @@ impl Component for Wrapper { .expect("state to be set"); html! {
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
// wrapper } } + + fn rendered(&mut self, ctx: &Context, _first_render: bool) { + let (shared, _) = ctx + .link() + .context::(Callback::noop()) + .expect("state to be set"); + + loop { + if let Some(msg) = shared.bus.borrow_mut().get_next_msg() { + let split_str: Vec<&str> = msg.split_whitespace().collect(); + if split_str.len() == 2 { + if split_str[0] == "pressed" { + if let Ok(idx) = split_str[1].parse::() { + let output_str: String = format!("Got {idx} pressed."); + log::info!("{}", &output_str); + if let Some(info_text) = + shared.info_text_ref.cast::() + { + // create the new text to be appended in the output + let window = web_sys::window().expect("no window exists"); + let document = + window.document().expect("window should have a document"); + let p = document + .create_element("p") + .expect("document should be able to create

"); + p.set_text_content(Some(&output_str)); + + // check if scrolled to bottom + let at_bottom: bool = info_text.scroll_top() + INFO_TEXT_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 bottom only if at bottom + if at_bottom { + info_text.set_scroll_top(info_text.scroll_height()); + } + } else { + log::warn!("Failed to get \"info_text\""); + } + } + } + } + } else { + break; + } + } + } + + fn update(&mut self, _ctx: &Context, _msg: Self::Message) -> bool { + true + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +struct InfoText {} + +impl Component for InfoText { + type Message = (); + 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! { +

+ {"Hello"} +
+ } + } } #[function_component(App)] @@ -240,5 +369,9 @@ fn app() -> Html { } fn main() { + // setup logging to browser console + wasm_logger::init(wasm_logger::Config::default()); + + // start webapp yew::start_app::(); } diff --git a/spreadsheets/.gitignore b/spreadsheets/.gitignore new file mode 100644 index 0000000..181d655 --- /dev/null +++ b/spreadsheets/.gitignore @@ -0,0 +1 @@ +.~lock*