timeline.rs (4867B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 use super::Gui; 6 7 pub struct Timeline { 8 pub current_frame: usize, 9 } 10 11 impl Timeline { 12 pub fn new() -> Self { 13 Timeline { 14 current_frame: 0, 15 } 16 } 17 } 18 19 pub fn ui(app: &mut Gui, ui: &mut egui::Ui) { 20 let area = ui.max_rect(); 21 let mut top_area = area; 22 let mut bottom_area = area; 23 top_area.max.y -= 20.0; 24 bottom_area.min.y = top_area.max.y; 25 26 ui.scope_builder(egui::UiBuilder::new().max_rect(bottom_area), |ui| { 27 show_timeline( 28 bottom_area, 29 &mut app.data_model.timeline.current_frame, 30 app.data_model.frame_log.first_frame_index(), 31 app.data_model.frame_log.last_frame_index(), 32 ui, 33 ); 34 }); 35 } 36 37 pub fn show_timeline( 38 rect: egui::Rect, 39 current_frame: &mut usize, 40 first_frame: usize, 41 last_frame: usize, 42 ui: &mut egui::Ui, 43 ) { 44 ui.horizontal(|ui| { 45 let style = ui.style().clone(); 46 let radius = style.visuals.widgets.inactive.corner_radius; 47 48 let button_size = egui::Vec2 { x: 20.0, y: rect.height() }; 49 let prev = egui::Button::new("←") 50 .min_size(button_size) 51 .corner_radius(egui::CornerRadius { nw: radius.nw, sw: radius.sw, ne: 0, se: 0 }); 52 let next = egui::Button::new("→") 53 .min_size(button_size) 54 .corner_radius(egui::CornerRadius { ne: radius.ne, se: radius.se, nw: 0, sw: 0 }); 55 56 let spacing = ui.spacing().item_spacing.x; 57 ui.spacing_mut().item_spacing.x = 1.0; 58 59 let prev_clicked = ui.add(prev).clicked(); 60 61 ui.spacing_mut().item_spacing.x = spacing; 62 63 let next_clicked = ui.add(next).clicked(); 64 65 if prev_clicked { 66 *current_frame = (*current_frame).max(first_frame + 1) - 1 67 } 68 69 if next_clicked { 70 *current_frame = (*current_frame + 1).min(last_frame) 71 } 72 73 let min = ui.cursor().min; 74 let max = rect.max; 75 let tl_rect = egui::Rect { min, max }; 76 let size = max - min; 77 78 let sense = egui::Sense::CLICK 79 | egui::Sense::HOVER 80 | egui::Sense::FOCUSABLE 81 | egui::Sense::DRAG; 82 let response = ui.allocate_response(size, sense); 83 84 let num_frames = last_frame - first_frame + 1; 85 let n = num_frames as f32; 86 87 let y0 = min.y; 88 let y1 = min.y + size.y; 89 90 let background = style.visuals.widgets.inactive.bg_fill; 91 let border = style.visuals.widgets.inactive.bg_stroke; 92 let separator = egui::Stroke { width: 1.0, color: style.visuals.panel_fill }; 93 ui.painter().rect(tl_rect, radius, background, border, egui::StrokeKind::Inside); 94 95 let mut hovered_frame = None; 96 let mut prev_x = min.x; 97 for i in 0..num_frames { 98 let x = min.x + (i + 1) as f32 * size.x / n; 99 100 let frame_rect = egui::Rect { 101 min: egui::Pos2 { x: prev_x, y: y0 }, 102 max: egui::Pos2 { x, y: y1 }, 103 }; 104 105 if ui.rect_contains_pointer(frame_rect) { 106 hovered_frame = Some((i, frame_rect)) 107 } 108 109 if i != num_frames - 1 { 110 ui.painter().vline(x, egui::Rangef { min: y0, max: y1 }, separator); 111 } 112 prev_x = x; 113 } 114 115 let selected_cell_idx = *current_frame - first_frame; 116 117 if num_frames > 1 { 118 let x0 = min.x + selected_cell_idx as f32 * size.x / n; 119 let x1 = min.x + (selected_cell_idx + 1) as f32 * size.x / n; 120 let selected_frame_rect = egui::Rect { 121 min: egui::Pos2 { x: x0, y: y0 }, 122 max: egui::Pos2 { x: x1, y: y1 }, 123 }; 124 ui.painter().rect( 125 selected_frame_rect, 126 0u8, 127 style.visuals.widgets.active.bg_fill, 128 style.visuals.widgets.active.bg_stroke, 129 egui::StrokeKind::Inside, 130 ); 131 } 132 133 if num_frames > 0 { 134 if let Some((idx, frame_rect)) = hovered_frame { 135 ui.painter().rect( 136 frame_rect, 0u8, 137 if idx == selected_cell_idx { 138 style.visuals.widgets.active.bg_fill 139 } else { 140 style.visuals.widgets.hovered.bg_fill 141 }, 142 style.visuals.widgets.hovered.bg_stroke, 143 egui::StrokeKind::Inside, 144 ); 145 146 if response.clicked() || response.is_pointer_button_down_on() { 147 *current_frame = first_frame + idx; 148 } 149 } 150 } 151 }); 152 }