|
|
@ -5,12 +5,11 @@ use eframe;
|
|
|
|
use eframe::egui;
|
|
|
|
use eframe::egui;
|
|
|
|
use std::boxed::Box;
|
|
|
|
use std::boxed::Box;
|
|
|
|
use std::collections::VecDeque;
|
|
|
|
use std::collections::VecDeque;
|
|
|
|
|
|
|
|
use std::marker::PhantomData;
|
|
|
|
|
|
|
|
|
|
|
|
struct PipeDash {
|
|
|
|
struct PipeDash {
|
|
|
|
msg_queue: VecDeque<Message>,
|
|
|
|
msg_queue: VecDeque<Message>,
|
|
|
|
selected_level: Option<usize>,
|
|
|
|
selected_level: Option<usize>,
|
|
|
|
selected_color: Option<Color>,
|
|
|
|
|
|
|
|
level_list: Vec<gd::OuterLevel>,
|
|
|
|
level_list: Vec<gd::OuterLevel>,
|
|
|
|
editor: Editor,
|
|
|
|
editor: Editor,
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -24,7 +23,6 @@ enum Color {
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
#[derive(Debug)]
|
|
|
|
enum Message {
|
|
|
|
enum Message {
|
|
|
|
ColorSelected(Color),
|
|
|
|
|
|
|
|
LevelSelected(usize),
|
|
|
|
LevelSelected(usize),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -34,38 +32,78 @@ struct Editor {
|
|
|
|
beats_per_bar: f32,
|
|
|
|
beats_per_bar: f32,
|
|
|
|
subdivisions: f32,
|
|
|
|
subdivisions: f32,
|
|
|
|
beat_rate: music::BeatRate,
|
|
|
|
beat_rate: music::BeatRate,
|
|
|
|
|
|
|
|
time_signatures: music::TimeSignature,
|
|
|
|
|
|
|
|
green_lines: music::Lines,
|
|
|
|
|
|
|
|
orange_lines: music::Lines,
|
|
|
|
|
|
|
|
yellow_lines: music::Lines,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type EditorWidget<'a> = &'a mut Editor;
|
|
|
|
struct Orange;
|
|
|
|
|
|
|
|
struct Yellow;
|
|
|
|
|
|
|
|
struct Green;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct BeatRateWidget<'a>(&'a mut Editor);
|
|
|
|
|
|
|
|
struct TimeSignatureWidget<'a>(&'a mut Editor);
|
|
|
|
|
|
|
|
struct LinesWidget<'a, C: WithColor>(&'a mut Editor, PhantomData<C>);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
trait WithColor {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl WithColor for Orange {}
|
|
|
|
|
|
|
|
impl WithColor for Yellow {}
|
|
|
|
|
|
|
|
impl WithColor for Green {}
|
|
|
|
|
|
|
|
|
|
|
|
impl Editor {
|
|
|
|
impl Editor {
|
|
|
|
pub fn widget(&mut self) -> EditorWidget {
|
|
|
|
pub fn beat_rate_widget(&mut self) -> BeatRateWidget {
|
|
|
|
self
|
|
|
|
BeatRateWidget(self)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn time_signature_widget(&mut self) -> TimeSignatureWidget {
|
|
|
|
|
|
|
|
TimeSignatureWidget(self)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn lines_widget<C: WithColor>(&mut self) -> LinesWidget<C> {
|
|
|
|
|
|
|
|
LinesWidget(self, Default::default())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl<'a> egui::Widget for EditorWidget<'a> {
|
|
|
|
impl<'a> egui::Widget for BeatRateWidget<'a> {
|
|
|
|
fn ui(self, ui: &mut egui::Ui) -> egui::Response {
|
|
|
|
fn ui(self, ui: &mut egui::Ui) -> egui::Response {
|
|
|
|
// 1. choose size
|
|
|
|
// 1. choose size
|
|
|
|
let max_rect = ui.max_rect();
|
|
|
|
let max_rect = ui.max_rect();
|
|
|
|
let preferred_size = max_rect.size();
|
|
|
|
let preferred_size = egui::Vec2::new(max_rect.size().x, 60.0);
|
|
|
|
// 2. allocate space
|
|
|
|
// 2. allocate space
|
|
|
|
let (rect, res) = ui.allocate_exact_size(preferred_size, egui::Sense::click_and_drag());
|
|
|
|
let (rect, res) = ui.allocate_exact_size(preferred_size, egui::Sense::click_and_drag());
|
|
|
|
// 3. handle interactions
|
|
|
|
// 3. handle interactions
|
|
|
|
// 4. draw widget
|
|
|
|
// 4. draw widget
|
|
|
|
if ui.is_rect_visible(rect) {
|
|
|
|
if ui.is_rect_visible(rect) {
|
|
|
|
ui.painter().rect_filled(rect, 0f32, eframe::epaint::Color32::from_gray(0));
|
|
|
|
ui.painter()
|
|
|
|
|
|
|
|
.rect_filled(rect, 0f32, eframe::epaint::Color32::from_gray(0));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
res
|
|
|
|
res
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl<'a> egui::Widget for TimeSignatureWidget<'a> {
|
|
|
|
|
|
|
|
fn ui(self, ui: &mut egui::Ui) -> egui::Response {
|
|
|
|
|
|
|
|
// 1. choose size
|
|
|
|
|
|
|
|
let max_rect = ui.max_rect();
|
|
|
|
|
|
|
|
let preferred_size = egui::Vec2::new(max_rect.size().x, 60.0);
|
|
|
|
|
|
|
|
// 2. allocate space
|
|
|
|
|
|
|
|
let (rect, res) = ui.allocate_exact_size(preferred_size, egui::Sense::click_and_drag());
|
|
|
|
|
|
|
|
// 3. handle interactions
|
|
|
|
|
|
|
|
// 4. draw widget
|
|
|
|
|
|
|
|
if ui.is_rect_visible(rect) {
|
|
|
|
|
|
|
|
ui.painter()
|
|
|
|
|
|
|
|
.rect_filled(rect, 0f32, eframe::epaint::Color32::from_gray(0));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
res
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl PipeDash {
|
|
|
|
impl PipeDash {
|
|
|
|
fn new(_cc: &eframe::CreationContext) -> Self {
|
|
|
|
fn new(_cc: &eframe::CreationContext) -> Self {
|
|
|
|
Self {
|
|
|
|
Self {
|
|
|
|
selected_level: None,
|
|
|
|
selected_level: None,
|
|
|
|
selected_color: None,
|
|
|
|
|
|
|
|
msg_queue: VecDeque::new(),
|
|
|
|
msg_queue: VecDeque::new(),
|
|
|
|
level_list: gd::OuterLevel::load_all(),
|
|
|
|
level_list: gd::OuterLevel::load_all(),
|
|
|
|
editor: Editor {
|
|
|
|
editor: Editor {
|
|
|
@ -74,49 +112,44 @@ impl PipeDash {
|
|
|
|
beats_per_bar: 4.0,
|
|
|
|
beats_per_bar: 4.0,
|
|
|
|
subdivisions: 4.0,
|
|
|
|
subdivisions: 4.0,
|
|
|
|
beat_rate: music::StaticBeatRate::from_bpm(120f32).into(),
|
|
|
|
beat_rate: music::StaticBeatRate::from_bpm(120f32).into(),
|
|
|
|
}
|
|
|
|
time_signatures: music::StaticTimeSignature::new(4, 4).into(),
|
|
|
|
|
|
|
|
green_lines: music::Lines::new(),
|
|
|
|
|
|
|
|
orange_lines: music::Lines::new(),
|
|
|
|
|
|
|
|
yellow_lines: music::Lines::new(),
|
|
|
|
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn side_panel(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
|
|
|
|
fn side_panel(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
|
|
|
|
egui::SidePanel::left("level_picker").default_width(100f32).show(ctx, |ui| {
|
|
|
|
egui::SidePanel::left("level_picker")
|
|
|
|
egui::ScrollArea::vertical().show(ui, |ui| {
|
|
|
|
.default_width(100f32)
|
|
|
|
ui.with_layout(egui::Layout::top_down_justified(egui::Align::Min), |ui| {
|
|
|
|
.show(ctx, |ui| {
|
|
|
|
for (idx, level) in self.level_list.iter().enumerate() {
|
|
|
|
egui::ScrollArea::vertical().show(ui, |ui| {
|
|
|
|
if ui.selectable_label(self.selected_level == Some(idx), level.display_name()).clicked() {
|
|
|
|
ui.with_layout(egui::Layout::top_down_justified(egui::Align::Min), |ui| {
|
|
|
|
self.msg_queue.push_back(Message::LevelSelected(idx));
|
|
|
|
for (idx, level) in self.level_list.iter().enumerate() {
|
|
|
|
|
|
|
|
if ui
|
|
|
|
|
|
|
|
.selectable_label(
|
|
|
|
|
|
|
|
self.selected_level == Some(idx),
|
|
|
|
|
|
|
|
level.display_name(),
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
.clicked()
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
self.msg_queue.push_back(Message::LevelSelected(idx));
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn center_panel(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
|
|
|
|
fn center_panel(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
|
|
|
|
use Message::*;
|
|
|
|
|
|
|
|
use Color::*;
|
|
|
|
|
|
|
|
egui::CentralPanel::default().show(ctx, |ui| {
|
|
|
|
egui::CentralPanel::default().show(ctx, |ui| {
|
|
|
|
ui.vertical_centered_justified(|ui| {
|
|
|
|
ui.vertical_centered_justified(|ui| {
|
|
|
|
ui.horizontal_top(|ui| {
|
|
|
|
ui.vertical(|ui| {
|
|
|
|
ui.vertical(|ui| {
|
|
|
|
ui.label("Song name");
|
|
|
|
ui.label("Song name");
|
|
|
|
ui.label("Song id");
|
|
|
|
ui.label("Song id");
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
ui.with_layout(egui::Layout::right_to_left(egui::Align::TOP), |ui| {
|
|
|
|
|
|
|
|
ui.vertical(|ui| {
|
|
|
|
|
|
|
|
if ui.selectable_label(self.selected_color == Some(Orange), "orange").clicked() {
|
|
|
|
|
|
|
|
self.msg_queue.push_back(ColorSelected(Orange));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if ui.selectable_label(self.selected_color == Some(Yellow), "yellow").clicked() {
|
|
|
|
|
|
|
|
self.msg_queue.push_back(ColorSelected(Yellow));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if ui.selectable_label(self.selected_color == Some(Green), "green").clicked() {
|
|
|
|
|
|
|
|
self.msg_queue.push_back(ColorSelected(Green));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
ui.add(self.editor.widget());
|
|
|
|
ui.add(self.editor.beat_rate_widget());
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -125,12 +158,9 @@ impl PipeDash {
|
|
|
|
for message in self.msg_queue.drain(..) {
|
|
|
|
for message in self.msg_queue.drain(..) {
|
|
|
|
println!("{:?}", message);
|
|
|
|
println!("{:?}", message);
|
|
|
|
match message {
|
|
|
|
match message {
|
|
|
|
Message::ColorSelected(color) => {
|
|
|
|
|
|
|
|
self.selected_color = Some(color);
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
Message::LevelSelected(idx) => {
|
|
|
|
Message::LevelSelected(idx) => {
|
|
|
|
self.selected_level = Some(idx);
|
|
|
|
self.selected_level = Some(idx);
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -152,4 +182,3 @@ fn main() {
|
|
|
|
let opts = eframe::NativeOptions::default();
|
|
|
|
let opts = eframe::NativeOptions::default();
|
|
|
|
eframe::run_native("PipeDash", opts, Box::new(|cc| Box::new(PipeDash::new(cc))));
|
|
|
|
eframe::run_native("PipeDash", opts, Box::new(|cc| Box::new(PipeDash::new(cc))));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|