use iced::{
theme,
- widget::{button, column, pick_list, row, slider, text, Column, Row},
+ widget::{
+ button, checkbox, column, pick_list, row, slider, text, vertical_slider, Column, Row,
+ },
Alignment, Element, Length,
};
pub fn top_controls<'a>(is_playing: bool) -> Element<'a, Message> {
let play_button = row![
button(if is_playing { "stop" } else { "play" }).on_press(Message::TogglePlayback),
- button("save")
+ button("save map")
.on_press(Message::Save)
.style(theme::Button::Positive),
- button("clear")
- .on_press(Message::Clear)
+ button("reset map")
+ .on_press(Message::Reset)
+ .style(theme::Button::Secondary),
+ button("clear map")
+ .on_press(Message::ClearMap)
+ .style(theme::Button::Destructive),
+ button("clear mask")
+ .on_press(Message::ClearMask)
.style(theme::Button::Destructive),
]
.width(Length::Fill)
.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),
- probability_section(message.info.probability).width(Length::Fixed(600.0)),
- midi_section(&message),
+ probability_section(&message).width(Length::Fixed(600.0)),
+ scale_selector(&message),
+ music_controls(&message)
]
.width(Length::Fill)
.height(Length::Fill)
.into()
}
-fn map_section<'a>(message: &ControlMessage) -> Row<'a, Message> {
+fn music_controls<'a>(message: &ControlMessage) -> Row<'a, Message> {
row![
- map_buttons(),
- randomize_section(message.randomness).width(Length::Fixed(500.0))
+ song_section(message),
+ midi_section(message),
+ velocity_sliders(message)
]
- .spacing(10)
+ .height(Length::Fill)
+ .padding(10)
+ .spacing(40)
+ .into()
}
-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}")),
- ]
- .spacing(10)
+fn song_section<'a>(message: &ControlMessage) -> Row<'a, Message> {
+ row![song_params(), song_vals(message)]
+ .height(Length::Fill)
+ .padding(10)
+ .spacing(20)
+ .into()
}
-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}")),
- ]
- .spacing(10)
+fn midi_section<'a>(message: &ControlMessage) -> Row<'a, Message> {
+ row![midi_params(), midi_vals(message)]
+ .height(Length::Fill)
+ .padding(10)
+ .spacing(20)
+ .into()
}
-fn map_buttons<'a>() -> Column<'a, Message> {
- column![row![
- button("reset")
- .on_press(Message::Reset)
- .style(theme::Button::Secondary),
- button("random")
- .on_press(Message::Randomize)
- .style(theme::Button::Primary),
+fn song_params<'a>() -> Column<'a, Message> {
+ column![
+ text("loop section"),
+ text("number of steps"),
+ text("bpm"),
+ text("note division"),
]
- .spacing(10)]
- .spacing(10)
+ .height(Length::Fill)
+ .padding(10)
+ .spacing(20)
+ .into()
}
-fn velocity_sliders<'a>(min: u8, max: u8) -> Column<'a, Message> {
+fn song_vals<'a>(message: &ControlMessage) -> Column<'a, Message> {
column![
+ checkbox("", message.song.is_looping, |_| { Message::ToggleLoop }),
row![
- text("maximum velocity")
- .style(theme::Text::Color(iced::Color::from_rgb8(0x60, 0x60, 0x60))),
- slider(0..=127, max, Message::NewVMax),
- text(format!("{max}")),
- ]
- .spacing(10),
+ button("-").on_press(Message::LoopLength(message.song.loop_len.saturating_sub(1))),
+ text(if message.song.is_looping {
+ format!("{}/{}", message.song.step_num, message.song.loop_len)
+ } else {
+ format!("{}", message.song.loop_len)
+ }),
+ button("+").on_press(Message::LoopLength(message.song.loop_len.saturating_add(1))),
+ ],
row![
- text("minimum velocity")
- .style(theme::Text::Color(iced::Color::from_rgb8(0x60, 0x60, 0x60))),
- slider(0..=127, min, Message::NewVMin),
- text(format!("{min}")),
+ button("-").on_press(Message::SpeedChanged(message.song.bpm.saturating_sub(1))),
+ text(format!("{}", message.song.bpm)),
+ button("+").on_press(Message::SpeedChanged(message.song.bpm.saturating_add(1))),
+ ],
+ row![
+ button("-").on_press(Message::NewDivision(message.song.divisor.saturating_sub(1))),
+ text(format!("{}", message.song.divisor)),
+ button("+").on_press(Message::NewDivision(message.song.divisor.saturating_add(1))),
]
- .spacing(10),
]
- .spacing(10)
+ .height(Length::Fill)
+ .padding(10)
+ .spacing(20)
+ .into()
}
-fn midi_section<'a>(message: &ControlMessage) -> Column<'a, Message> {
+fn midi_params<'a>() -> Column<'a, Message> {
column![
- song_section(message),
- velocity_sliders(message.info.velocity.min(), message.info.velocity.max())
- .width(Length::Fixed(500.0))
+ text("center octave"),
+ text("octave range"),
+ text("number of voices"),
+ text("midi channel"),
]
- .spacing(10)
+ .height(Length::Fill)
+ .padding(10)
+ .spacing(20)
+ .into()
}
-fn song_section<'a>(message: &ControlMessage) -> Row<'a, Message> {
- row![
- column![
- loop_controls(
- message.song.is_looping,
- message.song.loop_len,
- message.song.step_num
- ),
- speed_controls(message.song.bpm),
- division_controls(message.song.divisor),
- ]
- .padding(10)
- .spacing(10)
- .align_items(Alignment::Center),
- column![
- voice_controls(message.info.voices, message.info.channel),
- octave_selector(message.info.octave.center(), message.info.octave.range()),
- scale_selector(
- message.info.scale,
- message.info.root.get_note(),
- message.info.root.get_accidental()
- )
+fn midi_vals<'a>(message: &ControlMessage) -> Column<'a, Message> {
+ column![
+ row![
+ button("-").on_press(Message::NewOctave(
+ message.info.octave.center.saturating_sub(1)
+ )),
+ text(format!("{}", message.info.octave.center)),
+ button("+").on_press(Message::NewOctave(
+ message.info.octave.center.saturating_add(1)
+ )),
+ ],
+ row![
+ button("-").on_press(Message::OctaveRange(
+ message.info.octave.range.saturating_sub(1)
+ )),
+ text(format!("{}", message.info.octave.range)),
+ button("+").on_press(Message::OctaveRange(
+ message.info.octave.range.saturating_add(1)
+ )),
+ ],
+ row![
+ button("-").on_press(Message::Voices(message.info.voices.saturating_sub(1))),
+ text(format!("{}", message.info.voices)),
+ button("+").on_press(Message::Voices(message.info.voices.saturating_add(1))),
+ ],
+ row![
+ button("-").on_press(Message::ChannelChange(
+ message.info.channel.saturating_sub(1)
+ )),
+ text(format!("{}", message.info.channel)),
+ button("+").on_press(Message::ChannelChange(
+ message.info.channel.saturating_add(1)
+ )),
]
- .padding(10)
- .spacing(10)
- .align_items(Alignment::Center)
]
- .spacing(10)
+ .height(Length::Fill)
+ .padding(10)
+ .spacing(20)
+ .into()
}
-fn octave_selector<'a>(oct: u8, range: u8) -> Row<'a, Message> {
+fn map_section<'a>(message: &ControlMessage) -> Row<'a, Message> {
row![
- button("-").on_press(Message::NewOctave(oct.saturating_sub(1))),
- text(format!("octave: {oct}")),
- button("+").on_press(Message::NewOctave(oct.saturating_add(1))),
- button("-").on_press(Message::OctaveRange(range.saturating_sub(1))),
- text(format!("range: +/-{range}")),
- button("+").on_press(Message::OctaveRange(range.saturating_add(1)))
+ map_buttons(),
+ randomize_section(message.randomness).width(Length::Fixed(500.0))
]
.spacing(10)
}
-fn loop_controls<'a>(looping: bool, len: usize, step: usize) -> Row<'a, Message> {
+fn probability_section<'a>(message: &ControlMessage) -> Row<'a, Message> {
row![
- button(if looping { "free" } else { "loop" }).on_press(Message::ToggleLoop),
- button("-").on_press(Message::LoopLength(len.saturating_sub(1))),
- text(if looping {
- format!("{step}/{len}")
- } else {
- format!("{len}")
+ text("probability a cell triggers a note"),
+ slider(0.0..=100.0, message.info.probability * 100.0, |x| {
+ Message::ProbChanged(x / 100.0)
}),
- button("+").on_press(Message::LoopLength(len.saturating_add(1)))
+ text(format!("{}", message.info.probability)),
]
.spacing(10)
}
-fn speed_controls<'a>(bpm: usize) -> Row<'a, Message> {
+fn randomize_section<'a>(r: f32) -> Row<'a, Message> {
row![
- button("<<").on_press(Message::SpeedChanged(bpm.saturating_sub(5))),
- button("<").on_press(Message::SpeedChanged(bpm.saturating_sub(1))),
- text(format!("{bpm}")).size(16),
- button(">").on_press(Message::SpeedChanged(bpm.saturating_add(1))),
- button(">>").on_press(Message::SpeedChanged(bpm.saturating_add(5))),
+ slider(0.0..=100.0, r * 100.0, |x| {
+ Message::RandChanged(x / 100.0)
+ }),
+ text(format!("{r}")),
]
.spacing(10)
}
-fn division_controls<'a>(divisor: usize) -> Row<'a, Message> {
+fn map_buttons<'a>() -> Row<'a, Message> {
row![
- button("-").on_press(if divisor > 1 {
- Message::NewDivision(divisor.saturating_sub(1))
- } else {
- Message::None
- }),
- text(format!("note division: {divisor}")),
- button("+").on_press(Message::NewDivision(divisor.saturating_add(1)))
+ button("randomize map")
+ .on_press(Message::RandomizeMap)
+ .style(theme::Button::Primary),
+ button("randomize mask")
+ .on_press(Message::RandomizeMask)
+ .style(theme::Button::Primary),
]
.spacing(10)
}
-fn voice_controls<'a>(voices: u8, channel: u8) -> Row<'a, Message> {
- row![
- button("-").on_press(Message::ChannelChange(channel.saturating_sub(1))),
- text(format!("channel: {channel}")),
- button("+").on_press(Message::ChannelChange(channel.saturating_add(1))),
- button("-").on_press(Message::Voices(voices.saturating_sub(1))),
- text(format!("voices: {voices}")),
- button("+").on_press(Message::Voices(voices.saturating_add(1))),
+fn velocity_sliders<'a>(message: &ControlMessage) -> Column<'a, Message> {
+ column![
+ text("velocity range"),
+ row![
+ column![
+ text(format!("{}", message.info.velocity.max())),
+ vertical_slider(0..=127, message.info.velocity.max(), Message::NewVMax),
+ text("max")
+ ],
+ column![
+ text(format!("{}", message.info.velocity.min())),
+ vertical_slider(0..=127, message.info.velocity.min(), Message::NewVMin),
+ text("min")
+ ],
+ ]
]
.spacing(10)
}
-fn scale_selector<'a>(scale: Scale, note: RootNote, acc: Accidental) -> Row<'a, Message> {
+fn scale_selector<'a>(message: &ControlMessage) -> Row<'a, Message> {
+ let scale = message.info.scale;
+ let note = message.info.root.note;
+ let accidental = message.info.root.accidental;
row![
pick_list(&RootNote::ALL[..], Some(note), move |note| {
- Message::NewNote(Root::new(note, acc))
+ Message::NewNote(Root { note, accidental })
})
.width(Length::Fixed(50.0)),
- pick_list(&Accidental::ALL[..], Some(acc), move |acc| {
- Message::NewNote(Root::new(note, acc))
+ pick_list(&Accidental::ALL[..], Some(accidental), move |accidental| {
+ Message::NewNote(Root { note, accidental })
})
.width(Length::Fixed(90.0)),
pick_list(&Scale::ALL[..], Some(scale), Message::Scale).width(Length::Fixed(160.0)),