You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
postsack/ps-gui/src/widgets/background.rs

233 lines
7.3 KiB
Rust

//! Various background utilities
use eframe::egui::{
self,
epaint::{RectShape, Shadow},
vec2, Color32, Painter, Pos2, Rect, Response, Shape, Stroke, Ui, Vec2,
};
use std::ops::Rem;
use crate::platform::{platform_colors, PlatformColors};
/// This will draw Ui with a background color and margins.
/// This can be used for calls that don't provide a `Frame`,
/// such as `horizontal` or `vertical`
pub fn color_background(
ui: &mut Ui,
padding: f32,
stroke: Stroke,
fill: Color32,
show: impl FnOnce(&mut Ui) -> Response,
) -> Response {
let outer_rect_bounds = ui.available_rect_before_wrap();
let where_to_put_background = ui.painter().add(egui::Shape::Noop);
let margin = egui::Vec2::splat(padding);
let inner_rect = outer_rect_bounds.shrink2(margin);
let mut content_ui = ui.child_ui(inner_rect, *ui.layout());
let ret = show(&mut content_ui);
let outer_rect = Rect::from_min_max(outer_rect_bounds.min, content_ui.min_rect().max + margin);
let (rect, _) = ui.allocate_at_least(outer_rect.size(), egui::Sense::hover());
ui.painter().set(
where_to_put_background,
egui::epaint::Shape::Rect(RectShape {
corner_radius: 0.0,
fill,
stroke,
rect,
}),
);
ret
}
/// Draw a rectangular background with a shadow
pub fn shadow_background(
painter: &Painter,
paint_rect: Rect,
fill: Color32,
stroke: Stroke,
corner_radius: f32,
shadow: Shadow,
) {
let frame_shape = Shape::Rect(RectShape {
rect: paint_rect,
corner_radius,
fill,
stroke,
});
let shadow = shadow.tessellate(paint_rect, corner_radius);
let shadow = Shape::Mesh(shadow);
let shape = Shape::Vec(vec![shadow, frame_shape]);
painter.add(shape);
}
/// 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
pub offset_counter: &'a mut usize,
}
impl<'a> AnimatedBackground<'a> {
pub fn draw_background(&mut self, ui: &mut egui::Ui, size: Vec2) {
let painter = ui.painter();
let divisions = self.divisions as f32;
// paint stuff
let rect_size = vec2(size.x / divisions, size.y / divisions);
let colors = platform_colors();
// 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;
}
// 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;
(offset, add)
} else {
(0.0, 0)
};
Self::draw_rectangles(
painter,
offset,
divisions,
rect_size,
&[
(4 + add, 4, 3),
(3 + add, 3, 2),
(1 + add, 1, 5),
(5 + add, 2, 5),
(2 + add, 1, 6),
(3 + add, 3, 7),
(4 + add, 5, 1),
(3 + add, 3, 7),
(6 + add, 1, 3),
(1 + add, 5, 4),
(3 + add, 6, 5),
],
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_addition = if colors.is_light {
-((color_adder % 50) as i8)
} else {
(color_adder % 50) as i8
};
let color = Color32::from_rgb(
(colors.animation_background.r() as i8 + color_addition) as u8,
(colors.animation_background.g() as i8 + color_addition) as u8,
(colors.animation_background.b() as i8 + color_addition) as u8,
);
painter.rect_filled(rect, 0.0, color);
painter.rect_stroke(rect, 0.0, Stroke::new(1.0, colors.line3));
}
}
let diff = ui.input().unstable_dt as f64;
*self.timer += diff;
ui.ctx().request_repaint();
}
fn draw_rectangles(
painter: &Painter,
offset: f64,
division: f32,
size: Vec2,
recurse: &[(i8, i8, i8)],
total: usize,
) {
let colors = platform_colors();
for y in 0..=(division + 2.0) as i8 {
for x in 0..=(division + 2.0) as i8 {
let fx = ((x - 1) as f32 * size.x) + (offset as f32);
let fy = (y - 1) as f32 * size.y;
let pos = Pos2::new(fx, fy);
let rect = Rect::from_min_size(pos, size);
painter.rect_stroke(rect, 0.0, Stroke::new(1.0, Color32::from_gray(70)));
for (rx, ry, rd) in recurse {
// on the x axis take the offset into account
let rx = (*rx).rem((total as i8) + 1);
if rx == x && ry == &y {
Self::draw_segmentation(painter, rect, *rd, colors);
}
}
}
}
}
fn draw_segmentation(painter: &Painter, into: Rect, divisions: i8, colors: &PlatformColors) {
let mut rect = into;
for d in 0..=divisions {
// division back and forth in direction
let next = if d % 2 == 0 {
Rect::from_min_size(
Pos2 {
x: rect.center().x,
y: rect.top(),
},
Vec2 {
x: rect.width() / 2.0,
y: rect.height(),
},
)
} else {
Rect::from_min_size(
Pos2 {
x: rect.left(),
y: rect.center().y,
},
Vec2 {
x: rect.width(),
y: rect.height() / 2.0,
},
)
};
painter.rect_stroke(next, 0.0, Stroke::new(1.0, colors.line4));
rect = next;
}
}
}