use std::cell::RefCell;
use std::ops::BitOr;
use std::rc::Rc;
use crabshell_reactive::{Owner, Signal, untrack};
use crate::cx::{Anchor, Cx};
use crate::geometry::{Rect, Size};
use crate::input::Handler;
use crate::keyboard::{KeyEvent, KeyHandler};
use crate::prop::{IntoProp, Prop};
use crate::region::Region;
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,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum KeyboardFocus {
#[default]
None,
Exclusive,
OnDemand,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Margins {
pub top: i32,
pub right: i32,
pub bottom: i32,
pub left: i32,
}
impl Margins {
pub const fn all(margin: i32) -> Margins {
Margins::new(margin, margin, margin, margin)
}
pub const fn new(top: i32, right: i32, bottom: i32, left: i32) -> Margins {
Margins {
top,
right,
bottom,
left,
}
}
pub const fn on(self, edge: Edge) -> i32 {
match edge {
Edge::Top => self.top,
Edge::Right => self.right,
Edge::Bottom => self.bottom,
Edge::Left => self.left,
}
}
}
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) margins: Option<Prop<Margins>>,
pub(crate) keyboard_focus: Option<Prop<KeyboardFocus>>,
pub(crate) namespace: Option<Prop<String>>,
pub(crate) mask: Option<Prop<Option<Region>>>,
pub(crate) hints: Hints,
pub(crate) on_key: Option<KeyHandler>,
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,
margins: None,
keyboard_focus: None,
namespace: None,
mask: None,
hints: Hints::default(),
on_key: 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 margins(mut self, margins: impl IntoProp<Margins>) -> PanelWindow {
self.margins = Some(margins.into_prop());
self
}
pub fn keyboard_focus(mut self, focus: impl IntoProp<KeyboardFocus>) -> PanelWindow {
self.keyboard_focus = Some(focus.into_prop());
self
}
pub fn namespace(mut self, namespace: impl IntoProp<String>) -> PanelWindow {
self.namespace = Some(namespace.into_prop());
self
}
pub fn mask(mut self, mask: impl IntoProp<Option<Region>>) -> PanelWindow {
self.mask = Some(mask.into_prop());
self
}
pub fn blur(mut self, region: impl IntoProp<Option<Region>>) -> PanelWindow {
self.hints.blur = Some(region.into_prop());
self
}
pub fn inhibit_idle(mut self, inhibit: impl IntoProp<bool>) -> PanelWindow {
self.hints.inhibit_idle = Some(inhibit.into_prop());
self
}
pub fn inhibit_shortcuts(mut self, inhibit: impl IntoProp<bool>) -> PanelWindow {
self.hints.inhibit_shortcuts = Some(inhibit.into_prop());
self
}
pub fn shortcuts_inhibited(mut self, signal: Signal<bool>) -> PanelWindow {
self.hints.shortcuts_inhibited = Some(signal);
self
}
pub fn focus_grab(mut self, signal: Signal<bool>) -> PanelWindow {
self.hints.focus_grab = Some(signal);
self
}
pub fn on_key(mut self, handler: impl Fn(&Cx, &KeyEvent) -> bool + 'static) -> PanelWindow {
self.on_key = Some(Rc::new(Handler::new(handler)));
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(Default)]
pub(crate) struct Hints {
pub(crate) blur: Option<Prop<Option<Region>>>,
pub(crate) inhibit_idle: Option<Prop<bool>>,
pub(crate) inhibit_shortcuts: Option<Prop<bool>>,
pub(crate) shortcuts_inhibited: Option<Signal<bool>>,
pub(crate) focus_grab: Option<Signal<bool>>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct SurfaceConfig {
pub anchors: Anchors,
pub width: u32,
pub height: u32,
pub exclusive_zone: i32,
pub layer: Layer,
pub margins: Margins,
pub keyboard_focus: KeyboardFocus,
pub namespace: String,
pub mask: Option<Region>,
}
impl Default for SurfaceConfig {
fn default() -> SurfaceConfig {
SurfaceConfig {
anchors: Anchors::NONE,
width: 0,
height: 0,
exclusive_zone: 0,
layer: Layer::Top,
margins: Margins::default(),
keyboard_focus: KeyboardFocus::None,
namespace: String::from(DEFAULT_NAMESPACE),
mask: None,
}
}
}
pub(crate) const DEFAULT_NAMESPACE: &str = "crabshell";
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,
margins: surface.margins,
keyboard_focus: surface.keyboard_focus,
namespace: surface.namespace.clone(),
mask: surface.mask.clone(),
}
}
pub fn blur(&self) -> Option<Region> {
self.tree.borrow().surface.blur.clone()
}
pub fn inhibits_idle(&self) -> bool {
self.tree.borrow().surface.inhibit_idle
}
pub fn inhibits_shortcuts(&self) -> bool {
self.tree.borrow().surface.inhibit_shortcuts
}
pub fn shortcuts_inhibited(&self) -> Option<Signal<bool>> {
self.tree.borrow().surface.shortcuts_inhibited
}
pub fn focus_grab(&self) -> Option<Signal<bool>> {
self.tree.borrow().surface.focus_grab
}
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.pointer_leave();
self.owner.dispose();
}
}