From 17c3326c36dd926e76ed2a6f1dc57ee3caa37c68 Mon Sep 17 00:00:00 2001 From: Benedikt Terhechte Date: Mon, 18 Oct 2021 14:16:24 +0200 Subject: [PATCH] Step towards the importer screen. --- Cargo.lock | 1 + Cargo.toml | 1 + src/gui/app.rs | 10 +-- src/gui/app_state/import.rs | 116 ++++++++++++++++++++++++++++++++++ src/gui/app_state/mod.rs | 1 + src/gui/app_state/startup.rs | 7 +- src/gui/widgets/background.rs | 65 ++++++++++++++----- 7 files changed, 179 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5c0172a..03320ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -860,6 +860,7 @@ dependencies = [ "mbox-reader", "num-format", "objc", + "rand", "rayon", "regex", "rfd", diff --git a/Cargo.toml b/Cargo.toml index af63a48..ee79996 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,6 +49,7 @@ emlx = { git = "https://github.com/terhechte/emlx", features = []} walkdir = "*" mbox-reader = "0.2.0" rfd = "0.5.1" +rand = "0.8.4" [features] default = ["gui"] diff --git a/src/gui/app.rs b/src/gui/app.rs index fefbf41..f825624 100644 --- a/src/gui/app.rs +++ b/src/gui/app.rs @@ -4,10 +4,11 @@ use eframe::{ }; use eyre::Result; -use super::app_state::{self, Startup, Visualize}; +use super::app_state::{self, Import, Startup, Visualize}; pub enum GmailDBApp { Startup { panel: Startup }, + Import { panel: Import }, Visualize { panel: Visualize }, } @@ -15,8 +16,8 @@ impl GmailDBApp { pub fn new() -> Result { // Temporarily create config without state machine let config = app_state::make_temporary_ui_config(); - Ok(GmailDBApp::Startup { - panel: Startup::default(), + Ok(GmailDBApp::Import { + panel: Import::new(config), }) } } @@ -50,6 +51,7 @@ impl epi::App for GmailDBApp { fn update(&mut self, ctx: &egui::CtxRef, frame: &mut epi::Frame<'_>) { match self { GmailDBApp::Startup { panel } => Self::update_panel(panel, ctx, frame), + GmailDBApp::Import { panel } => Self::update_panel(panel, ctx, frame), _ => panic!(), } @@ -59,7 +61,7 @@ impl epi::App for GmailDBApp { } impl GmailDBApp { - fn update_panel(panel: &mut Startup, ctx: &egui::CtxRef, _frame: &mut epi::Frame<'_>) { + fn update_panel(panel: impl egui::Widget, ctx: &egui::CtxRef, _frame: &mut epi::Frame<'_>) { egui::CentralPanel::default() .frame(egui::containers::Frame::none()) .show(ctx, |ui| { diff --git a/src/gui/app_state/import.rs b/src/gui/app_state/import.rs index e69de29..c6da3fe 100644 --- a/src/gui/app_state/import.rs +++ b/src/gui/app_state/import.rs @@ -0,0 +1,116 @@ +//! The startup form to configure what and how to import +use eframe::egui::epaint::Shadow; +use eframe::egui::{self, vec2, Color32, Pos2, Rect, Response, Stroke, TextStyle, Vec2, Widget}; +use rand::seq::SliceRandom; + +use super::super::platform::platform_colors; +use super::super::widgets::background::{shadow_background, AnimatedBackground}; +use crate::types::Config; +use crate::types::FormatType; + +pub struct Import { + config: Config, + /// The animation divisions + animation_divisions: usize, + /// time counter + timer: f64, + /// recursive offset counter + offset_counter: usize, + /// We use this to have the initial background resize + /// animation + intro_timer: f64, + /// This defines the amount of progress blocks we intend + /// to animate + progress_blocks: Vec, + /// The progress divisions + progress_divisions: usize, +} + +impl Import { + pub fn new(config: Config) -> Self { + // Build a random distribution of elements + // to animate the import process + let mut rng = rand::thread_rng(); + let animation_divisions = 6; + let progress_divisions = 4; + + // the amount of progress blocks + let progress_block_count = + (animation_divisions * progress_divisions) * (animation_divisions * progress_divisions); + let mut progress_blocks: Vec = (0..progress_block_count).collect(); + dbg!(progress_block_count); + progress_blocks.shuffle(&mut rng); + + Self { + animation_divisions, + config, + timer: 0.0, + offset_counter: 0, + intro_timer: 0.0, + progress_blocks, + progress_divisions, + } + } +} + +impl Widget for &mut Import { + fn ui(self, ui: &mut egui::Ui) -> Response { + self.intro_timer += ui.input().unstable_dt as f64; + let growth = self.intro_timer.clamp(0.0, 1.0); + + let available = ui.available_size(); + + // We take the progress as a value fromt the blocks + // FIXME: temporary using intro timer + let p = ((self.intro_timer * 5.0) / 100.0); + let n = (self.progress_blocks.len() as f64 * p) as usize; + //println!("{} / {}", n, self.progress_blocks.len()); + let slice = &self.progress_blocks[0..=n]; + + AnimatedBackground { + divisions: self.animation_divisions, + animate_progress: Some((slice, self.progress_divisions)), + timer: &mut self.timer, + offset_counter: &mut self.offset_counter, + } + .draw_background(ui, available); + + let desired_height = 370.0 - (270.0 * growth) as f32; + let desired_size = egui::vec2(330.0, desired_height); + + let paint_rect = Rect::from_min_size( + Pos2 { + x: available.x / 2.0 - desired_size.x / 2.0, + y: available.y / 2.0 - desired_size.y / 2.0, + }, + desired_size, + ); + + // calculate in margin + let center = paint_rect.shrink(15.0); + + let colors = platform_colors(); + + // Draw a backround with a shadow + shadow_background( + ui.painter(), + paint_rect, + colors.window_background_dark, + Stroke::new(1.0, Color32::from_gray(90)), + 12.0, + Shadow::big_dark(), + ); + + ui.allocate_ui_at_rect(center, |ui| { + ui.centered_and_justified(|ui| { + ui.vertical_centered_justified(|ui| { + ui.heading("Import in Progress"); + let bar = egui::widgets::ProgressBar::new(0.5).animate(true); + ui.add(bar); + ui.small("133 / 1000"); + }); + }) + }) + .response + } +} diff --git a/src/gui/app_state/mod.rs b/src/gui/app_state/mod.rs index 7828a34..dd13217 100644 --- a/src/gui/app_state/mod.rs +++ b/src/gui/app_state/mod.rs @@ -2,6 +2,7 @@ mod import; mod startup; mod visualize; +pub use import::Import; pub use startup::Startup; pub use visualize::{UIState, Visualize}; diff --git a/src/gui/app_state/startup.rs b/src/gui/app_state/startup.rs index 5c1ec56..5bac3c3 100644 --- a/src/gui/app_state/startup.rs +++ b/src/gui/app_state/startup.rs @@ -1,7 +1,6 @@ +//! The startup form to configure what and how to import use eframe::egui::epaint::Shadow; -use eframe::egui::{ - self, vec2, Color32, Painter, Pos2, Rect, Response, Shape, Stroke, TextStyle, Vec2, Widget, -}; +use eframe::egui::{self, vec2, Color32, Pos2, Rect, Response, Stroke, TextStyle, Vec2, Widget}; use rfd; use std::path::PathBuf; @@ -34,6 +33,8 @@ impl Widget for &mut Startup { let available = ui.available_size(); AnimatedBackground { + divisions: 6, + animate_progress: None, timer: &mut self.timer, offset_counter: &mut self.offset_counter, } diff --git a/src/gui/widgets/background.rs b/src/gui/widgets/background.rs index 64133a4..0f8d15d 100644 --- a/src/gui/widgets/background.rs +++ b/src/gui/widgets/background.rs @@ -62,6 +62,10 @@ pub fn shadow_background( /// A animated backwround with some parameters. /// Used in some `app_states`. pub struct AnimatedBackground<'a> { + /// The divisions + pub divisions: usize, + /// For each division cell, we take 8 sub divisions + pub animate_progress: Option<(&'a [usize], usize)>, /// time counter pub timer: &'a mut f64, /// recursive offset counter @@ -72,30 +76,37 @@ impl<'a> AnimatedBackground<'a> { pub fn draw_background(&mut self, ui: &mut egui::Ui, size: Vec2) { let painter = ui.painter(); - let division = 6.0; + let divisions = self.divisions as f32; // paint stuff - let rect_size = vec2(size.x / division, size.y / division); + let rect_size = vec2(size.x / divisions, size.y / divisions); - let offset = *self.timer * 42.5; + // we only animate if there's no progress + let (offset, add) = if self.animate_progress.is_none() { + // Define the animation speed + let offset = *self.timer * 42.5; - if offset > rect_size.x as f64 { - *self.timer = 0.0; - *self.offset_counter += 1; - } + if offset > rect_size.x as f64 { + *self.timer = 0.0; + *self.offset_counter += 1; + } - // Reset the offset counter as we're going out of the size - if (*self.offset_counter as f32 * rect_size.x) > (size.x * 1.1) { - *self.offset_counter = 0; - } + // Reset the offset counter as we're going out of the size + if (*self.offset_counter as f32 * rect_size.x) > (size.x * 1.1) { + *self.offset_counter = 0; + } - // figure out the offset addition - let add = *self.offset_counter as i8; + // figure out the offset addition + let add = *self.offset_counter as i8; + (offset, add) + } else { + (0.0, 0) + }; Self::draw_rectangles( painter, offset, - division, + divisions, rect_size, &[ (4 + add, 4, 3), @@ -110,9 +121,33 @@ impl<'a> AnimatedBackground<'a> { (1 + add, 5, 4), (3 + add, 6, 5), ], - division as usize, + self.divisions, ); + // Next, draw the rectangles + if let Some((blocks, d)) = self.animate_progress { + // the resolution of the block animation + let divisor = self.divisions * d; + let w = rect_size.x / d as f32; + let h = rect_size.y / d as f32; + let mut color_adder = self.divisions; + for n in blocks { + // calculate x/y from the value + let y = n / divisor; + let x = n % divisor; + let y = y as f32; + let x = x as f32; + let pos = Pos2::new(x * w, y * h); + let size = vec2(w, h); + let rect = Rect::from_min_size(pos, size); + // the fill color is based on the added block count + color_adder += *n; + let color = (color_adder % 50) as u8; + painter.rect_filled(rect, 0.0, Color32::from_gray(color)); + painter.rect_stroke(rect, 0.0, Stroke::new(1.0, Color32::from_gray(110))); + } + } + let diff = ui.input().unstable_dt as f64; *self.timer += diff;