use iced::widget::{button, column, container, row, slider, text};
use iced::{Alignment, Application, Command, Element, Length, Point, Subscription};
-use rustc_hash::{FxHashMap, FxHashSet};
+use rustc_hash::FxHashSet;
use std::time::{Duration, Instant};
mod map;
fn view(&self) -> Element<Message> {
let bpm = self.bpm;
let controls = view_controls(self.is_playing, bpm);
- let map = self.map.view().map(Message::Map);
+ let map = row![
+ self.map.view().map(Message::Map),
+ self.mask.view().map(Message::Mask)
+ ]
+ .width(Length::Fill)
+ .spacing(40);
let content = column![controls, map,];
-use iced::mouse::{self, Button::Left, Event::ButtonPressed};
-use iced::widget::canvas;
use iced::widget::canvas::event::{self, Event};
use iced::widget::canvas::{Cache, Canvas, Cursor, Geometry, Path};
+use iced::{
+ mouse::{self, Button::Left, Event::ButtonPressed},
+ widget::canvas::Program,
+};
use iced::{Color, Element, Length, Point, Rectangle, Size, Theme};
use super::*;
}
}
-impl canvas::Program<Message> for Map {
+impl Program<Message> for Map {
type State = bool;
fn update(
-use super::*;
+use iced::mouse::Interaction;
+use iced::widget::canvas::event::{self, Event};
+use iced::widget::canvas::{Cache, Canvas, Cursor, Geometry, Path};
+use iced::{
+ mouse::{Button::Left, Event::ButtonPressed},
+ widget::canvas::Program,
+};
+use iced::{Color, Element, Length, Point, Rectangle, Size, Theme};
+use crate::{Cell, CellMap};
+use itertools::Itertools;
use rustc_hash::FxHashMap;
-pub type Note = usize;
+#[derive(Debug, Clone)]
+pub enum Message {
+ Check(Cell),
+ Uncheck(Cell),
+ Tick(CellMap),
+}
+
+#[derive(Default, Debug, Clone, Copy, Eq, PartialEq, Hash)]
+pub enum State {
+ #[default]
+ Off,
+ On,
+}
-#[derive(Clone, Default)]
+#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct Note {
+ value: usize,
+ action: State,
+}
+
+#[derive(Default, Debug)]
pub struct Mask {
cells: FxHashMap<Cell, Note>,
+ mask_cache: Cache,
}
impl Mask {
let _ = self.cells.remove(&cell);
}
- pub fn iter(&self) -> impl Iterator<Item = (&Cell, &Note)> {
- self.cells.iter()
+ pub fn get_note(&self, cell: &Cell) -> Option<&Note> {
+ self.cells.get(cell)
+ }
+
+ pub fn cells(&self) -> impl Iterator<Item = &Cell> {
+ self.cells.keys()
+ }
+
+ pub fn update(&mut self, message: Message) {
+ match message {
+ Message::Check(cell) => self.check(cell),
+ Message::Uncheck(cell) => self.uncheck(cell),
+ Message::Tick(life) => {}
+ }
+ }
+
+ pub fn view(&self) -> Element<Message> {
+ Canvas::new(self)
+ .width(Length::Fixed(Cell::SIZE as f32 * 24.0))
+ .height(Length::Fixed(Cell::SIZE as f32 * 24.0))
+ .into()
+ }
+}
+
+impl Program<Message> for Mask {
+ type State = bool;
+ fn draw(
+ &self,
+ _state: &Self::State,
+ _theme: &Theme,
+ bounds: Rectangle,
+ _cursor: Cursor,
+ ) -> Vec<Geometry> {
+ vec![self.mask_cache.draw(bounds.size(), |frame| {
+ let background = Path::rectangle(Point::ORIGIN, frame.size());
+ frame.fill(&background, Color::from_rgb8(0x26, 0x26, 0x2E));
+
+ frame.with_save(|frame| {
+ frame.scale(Cell::SIZE as f32);
+
+ (0..24)
+ .cartesian_product(0..24)
+ .filter(|x| self.contains(&Cell { i: x.1, j: x.0 }))
+ .for_each(|x| {
+ frame.fill_rectangle(
+ Point::new(x.0 as f32, x.1 as f32),
+ Size::UNIT,
+ Color::WHITE,
+ );
+ })
+ });
+ })]
+ }
+
+ fn update(
+ &self,
+ _state: &mut Self::State,
+ event: Event,
+ bounds: Rectangle,
+ cursor: Cursor,
+ ) -> (event::Status, Option<Message>) {
+ if let Some(position) = cursor.position_in(&bounds) {
+ if let Event::Mouse(ButtonPressed(Left)) = event {
+ let cell = Cell::at(position);
+ return (
+ event::Status::Captured,
+ if self.contains(&cell) {
+ Some(Message::Uncheck(cell))
+ } else {
+ Some(Message::Check(cell))
+ },
+ );
+ }
+ }
+
+ (event::Status::Ignored, None)
+ }
+
+ fn mouse_interaction(
+ &self,
+ _state: &Self::State,
+ _bounds: Rectangle,
+ _cursor: Cursor,
+ ) -> Interaction {
+ Interaction::default()
}
}