use std::cell::RefCell;
use std::ops::BitOr;
use std::rc::Rc;
use crabshell_reactive::{Owner, untrack};
use crate::cx::{Anchor, Cx};
use crate::geometry::{Rect, Size};
use crate::prop::{IntoProp, Prop};
use crate::style::Style;
use crate::text::TextSystem;
use crate::tree::{Tree, borrow};
use crate::view::WindowView;
use crate::widgets::Widget;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Output {
name: String,
}
impl Output {
pub fn new(name: impl Into<String>) -> Output {
Output { name: name.into() }
}
pub fn name(&self) -> String {
self.name.clone()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Edge {
Top,
Bottom,
Left,
Right,
}
impl Edge {
const fn bit(self) -> u8 {
match self {
Edge::Top => 1,
Edge::Bottom => 2,
Edge::Left => 4,
Edge::Right => 8,
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Anchors(u8);
impl Anchors {
pub const NONE: Anchors = Anchors(0);
pub const fn contains(self, edge: Edge) -> bool {
self.0 & edge.bit() != 0
}
pub fn edge(self) -> Option<Edge> {
let [top, bottom, left, right] =
[Edge::Top, Edge::Bottom, Edge::Left, Edge::Right].map(|edge| self.contains(edge));
match (top, bottom, left, right) {
(true, false, false, false) | (true, false, true, true) => Some(Edge::Top),
(false, true, false, false) | (false, true, true, true) => Some(Edge::Bottom),
(false, false, true, false) | (true, true, true, false) => Some(Edge::Left),
(false, false, false, true) | (true, true, false, true) => Some(Edge::Right),
_ => None,
}
}
}
impl From<Edge> for Anchors {
fn from(edge: Edge) -> Anchors {
Anchors(edge.bit())
}
}
impl BitOr for Edge {
type Output = Anchors;
fn bitor(self, other: Edge) -> Anchors {
Anchors(self.bit() | other.bit())
}
}
impl BitOr<Edge> for Anchors {
type Output = Anchors;
fn bitor(self, edge: Edge) -> Anchors {
Anchors(self.0 | edge.bit())
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum Layer {
Background,
Bottom,
#[default]
Top,
Overlay,
}
pub struct PanelWindow {
pub(crate) output: Output,
pub(crate) anchors: Option<Prop<Anchors>>,
pub(crate) width: Option<Prop<u32>>,
pub(crate) height: Option<Prop<u32>>,
pub(crate) exclusive_zone: Option<Prop<i32>>,
pub(crate) layer: Option<Prop<Layer>>,
pub(crate) style: Option<Prop<Style>>,
pub(crate) child: Option<Box<dyn Widget>>,
}
impl PanelWindow {
pub fn new(output: &Output) -> PanelWindow {
PanelWindow {
output: output.clone(),
anchors: None,
width: None,
height: None,
exclusive_zone: None,
layer: None,
style: None,
child: None,
}
}
pub fn anchors(mut self, anchors: impl IntoProp<Anchors>) -> PanelWindow {
self.anchors = Some(anchors.into_prop());
self
}
pub fn width(mut self, width: impl IntoProp<u32>) -> PanelWindow {
self.width = Some(width.into_prop());
self
}
pub fn height(mut self, height: impl IntoProp<u32>) -> PanelWindow {
self.height = Some(height.into_prop());
self
}
pub fn exclusive_zone(mut self, zone: impl IntoProp<i32>) -> PanelWindow {
self.exclusive_zone = Some(zone.into_prop());
self
}
pub fn layer(mut self, layer: impl IntoProp<Layer>) -> PanelWindow {
self.layer = Some(layer.into_prop());
self
}
pub fn style(mut self, style: impl IntoProp<Style>) -> PanelWindow {
self.style = Some(style.into_prop());
self
}
pub fn child(mut self, child: impl Widget) -> PanelWindow {
self.child = Some(Box::new(child));
self
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct SurfaceConfig {
pub anchors: Anchors,
pub width: u32,
pub height: u32,
pub exclusive_zone: i32,
pub layer: Layer,
}
pub struct Window {
pub(crate) tree: Rc<RefCell<Tree>>,
pub(crate) owner: Owner,
}
impl Window {
pub fn new(cx: &Cx, mut panel: PanelWindow) -> Window {
let child = untrack(|| panel.child.take().map(|child| child.into_element()));
let path: Rc<str> = format!("PanelWindow[{}]", panel.output.name).into();
let cx = cx
.child()
.named(path.to_string())
.at(Rc::new(Anchor::default()));
let owner = cx.owner();
let output = panel.output.clone();
let tree =
Rc::new_cyclic(|this| RefCell::new(Tree::new(this.clone(), "PanelWindow", output, cx, path)));
borrow(&tree).mount_panel(panel, child);
Window { tree, owner }
}
pub fn output(&self) -> Output {
self.tree.borrow().output.clone()
}
pub fn surface(&self) -> SurfaceConfig {
let tree = self.tree.borrow();
let surface = &tree.surface;
let exclusive_zone = surface.exclusive_zone.unwrap_or_else(|| {
let thickness = match surface.anchors.edge() {
Some(Edge::Top | Edge::Bottom) => surface.height,
Some(Edge::Left | Edge::Right) => surface.width,
None => 0,
};
i32::try_from(thickness).unwrap_or(i32::MAX)
});
SurfaceConfig {
anchors: surface.anchors,
width: surface.width,
height: surface.height,
exclusive_zone,
layer: surface.layer,
}
}
pub fn take_surface_changed(&self) -> bool {
std::mem::take(&mut borrow(&self.tree).surface.changed)
}
pub fn size(&self) -> Size {
self.tree.borrow().size
}
pub fn set_size(&self, size: Size) {
let mut tree = borrow(&self.tree);
if tree.size != size {
tree.size = size;
tree.needs_layout = true;
}
}
pub fn needs_layout(&self) -> bool {
self.tree.borrow().needs_layout
}
pub fn layout(&self, text: &mut TextSystem) {
borrow(&self.tree).layout(text);
}
pub fn has_damage(&self) -> bool {
!self.tree.borrow().damage.is_empty()
}
pub fn take_damage(&self) -> Vec<Rect> {
std::mem::take(&mut borrow(&self.tree).damage)
}
pub fn view(&self) -> WindowView<'_> {
WindowView::new(self.tree.borrow())
}
}
impl Drop for Window {
fn drop(&mut self) {
self.owner.dispose();
}
}