use super::*;
pub(super) fn pinned_layer(layer: AnyElement) -> AnyElement {
div()
.absolute()
.top_0()
.left_0()
.size_0()
.child(layer)
.into_any_element()
}
pub(super) fn exit_progress(since: web_time::Instant) -> f32 {
let total = motion::MENU_OUT
.total()
.mul_f32(motion::speed_scale())
.as_secs_f32();
let raw = if total <= 0.0 {
1.0
} else {
(since.elapsed().as_secs_f32() / total).clamp(0.0, 1.0)
};
motion::MENU_OUT.progress(raw)
}
pub(super) fn material_menu(content: AnyElement) -> AnyElement {
crate::surface::popover(Theme::surface_radius(), content).into_any_element()
}
pub(super) fn menu_motion(id: SharedString, exit: Option<f32>, inner: gpui::Div) -> AnyElement {
if let Some(t) = exit {
let inner = inner.relative().child(div().absolute().inset_0().occlude());
motion::menu_out(SharedString::from(format!("{id}-out")), t, inner).into_any_element()
} else {
motion::menu_in(id, inner).into_any_element()
}
}
pub(super) fn menu_layer(
id: impl Into<SharedString>,
content: AnyElement,
closing: Option<web_time::Instant>,
anchor: Anchor,
position: Option<Point<Pixels>>,
gap: f32,
) -> AnyElement {
let content = material_menu(content);
let inner = match anchor {
Anchor::BottomLeft | Anchor::BottomRight => div().occlude().pb(px(gap)),
_ => div().occlude().pt(px(gap)),
}
.child(content);
let mut layer = gpui::anchored()
.anchor(anchor)
.snap_to_window_with_margin(px(SNAP));
if let Some(position) = position {
layer = layer.position(position);
}
gpui::deferred(layer.child(menu_motion(id.into(), closing.map(exit_progress), inner)))
.priority(1)
.into_any_element()
}
pub fn anchored_menu(
id: impl Into<SharedString>,
content: AnyElement,
closing: Option<web_time::Instant>,
) -> AnyElement {
pinned_layer(menu_layer(id, content, closing, Anchor::TopLeft, None, 6.0))
}
pub fn anchored_menu_below(
id: impl Into<SharedString>,
content: AnyElement,
closing: Option<web_time::Instant>,
) -> AnyElement {
anchored_menu_below_gap(id, content, closing, 6.0)
}
pub fn anchored_menu_below_gap(
id: impl Into<SharedString>,
content: AnyElement,
closing: Option<web_time::Instant>,
gap: f32,
) -> AnyElement {
div()
.absolute()
.bottom_0()
.left_0()
.size_0()
.child(menu_layer(id, content, closing, Anchor::TopLeft, None, gap))
.into_any_element()
}
pub fn anchored_submenu(
id: impl Into<SharedString>,
content: AnyElement,
chain: &Chain,
) -> AnyElement {
let content = div().occlude().child(material_menu(content));
submenu::layer(menu_motion(id.into(), None, content), chain)
}
pub fn anchored_menu_above(
id: impl Into<SharedString>,
content: AnyElement,
closing: Option<web_time::Instant>,
) -> AnyElement {
pinned_layer(menu_layer(
id,
content,
closing,
Anchor::BottomLeft,
None,
6.0,
))
}
pub fn anchored_menu_above_at(
id: impl Into<SharedString>,
position: Point<Pixels>,
content: AnyElement,
closing: Option<web_time::Instant>,
) -> AnyElement {
div()
.absolute()
.left(position.x)
.top(position.y)
.size_0()
.child(anchored_menu_above(id, content, closing))
.into_any_element()
}
pub fn anchored_menu_above_end(
id: impl Into<SharedString>,
content: AnyElement,
closing: Option<web_time::Instant>,
) -> AnyElement {
div()
.absolute()
.top_0()
.right_0()
.size_0()
.child(menu_layer(
id,
content,
closing,
Anchor::BottomRight,
None,
6.0,
))
.into_any_element()
}
pub fn anchored_menu_below_end(
id: impl Into<SharedString>,
content: AnyElement,
closing: Option<web_time::Instant>,
) -> AnyElement {
div()
.absolute()
.bottom_0()
.right_0()
.size_0()
.child(menu_layer(
id,
content,
closing,
Anchor::TopRight,
None,
6.0,
))
.into_any_element()
}
pub fn menu_at(
id: impl Into<SharedString>,
position: Point<Pixels>,
content: AnyElement,
closing: Option<web_time::Instant>,
) -> AnyElement {
menu_layer(id, content, closing, Anchor::TopLeft, Some(position), 0.0)
}