Skip to content

Commit 05daf18

Browse files
committed
feat: add smooth marquee scrolling for track information and improve path display formatting
1 parent a0fb789 commit 05daf18

4 files changed

Lines changed: 462 additions & 83 deletions

File tree

src/audio.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1454,6 +1454,7 @@ pub fn start_audio_thread(file_path: &str, mic: bool, shared_state: Arc<Mutex<Ap
14541454

14551455
{
14561456
let mut state = shared_state.lock().unwrap();
1457+
state.track_ended = false;
14571458
state.artist = "Bitstream Active".to_string();
14581459
state.module_type = "Hardware Passthrough".to_string();
14591460
state.stats.bitstream_active = true;
@@ -1579,6 +1580,7 @@ pub fn start_audio_thread(file_path: &str, mic: bool, shared_state: Arc<Mutex<Ap
15791580

15801581
{
15811582
let mut state = shared_state.lock().unwrap();
1583+
state.track_ended = false;
15821584
state.artist = audio_source.get_artist();
15831585
state.module_type = audio_source.get_type();
15841586
state.duration_seconds = audio_source.get_duration_seconds();

src/engine.rs

Lines changed: 97 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3194,14 +3194,56 @@ impl<'a> VulkanEngine<'a> {
31943194
out_track_info_rect = Some(rect);
31953195
} else {
31963196
columns[2].style_mut().visuals.override_text_color = Some(egui::Color32::from_gray(235)); // Slightly lighter for contrast
3197-
columns[2].heading("Track Info");
3197+
let heading_text = if state.playlist.len() > 1 {
3198+
format!("Track Info ({}/{})", state.playlist_index + 1, state.playlist.len())
3199+
} else {
3200+
"Track Info".to_string()
3201+
};
3202+
columns[2].heading(heading_text);
31983203
columns[2].separator();
3204+
3205+
let render_smooth_marquee = |ui: &mut egui::Ui, text: &str, size: f32, is_title: bool| {
3206+
let available_width = ui.available_width();
3207+
let font_id = egui::FontId::proportional(size);
3208+
let color = ui.style().visuals.override_text_color.unwrap_or(egui::Color32::from_gray(235));
3209+
let text_color = if is_title { egui::Color32::WHITE } else { color };
3210+
3211+
let galley = ui.painter().layout_no_wrap(text.to_string(), font_id, text_color);
3212+
let text_width = galley.rect.width();
3213+
let height = galley.rect.height();
3214+
3215+
let (rect, _response) = ui.allocate_exact_size(egui::vec2(available_width, height), egui::Sense::hover());
3216+
let painter = ui.painter().with_clip_rect(rect);
3217+
3218+
if text_width > available_width {
3219+
let max_scroll = text_width - available_width;
3220+
let scroll_duration = max_scroll / 35.0; // 35 pixels per second
3221+
let total_period = 2.0 + scroll_duration + 2.0 + scroll_duration;
3222+
let t = (state.current_seconds as f32) % total_period;
3223+
3224+
let offset = if t < 2.0 {
3225+
0.0
3226+
} else if t < 2.0 + scroll_duration {
3227+
let progress = (t - 2.0) / scroll_duration;
3228+
progress * max_scroll
3229+
} else if t < 2.0 + scroll_duration + 2.0 {
3230+
max_scroll
3231+
} else {
3232+
let progress = (t - (2.0 + scroll_duration + 2.0)) / scroll_duration;
3233+
max_scroll - (progress * max_scroll)
3234+
};
3235+
3236+
painter.galley(rect.min + egui::vec2(-offset, 0.0), galley, text_color);
3237+
} else {
3238+
painter.galley(rect.min, galley, text_color);
3239+
}
3240+
};
3241+
31993242
let current_path_str = if state.playlist_index < state.playlist.len() {
32003243
state.playlist[state.playlist_index].clone()
32013244
} else {
32023245
state.song_title.clone()
32033246
};
3204-
32053247
let is_network = current_path_str.starts_with("http://") || current_path_str.starts_with("https://");
32063248
let file_name = if is_network {
32073249
state.song_title.clone()
@@ -3211,48 +3253,64 @@ impl<'a> VulkanEngine<'a> {
32113253
let file_dir = if is_network {
32123254
current_path_str
32133255
} else {
3214-
std::path::Path::new(&current_path_str).parent().unwrap_or(std::path::Path::new("")).to_string_lossy().to_string()
3215-
};
3216-
3217-
columns[2].horizontal(|ui| { ui.label("File/Title"); ui.label(&file_name); });
3218-
columns[2].horizontal(|ui| { ui.label("Artist"); ui.label(&state.artist); });
3219-
columns[2].horizontal(|ui| { ui.label(if is_network { "URL" } else { "Path" }); ui.label(&file_dir); });
3220-
if state.playlist.len() > 1 {
3221-
columns[2].horizontal(|ui| { ui.label("Playlist"); ui.label(format!("{} / {}", state.playlist_index + 1, state.playlist.len())); });
3222-
}
3223-
if state.playlist_index + 1 < state.playlist.len() {
3224-
let next_path = std::path::Path::new(&state.playlist[state.playlist_index + 1]);
3225-
let next_song = next_path.file_name().unwrap_or_default().to_string_lossy().to_string();
3226-
let max_len = 30;
3227-
let display_str = if next_song.chars().count() > max_len {
3228-
let chars_count = next_song.chars().count();
3229-
let offset = (state.current_seconds * 4.0) as usize % (chars_count + 10);
3230-
if offset < chars_count {
3231-
let mut padded = next_song.clone();
3232-
padded.push_str(" ");
3233-
padded.push_str(&next_song);
3234-
padded.chars().skip(offset).take(max_len).collect::<String>()
3256+
let abs_path = if std::path::Path::new(&current_path_str).is_absolute() {
3257+
std::path::PathBuf::from(&current_path_str)
3258+
} else if let Ok(cwd) = std::env::current_dir() {
3259+
cwd.join(&current_path_str)
3260+
} else {
3261+
std::path::PathBuf::from(&current_path_str)
3262+
};
3263+
let path_str = abs_path.to_string_lossy().to_string();
3264+
if let Ok(home) = std::env::var("HOME") {
3265+
if path_str.starts_with(&home) {
3266+
path_str.replacen(&home, "~", 1)
32353267
} else {
3236-
next_song.chars().take(max_len).collect::<String>()
3268+
path_str
3269+
}
3270+
} else if let Ok(home) = std::env::var("USERPROFILE") {
3271+
if path_str.starts_with(&home) {
3272+
path_str.replacen(&home, "%USERPROFILE%", 1)
3273+
} else {
3274+
path_str
32373275
}
32383276
} else {
3239-
next_song
3240-
};
3241-
columns[2].horizontal(|ui| { ui.label("Next Song:"); ui.label(display_str); });
3242-
}
3277+
path_str
3278+
}
3279+
};
3280+
3281+
// 1. Title/File Name (Prominent at top, smooth marquee if long)
3282+
render_smooth_marquee(&mut columns[2], &file_name, 15.0, true);
3283+
3284+
// 2. Artist
3285+
columns[2].horizontal(|ui| { ui.label("Artist"); ui.label(&state.artist); });
3286+
3287+
// 3. Path / URL (Smooth marquee if long, stretches to the right edge)
3288+
let path_label = if is_network { "URL" } else { "Path" };
3289+
columns[2].horizontal(|ui| {
3290+
ui.label(path_label);
3291+
render_smooth_marquee(ui, &file_dir, 14.0, false);
3292+
});
3293+
3294+
// 4. Type
32433295
columns[2].horizontal(|ui| { ui.label("Type"); ui.label(&state.module_type); });
3296+
3297+
// 5. Video
32443298
if let Some(video) = &state.video_info {
32453299
if video == "Unsupported Codec" {
32463300
columns[2].horizontal(|ui| { ui.label("Video"); ui.label(video); });
32473301
} else {
32483302
columns[2].horizontal(|ui| { ui.label("Video"); ui.label(format!("{} (Video stream available)", video)); });
32493303
}
32503304
}
3305+
3306+
// 6. Track layout parameters
32513307
if state.bpm > 0 { columns[2].horizontal(|ui| { ui.label("BPM"); ui.label(format!("{}", state.bpm)); }); }
32523308
if state.speed > 0 { columns[2].horizontal(|ui| { ui.label("Speed"); ui.label(format!("{}", state.speed)); }); }
32533309
if state.num_patterns > 0 { columns[2].horizontal(|ui| { ui.label("Patterns"); ui.label(format!("{}", state.num_patterns)); }); }
32543310
if state.num_instruments > 0 { columns[2].horizontal(|ui| { ui.label("Instruments"); ui.label(format!("{}", state.num_instruments)); }); }
32553311
if state.num_samples > 0 { columns[2].horizontal(|ui| { ui.label("Samples"); ui.label(format!("{}", state.num_samples)); }); }
3312+
3313+
// 7. Audio technical parameters
32563314
columns[2].horizontal(|ui| { ui.label("Sample Rate"); ui.label(format!("{} Hz", state.current_sample_rate as u32)); });
32573315
columns[2].horizontal(|ui| { ui.label("Bitrate"); ui.label(state.bitrate.map(|b| format!("{} kbps", b)).unwrap_or_else(|| "Unknown".to_string())); });
32583316
columns[2].horizontal(|ui| {
@@ -3277,6 +3335,17 @@ impl<'a> VulkanEngine<'a> {
32773335
ui.label(format!("{:.1}s", state.duration_seconds));
32783336
}
32793337
});
3338+
3339+
// 8. Next Song (placed at the bottom, smooth marquee if long)
3340+
if state.playlist_index + 1 < state.playlist.len() {
3341+
let next_path = std::path::Path::new(&state.playlist[state.playlist_index + 1]);
3342+
let next_song = next_path.file_name().unwrap_or_default().to_string_lossy().to_string();
3343+
columns[2].horizontal(|ui| {
3344+
ui.label("Next Song:");
3345+
render_smooth_marquee(ui, &next_song, 14.0, false);
3346+
});
3347+
}
3348+
32803349
out_track_info_rect = Some(columns[2].min_rect());
32813350
}
32823351
});

0 commit comments

Comments
 (0)