}
pub fn top_controls<'a>(is_playing: bool) -> Element<'a, Message> {
- let play_button =
- button(if is_playing { "pause" } else { "play" }).on_press(Message::TogglePlayback);
+ let play_button = row![
+ button(if is_playing { "stop" } else { "play" }).on_press(Message::TogglePlayback),
+ button("save")
+ .on_press(Message::Save)
+ .style(theme::Button::Positive),
+ button("clear")
+ .on_press(Message::Clear)
+ .style(theme::Button::Destructive),
+ ]
+ .width(Length::Fill)
+ .spacing(10);
let other_controls = row![button("quit")
.on_press(Message::Quit)
.style(theme::Button::Destructive),]
.width(Length::Fill)
- .align_items(Alignment::End)
.spacing(10);
row![play_button, other_controls]
}
pub fn bottom_controls<'a>(message: ControlMessage) -> Element<'a, Message> {
- column![map_section(&message), midi_section(&message),]
- .width(Length::Fill)
- .height(Length::Fill)
- .padding(10)
- .spacing(20)
- .align_items(Alignment::Center)
- .into()
+ column![
+ map_section(&message),
+ probability_section(message.info.probability).width(Length::Fixed(600.0)),
+ midi_section(&message),
+ ]
+ .width(Length::Fill)
+ .height(Length::Fill)
+ .padding(10)
+ .spacing(20)
+ .align_items(Alignment::Center)
+ .into()
}
fn map_section<'a>(message: &ControlMessage) -> Row<'a, Message> {
row![
map_buttons(),
- column![
- probability_section(message.info.probability),
- randomize_section(message.randomness)
- ]
- .padding(10)
- .spacing(10)
- .align_items(Alignment::Center)
+ randomize_section(message.randomness).width(Length::Fixed(500.0))
]
.spacing(10)
}
fn probability_section<'a>(p: f32) -> Row<'a, Message> {
row![
+ text("probability a cell gets triggered")
+ .style(theme::Text::Color(iced::Color::from_rgb8(0x60, 0x60, 0x60))),
slider(0.0..=100.0, p * 100.0, |x| {
Message::ProbChanged(x / 100.0)
}),
text(format!("{p}")),
- text("probability a cell gets triggered")
- .style(theme::Text::Color(iced::Color::from_rgb8(0x40, 0x40, 0x40)))
]
.spacing(10)
}
fn randomize_section<'a>(r: f32) -> Row<'a, Message> {
row![
+ text("percent of board to fill on randomize")
+ .style(theme::Text::Color(iced::Color::from_rgb8(0x60, 0x60, 0x60))),
slider(0.0..=100.0, r * 100.0, |x| {
Message::RandChanged(x / 100.0)
}),
text(format!("{r}")),
- text("percent of board to fill on randomize")
- .style(theme::Text::Color(iced::Color::from_rgb8(0x40, 0x40, 0x40)))
]
.spacing(10)
}
fn map_buttons<'a>() -> Column<'a, Message> {
- column![
- row![
- button("save")
- .on_press(Message::Save)
- .style(theme::Button::Positive),
- button("clear")
- .on_press(Message::Clear)
- .style(theme::Button::Destructive),
- ]
- .spacing(10),
- row![
- button("reset")
- .on_press(Message::Reset)
- .style(theme::Button::Secondary),
- button("random")
- .on_press(Message::Randomize)
- .style(theme::Button::Primary),
- ]
- .spacing(10)
+ column![row![
+ button("reset")
+ .on_press(Message::Reset)
+ .style(theme::Button::Secondary),
+ button("random")
+ .on_press(Message::Randomize)
+ .style(theme::Button::Primary),
]
+ .spacing(10)]
.spacing(10)
}
fn velocity_sliders<'a>(min: u8, max: u8) -> Column<'a, Message> {
column![
row![
- text(format!("{max}")),
- slider(0..=127, max, Message::NewVMax),
text("maximum velocity")
- .style(theme::Text::Color(iced::Color::from_rgb8(0x40, 0x40, 0x40)))
+ .style(theme::Text::Color(iced::Color::from_rgb8(0x60, 0x60, 0x60))),
+ slider(0..=127, max, Message::NewVMax),
+ text(format!("{max}")),
]
.spacing(10),
row![
- text(format!("{min}")),
- slider(0..=127, min, Message::NewVMin),
text("minimum velocity")
- .style(theme::Text::Color(iced::Color::from_rgb8(0x40, 0x40, 0x40)))
+ .style(theme::Text::Color(iced::Color::from_rgb8(0x60, 0x60, 0x60))),
+ slider(0..=127, min, Message::NewVMin),
+ text(format!("{min}")),
]
.spacing(10),
]
column![
song_section(message),
velocity_sliders(message.info.velocity.min(), message.info.velocity.max())
+ .width(Length::Fixed(500.0))
]
.spacing(10)
}
row![
pick_list(&RootNote::ALL[..], Some(note), move |note| {
Message::NewNote(Root::new(note, acc))
- }),
+ })
+ .width(Length::Fixed(50.0)),
pick_list(&Accidental::ALL[..], Some(acc), move |acc| {
Message::NewNote(Root::new(note, acc))
- }),
- pick_list(&Scale::ALL[..], Some(scale), Message::Scale),
+ })
+ .width(Length::Fixed(90.0)),
+ pick_list(&Scale::ALL[..], Some(scale), Message::Scale).width(Length::Fixed(160.0)),
]
.align_items(Alignment::Center)
}
theme::Theme,
time,
widget::{column, container, row},
- {Alignment, Application, Command, Element, Length, Point, Subscription},
+ window, {Alignment, Application, Command, Element, Length, Point, Subscription},
};
use itertools::Itertools;
Self {
is_playing: false,
bpm: 120,
- divisor: 1,
+ divisor: 4,
is_looping: false,
loop_len: 16,
step_num: 0,
fn update(&mut self, message: Message) -> Command<Message> {
match message {
- Message::Quit => todo!(), // TODO: figure out how to cleanly quit
Message::None => {}
Message::MapMessage(message) => self.map.update(message),
Message::MaskMessage(message) => self.mask.update(message),
Message::OctaveRange(r) => self.info.octave.set_range(r),
Message::NewNote(r) => self.info.root = r,
Message::Voices(v) => self.info.voices = v,
+ Message::Quit => return window::close(),
}
Command::none()
fn subscription(&self) -> Subscription<Message> {
if self.song.is_playing {
time::every(Duration::from_millis(
- 60000 / (self.song.bpm * self.song.divisor) as u64,
+ 240000 / (self.song.bpm * self.song.divisor) as u64,
))
.map(Message::Tick)
} else {
self.mask.view().map(Message::MaskMessage)
]
.align_items(Alignment::Center)
- .width(Length::Fill)
.spacing(40)
- .padding(20);
+ .padding(40);
let bottom = bottom_controls(self.control_message());
- let content = column![top, map, bottom];
+ let content = column![top, map, bottom].width(Length::Fill);
container(content)
.width(Length::Fill)