Skip to content

Commit

Permalink
Note Range Settings
Browse files Browse the repository at this point in the history
  • Loading branch information
PolyMeilex committed Dec 30, 2023
1 parent 39f6a84 commit 5560708
Show file tree
Hide file tree
Showing 4 changed files with 282 additions and 8 deletions.
115 changes: 107 additions & 8 deletions neothesia/src/scene/menu_scene/iced_menu/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use iced_core::{
Alignment, Length, Padding,
};
use iced_runtime::Command;
use iced_widget::{column as col, container, pick_list, row, toggler};
use iced_widget::{button, column as col, container, pick_list, row, toggler};

use crate::{
iced_utils::iced_state::Element,
Expand All @@ -14,12 +14,18 @@ use crate::{
icons,
layout::{BarLayout, Layout, PushIf},
neo_btn::NeoBtn,
preferences_group,
preferences_group, scroll_listener,
},
target::Target,
};

use super::{centered_text, theme, top_padded, Data, InputDescriptor, Message};
use super::{centered_text, theme, Data, InputDescriptor, Message};

#[derive(Debug, Clone)]
pub enum RangeUpdateKind {
Add,
Sub,
}

#[derive(Debug, Clone)]
pub enum SettingsMessage {
Expand All @@ -29,6 +35,9 @@ pub enum SettingsMessage {

OpenSoundFontPicker,
SoundFontFileLoaded(Option<PathBuf>),

RangeStart(RangeUpdateKind),
RangeEnd(RangeUpdateKind),
}

impl From<SettingsMessage> for Message {
Expand Down Expand Up @@ -72,6 +81,28 @@ pub(super) fn update(
}
data.is_loading = false;
}
SettingsMessage::RangeStart(kind) => match kind {
RangeUpdateKind::Add => {
let v = (target.config.piano_range().start() + 1).min(127);
if v + 24 < *target.config.piano_range().end() {
target.config.piano_range.0 = v;
}
}
RangeUpdateKind::Sub => {
target.config.piano_range.0 = target.config.piano_range.0.saturating_sub(1);
}
},
SettingsMessage::RangeEnd(kind) => match kind {
RangeUpdateKind::Add => {
target.config.piano_range.1 = (target.config.piano_range.1 + 1).min(127);
}
RangeUpdateKind::Sub => {
let v = target.config.piano_range().end().saturating_sub(1);
if *target.config.piano_range().start() + 24 < v {
target.config.piano_range.1 = v;
}
}
},
}

Command::none()
Expand Down Expand Up @@ -139,6 +170,57 @@ fn input_group<'a>(data: &'a Data, _target: &Target) -> Element<'a, Message> {
.build()
}

fn counter<'a>(
value: u8,
msg: fn(RangeUpdateKind) -> SettingsMessage,
) -> Element<'a, SettingsMessage> {
let label = centered_text(value);
let sub = button(centered_text("-").width(30).height(30))
.padding(0)
.style(theme::round_button())
.on_press(msg(RangeUpdateKind::Sub));
let add = button(centered_text("+").width(30).height(30))
.padding(0)
.style(theme::round_button())
.on_press(msg(RangeUpdateKind::Add));

let row = row![label, sub, add]
.spacing(10)
.align_items(Alignment::Center);

scroll_listener::ScrollListener::new(row, move |delta| {
if delta.is_sign_positive() {
msg(RangeUpdateKind::Add)
} else {
msg(RangeUpdateKind::Sub)
}
})
.into()
}

fn note_range_group<'a>(_data: &'a Data, target: &Target) -> Element<'a, Message> {
let start = counter(
*target.config.piano_range().start(),
SettingsMessage::RangeStart,
)
.map(Message::Settings);
let end = counter(
*target.config.piano_range().end(),
SettingsMessage::RangeEnd,
)
.map(Message::Settings);

preferences_group::PreferencesGroup::new()
.title("Note Range")
.push(
preferences_group::ActionRow::new()
.title("Start")
.suffix(start),
)
.push(preferences_group::ActionRow::new().title("End").suffix(end))
.build()
}

fn guidelines_group<'a>(_data: &'a Data, target: &Target) -> Element<'a, Message> {
let toggler = toggler(None, target.config.vertical_guidelines, |v| {
SettingsMessage::VerticalGuidelines(v).into()
Expand All @@ -159,12 +241,18 @@ fn guidelines_group<'a>(_data: &'a Data, target: &Target) -> Element<'a, Message
pub(super) fn view<'a>(data: &'a Data, target: &Target) -> Element<'a, Message> {
let output_group = output_group(data, target);
let input_group = input_group(data, target);
let note_range_group = note_range_group(data, target);
let guidelines_group = guidelines_group(data, target);

let column = col![output_group, input_group, guidelines_group]
.spacing(10)
.width(Length::Fill)
.align_items(Alignment::Center);
let column = col![
output_group,
input_group,
note_range_group,
guidelines_group
]
.spacing(10)
.width(Length::Fill)
.align_items(Alignment::Center);

let left = {
let back = NeoBtn::new(
Expand Down Expand Up @@ -194,8 +282,19 @@ pub(super) fn view<'a>(data: &'a Data, target: &Target) -> Element<'a, Message>
left: 10.0,
});

let body = container(column).max_width(650).padding(Padding {
top: 50.0,
..Padding::ZERO
});

let body = col![body]
.width(Length::Fill)
.align_items(Alignment::Center);

let column = iced_widget::scrollable(body);

Layout::new()
.body(top_padded(column))
.body(column)
.bottom(BarLayout::new().left(left))
.into()
}
Expand Down
24 changes: 24 additions & 0 deletions neothesia/src/scene/menu_scene/iced_menu/theme.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,30 @@ impl iced_style::button::StyleSheet for ButtonStyle {
}
}

pub fn round_button() -> iced_style::theme::Button {
iced_style::theme::Button::Custom(Box::new(RoundButtonStyle))
}

struct RoundButtonStyle;

impl iced_style::button::StyleSheet for RoundButtonStyle {
type Style = iced_style::Theme;

fn active(&self, style: &Self::Style) -> button::Appearance {
button::Appearance {
border_radius: BorderRadius::from(f32::MAX),
..ButtonStyle::active(&ButtonStyle, style)
}
}

fn hovered(&self, style: &Self::Style) -> button::Appearance {
button::Appearance {
border_radius: BorderRadius::from(f32::MAX),
..ButtonStyle::hovered(&ButtonStyle, style)
}
}
}

pub fn _checkbox() -> iced_style::theme::Checkbox {
iced_style::theme::Checkbox::Custom(Box::new(CheckboxStyle))
}
Expand Down
1 change: 1 addition & 0 deletions neothesia/src/scene/menu_scene/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mod icons;
mod layout;
mod neo_btn;
mod preferences_group;
mod scroll_listener;
mod segment_button;
mod track_card;
mod wrap;
Expand Down
150 changes: 150 additions & 0 deletions neothesia/src/scene/menu_scene/scroll_listener/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
use iced_core::{mouse::ScrollDelta, Widget};

use crate::iced_utils::iced_state::Element;

pub struct ScrollListener<'a, M> {
content: Element<'a, M>,
on_scroll: Box<dyn Fn(f32) -> M>,
}

impl<'a, M> ScrollListener<'a, M> {
pub fn new(content: impl Into<Element<'a, M>>, on_scroll: impl Fn(f32) -> M + 'static) -> Self {
Self {
content: content.into(),
on_scroll: Box::new(on_scroll),
}
}
}

impl<'a, M> Widget<M, super::Renderer> for ScrollListener<'a, M> {
fn width(&self) -> iced_core::Length {
self.content.as_widget().width()
}

fn height(&self) -> iced_core::Length {
self.content.as_widget().height()
}

fn layout(
&self,
tree: &mut iced_core::widget::Tree,
renderer: &super::Renderer,
limits: &iced_core::layout::Limits,
) -> iced_core::layout::Node {
self.content.as_widget().layout(tree, renderer, limits)
}

fn draw(
&self,
tree: &iced_core::widget::Tree,
renderer: &mut super::Renderer,
theme: &<super::Renderer as iced_core::Renderer>::Theme,
style: &iced_core::renderer::Style,
layout: iced_core::Layout<'_>,
cursor: iced_core::mouse::Cursor,
viewport: &iced_core::Rectangle,
) {
self.content
.as_widget()
.draw(tree, renderer, theme, style, layout, cursor, viewport)
}

fn tag(&self) -> iced_core::widget::tree::Tag {
self.content.as_widget().tag()
}

fn state(&self) -> iced_core::widget::tree::State {
self.content.as_widget().state()
}

fn children(&self) -> Vec<iced_core::widget::Tree> {
self.content.as_widget().children()
}

fn diff(&self, tree: &mut iced_core::widget::Tree) {
self.content.as_widget().diff(tree)
}

fn operate(
&self,
state: &mut iced_core::widget::Tree,
layout: iced_core::Layout<'_>,
renderer: &super::Renderer,
operation: &mut dyn iced_core::widget::Operation<M>,
) {
self.content
.as_widget()
.operate(state, layout, renderer, operation)
}

fn on_event(
&mut self,
state: &mut iced_core::widget::Tree,
event: iced_core::Event,
layout: iced_core::Layout<'_>,
cursor: iced_core::mouse::Cursor,
renderer: &super::Renderer,
clipboard: &mut dyn iced_core::Clipboard,
shell: &mut iced_core::Shell<'_, M>,
viewport: &iced_core::Rectangle,
) -> iced_core::event::Status {
if let iced_core::event::Status::Captured = self.content.as_widget_mut().on_event(
state,
event.clone(),
layout,
cursor,
renderer,
clipboard,
shell,
viewport,
) {
return iced_core::event::Status::Captured;
}

if let iced_core::Event::Mouse(iced_core::mouse::Event::WheelScrolled { delta }) = event {
let bounds = layout.bounds();

if cursor.is_over(bounds) {
let (ScrollDelta::Lines { y, .. } | ScrollDelta::Pixels { y, .. }) = delta;

if y.abs() != 0.0 {
let msg = (self.on_scroll)(y);
shell.publish(msg);
return iced_core::event::Status::Captured;
}
}
}

iced_core::event::Status::Ignored
}

fn mouse_interaction(
&self,
state: &iced_core::widget::Tree,
layout: iced_core::Layout<'_>,
cursor: iced_core::mouse::Cursor,
viewport: &iced_core::Rectangle,
renderer: &super::Renderer,
) -> iced_core::mouse::Interaction {
self.content
.as_widget()
.mouse_interaction(state, layout, cursor, viewport, renderer)
}

fn overlay<'b>(
&'b mut self,
state: &'b mut iced_core::widget::Tree,
layout: iced_core::Layout<'_>,
renderer: &super::Renderer,
) -> Option<iced_core::overlay::Element<'b, M, super::Renderer>> {
self.content
.as_widget_mut()
.overlay(state, layout, renderer)
}
}

impl<'a, M: 'a> From<ScrollListener<'a, M>> for Element<'a, M> {
fn from(value: ScrollListener<'a, M>) -> Self {
Self::new(value)
}
}

0 comments on commit 5560708

Please sign in to comment.