use gpui::{
div, prelude::FluentBuilder, relative, AnyElement, App, IntoElement, ParentElement, Pixels,
RenderOnce, Styled, Window,
};
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum AspectRatioPreset {
Square,
Standard,
Portrait,
Video,
Ultrawide,
Portrait23,
Landscape32,
}
impl AspectRatioPreset {
pub fn ratio(&self) -> f32 {
match self {
AspectRatioPreset::Square => 1.0,
AspectRatioPreset::Standard => 4.0 / 3.0,
AspectRatioPreset::Portrait => 3.0 / 4.0,
AspectRatioPreset::Video => 16.0 / 9.0,
AspectRatioPreset::Ultrawide => 21.0 / 9.0,
AspectRatioPreset::Portrait23 => 2.0 / 3.0,
AspectRatioPreset::Landscape32 => 3.0 / 2.0,
}
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum ObjectFit {
#[default]
Cover,
Contain,
Fill,
None,
}
#[derive(IntoElement)]
pub struct AspectRatio {
ratio: f32,
child: Option<AnyElement>,
object_fit: ObjectFit,
width: Option<Pixels>,
}
impl AspectRatio {
pub fn new(ratio: f32) -> Self {
Self {
ratio: ratio.max(0.001), child: None,
object_fit: ObjectFit::default(),
width: None,
}
}
pub fn preset(preset: AspectRatioPreset) -> Self {
Self::new(preset.ratio())
}
pub fn child(mut self, child: impl IntoElement) -> Self {
self.child = Some(child.into_any_element());
self
}
pub fn object_fit(mut self, fit: ObjectFit) -> Self {
self.object_fit = fit;
self
}
pub fn width(mut self, width: impl Into<Pixels>) -> Self {
self.width = Some(width.into());
self
}
pub fn ratio(mut self, ratio: f32) -> Self {
self.ratio = ratio.max(0.001);
self
}
}
impl Default for AspectRatio {
fn default() -> Self {
Self::new(1.0)
}
}
impl RenderOnce for AspectRatio {
fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
let padding_percent = (1.0 / self.ratio).clamp(0.0, 10.0);
let outer = div()
.relative()
.overflow_hidden()
.when_some(self.width, |el, w| el.w(w))
.when(self.width.is_none(), |el| el.w_full())
.h(relative(0.0))
.pb(relative(padding_percent));
let inner = div()
.absolute()
.inset_0()
.flex()
.items_center()
.justify_center()
.when(matches!(self.object_fit, ObjectFit::Fill), |el| {
el.size_full()
})
.when(matches!(self.object_fit, ObjectFit::Cover), |el| {
el.size_full().overflow_hidden()
})
.when(matches!(self.object_fit, ObjectFit::Contain), |el| {
el.size_full()
})
.when(matches!(self.object_fit, ObjectFit::None), |el| el)
.when_some(self.child, |el, child| el.child(child));
outer.child(inner)
}
}
pub fn aspect_ratio(ratio: f32) -> AspectRatio {
AspectRatio::new(ratio)
}
pub fn aspect_ratio_square() -> AspectRatio {
AspectRatio::preset(AspectRatioPreset::Square)
}
pub fn aspect_ratio_video() -> AspectRatio {
AspectRatio::preset(AspectRatioPreset::Video)
}
pub fn aspect_ratio_portrait() -> AspectRatio {
AspectRatio::preset(AspectRatioPreset::Portrait)
}
pub fn aspect_ratio_standard() -> AspectRatio {
AspectRatio::preset(AspectRatioPreset::Standard)
}
pub fn aspect_ratio_ultrawide() -> AspectRatio {
AspectRatio::preset(AspectRatioPreset::Ultrawide)
}