Files
oak-gpui/crates/gpui/src/elements/overlay.rs
T

158 lines
4.3 KiB
Rust

use std::ops::Range;
use crate::{
geometry::{rect::RectF, vector::Vector2F},
json::ToJson,
presenter::MeasurementContext,
DebugContext, Element, ElementBox, Event, EventContext, LayoutContext, MouseRegion,
PaintContext, SizeConstraint,
};
use serde_json::json;
pub struct Overlay {
child: ElementBox,
abs_position: Option<Vector2F>,
fit_mode: OverlayFitMode,
hoverable: bool,
}
#[derive(Copy, Clone)]
pub enum OverlayFitMode {
SnapToWindow,
FlipAlignment,
None,
}
impl Overlay {
pub fn new(child: ElementBox) -> Self {
Self {
child,
abs_position: None,
fit_mode: OverlayFitMode::None,
hoverable: false,
}
}
pub fn with_abs_position(mut self, position: Vector2F) -> Self {
self.abs_position = Some(position);
self
}
pub fn fit_mode(mut self, fit_mode: OverlayFitMode) -> Self {
self.fit_mode = fit_mode;
self
}
pub fn hoverable(mut self, hoverable: bool) -> Self {
self.hoverable = hoverable;
self
}
}
impl Element for Overlay {
type LayoutState = Vector2F;
type PaintState = ();
fn layout(
&mut self,
constraint: SizeConstraint,
cx: &mut LayoutContext,
) -> (Vector2F, Self::LayoutState) {
let constraint = if self.abs_position.is_some() {
SizeConstraint::new(Vector2F::zero(), cx.window_size)
} else {
constraint
};
let size = self.child.layout(constraint, cx);
(Vector2F::zero(), size)
}
fn paint(
&mut self,
bounds: RectF,
_: RectF,
size: &mut Self::LayoutState,
cx: &mut PaintContext,
) {
let mut bounds = RectF::new(self.abs_position.unwrap_or(bounds.origin()), *size);
cx.scene.push_stacking_context(None);
if self.hoverable {
cx.scene.push_mouse_region(MouseRegion {
view_id: cx.current_view_id(),
bounds,
..Default::default()
});
}
match self.fit_mode {
OverlayFitMode::SnapToWindow => {
// Snap the right edge of the overlay to the right edge of the window if
// its horizontal bounds overflow.
if bounds.lower_right().x() > cx.window_size.x() {
bounds.set_origin_x((cx.window_size.x() - bounds.width()).max(0.));
}
// Snap the bottom edge of the overlay to the bottom edge of the window if
// its vertical bounds overflow.
if bounds.lower_right().y() > cx.window_size.y() {
bounds.set_origin_y((cx.window_size.y() - bounds.height()).max(0.));
}
}
OverlayFitMode::FlipAlignment => {
// Right-align overlay if its horizontal bounds overflow.
if bounds.lower_right().x() > cx.window_size.x() {
bounds.set_origin_x(bounds.origin_x() - bounds.width());
}
// Bottom-align overlay if its vertical bounds overflow.
if bounds.lower_right().y() > cx.window_size.y() {
bounds.set_origin_y(bounds.origin_y() - bounds.height());
}
}
OverlayFitMode::None => {}
}
self.child.paint(bounds.origin(), bounds, cx);
cx.scene.pop_stacking_context();
}
fn dispatch_event(
&mut self,
event: &Event,
_: RectF,
_: RectF,
_: &mut Self::LayoutState,
_: &mut Self::PaintState,
cx: &mut EventContext,
) -> bool {
self.child.dispatch_event(event, cx)
}
fn rect_for_text_range(
&self,
range_utf16: Range<usize>,
_: RectF,
_: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
cx: &MeasurementContext,
) -> Option<RectF> {
self.child.rect_for_text_range(range_utf16, cx)
}
fn debug(
&self,
_: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
cx: &DebugContext,
) -> serde_json::Value {
json!({
"type": "Overlay",
"abs_position": self.abs_position.to_json(),
"child": self.child.debug(cx),
})
}
}