Skip to content

Commit 61f407d

Browse files
authored
Move main page to separate component & Don't hardcode keybinds (#162)
1 parent 08c3cf8 commit 61f407d

File tree

7 files changed

+290
-223
lines changed

7 files changed

+290
-223
lines changed

neothesia/src/scene/menu_scene/iced_menu/exit.rs

+16
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,20 @@ impl Page for ExitPage {
4747

4848
center_x(controls).center_y().into()
4949
}
50+
51+
fn keyboard_input(event: &iced_runtime::keyboard::Event, _ctx: &Context) -> Option<Message> {
52+
use iced_runtime::keyboard::{key::Named, Event, Key};
53+
54+
match event {
55+
Event::KeyPressed {
56+
key: Key::Named(key),
57+
..
58+
} => match key {
59+
Named::Enter => Some(Message::ExitApp),
60+
Named::Escape => Some(Message::GoBack),
61+
_ => None,
62+
},
63+
_ => None,
64+
}
65+
}
5066
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
use std::path::PathBuf;
2+
3+
use iced_core::{
4+
alignment::{Horizontal, Vertical},
5+
Alignment, Length, Padding,
6+
};
7+
use iced_runtime::Command;
8+
use iced_widget::{column, container, image, row, text};
9+
use neothesia_iced_widgets::{BarLayout, Layout, NeoBtn};
10+
11+
use crate::{context::Context, scene::menu_scene::icons, song::Song};
12+
13+
use super::{page::Page, top_padded, Data, Message, Step};
14+
15+
#[derive(Debug, Clone)]
16+
pub enum Event {
17+
Play,
18+
GoToPage(Step),
19+
MidiFilePicker(MidiFilePickerMessage),
20+
}
21+
22+
pub struct MainPage;
23+
24+
impl Page for MainPage {
25+
type Event = Event;
26+
27+
fn update(data: &mut Data, msg: Self::Event, ctx: &mut Context) -> Command<Message> {
28+
let msg = match msg {
29+
Event::Play => Message::Play,
30+
Event::GoToPage(step) => Message::GoToPage(step),
31+
Event::MidiFilePicker(msg) => return midi_file_picker_update(data, msg, ctx),
32+
};
33+
34+
Command::perform(async {}, move |_| msg)
35+
}
36+
37+
fn view<'a>(data: &'a Data, ctx: &Context) -> neothesia_iced_widgets::Element<'a, Self::Event> {
38+
let buttons = column![
39+
NeoBtn::new_with_label("Select File")
40+
.on_press(Event::MidiFilePicker(MidiFilePickerMessage::open()))
41+
.width(Length::Fill)
42+
.height(Length::Fixed(80.0)),
43+
NeoBtn::new_with_label("Settings")
44+
.on_press(Event::GoToPage(Step::Settings))
45+
.width(Length::Fill)
46+
.height(Length::Fixed(80.0)),
47+
NeoBtn::new_with_label("Exit")
48+
.on_press(Event::GoToPage(Step::Exit))
49+
.width(Length::Fill)
50+
.height(Length::Fixed(80.0)),
51+
]
52+
.width(Length::Fixed(450.0))
53+
.spacing(10);
54+
55+
let column = column![image(data.logo_handle.clone()), buttons]
56+
.spacing(40)
57+
.align_items(Alignment::Center);
58+
59+
let mut layout = Layout::new().body(top_padded(column));
60+
61+
if let Some(song) = ctx.song.as_ref() {
62+
let tracks = NeoBtn::new(
63+
icons::note_list_icon()
64+
.size(30.0)
65+
.vertical_alignment(Vertical::Center)
66+
.horizontal_alignment(Horizontal::Center),
67+
)
68+
.height(Length::Fixed(60.0))
69+
.min_width(80.0)
70+
.on_press(Event::GoToPage(Step::TrackSelection));
71+
72+
let play = NeoBtn::new(
73+
icons::play_icon()
74+
.size(30.0)
75+
.vertical_alignment(Vertical::Center)
76+
.horizontal_alignment(Horizontal::Center),
77+
)
78+
.height(Length::Fixed(60.0))
79+
.min_width(80.0)
80+
.on_press(Event::Play);
81+
82+
let row = row![tracks, play]
83+
.spacing(10)
84+
.align_items(Alignment::Center);
85+
86+
let container = container(row)
87+
.width(Length::Fill)
88+
.align_x(Horizontal::Right)
89+
.padding(Padding {
90+
top: 0.0,
91+
right: 10.0,
92+
bottom: 10.0,
93+
left: 0.0,
94+
});
95+
96+
layout = layout.bottom(
97+
BarLayout::new()
98+
.center(
99+
text(&song.file.name)
100+
.width(Length::Fill)
101+
.vertical_alignment(Vertical::Center)
102+
.horizontal_alignment(Horizontal::Center),
103+
)
104+
.right(container),
105+
);
106+
}
107+
108+
layout.into()
109+
}
110+
111+
fn keyboard_input(event: &iced_runtime::keyboard::Event, _ctx: &Context) -> Option<Message> {
112+
use iced_runtime::keyboard::{key::Named, Event, Key};
113+
114+
match event {
115+
Event::KeyPressed {
116+
key: Key::Named(key),
117+
..
118+
} => match key {
119+
Named::Tab => Some(MidiFilePickerMessage::open().into()),
120+
Named::Enter => Some(Message::Play),
121+
Named::Escape => Some(Message::GoBack),
122+
_ => None,
123+
},
124+
Event::KeyPressed {
125+
key: Key::Character(ch),
126+
..
127+
} => match ch.as_ref() {
128+
"s" => Some(Message::GoToPage(Step::Settings)),
129+
"t" => Some(Message::GoToPage(Step::TrackSelection)),
130+
_ => None,
131+
},
132+
_ => None,
133+
}
134+
}
135+
}
136+
137+
#[derive(Debug, Clone)]
138+
pub enum MidiFilePickerMessage {
139+
OpenMidiFilePicker,
140+
MidiFileLoaded(Option<(midi_file::MidiFile, PathBuf)>),
141+
}
142+
143+
impl MidiFilePickerMessage {
144+
pub(super) fn open() -> Self {
145+
Self::OpenMidiFilePicker
146+
}
147+
}
148+
149+
impl From<MidiFilePickerMessage> for Message {
150+
fn from(msg: MidiFilePickerMessage) -> Self {
151+
Message::MainPage(super::main::Event::MidiFilePicker(msg))
152+
}
153+
}
154+
155+
fn midi_file_picker_update(
156+
data: &mut Data,
157+
msg: MidiFilePickerMessage,
158+
ctx: &mut Context,
159+
) -> Command<Message> {
160+
match msg {
161+
MidiFilePickerMessage::OpenMidiFilePicker => {
162+
data.is_loading = true;
163+
return open_midi_file_picker(|v| MidiFilePickerMessage::MidiFileLoaded(v).into());
164+
}
165+
MidiFilePickerMessage::MidiFileLoaded(midi) => {
166+
if let Some((midi, path)) = midi {
167+
ctx.config.last_opened_song = Some(path);
168+
ctx.song = Some(Song::new(midi));
169+
}
170+
data.is_loading = false;
171+
}
172+
}
173+
174+
Command::none()
175+
}
176+
177+
fn open_midi_file_picker(
178+
f: impl FnOnce(Option<(midi_file::MidiFile, PathBuf)>) -> Message + 'static + Send,
179+
) -> Command<Message> {
180+
Command::perform(
181+
async {
182+
let file = rfd::AsyncFileDialog::new()
183+
.add_filter("midi", &["mid", "midi"])
184+
.pick_file()
185+
.await;
186+
187+
if let Some(file) = file {
188+
log::info!("File path = {:?}", file.path());
189+
190+
let thread = async_thread::Builder::new()
191+
.name("midi-loader".into())
192+
.spawn(move || {
193+
let midi = midi_file::MidiFile::new(file.path());
194+
195+
if let Err(e) = &midi {
196+
log::error!("{}", e);
197+
}
198+
199+
midi.map(|midi| (midi, file.path().to_path_buf())).ok()
200+
});
201+
202+
if let Ok(thread) = thread {
203+
thread.join().await.ok().flatten()
204+
} else {
205+
None
206+
}
207+
} else {
208+
log::info!("User canceled dialog");
209+
None
210+
}
211+
},
212+
f,
213+
)
214+
}

neothesia/src/scene/menu_scene/iced_menu/midi_file_picker.rs

-84
This file was deleted.

0 commit comments

Comments
 (0)