153 lines
6.0 KiB
Rust
153 lines
6.0 KiB
Rust
//! Panel bodies. `show` dispatches on [`PanelKind`]; drag-and-drop between panels (project items,
|
|
//! effects) is carried in egui temp data so the timeline/monitors can accept drops.
|
|
|
|
pub mod dialogs;
|
|
pub mod effect_controls;
|
|
pub mod effects;
|
|
pub mod export_mode;
|
|
pub mod import_mode;
|
|
pub mod lumetri;
|
|
pub mod meters;
|
|
pub mod misc;
|
|
pub mod monitor;
|
|
pub mod project;
|
|
pub mod text;
|
|
pub mod timeline;
|
|
pub mod timeline_captions;
|
|
pub mod tools;
|
|
|
|
use egui::{Align2, Color32, Rect};
|
|
use filmcraft_project::ItemId;
|
|
|
|
use crate::FilmcraftApp;
|
|
use crate::dock::PanelKind;
|
|
use crate::theme::Tokens;
|
|
|
|
pub fn show(app: &mut FilmcraftApp, ui: &mut egui::Ui, p: PanelKind, rect: Rect) {
|
|
match p {
|
|
PanelKind::Program => monitor::show(app, ui, rect, monitor::Which::Program),
|
|
PanelKind::Source => monitor::show(app, ui, rect, monitor::Which::Source),
|
|
PanelKind::Timeline => timeline::show(app, ui, rect),
|
|
PanelKind::Project => project::show(app, ui, rect),
|
|
PanelKind::Tools => tools::show(app, ui, rect),
|
|
PanelKind::Effects => effects::show(app, ui, rect),
|
|
PanelKind::EffectControls => effect_controls::show(app, ui, rect),
|
|
PanelKind::AudioMeters => meters::show(app, ui, rect),
|
|
PanelKind::LumetriColor => lumetri::show(app, ui, rect),
|
|
PanelKind::Properties => effect_controls::properties_panel(app, ui, rect),
|
|
PanelKind::History => misc::history(app, ui, rect),
|
|
PanelKind::Markers => misc::markers(app, ui, rect),
|
|
PanelKind::Info => misc::info(app, ui, rect),
|
|
PanelKind::MediaBrowser => misc::media_browser(app, ui, rect),
|
|
PanelKind::AudioTrackMixer => meters::track_mixer(app, ui, rect),
|
|
PanelKind::LumetriScopes => misc::scopes(app, ui, rect),
|
|
PanelKind::Text => text::show(app, ui, rect),
|
|
other => crate::dock::placeholder(ui, rect, &app.tokens, &format!("{} — coming in a later milestone", other.title())),
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
enum DragPayload {
|
|
Item(ItemId),
|
|
Effect(String),
|
|
}
|
|
|
|
fn payload_id() -> egui::Id {
|
|
egui::Id::new("filmcraft-drag-payload")
|
|
}
|
|
|
|
pub fn start_drag_item(ui: &egui::Ui, item: ItemId) {
|
|
ui.ctx().data_mut(|d| d.insert_temp(payload_id(), Some(DragPayloadBox(DragPayload::Item(item)))));
|
|
}
|
|
pub fn start_drag_effect(ui: &egui::Ui, id: &str) {
|
|
ui.ctx().data_mut(|d| d.insert_temp(payload_id(), Some(DragPayloadBox(DragPayload::Effect(id.to_string())))));
|
|
}
|
|
#[derive(Clone, Debug)]
|
|
struct DragPayloadBox(DragPayload);
|
|
|
|
fn payload(ui: &egui::Ui) -> Option<DragPayload> {
|
|
ui.ctx().data(|d| d.get_temp::<Option<DragPayloadBox>>(payload_id())).flatten().map(|b| b.0)
|
|
}
|
|
pub fn dragged_project_item(ui: &egui::Ui) -> Option<ItemId> {
|
|
match payload(ui) {
|
|
Some(DragPayload::Item(i)) => Some(i),
|
|
_ => None,
|
|
}
|
|
}
|
|
pub fn dragged_effect(ui: &egui::Ui) -> Option<String> {
|
|
match payload(ui) {
|
|
Some(DragPayload::Effect(e)) => Some(e),
|
|
_ => None,
|
|
}
|
|
}
|
|
pub fn clear_drag(ui: &egui::Ui) {
|
|
ui.ctx().data_mut(|d| d.insert_temp::<Option<DragPayloadBox>>(payload_id(), None));
|
|
}
|
|
|
|
/// Draw the drag ghost near the pointer and clear the payload after release.
|
|
pub fn drag_ghost(app: &FilmcraftApp, ui: &egui::Ui) {
|
|
let Some(pl) = payload(ui) else { return };
|
|
let ctx = ui.ctx();
|
|
if let Some(p) = ctx.pointer_hover_pos() {
|
|
let label = match &pl {
|
|
DragPayload::Item(i) => app.session.project.item(*i).map(|x| x.name.clone()).unwrap_or_default(),
|
|
DragPayload::Effect(e) => filmcraft_project::find_effect(e).map(|d| d.name.to_string()).unwrap_or_default(),
|
|
};
|
|
let painter = ctx.layer_painter(egui::LayerId::new(egui::Order::Tooltip, egui::Id::new("drag-ghost")));
|
|
let r = Rect::from_min_size(p + egui::vec2(12.0, 8.0), egui::vec2(label.len() as f32 * 7.0 + 16.0, 20.0));
|
|
painter.rect_filled(r, 4.0, Color32::from_black_alpha(200));
|
|
painter.text(r.center(), Align2::CENTER_CENTER, label, Tokens::ui(11.5), Color32::WHITE);
|
|
}
|
|
if ctx.input(|i| i.pointer.any_released()) {
|
|
// cleared next frame so drop targets see the release this frame
|
|
let f = ctx.cumulative_frame_nr();
|
|
let k = egui::Id::new("drag-release-frame");
|
|
match ctx.data(|d| d.get_temp::<u64>(k)) {
|
|
Some(prev) if prev < f => {
|
|
clear_drag(ui);
|
|
ctx.data_mut(|d| d.remove::<u64>(k));
|
|
}
|
|
None => {
|
|
ctx.data_mut(|d| d.insert_temp(k, f));
|
|
}
|
|
Some(_) => {}
|
|
}
|
|
} else if !ctx.input(|i| i.pointer.any_down()) {
|
|
clear_drag(ui);
|
|
}
|
|
}
|
|
|
|
/// The panel "≡" menu.
|
|
pub fn panel_menu_popup(app: &mut FilmcraftApp, ui: &mut egui::Ui) {
|
|
drag_ghost(app, ui);
|
|
let id = egui::Id::new("panel-menu");
|
|
let Some((p, pos)) = ui.ctx().data(|d| d.get_temp::<(PanelKind, egui::Pos2)>(id)) else { return };
|
|
let mut close = false;
|
|
let area = egui::Area::new(id.with("area")).order(egui::Order::Foreground).fixed_pos(pos).show(ui.ctx(), |ui| {
|
|
egui::Frame::popup(ui.style()).show(ui, |ui| {
|
|
ui.set_min_width(190.0);
|
|
if ui.button("Close Panel").clicked() {
|
|
app.ui.dock.close(p);
|
|
close = true;
|
|
}
|
|
if ui.button("Maximize Frame").clicked() {
|
|
app.ui.dock = crate::dock::DockNode::Tabs { panels: vec![p], active: 0 };
|
|
close = true;
|
|
}
|
|
if ui.button("Restore Workspace").clicked() {
|
|
let w = app.ui.workspace.clone();
|
|
app.set_workspace(&w);
|
|
close = true;
|
|
}
|
|
if p == PanelKind::Timeline {
|
|
ui.separator();
|
|
ui.checkbox(&mut app.ui.timeline.show_thumbnails, "Video Thumbnails");
|
|
ui.checkbox(&mut app.ui.timeline.show_waveforms, "Audio Waveforms");
|
|
}
|
|
});
|
|
});
|
|
if close || (area.response.clicked_elsewhere() && ui.input(|i| i.pointer.any_pressed())) {
|
|
ui.ctx().data_mut(|d| d.remove::<(PanelKind, egui::Pos2)>(id));
|
|
}
|
|
}
|