1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
use std::{hash::Hash, time::Duration};
use ggez::graphics::Rect;
use crate::ui::{UiContent, UiElement};
use super::{Layout, Visuals};
/// A Transition stuct that can be added to an UiElement to slowly change that elements properties over time.
/// A transition can change the elements layout, visuals, hover_visuals, content and tooltip by first augmenting the transition with the relevant methods.
pub struct Transition<T: Copy + Eq + Hash> {
/// The layout transitioned to.
pub(crate) new_layout: Option<Layout>,
/// The visuals transitioned to.
pub(crate) new_visuals: Option<Visuals>,
/// The hover visuals transitioned to.
pub(crate) new_hover_visuals: Option<Option<Visuals>>,
/// The content transitioned to.
pub(crate) new_content: Option<Box<dyn UiContent<T>>>,
/// The tooltip transitioned to.
pub(crate) new_tooltip: Option<Option<Box<UiElement<T>>>>,
/// The total duration this transition will take.
total_duration: Duration,
/// The duration that has already passed with this transition active.
progressed_duration: Duration,
}
impl<T: Copy + Eq + Hash> std::fmt::Debug for Transition<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Transition")
.field("new_layout", &self.new_layout)
.field("new_visuals", &self.new_visuals)
.field("new_hover_visuals", &self.new_hover_visuals)
.field("new_tooltip", &self.new_tooltip)
.field("total_duration", &self.total_duration)
.field("progressed_duration", &self.progressed_duration)
.finish()
}
}
impl<T: Copy + Eq + Hash> Transition<T> {
/// Creates a new transition with the specified duration and all possible augmentations set to none. Can be used without adding changes to delay transitions added later.
pub fn new(duration: Duration) -> Self {
Self {
new_layout: None,
new_visuals: None,
new_hover_visuals: None,
new_content: None,
new_tooltip: None,
total_duration: duration,
progressed_duration: Duration::ZERO,
}
}
/// Augments this transition to now (gradually) change the layout of the UiElement it is added to, moving it smoothly between the two positions.
pub fn with_new_layout(mut self, new_layout: Layout) -> Self {
self.new_layout = Some(new_layout);
self
}
/// Augments the transition to now (gradually) change the visuals of the UiElement it is added to, blending over smoothly.
pub fn with_new_visuals(mut self, new_visuals: Visuals) -> Self {
self.new_visuals = Some(new_visuals);
self
}
/// Augments this transition to now (gradually) change the visuals of the UiElement it is added to when hovered, blending over smoothly.
/// Can be set to None to remove any special visuals when hovered. In this case, the transition will make the elements hover visuals slowly blend to the elements non-hover visuals.
pub fn with_new_hover_visuals(mut self, new_hover_visuals: Option<Visuals>) -> Self {
self.new_hover_visuals = Some(new_hover_visuals);
self
}
/// Augment this transition to now change the content of the UiElement it is added to. This change happens in a single frame as soon as the transitions duration has elapsed.
pub fn with_new_content<E>(mut self, new_content: E) -> Self
where
E: UiContent<T> + 'static,
{
self.new_content = Some(Box::new(new_content));
self
}
/// Augment this transition to now change the tooltip of the UiElement it is added to. This change happens in a single frame as soon as the transitions duration has elapsed.
pub fn with_new_tooltip(mut self, new_tooltip: Option<UiElement<T>>) -> Self {
self.new_tooltip = new_tooltip.map(|element| Some(Box::new(element)));
self
}
/// Progresses the internal timer of this transition by the specified amount. Returns true if the Transition is now complete and false otherwise.
pub(crate) fn progress(&mut self, delta: Duration) -> bool {
self.progressed_duration += delta;
self.progressed_duration >= self.total_duration
}
/// Returns a float in between 0. and 1. describung how much of this transitions total duration has elapsed already.
pub(crate) fn get_progress_ratio(&self) -> f32 {
self.progressed_duration.as_secs_f32() / self.total_duration.as_secs_f32()
}
}
/// Returns the average of two rectangles (all four values are averaged). rect1 is weighted by (1-ratio), while rect2 is weighted by ratio.
/// Thus, ratio=0 returns rect1 and ratio=1 returns rect2. The progression between the two is linear and continuous.
pub(crate) fn average_rect(rect1: &Rect, rect2: &Rect, ratio: f32) -> Rect {
Rect {
x: rect1.x * (1. - ratio) + rect2.x * ratio,
y: rect1.y * (1. - ratio) + rect2.y * ratio,
w: rect1.w * (1. - ratio) + rect2.w * ratio,
h: rect1.h * (1. - ratio) + rect2.h * ratio,
}
}