Set default timeline mode based on activity
This commit is contained in:
parent
121fa3a50a
commit
0d7f15853c
|
|
@ -133,6 +133,15 @@ impl Default for TimeSignature {
|
|||
|
||||
fn default_bpm() -> f64 { 120.0 }
|
||||
|
||||
/// How time is displayed in the timeline
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
|
||||
pub enum TimelineMode {
|
||||
#[default]
|
||||
Seconds,
|
||||
Measures,
|
||||
Frames,
|
||||
}
|
||||
|
||||
/// Asset category for folder tree access
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum AssetCategory {
|
||||
|
|
@ -226,6 +235,10 @@ pub struct Document {
|
|||
#[serde(default)]
|
||||
pub script_folders: AssetFolderTree,
|
||||
|
||||
/// How time is displayed in the timeline (saved with document)
|
||||
#[serde(default)]
|
||||
pub timeline_mode: TimelineMode,
|
||||
|
||||
/// Current UI layout state (serialized for save/load)
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub ui_layout: Option<LayoutNode>,
|
||||
|
|
@ -270,6 +283,7 @@ impl Default for Document {
|
|||
effect_folders: AssetFolderTree::new(),
|
||||
script_definitions: HashMap::new(),
|
||||
script_folders: AssetFolderTree::new(),
|
||||
timeline_mode: TimelineMode::Seconds,
|
||||
ui_layout: None,
|
||||
ui_layout_base: None,
|
||||
current_time: 0.0,
|
||||
|
|
|
|||
|
|
@ -1490,6 +1490,13 @@ impl EditorApp {
|
|||
}
|
||||
};
|
||||
|
||||
// Set default timeline mode based on activity
|
||||
document.timeline_mode = match layout_index {
|
||||
2 => lightningbeam_core::document::TimelineMode::Measures, // Music
|
||||
1 => lightningbeam_core::document::TimelineMode::Seconds, // Video
|
||||
_ => lightningbeam_core::document::TimelineMode::Frames, // Animation, Painting, etc.
|
||||
};
|
||||
|
||||
// Reset action executor with new document
|
||||
self.action_executor = lightningbeam_core::action::ActionExecutor::new(document);
|
||||
|
||||
|
|
|
|||
|
|
@ -136,13 +136,7 @@ enum ClipDragType {
|
|||
LoopExtendLeft,
|
||||
}
|
||||
|
||||
/// How time is displayed in the ruler and header
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
enum TimeDisplayFormat {
|
||||
Seconds,
|
||||
Measures,
|
||||
Frames,
|
||||
}
|
||||
use lightningbeam_core::document::TimelineMode;
|
||||
|
||||
/// State for an in-progress layer header drag-to-reorder operation.
|
||||
struct LayerDragState {
|
||||
|
|
@ -194,7 +188,7 @@ pub struct TimelinePane {
|
|||
context_menu_clip: Option<(Option<uuid::Uuid>, egui::Pos2)>,
|
||||
|
||||
/// Whether to display time as seconds or measures
|
||||
time_display_format: TimeDisplayFormat,
|
||||
time_display_format: TimelineMode,
|
||||
|
||||
/// Waveform upload progress: pool_index -> frames uploaded so far.
|
||||
/// Tracks chunked GPU uploads across frames to avoid hitches.
|
||||
|
|
@ -673,7 +667,7 @@ impl TimelinePane {
|
|||
mousedown_pos: None,
|
||||
layer_control_clicked: false,
|
||||
context_menu_clip: None,
|
||||
time_display_format: TimeDisplayFormat::Seconds,
|
||||
time_display_format: TimelineMode::Seconds,
|
||||
waveform_upload_progress: std::collections::HashMap::new(),
|
||||
video_thumbnail_textures: std::collections::HashMap::new(),
|
||||
layer_drag: None,
|
||||
|
|
@ -691,7 +685,7 @@ impl TimelinePane {
|
|||
|
||||
/// Returns true if the timeline is currently in Measures display mode.
|
||||
pub fn is_measures_mode(&self) -> bool {
|
||||
self.time_display_format == TimeDisplayFormat::Measures
|
||||
self.time_display_format == TimelineMode::Measures
|
||||
}
|
||||
|
||||
/// Execute a view action with the given parameters
|
||||
|
|
@ -931,7 +925,7 @@ impl TimelinePane {
|
|||
// Must happen before Step 4 so no clips or backend recordings are created yet.
|
||||
if *shared.count_in_enabled
|
||||
&& *shared.metronome_enabled
|
||||
&& self.time_display_format == TimeDisplayFormat::Measures
|
||||
&& self.time_display_format == TimelineMode::Measures
|
||||
{
|
||||
let (bpm, beats_per_measure) = {
|
||||
let doc = shared.action_executor.document();
|
||||
|
|
@ -1358,8 +1352,8 @@ impl TimelinePane {
|
|||
framerate: f64,
|
||||
) -> Option<f64> {
|
||||
match self.time_display_format {
|
||||
TimeDisplayFormat::Frames => Some(1.0 / framerate),
|
||||
TimeDisplayFormat::Measures => {
|
||||
TimelineMode::Frames => Some(1.0 / framerate),
|
||||
TimelineMode::Measures => {
|
||||
use lightningbeam_core::beat_time::{beat_duration, measure_duration};
|
||||
let beat = beat_duration(bpm);
|
||||
let measure = measure_duration(bpm, time_sig);
|
||||
|
|
@ -1379,7 +1373,7 @@ impl TimelinePane {
|
|||
}
|
||||
Some(measure)
|
||||
}
|
||||
TimeDisplayFormat::Seconds => None,
|
||||
TimelineMode::Seconds => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1456,7 +1450,7 @@ impl TimelinePane {
|
|||
let text_color = text_style.text_color.unwrap_or(egui::Color32::from_gray(200));
|
||||
|
||||
match self.time_display_format {
|
||||
TimeDisplayFormat::Seconds => {
|
||||
TimelineMode::Seconds => {
|
||||
let interval = self.calculate_ruler_interval();
|
||||
let start_time = (self.viewport_start_time / interval).floor() * interval;
|
||||
let end_time = self.x_to_time(rect.width());
|
||||
|
|
@ -1489,7 +1483,7 @@ impl TimelinePane {
|
|||
time += interval;
|
||||
}
|
||||
}
|
||||
TimeDisplayFormat::Measures => {
|
||||
TimelineMode::Measures => {
|
||||
let beats_per_second = bpm / 60.0;
|
||||
let beat_dur = lightningbeam_core::beat_time::beat_duration(bpm);
|
||||
let bpm_count = time_sig.numerator;
|
||||
|
|
@ -1542,7 +1536,7 @@ impl TimelinePane {
|
|||
}
|
||||
}
|
||||
}
|
||||
TimeDisplayFormat::Frames => {
|
||||
TimelineMode::Frames => {
|
||||
let interval = self.calculate_ruler_interval_frames(framerate);
|
||||
let start_frame = (self.viewport_start_time.max(0.0) * framerate).floor() as i64;
|
||||
let end_frame = (self.x_to_time(rect.width()) * framerate).ceil() as i64;
|
||||
|
|
@ -2580,7 +2574,7 @@ impl TimelinePane {
|
|||
|
||||
// Grid lines matching ruler
|
||||
match self.time_display_format {
|
||||
TimeDisplayFormat::Seconds => {
|
||||
TimelineMode::Seconds => {
|
||||
let interval = self.calculate_ruler_interval();
|
||||
let start_time = (self.viewport_start_time / interval).floor() * interval;
|
||||
let end_time = self.x_to_time(rect.width());
|
||||
|
|
@ -2597,7 +2591,7 @@ impl TimelinePane {
|
|||
time += interval;
|
||||
}
|
||||
}
|
||||
TimeDisplayFormat::Measures => {
|
||||
TimelineMode::Measures => {
|
||||
let beats_per_second = document.bpm / 60.0;
|
||||
let bpm_count = document.time_signature.numerator;
|
||||
let start_beat = (self.viewport_start_time.max(0.0) * beats_per_second).floor() as i64;
|
||||
|
|
@ -2615,7 +2609,7 @@ impl TimelinePane {
|
|||
);
|
||||
}
|
||||
}
|
||||
TimeDisplayFormat::Frames => {
|
||||
TimelineMode::Frames => {
|
||||
let framerate = document.framerate;
|
||||
let px_per_frame = self.pixels_per_second / framerate as f32;
|
||||
|
||||
|
|
@ -4723,6 +4717,9 @@ impl TimelinePane {
|
|||
|
||||
impl PaneRenderer for TimelinePane {
|
||||
fn render_header(&mut self, ui: &mut egui::Ui, shared: &mut SharedPaneState) -> bool {
|
||||
// Sync timeline mode from document (document is source of truth)
|
||||
self.time_display_format = shared.action_executor.document().timeline_mode;
|
||||
|
||||
// Fire deferred recording commands once count-in pre-roll has elapsed
|
||||
self.check_pending_recording_start(shared);
|
||||
|
||||
|
|
@ -4823,7 +4820,7 @@ impl PaneRenderer for TimelinePane {
|
|||
}
|
||||
|
||||
// Metronome toggle — only visible in Measures mode
|
||||
if self.time_display_format == TimeDisplayFormat::Measures {
|
||||
if self.time_display_format == TimelineMode::Measures {
|
||||
ui.add_space(4.0);
|
||||
|
||||
let metro_tint = if *shared.metronome_enabled {
|
||||
|
|
@ -4897,10 +4894,10 @@ impl PaneRenderer for TimelinePane {
|
|||
};
|
||||
|
||||
match self.time_display_format {
|
||||
TimeDisplayFormat::Seconds => {
|
||||
TimelineMode::Seconds => {
|
||||
ui.colored_label(text_color, format!("Time: {:.2}s / {:.2}s", *shared.playback_time, self.duration));
|
||||
}
|
||||
TimeDisplayFormat::Measures => {
|
||||
TimelineMode::Measures => {
|
||||
let time_sig = lightningbeam_core::document::TimeSignature { numerator: time_sig_num, denominator: time_sig_den };
|
||||
let pos = lightningbeam_core::beat_time::time_to_measure(
|
||||
*shared.playback_time, bpm, &time_sig,
|
||||
|
|
@ -4911,7 +4908,7 @@ impl PaneRenderer for TimelinePane {
|
|||
time_sig_num, time_sig_den,
|
||||
));
|
||||
}
|
||||
TimeDisplayFormat::Frames => {
|
||||
TimelineMode::Frames => {
|
||||
let current_frame = (*shared.playback_time * framerate).floor() as i64 + 1;
|
||||
let total_frames = (self.duration * framerate).ceil() as i64;
|
||||
ui.colored_label(text_color, format!(
|
||||
|
|
@ -4930,16 +4927,18 @@ impl PaneRenderer for TimelinePane {
|
|||
// Time display format toggle
|
||||
egui::ComboBox::from_id_salt("time_format")
|
||||
.selected_text(match self.time_display_format {
|
||||
TimeDisplayFormat::Seconds => "Seconds",
|
||||
TimeDisplayFormat::Measures => "Measures",
|
||||
TimeDisplayFormat::Frames => "Frames",
|
||||
TimelineMode::Seconds => "Seconds",
|
||||
TimelineMode::Measures => "Measures",
|
||||
TimelineMode::Frames => "Frames",
|
||||
})
|
||||
.width(80.0)
|
||||
.show_ui(ui, |ui| {
|
||||
ui.selectable_value(&mut self.time_display_format, TimeDisplayFormat::Seconds, "Seconds");
|
||||
ui.selectable_value(&mut self.time_display_format, TimeDisplayFormat::Measures, "Measures");
|
||||
ui.selectable_value(&mut self.time_display_format, TimeDisplayFormat::Frames, "Frames");
|
||||
ui.selectable_value(&mut self.time_display_format, TimelineMode::Seconds, "Seconds");
|
||||
ui.selectable_value(&mut self.time_display_format, TimelineMode::Measures, "Measures");
|
||||
ui.selectable_value(&mut self.time_display_format, TimelineMode::Frames, "Frames");
|
||||
});
|
||||
// Write change back to document so it persists and is the source of truth
|
||||
shared.action_executor.document_mut().timeline_mode = self.time_display_format;
|
||||
|
||||
ui.separator();
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue