pub(crate) mod event_queue;
pub(crate) mod handler;
pub(crate) mod helper;
use as_any::AsAny;
use singlevec::SingleVec;
use uid::IdU64;
use vector2d::Vector2D;
use crate::canvas::{BorderCanvas, Canvas};
use crate::common::*;
use std::cell::RefCell;
use std::rc::*;
pub use event_queue::*;
pub use handler::WindowHandler;
pub use helper::*;
#[derive(Clone, Copy, Debug, PartialEq, Default)]
pub enum BorderKind {
#[default]
Solid,
Thick,
Dashed,
}
impl BorderKind {
pub const fn corner_style(&self) -> [Grapheme; 4] {
match self {
BorderKind::Solid => [
Grapheme::new_unchecked("┌", GlyphWidth::Half),
Grapheme::new_unchecked("┐", GlyphWidth::Half),
Grapheme::new_unchecked("└", GlyphWidth::Half),
Grapheme::new_unchecked("┘", GlyphWidth::Half),
],
BorderKind::Dashed => [
Grapheme::new_unchecked("╔", GlyphWidth::Half),
Grapheme::new_unchecked("╗", GlyphWidth::Half),
Grapheme::new_unchecked("╚", GlyphWidth::Half),
Grapheme::new_unchecked("╝", GlyphWidth::Half),
],
BorderKind::Thick => [
Grapheme::new_unchecked("┏", GlyphWidth::Half),
Grapheme::new_unchecked("┓", GlyphWidth::Half),
Grapheme::new_unchecked("┗", GlyphWidth::Half),
Grapheme::new_unchecked("┛", GlyphWidth::Half),
],
}
}
pub const fn cross_style(&self) -> Grapheme {
match self {
BorderKind::Solid => Grapheme::new_unchecked("┼", GlyphWidth::Half),
BorderKind::Dashed => Grapheme::new_unchecked("╬", GlyphWidth::Half),
BorderKind::Thick => Grapheme::new_unchecked("╋", GlyphWidth::Half),
}
}
pub const fn connector_style(&self) -> [Grapheme; 4] {
match self {
BorderKind::Solid => [
Grapheme::new_unchecked("├", GlyphWidth::Half),
Grapheme::new_unchecked("┤", GlyphWidth::Half),
Grapheme::new_unchecked("┬", GlyphWidth::Half),
Grapheme::new_unchecked("┴", GlyphWidth::Half),
],
BorderKind::Dashed => [
Grapheme::new_unchecked("╠", GlyphWidth::Half),
Grapheme::new_unchecked("╣", GlyphWidth::Half),
Grapheme::new_unchecked("╦", GlyphWidth::Half),
Grapheme::new_unchecked("╩", GlyphWidth::Half),
],
BorderKind::Thick => [
Grapheme::new_unchecked("┣", GlyphWidth::Half),
Grapheme::new_unchecked("┫", GlyphWidth::Half),
Grapheme::new_unchecked("┳", GlyphWidth::Half),
Grapheme::new_unchecked("┻", GlyphWidth::Half),
],
}
}
pub const fn line_style(&self) -> [Grapheme; 2] {
match self {
BorderKind::Solid => [
Grapheme::new_unchecked("─", GlyphWidth::Half),
Grapheme::new_unchecked("│", GlyphWidth::Half),
],
BorderKind::Dashed => [
Grapheme::new_unchecked("═", GlyphWidth::Half),
Grapheme::new_unchecked("║", GlyphWidth::Half),
],
BorderKind::Thick => [
Grapheme::new_unchecked("━", GlyphWidth::Half),
Grapheme::new_unchecked("┃", GlyphWidth::Half),
],
}
}
}
pub type CustomStyleFn = fn(&dyn Window, BorderCanvas);
#[derive(Copy, Clone, Debug, Default)]
pub enum BorderStyle {
#[default]
None,
Preset {
kind: BorderKind,
bg: Option<Color>,
fg: Option<Color>,
},
Custom(CustomStyleFn, Thickness),
}
impl PartialEq for BorderStyle {
fn eq(&self, other: &Self) -> bool {
match self {
Self::None => matches!(other, Self::None),
Self::Preset {
kind: kind0,
bg: bg0,
fg: fg0,
} => match other {
Self::Preset { kind, bg, fg } => kind0 == kind && bg0 == bg && fg0 == fg,
_ => false,
},
Self::Custom(_, _) => false,
}
}
}
impl BorderStyle {
pub const fn new(kind: BorderKind, bg: Option<Color>, fg: Option<Color>) -> Self {
Self::Preset { kind, bg, fg }
}
pub const fn custom(f: CustomStyleFn, thickness: Thickness) -> Self {
Self::Custom(f, thickness)
}
pub const fn thickness(&self) -> Thickness {
match *self {
BorderStyle::None => Thickness::from(0),
BorderStyle::Custom(_, tn) => tn,
_ => Thickness::from(1),
}
}
pub const fn get_borderkind(&self) -> Option<BorderKind> {
match self {
BorderStyle::Preset { kind, .. } => Some(*kind),
_ => None,
}
}
}
#[derive(Default, Clone, Copy, Debug, PartialEq)]
pub struct Thickness {
pub top: TSize,
pub left: TSize,
pub right: TSize,
pub bottom: TSize,
}
impl Thickness {
pub const fn from(size: TSize) -> Self {
Self {
top: size,
left: size,
right: size,
bottom: size,
}
}
pub const fn new(top: TSize, left: TSize, right: TSize, bottom: TSize) -> Self {
Self {
top,
left,
right,
bottom,
}
}
pub const fn width(&self) -> TSize {
self.left + self.right
}
pub const fn height(&self) -> TSize {
self.top + self.bottom
}
}
impl From<CustomStyleFn> for BorderStyle {
fn from(value: CustomStyleFn) -> Self {
Self::Custom(value, Thickness::from(1))
}
}
impl From<BorderKind> for BorderStyle {
fn from(value: BorderKind) -> Self {
Self::Preset {
kind: value,
bg: None,
fg: None,
}
}
}
#[derive(Copy, Clone, Debug, Default)]
pub struct RectsOverlapping {}
pub struct SubWindowBuilder {
base_rect: Rect,
children: SingleVec<WindowRef>,
rects: SingleVec<Rect>,
overlapping: bool,
}
impl SubWindowBuilder {
pub(crate) fn from(base_rect: Rect) -> Self {
Self {
base_rect,
children: SingleVec::new(),
rects: SingleVec::new(),
overlapping: true,
}
}
pub fn enable_overlapping(&mut self) {
self.overlapping = true;
}
pub fn disable_overlapping(&mut self) {
self.overlapping = false;
}
pub fn add_child(&mut self, child: WindowRef) {
self.children.push(child);
}
pub fn add_rect(&mut self, rect: Rect) -> Result<(), RectsOverlapping> {
if self.check_overlapping(rect) {
return Err(RectsOverlapping::default());
}
self.rects.push(rect);
Ok(())
}
pub fn add_pair(
&mut self,
child: WindowRef,
rect: Option<Rect>,
) -> Result<(), RectsOverlapping> {
let rect = rect.unwrap_or(self.base_rect);
if self.check_overlapping(rect) {
return Err(RectsOverlapping::default());
}
self.children.push(child);
self.rects.push(rect);
Ok(())
}
#[inline(always)]
pub fn base_rect(&self) -> Rect {
self.base_rect
}
pub fn build(self) -> Option<SubWindows> {
if self.children.len() != self.rects.len() {
return None;
}
Some(SubWindows {
children: self.children,
rects: self.rects,
})
}
fn check_overlapping(&self, rect: Rect) -> bool {
if !self.overlapping {
for rt in self.rects.iter() {
if rt.overlaps(&rect) {
return true;
}
}
}
false
}
}
#[derive(Default)]
pub struct SubWindows {
children: SingleVec<WindowRef>,
rects: SingleVec<Rect>,
}
impl SubWindows {
pub fn children(&self) -> &[WindowRef] {
&self.children
}
pub fn rects(&self) -> &[Rect] {
&self.rects
}
}
pub type WindowRef = Rc<RefCell<dyn Window>>;
pub type WindowWeakRef = Weak<RefCell<dyn Window>>;
pub trait Window: WindowLayout + HasWindowUID + AsAny {
fn handle_event(&mut self, event: &mut WindowEvent) {
let _ = event;
}
fn children(&mut self, builder: SubWindowBuilder) -> SubWindows {
let _ = builder;
SubWindows::default()
}
fn render(&self, canvas: &mut Canvas);
fn focus(&self) -> Option<WindowRef> {
None
}
fn is_enabled(&self) -> bool {
true
}
}
pub trait WindowLayout {
fn alignment(&self) -> (HorizontalAlignment, VerticalAlignment) {
(HorizontalAlignment::default(), VerticalAlignment::default())
}
fn desired_size_pre_hook(&mut self, available_size: TPoint) {
_ = available_size;
}
fn desired_size(&self, available_size: TPoint) -> TPoint {
available_size
}
fn margin(&self) -> Thickness {
Thickness::default()
}
fn border(&self) -> BorderStyle {
BorderStyle::default()
}
fn is_visible(&self) -> bool {
true
}
}
pub trait HasWindowUID {
fn uid(&self) -> WindowUID;
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct WindowUID(u64);
impl std::fmt::Display for WindowUID {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl Default for WindowUID {
fn default() -> Self {
Self::new()
}
}
impl WindowUID {
pub fn new() -> Self {
Self(IdU64::<Self>::new().get())
}
}