use gpui::{
AnyElement, App, IntoElement, ParentElement, RenderOnce, Styled, Window, div, prelude::*,
};
use gpui_kit_semantics::{NodeSpec, Role, Semantic};
use crate::foundation::Ident;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum AspectFit {
#[default]
Width,
Height,
}
#[derive(IntoElement)]
pub struct AspectRatio {
ident: Ident,
ratio: f32,
fit: AspectFit,
child: Option<AnyElement>,
}
impl std::fmt::Debug for AspectRatio {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("AspectRatio")
.field("ident", &self.ident)
.field("ratio", &self.ratio)
.field("fit", &self.fit)
.field("has_child", &self.child.is_some())
.finish()
}
}
impl AspectRatio {
pub fn new(ident: impl Into<Ident>, ratio: f32) -> Self {
Self {
ident: ident.into(),
ratio,
fit: AspectFit::default(),
child: None,
}
}
pub fn of(ident: impl Into<Ident>, width: f32, height: f32) -> Self {
Self::new(ident, if height == 0.0 { 1.0 } else { width / height })
}
pub fn fit(mut self, fit: AspectFit) -> Self {
self.fit = fit;
self
}
pub fn width_driven(self) -> Self {
self.fit(AspectFit::Width)
}
pub fn height_driven(self) -> Self {
self.fit(AspectFit::Height)
}
pub fn child(mut self, child: impl IntoElement) -> Self {
self.child = Some(child.into_any_element());
self
}
pub fn ratio(&self) -> f32 {
self.ratio
}
}
impl RenderOnce for AspectRatio {
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
let ratio = if self.ratio.is_finite() && self.ratio > 0.0 {
self.ratio
} else {
1.0
};
div()
.flex_none()
.overflow_hidden()
.aspect_ratio(ratio)
.map(|frame| match self.fit {
AspectFit::Width => frame.w_full().self_start(),
AspectFit::Height => frame.h_full().self_start(),
})
.children(self.child)
.semantic_in(cx, NodeSpec::new(self.ident.semantic_id(), Role::Region))
}
}