From ba3eca603bcc78728410e71fab7009085bc25441 Mon Sep 17 00:00:00 2001 From: Emilia Date: Mon, 22 May 2023 11:26:08 -0400 Subject: [PATCH] Complete port to yew This is the biggest commit of all time. Yew requires so much cloning smh As a side note, if you drag to select the plate a lot, it uses a lot of CPU resources. --- {src/components => assets/css}/global.css | 0 .../plates => assets/css}/plate.css | 0 .../plates => assets/css}/plate_container.css | 0 index.html | 10 ++ src/components/main_window.rs | 25 ++- src/components/plates/destination_plate.rs | 54 +++++- src/components/plates/plate_container.rs | 32 ++-- src/components/plates/source_plate.rs | 139 +++++++++++----- src/components/plates/source_plate_old.rs | 157 ++++++++++++++++++ src/components/transfer_menu.rs | 39 ++--- src/components/tree.rs | 15 +- src/lib.rs | 13 +- src/main.rs | 3 +- 13 files changed, 368 insertions(+), 119 deletions(-) rename {src/components => assets/css}/global.css (100%) rename {src/components/plates => assets/css}/plate.css (100%) rename {src/components/plates => assets/css}/plate_container.css (100%) create mode 100644 index.html create mode 100644 src/components/plates/source_plate_old.rs diff --git a/src/components/global.css b/assets/css/global.css similarity index 100% rename from src/components/global.css rename to assets/css/global.css diff --git a/src/components/plates/plate.css b/assets/css/plate.css similarity index 100% rename from src/components/plates/plate.css rename to assets/css/plate.css diff --git a/src/components/plates/plate_container.css b/assets/css/plate_container.css similarity index 100% rename from src/components/plates/plate_container.css rename to assets/css/plate_container.css diff --git a/index.html b/index.html new file mode 100644 index 0000000..db6c305 --- /dev/null +++ b/index.html @@ -0,0 +1,10 @@ + + + + + + + + Yew App + + diff --git a/src/components/main_window.rs b/src/components/main_window.rs index 9eb47f1..0624f1c 100644 --- a/src/components/main_window.rs +++ b/src/components/main_window.rs @@ -1,22 +1,17 @@ #![allow(non_snake_case)] -use dioxus::prelude::*; +use yew::prelude::*; use super::plates::plate_container::PlateContainer; use super::tree::Tree; use super::transfer_menu::TransferMenu; -static STYLE: &'static str = include_str!("global.css"); -pub fn MainWindow(cx: Scope) -> Element { - cx.render(rsx! { - style { STYLE }, - div { - class: "main_container", - Tree {}, - TransferMenu {}, - PlateContainer { - source_dims: (24,16), - destination_dims: (24,16) - } - } - }) +#[function_component] +pub fn MainWindow() -> Html { + html!{ +
+ + + +
+ } } diff --git a/src/components/plates/destination_plate.rs b/src/components/plates/destination_plate.rs index ccc76a1..f168d43 100644 --- a/src/components/plates/destination_plate.rs +++ b/src/components/plates/destination_plate.rs @@ -1,8 +1,35 @@ #![allow(non_snake_case)] -use dioxus::prelude::*; +use yew::prelude::*; -#[inline_props] -pub fn DestinationPlate(cx: Scope, width: u8, height: u8) -> Element { +#[derive(Properties, PartialEq)] +pub struct DestinationPlateProps { + pub width: u8, + pub height: u8, +} + +#[function_component] +pub fn DestinationPlate(props: &DestinationPlateProps) -> Html { + let rows = (1..=props.height).map(|i| { + let row = (1..=props.width).map(|j| { + html! { + + } + }).collect::(); + html! { + + { row } + + } + }).collect::(); + + html! { +
+ + { rows } +
+
+ } + /* cx.render(rsx! { div { class: "dest_plate", @@ -18,15 +45,29 @@ pub fn DestinationPlate(cx: Scope, width: u8, height: u8) -> Element { } } }) + */ } -#[inline_props] -fn DestPlateCell(cx: Scope, i: u8, j: u8, color: Option) -> Element { - let color_string = match color { +#[derive(Properties, PartialEq)] +pub struct DestPlateCellProps { + pub i: u8, + pub j: u8, + pub color: Option +} + +#[function_component] +fn DestPlateCell(props: &DestPlateCellProps) -> Html { + let color_string = match &props.color { Some(c) => c.clone(), None => "None".to_string(), }; + html! { + +
+ + } + /* cx.render(rsx! { td { class: "plate_cell", @@ -37,4 +78,5 @@ fn DestPlateCell(cx: Scope, i: u8, j: u8, color: Option) } } }) + */ } diff --git a/src/components/plates/plate_container.rs b/src/components/plates/plate_container.rs index d1b4009..9e5910f 100644 --- a/src/components/plates/plate_container.rs +++ b/src/components/plates/plate_container.rs @@ -1,20 +1,20 @@ #![allow(non_snake_case)] -use dioxus::prelude::*; -use super::source_plate::SourcePlate; +use yew::prelude::*; + use super::source_plate::SourcePlate; use super::destination_plate::DestinationPlate; -static STYLE: &'static str = include_str!("plate_container.css"); - -#[inline_props] -pub fn PlateContainer(cx: Scope, source_dims: (u8,u8), destination_dims: (u8,u8)) -> Element { - cx.render(rsx! { - style { STYLE } - div { - class: "plate_container", - SourcePlate {width: source_dims.0, - height: source_dims.1}, - DestinationPlate {width: destination_dims.0, - height: destination_dims.1} - } - }) +#[derive(Properties, PartialEq)] +pub struct PlateContainerProps { + pub source_dims: (u8,u8), + pub destination_dims: (u8,u8) +} + +#[function_component] +pub fn PlateContainer(props: &PlateContainerProps) -> Html { + html! { +
+ + +
+ } } diff --git a/src/components/plates/source_plate.rs b/src/components/plates/source_plate.rs index 181c513..5de1991 100644 --- a/src/components/plates/source_plate.rs +++ b/src/components/plates/source_plate.rs @@ -1,62 +1,120 @@ #![allow(non_snake_case)] -use dioxus::prelude::*; -static STYLE: &'static str = include_str!("plate.css"); +use yew::prelude::*; -#[derive(PartialEq, Props)] +#[derive(PartialEq, Properties)] pub struct SourcePlateProps { - width: u8, - height: u8, -} -struct SelectionState { - m_start: Option<(u8, u8)>, - m_end: Option<(u8, u8)>, - m_stat: bool, + pub width: u8, + pub height: u8, } -pub fn SourcePlate(cx: Scope) -> Element { - use_shared_state_provider(cx, || SelectionState { +#[function_component] +pub fn SourcePlate(props: &SourcePlateProps) -> Html { + /* + let selection_state = use_state_eq(|| SelectionState{ m_start: None, m_end: None, - m_stat: false, + m_stat: false }); + */ - cx.render(rsx! { - div{ - class: "source_plate", - style { STYLE } - table { - draggable: "false", - for i in 1..=cx.props.height { - tr { - draggable: "false", - for j in 1..=cx.props.width { - SourcePlateCell {i: i, j: j} - } - } - }, + let m_start_handle: UseStateHandle> = use_state_eq(|| None); + let m_end_handle: UseStateHandle> = use_state_eq(|| None); + let m_stat_handle: UseStateHandle = use_state_eq(|| false); + let m_stat_handle2 = m_stat_handle.clone(); + let m_start = m_start_handle.clone(); + let m_end = m_end_handle.clone(); + + let mouse_callback = Callback::from(move |(i,j,t)| { + match t { + MouseEventType::MOUSEDOWN => { + m_start_handle.set(Some((i,j))); + m_end_handle.set(None); + m_stat_handle.set(true); + }, + MouseEventType::MOUSEENTER => { + if *m_stat_handle { + m_end_handle.set(Some((i,j))) + } } } - }) + }); + let mouseup_callback = Callback::from(move |_: MouseEvent| { + m_stat_handle2.set(false); + }); + let mouseleave_callback = Callback::clone(&mouseup_callback); + + let rows = (1..=props.height).map(|i| { + let row = (1..=props.width).map(|j| { + html! { + + } + }).collect::(); + html! { + + { row } + + } + }).collect::(); + + html! { +
+ + { rows } +
+
+ } } -#[inline_props] -fn SourcePlateCell(cx: Scope, i: u8, j: u8, color: Option) -> Element { - let selection_state = use_shared_state::(cx).unwrap(); - let selected = in_rect( - selection_state.read().m_start, - selection_state.read().m_end, - (*i, *j), - ); - let selected_class = match selected { - true => "current_select", - false => "", +#[derive(PartialEq, Properties)] +pub struct SourcePlateCellProps { + i: u8, + j: u8, + selected: bool, + mouse: Callback<(u8,u8, MouseEventType)>, + color: Option +} +#[derive(Debug)] +pub enum MouseEventType { + MOUSEDOWN, + MOUSEENTER +} + +#[function_component] +fn SourcePlateCell(props: &SourcePlateCellProps) -> Html { + let selected_class = match props.selected { + true => Some("current_select"), + false => None, }; - let color_string = match color { + let color_string = match &props.color { Some(c) => c.clone(), None => "None".to_string(), }; + let mouse = Callback::clone(&props.mouse); + let mouse2 = Callback::clone(&props.mouse); + let (i,j) = (props.i.clone(), props.j.clone()); + html! { + +
+ + } + + /* cx.render(rsx! { td { class: "plate_cell {selected_class}", @@ -83,6 +141,7 @@ fn SourcePlateCell(cx: Scope, i: u8, j: u8, color: Option, corner2: Option<(u8, u8)>, pt: (u8, u8)) -> bool { diff --git a/src/components/plates/source_plate_old.rs b/src/components/plates/source_plate_old.rs new file mode 100644 index 0000000..3de68e1 --- /dev/null +++ b/src/components/plates/source_plate_old.rs @@ -0,0 +1,157 @@ +#![allow(non_snake_case)] +use yew::prelude::*; + +#[derive(PartialEq, Properties)] +pub struct SourcePlateProps { + width: u8, + height: u8, +} +struct SelectionState { + m_start: Option<(u8, u8)>, + m_end: Option<(u8, u8)>, + m_stat: bool, +} + +pub fn SourcePlate(props: &SourcePlateProps) -> Html { + use_shared_state_provider(cx, || SelectionState { + m_start: None, + m_end: None, + m_stat: false, + }); + + cx.render(rsx! { + div{ + class: "source_plate", + style { STYLE } + table { + draggable: "false", + for i in 1..=cx.props.height { + tr { + draggable: "false", + for j in 1..=cx.props.width { + SourcePlateCell {i: i, j: j} + } + } + }, + } + } + }) +} + +#[derive(PartialEq, Properties)] +pub struct SourcePlateCellProps { + i: u8, + j: u8, + color: Option +} + +fn SourcePlateCell(props: &SourcePlateCe) -> Element { + let selection_state = use_shared_state::(cx).unwrap(); + let selected = in_rect( + selection_state.read().m_start, + selection_state.read().m_end, + (*i, *j), + ); + let selected_class = match selected { + true => "current_select", + false => "", + }; + let color_string = match color { + Some(c) => c.clone(), + None => "None".to_string(), + }; + + cx.render(rsx! { + td { + class: "plate_cell {selected_class}", + draggable: "false", + style: "background: {color_string}", + onmousedown: move |_| { + selection_state.write().m_start = Some((*i,*j)); + selection_state.write().m_end = None; + selection_state.write().m_stat = true; + }, + onmouseenter: move |me: MouseEvent| { + if me.data.held_buttons().is_empty() { + selection_state.write().m_stat = false; + } + if selection_state.read().m_stat { + selection_state.write().m_end = Some((*i,*j)) + } + }, + onmouseup: move |_| { + selection_state.write().m_stat = false + }, + div { + class: "plate_cell_inner" + } + } + }) +} + +fn in_rect(corner1: Option<(u8, u8)>, corner2: Option<(u8, u8)>, pt: (u8, u8)) -> bool { + if let (Some(c1), Some(c2)) = (corner1, corner2) { + return pt.0 <= u8::max(c1.0, c2.0) + && pt.0 >= u8::min(c1.0, c2.0) + && pt.1 <= u8::max(c1.1, c2.1) + && pt.1 >= u8::min(c1.1, c2.1); + } else { + return false; + } +} + +#[cfg(test)] +mod tests { + use super::in_rect; + + // in_rect tests + #[test] + fn test_in_rect1() { + // Test in center of rect + let c1 = (1, 1); + let c2 = (10, 10); + let pt = (5, 5); + assert!(in_rect(Some(c1), Some(c2), pt)); + // Order of the corners should not matter: + assert!(in_rect(Some(c2), Some(c1), pt)); + } + + #[test] + fn test_in_rect2() { + // Test on top/bottom edges of rect + let c1 = (1, 1); + let c2 = (10, 10); + let pt1 = (1, 5); + let pt2 = (10, 5); + assert!(in_rect(Some(c1), Some(c2), pt1)); + assert!(in_rect(Some(c1), Some(c2), pt2)); + // Order of the corners should not matter: + assert!(in_rect(Some(c2), Some(c1), pt1)); + assert!(in_rect(Some(c2), Some(c1), pt2)); + } + + #[test] + fn test_in_rect3() { + // Test on left/right edges of rect + let c1 = (1, 1); + let c2 = (10, 10); + let pt1 = (5, 1); + let pt2 = (5, 10); + assert!(in_rect(Some(c1), Some(c2), pt1)); + assert!(in_rect(Some(c1), Some(c2), pt2)); + // Order of the corners should not matter: + assert!(in_rect(Some(c2), Some(c1), pt1)); + assert!(in_rect(Some(c2), Some(c1), pt2)); + } + + #[test] + fn test_in_rect4() { + // Test cases that should fail + let c1 = (1, 1); + let c2 = (10, 10); + let pt1 = (0, 0); + let pt2 = (15, 15); + assert_eq!(false, in_rect(Some(c1), Some(c2), pt1)); + assert_eq!(false, in_rect(Some(c1), Some(c2), pt2)); + } +} diff --git a/src/components/transfer_menu.rs b/src/components/transfer_menu.rs index abfb7c9..e8e55b1 100644 --- a/src/components/transfer_menu.rs +++ b/src/components/transfer_menu.rs @@ -1,33 +1,20 @@ #![allow(non_snake_case)] -use dioxus::prelude::*; +use yew::prelude::*; use regex::Regex; use lazy_static::lazy_static; -#[inline_props] -pub fn TransferMenu(cx: Scope) -> Element { - cx.render(rsx! { - div { - class: "transfer_menu", - form{ - label { - r#for: "src_region", - "Source Region:" - }, - input { - r#type: "text", - name: "src_region", - }, - label { - r#for: "dest_region", - "Destination Region:" - }, - input { - r#type: "text", - name: "dest_region", - } - } - } - }) +#[function_component] +pub fn TransferMenu() -> Html { + html! { +
+
+ + + + +
+
+ } } #[derive(PartialEq, Eq, Debug)] diff --git a/src/components/tree.rs b/src/components/tree.rs index a70acad..6a5b349 100644 --- a/src/components/tree.rs +++ b/src/components/tree.rs @@ -1,11 +1,10 @@ #![allow(non_snake_case)] -use dioxus::prelude::*; +use yew::prelude::*; -#[inline_props] -pub fn Tree(cx: Scope) -> Element { - cx.render(rsx! { - div { - class: "tree", - } - }) +#[function_component] +pub fn Tree() -> Html { + html! { +
+
+ } } diff --git a/src/lib.rs b/src/lib.rs index e6d7195..51fc459 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,18 +2,17 @@ mod components; mod data; +use yew::prelude::*; use components::main_window::MainWindow; -use dioxus::prelude::*; -use fermi::*; #[cfg(debug_assertions)] use data::*; -pub fn App(cx: Scope) -> Element { - use_init_atom_root(cx); - cx.render(rsx! { - MainWindow {} - }) +#[function_component] +pub fn App() -> Html { + html! { + + } } #[cfg(debug_assertions)] diff --git a/src/main.rs b/src/main.rs index 0fe845a..ba179e7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,9 +3,10 @@ use plate_tool::plate_test; use plate_tool::App; use wasm_logger; +use yew::prelude::*; fn main() { wasm_logger::init(wasm_logger::Config::default()); - dioxus_web::launch(App); + yew::Renderer::::new().render(); //plate_test(); }