Fix and display metronome

This commit is contained in:
2025-06-21 22:58:49 +02:00
parent 89645db1d9
commit 7e6a539296
19 changed files with 345 additions and 131 deletions

View File

@@ -28,7 +28,7 @@ async fn main() -> Result<()> {
// Logger
simple_logger::SimpleLogger::new()
.with_level(log::LevelFilter::Error)
.with_level(log::LevelFilter::Info)
.with_module_level("gui::osc_client", log::LevelFilter::Off)
.init()
.expect("Could not initialize logger");

View File

@@ -11,23 +11,24 @@ impl App {
impl eframe::App for App {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
let state = self.state_receiver.borrow_and_update().clone();
ctx.style_mut(|style| {
style.visuals.panel_fill = egui::Color32::from_rgb(17, 17, 17);
style.visuals.window_fill = egui::Color32::from_rgb(17, 17, 17);
style.spacing.item_spacing = egui::vec2(0.0, 0.0);
});
egui::CentralPanel::default().show(ctx, |ui| {
ui.columns(state.columns, |ui| {
for (i, column) in state.cells.iter().enumerate() {
let selected_row = if i == state.selected_column {
Some(state.selected_row)
} else {
None
};
ui[i].add(super::Column::new(column, selected_row));
}
ui.vertical(|ui| {
ui.add(super::Metronome::new(state.metronome_position));
ui.add(super::Matrix::new(
&state.cells,
state.selected_column,
state.selected_row,
));
});
});
ctx.request_repaint();
}
}

35
gui/src/ui/matrix.rs Normal file
View File

@@ -0,0 +1,35 @@
pub struct Matrix<'a> {
cells: &'a Vec<Vec<osc::Track>>,
selected_column: usize,
selected_row: usize,
}
impl<'a> Matrix<'a> {
pub fn new(
cells: &'a Vec<Vec<osc::Track>>,
selected_column: usize,
selected_row: usize,
) -> Self {
Self {
cells,
selected_column,
selected_row,
}
}
}
impl<'a> egui::Widget for Matrix<'a> {
fn ui(self, ui: &mut egui::Ui) -> egui::Response {
ui.columns(self.cells.len(), |ui| {
for (i, column) in self.cells.iter().enumerate() {
let selected_row = if i == self.selected_column {
Some(self.selected_row)
} else {
None
};
ui[i].add(super::Column::new(column, selected_row));
}
});
ui.response()
}
}

65
gui/src/ui/metronome.rs Normal file
View File

@@ -0,0 +1,65 @@
pub struct Metronome {
position: f32, // 0.0 - 1.0
}
impl Metronome {
pub fn new(position: f32) -> Self {
Self {
position: position.clamp(0.0, 1.0),
}
}
}
impl egui::Widget for Metronome {
fn ui(self, ui: &mut egui::Ui) -> egui::Response {
let sf = ui.available_width() / 100.0;
// Simple scaling values
let rail_height = sf * 1.0;
let circle_radius = sf * 2.0;
let h_padding = sf * 5.0;
let v_padding = sf * 3.0;
// Colors
let rail_color = egui::Color32::from_rgb(51, 51, 51); // #333333
let circle_color = egui::Color32::from_rgb(0, 255, 136); // #00ff88
// Calculate widget size
let width = ui.available_width();
let height = rail_height + 2.0 * v_padding;
let size = egui::vec2(width, height);
// Allocate the space we need
let (rect, response) = ui.allocate_exact_size(size, egui::Sense::hover());
// Calculate rail position (centered)
let rail_left = rect.min.x + h_padding;
let rail_right = rect.max.x - h_padding;
let rail_width = rail_right - rail_left;
let rail_center_y = rect.center().y;
let rail_top = rail_center_y - rail_height / 2.0;
let rail_bottom = rail_center_y + rail_height / 2.0;
let rail_rect = egui::Rect::from_min_max(
egui::pos2(rail_left, rail_top),
egui::pos2(rail_right, rail_bottom),
);
// Calculate circle position
let circle_x = rail_left + self.position * rail_width;
let circle_center = egui::pos2(circle_x, rail_center_y);
// Draw rail
ui.painter().rect_filled(
rail_rect,
egui::Rounding::same(rail_height / 2.0),
rail_color,
);
// Draw circle
ui.painter()
.circle_filled(circle_center, circle_radius, circle_color);
response
}
}

View File

@@ -1,8 +1,12 @@
mod app;
mod column;
mod matrix;
mod metronome;
mod track;
mod ui_rows_ext;
pub use app::App;
use column::Column;
use matrix::Matrix;
use metronome::Metronome;
use track::Track;

View File

@@ -66,25 +66,22 @@ impl<'a> egui::Widget for Track<'a> {
let volume_bar_height = 2.0 * sf;
let margin = 4.0 * sf;
let frame_rect = frame_response.response.rect;
// Calculate volume bar position (bottom of the frame, inside the margins)
let inner_rect = frame_rect.shrink(5.0 * sf); // Match the outer_margin
let volume_bar_rect = egui::Rect::from_min_max(
egui::pos2(
inner_rect.min.x + margin,
inner_rect.max.y - volume_bar_height - margin
inner_rect.max.y - volume_bar_height - margin,
),
egui::pos2(
inner_rect.max.x - margin,
inner_rect.max.y - margin
)
egui::pos2(inner_rect.max.x - margin, inner_rect.max.y - margin),
);
// Draw volume bar background
ui.painter().rect_filled(
volume_bar_rect,
egui::Rounding::same(1.0 * sf),
egui::Color32::from_rgb(51, 51, 51) // #333333
egui::Color32::from_rgb(51, 51, 51), // #333333
);
// Draw volume bar fill (only if volume > 0)
@@ -93,17 +90,17 @@ impl<'a> egui::Widget for Track<'a> {
if fill_width > 0.0 {
let fill_rect = egui::Rect::from_min_size(
volume_bar_rect.min,
egui::vec2(fill_width, volume_bar_rect.height())
egui::vec2(fill_width, volume_bar_rect.height()),
);
ui.painter().rect_filled(
fill_rect,
egui::Rounding::same(1.0 * sf),
egui::Color32::from_rgb(0, 255, 136) // #00ff88
egui::Color32::from_rgb(0, 255, 136), // #00ff88
);
}
}
frame_response.response
}
}
}