tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }