diff --git a/Cargo.toml b/Cargo.toml index ad6c2a6..c52b2f4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,13 +17,18 @@ simple-error = "0.3.1" wasm-bindgen = "0.2.95" wasm-logger = "0.2.0" web-sys = { version = "0.3.72", features = [ + "CssRule", + "CssRuleList", "CssStyleDeclaration", + "CssStyleSheet", "Document", "Element", "HtmlCollection", "HtmlDivElement", "Location", "MouseEvent", + "StyleSheet", + "StyleSheetList", "Touch", "TouchEvent", "TouchList", diff --git a/src/ui/buttons.rs b/src/ui/buttons.rs index c712aa4..396f155 100644 --- a/src/ui/buttons.rs +++ b/src/ui/buttons.rs @@ -1,5 +1,5 @@ use wasm_bindgen::prelude::*; -use web_sys::{window, HtmlElement, MouseEvent}; +use web_sys::{window, CssStyleSheet, HtmlElement, MouseEvent}; use crate::{ board::{get_empty_field_idx, get_shuffle_sequence}, @@ -145,6 +145,7 @@ fn get_optimal_solve_callback(size: usize) -> Closure { return; } + // TODO: Solver aborting after a certain size? let ids = BOARD.with_borrow(|b| b.board().indices2ids().clone()); match find_swap_order(&ids, size, size) { Ok(solve_sequence) => { @@ -228,3 +229,50 @@ fn apply_solve_sequence(solve_sequence: Vec<(usize, usize)>, interval: i32) { .unwrap(); finish_callback.forget(); } + +const ACTIVE_BUTTON_COLOR: &str = "rgb(233, 233, 237)"; +const INACTIVE_BUTTON_COLOR: &str = "rgb(208, 208, 215)"; + +pub(crate) fn deactivate_buttons() { + replace_in_button_style(&[ + ("pointer", "wait"), + (ACTIVE_BUTTON_COLOR, INACTIVE_BUTTON_COLOR), + ]); +} + +pub(crate) fn activate_buttons() { + replace_in_button_style(&[ + ("wait", "pointer"), + (INACTIVE_BUTTON_COLOR, ACTIVE_BUTTON_COLOR), + ]); +} + +fn replace_in_button_style(replaces: &[(&str, &str)]) { + let document = window().unwrap().document().unwrap(); + let style_sheets = document.style_sheets(); + + // The global style sheet is the first one. + if let Some(global_sheet) = style_sheets.get(0) { + let global_css = global_sheet.dyn_into::().unwrap(); + let rule_list = global_css.css_rules().unwrap(); + + // Iterate rules until we find the rule for the button class. + for i in 0..rule_list.length() { + match rule_list.get(i) { + Some(rule) if rule.css_text().contains("button {") => { + let existing_rule = rule.css_text(); + let updated_rule = replaces + .iter() + .fold(existing_rule, |rule, (old, new)| rule.replace(old, new)); + log::debug!("Updating to {updated_rule}"); + // Replace existing with updated rule in two steps. + // `replace` would be better but returns a `Promise` which we do not want to handle in this context. + global_css.delete_rule(i).unwrap(); + global_css.insert_rule(&updated_rule).unwrap(); + return; + } + _ => (), + } + } + } +} diff --git a/src/ui/mod.rs b/src/ui/mod.rs index ccfc0c0..140e3c3 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -1,3 +1,5 @@ +use buttons::{activate_buttons, deactivate_buttons}; + use crate::UI_LOCKED; pub(crate) mod board; @@ -23,6 +25,7 @@ pub(crate) fn lock_ui() -> bool { false } else { *locked = true; + deactivate_buttons(); log::debug!("Locked UI"); true } @@ -35,6 +38,7 @@ pub(crate) fn unlock_ui() { log::warn!("Should unlock UI which was not locked"); } else { *locked = false; + activate_buttons(); log::debug!("Unlocked UI"); } }) @@ -43,6 +47,3 @@ pub(crate) fn unlock_ui() { pub(crate) fn ui_locked() -> bool { UI_LOCKED.with(|locked| *locked.borrow()) } - -// TODO: Change button colors when locking UI. -// TODO: Solver not attempting / button greyed out at a certain size diff --git a/www/index.html b/www/index.html index bde8c4b..0a0b056 100644 --- a/www/index.html +++ b/www/index.html @@ -2,7 +2,7 @@ - +
diff --git a/www/style.css b/www/style.css index f2c2c40..6a1965f 100644 --- a/www/style.css +++ b/www/style.css @@ -11,10 +11,11 @@ button { border-radius: 20px; padding: 0.5rem; cursor: pointer; + background-color: rgb(233, 233, 237); } -.clickable:hover { - cursor: pointer; +button:hover { + background-color: rgb(208, 208, 215); } .content {