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