use std::cell::RefCell;
use std::rc::Rc;
use crate::base::{Point, Rect, Size};
use crate::reactive::Scope;
use crate::ui::View;
use super::overlays::Overlays;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct PanelAnchor {
pub rect: Rect,
}
impl PanelAnchor {
pub fn cell(p: Point) -> PanelAnchor {
PanelAnchor {
rect: Rect::new(p.x, p.y, 1, 1),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum PanelWidth {
MatchAnchor,
Content { min: i32, max: i32 },
}
pub fn place_panel(viewport: Size, anchor: Rect, content: Size, width: PanelWidth) -> Rect {
let w = match width {
PanelWidth::MatchAnchor => anchor.w,
PanelWidth::Content { min, max } => content.w.clamp(min, max.max(min)),
}
.clamp(1, viewport.w.max(1));
let below = (viewport.h - anchor.bottom()).max(0);
let above = anchor.y.max(0);
let (y, h) = if below >= content.h || below >= above {
(anchor.bottom(), content.h.min(below))
} else {
let h = content.h.min(above);
(anchor.y - h, h)
};
let x = anchor.x.min(viewport.w - w).max(0);
Rect::new(x, y, w, h.max(0))
}
struct OpenLayer {
layer: super::overlays::LayerHandle,
scope: Scope,
rect: Rect,
}
struct PanelInner {
overlays: Overlays,
owner: Scope,
width: PanelWidth,
build: Rc<dyn Fn(Scope) -> View>,
open: Option<OpenLayer>,
closed: bool,
}
impl PanelInner {
fn drop_layer(&mut self) {
if let Some(open) = self.open.take() {
open.layer.remove();
open.scope.dispose();
}
}
}
#[derive(Clone)]
pub struct AnchoredPanel {
inner: Rc<RefCell<PanelInner>>,
}
impl AnchoredPanel {
pub fn open_passive(
overlays: &Overlays,
cx: Scope,
viewport: Size,
anchor: PanelAnchor,
width: PanelWidth,
content: Size,
build: impl Fn(Scope) -> View + 'static,
) -> AnchoredPanel {
let panel = AnchoredPanel {
inner: Rc::new(RefCell::new(PanelInner {
overlays: overlays.clone(),
owner: cx,
width,
build: Rc::new(build),
open: None,
closed: false,
})),
};
let weak = Rc::downgrade(&panel.inner);
cx.on_cleanup(move || {
if let Some(inner) = weak.upgrade() {
let mut inner = inner.borrow_mut();
inner.closed = true;
inner.drop_layer();
}
});
panel.apply(viewport, anchor, content);
panel
}
pub fn update(&self, viewport: Size, anchor: PanelAnchor, content: Size) {
if self.inner.borrow().closed {
return;
}
self.apply(viewport, anchor, content);
}
fn apply(&self, viewport: Size, anchor: PanelAnchor, content: Size) {
let mut inner = self.inner.borrow_mut();
let rect = place_panel(viewport, anchor.rect, content, inner.width);
if rect.h <= 0 || rect.w <= 0 || viewport.w <= 0 || viewport.h <= 0 {
inner.drop_layer();
return;
}
match &mut inner.open {
Some(open) if open.rect == rect => {}
Some(open) if open.rect.size() == rect.size() => {
open.layer.set_offset(rect.origin());
open.rect = rect;
}
_ => {
inner.drop_layer();
let scope = inner.owner.child();
let view = (inner.build)(scope);
let z = inner.overlays.top_z() + 1;
let layer = inner.overlays.layer_tree(z, rect, false, scope, view);
inner.open = Some(OpenLayer { layer, scope, rect });
}
}
}
pub fn close(&self) {
let mut inner = self.inner.borrow_mut();
inner.closed = true;
inner.drop_layer();
}
pub fn is_open(&self) -> bool {
self.inner.borrow().open.is_some()
}
pub fn rect(&self) -> Option<Rect> {
self.inner.borrow().open.as_ref().map(|o| o.rect)
}
pub fn layer(&self) -> Option<super::overlays::LayerHandle> {
self.inner.borrow().open.as_ref().map(|o| o.layer.clone())
}
}
#[path = "anchored_completion.rs"]
mod completion;
pub use completion::{Completion, CompletionCandidate};
#[path = "anchored_owned.rs"]
mod owned;
pub use owned::{DismissReason, Popup, Tooltip};
#[cfg(test)]
#[path = "anchored_tests.rs"]
mod tests;