mirror of
https://github.com/storytold/soundcraft.git
synced 2026-10-14 14:26:46 -04:00
Merge pull request #329 from grovebit/record-unarmed
Say at the Record button why Record did nothing
This commit is contained in:
4 files changed
+80
-6
No files matched your search
@@ -266,6 +266,8 @@ pub struct SoundApp {
|
||||
pub(crate) docking_context: Option<egui::Context>,
|
||||
/// Transport simulation when there is no player (tests, offscreen renders).
|
||||
sim: Option<(Samples, Option<Samples>, Option<Range>)>,
|
||||
/// When Record last found nothing to record on, and why: the Record buttons show it for a moment.
|
||||
record_hint: Option<(f64, &'static str)>,
|
||||
pub quit_requested: bool,
|
||||
/// Reset floating-window positions next frame (Window › Arrange).
|
||||
pub arrange_request: bool,
|
||||
@@ -330,6 +332,7 @@ impl SoundApp {
|
||||
theme_applied: None,
|
||||
docking_context: None,
|
||||
sim: None,
|
||||
record_hint: None,
|
||||
quit_requested: false,
|
||||
arrange_request: false,
|
||||
touched: std::collections::HashSet::new(),
|
||||
@@ -383,6 +386,13 @@ impl SoundApp {
|
||||
self.engine.transport.playing
|
||||
}
|
||||
|
||||
/// Why the last Record press did nothing, for a few seconds and while it still holds.
|
||||
pub(crate) fn record_hint(&self) -> Option<&'static str> {
|
||||
let (at, why) = self.record_hint?;
|
||||
let fresh = self.last_frame.unwrap_or_default() - at < 4.0;
|
||||
(fresh && !self.engine.session().tracks.iter().any(|t| t.mixer.record_arm)).then_some(why)
|
||||
}
|
||||
|
||||
/// Current transport position (playhead while playing, else the edit insertion).
|
||||
pub fn position(&self) -> Samples {
|
||||
if self.engine.transport.playing { self.engine.transport.position } else { self.engine.session().edit.selection.start }
|
||||
@@ -450,7 +460,13 @@ impl SoundApp {
|
||||
fn start_recording(&mut self) {
|
||||
let armed = self.engine.session().tracks.iter().any(|t| t.mixer.record_arm);
|
||||
if !armed {
|
||||
self.ui.status = "Record-enable a track first (the red button in its header).".into();
|
||||
let why = if self.engine.session().tracks.is_empty() {
|
||||
"Nothing to record on yet: add a track with Track › New…, then click its R button."
|
||||
} else {
|
||||
"No track is record-enabled: click the R button on the track to record."
|
||||
};
|
||||
self.ui.status = why.into();
|
||||
self.record_hint = Some((self.last_frame.unwrap_or_default(), why));
|
||||
return;
|
||||
}
|
||||
self.ensure_input();
|
||||
@@ -1021,6 +1037,29 @@ mod tests {
|
||||
assert_eq!(app.engine.transport.position, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn record_with_nothing_armed_says_what_to_do() {
|
||||
let mut app = SoundApp::new(soundcraft_engine::Engine::default(), None, Services::default());
|
||||
app.run("transport.record", json!({})).unwrap();
|
||||
app.handle_transport(0.25);
|
||||
assert!(!app.is_playing() && !app.engine.transport.recording);
|
||||
assert!(app.record_hint().is_some_and(|why| why.contains("Track › New")), "{:?}", app.record_hint());
|
||||
|
||||
app.run("track.new", json!({"count": 1, "format": "mono"})).unwrap();
|
||||
app.run("transport.record", json!({})).unwrap();
|
||||
app.handle_transport(0.25);
|
||||
assert!(app.record_hint().is_some_and(|why| why.contains("R button")), "{:?}", app.record_hint());
|
||||
assert_eq!(Some(app.ui.status.as_str()), app.record_hint());
|
||||
app.last_frame = Some(5.0);
|
||||
assert_eq!(app.record_hint(), None, "the hint shows for a few seconds");
|
||||
|
||||
app.last_frame = None;
|
||||
app.run("transport.record", json!({})).unwrap();
|
||||
app.handle_transport(0.25);
|
||||
app.run("mix.record_arm", json!({})).unwrap();
|
||||
assert_eq!(app.record_hint(), None, "arming a track answers the hint");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_hidden_window_still_autosaves() {
|
||||
let dir = std::env::temp_dir().join(format!("soundcraft-hidden-autosave-{}", std::process::id()));
|
||||
|
||||
@@ -272,15 +272,19 @@ fn transport_window(app: &mut SoundApp, ctx: &egui::Context) {
|
||||
("end", "transport.go_to_end"),
|
||||
("record", "transport.record"),
|
||||
] {
|
||||
if crate::widgets::icon_button(
|
||||
let resp = crate::widgets::icon_button(
|
||||
ui,
|
||||
vec2(34.0, 26.0),
|
||||
icon,
|
||||
(cmd == "transport.play" && app.is_playing()) || (cmd == "transport.record" && app.engine.transport.recording),
|
||||
cmd,
|
||||
)
|
||||
.clicked()
|
||||
);
|
||||
if cmd == "transport.record"
|
||||
&& let Some(why) = app.record_hint()
|
||||
{
|
||||
egui::Tooltip::for_widget(&resp).show(|ui| ui.label(why));
|
||||
}
|
||||
if resp.clicked() {
|
||||
let _ = app.run(cmd, json!({}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,7 +283,7 @@ fn transport(app: &mut SoundApp, ui: &mut Ui) {
|
||||
let playing = app.is_playing();
|
||||
let recording = app.engine.transport.recording;
|
||||
let looped = app.engine.session().edit.loop_playback;
|
||||
group(ui, 176.0, 64.0, |ui, _| {
|
||||
group(ui, 176.0, 64.0, |ui, g| {
|
||||
ui.vertical(|ui| {
|
||||
ui.spacing_mut().item_spacing = vec2(3.0, 4.0);
|
||||
ui.horizontal(|ui| {
|
||||
@@ -306,7 +306,13 @@ fn transport(app: &mut SoundApp, ui: &mut Ui) {
|
||||
let (r, resp) = ui.allocate_exact_size(vec2(38.0, 28.0), Sense::click());
|
||||
key(ui.painter(), r, &resp, if recording { Color32::from_rgb(110, 30, 30) } else { key_fill(&resp) });
|
||||
icons::draw(ui.painter(), r.shrink(5.0), "record", t.rec);
|
||||
if resp.on_hover_text("Record (Cmd+Space)").clicked() {
|
||||
let resp = resp.on_hover_text("Record (Cmd+Space)");
|
||||
if let Some(why) = app.record_hint() {
|
||||
// Below the whole group, so the hint covers none of its buttons.
|
||||
let below = Rect::from_min_max(r.min, pos2(r.max.x, g.max.y));
|
||||
egui::Tooltip::always_open(ui.ctx().clone(), resp.layer_id, resp.id, below).show(|ui| ui.label(why));
|
||||
}
|
||||
if resp.clicked() {
|
||||
let _ = app.run("transport.record", json!({}));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
//! Record with nothing to record on explains itself at the Record buttons.
|
||||
|
||||
use egui::vec2;
|
||||
use egui_kittest::Harness;
|
||||
use egui_kittest::kittest::Queryable;
|
||||
use soundcraft_ui_egui::{Services, SoundApp};
|
||||
|
||||
#[test]
|
||||
fn record_in_an_empty_session_shows_why_at_both_record_buttons() {
|
||||
let mut app = SoundApp::new(soundcraft_engine::Engine::default(), None, Services::default());
|
||||
app.ui.show_transport = true;
|
||||
let mut h = Harness::builder().with_size(vec2(1600.0, 1000.0)).build_ui_state(
|
||||
|ui, app: &mut SoundApp| {
|
||||
app.logic(&ui.ctx().clone());
|
||||
app.ui(ui);
|
||||
},
|
||||
app,
|
||||
);
|
||||
h.run_steps(5);
|
||||
assert_eq!(h.query_all_by_label_contains("Nothing to record on").count(), 0);
|
||||
h.get_by_label("transport.record").click();
|
||||
h.run_steps(3);
|
||||
assert!(!h.state().engine.transport.recording);
|
||||
assert_eq!(h.query_all_by_label_contains("Nothing to record on").count(), 2, "the toolbar's and the Transport window's");
|
||||
}
|
||||
Reference in new issue
Block a user