459 lines
12 KiB
Rust
459 lines
12 KiB
Rust
use crate::buffer::Buffer;
|
|
use crate::color::Color;
|
|
pub use crate::keyboard::{KeyState, ModifiersState};
|
|
use chrono::NaiveDateTime;
|
|
use nix::poll::PollFd;
|
|
|
|
pub struct DrawContext<'a> {
|
|
pub buf: &'a mut Buffer<'a>,
|
|
pub bg: &'a Color,
|
|
pub time: NaiveDateTime,
|
|
pub force: bool,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct DrawReport {
|
|
pub width: u32,
|
|
pub height: u32,
|
|
pub damage: Vec<(i32, i32, i32, i32)>,
|
|
pub full_damage: bool,
|
|
}
|
|
|
|
impl DrawReport {
|
|
pub fn empty(width: u32, height: u32) -> DrawReport {
|
|
DrawReport {
|
|
width,
|
|
height,
|
|
damage: Vec::new(),
|
|
full_damage: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct WaitContext {
|
|
pub fds: Vec<PollFd>,
|
|
pub target_time: Option<NaiveDateTime>,
|
|
}
|
|
|
|
impl WaitContext {
|
|
pub fn set_time(&mut self, new_time: NaiveDateTime) {
|
|
if let Some(ot) = self.target_time {
|
|
if new_time < ot {
|
|
self.target_time = Some(new_time);
|
|
}
|
|
} else {
|
|
self.target_time = Some(new_time);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub trait Widget {
|
|
fn wait(&mut self, ctx: &mut WaitContext);
|
|
fn enter(&mut self);
|
|
fn leave(&mut self);
|
|
fn size(&self) -> (u32, u32);
|
|
fn draw(
|
|
&mut self,
|
|
ctx: &mut DrawContext,
|
|
pos: (u32, u32),
|
|
expansion: (u32, u32),
|
|
) -> Result<DrawReport, ::std::io::Error>;
|
|
|
|
fn keyboard_input(
|
|
&mut self,
|
|
keysym: u32,
|
|
modifier_state: ModifiersState,
|
|
key_state: KeyState,
|
|
interpreted: Option<String>,
|
|
);
|
|
fn mouse_click(&mut self, button: u32, pos: (u32, u32));
|
|
fn mouse_scroll(&mut self, scroll: (f64, f64), pos: (u32, u32));
|
|
}
|
|
|
|
pub struct VerticalLayout<'a> {
|
|
pub children: Vec<Box<dyn Widget + Send + 'a>>,
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
impl<'a> VerticalLayout<'a> {
|
|
pub fn new(children: Vec<Box<dyn Widget + Send + 'a>>) -> Box<VerticalLayout> {
|
|
Box::new(VerticalLayout { children })
|
|
}
|
|
}
|
|
|
|
impl<'a> Widget for VerticalLayout<'a> {
|
|
fn wait(&mut self, ctx: &mut WaitContext) {
|
|
for child in &mut self.children {
|
|
child.wait(ctx);
|
|
}
|
|
}
|
|
fn enter(&mut self) {
|
|
for child in &mut self.children {
|
|
child.enter();
|
|
}
|
|
}
|
|
fn leave(&mut self) {
|
|
for child in &mut self.children {
|
|
child.leave();
|
|
}
|
|
}
|
|
fn size(&self) -> (u32, u32) {
|
|
let mut width = 0;
|
|
let mut height = 0;
|
|
|
|
for child in &self.children {
|
|
let size = child.size();
|
|
if size.0 > width {
|
|
width = size.0;
|
|
}
|
|
height += size.1;
|
|
}
|
|
|
|
(width, height)
|
|
}
|
|
|
|
fn draw(
|
|
&mut self,
|
|
ctx: &mut DrawContext,
|
|
pos: (u32, u32),
|
|
expansion: (u32, u32),
|
|
) -> Result<DrawReport, ::std::io::Error> {
|
|
let mut offset = 0;
|
|
let mut width = 0;
|
|
let mut damage = Vec::new();
|
|
let mut full_damage = false;
|
|
for child in &mut self.children {
|
|
let mut report = child.draw(
|
|
ctx,
|
|
(pos.0, offset + pos.1),
|
|
(expansion.0, expansion.1 - offset),
|
|
)?;
|
|
if report.width > width {
|
|
width = report.width
|
|
}
|
|
offset += report.height;
|
|
full_damage |= report.full_damage;
|
|
damage.append(&mut report.damage);
|
|
}
|
|
|
|
Ok(DrawReport {
|
|
width,
|
|
height: offset,
|
|
damage,
|
|
full_damage,
|
|
})
|
|
}
|
|
|
|
fn keyboard_input(
|
|
&mut self,
|
|
keysym: u32,
|
|
modifier_state: ModifiersState,
|
|
key_state: KeyState,
|
|
interpreted: Option<String>,
|
|
) {
|
|
for child in &mut self.children {
|
|
child.keyboard_input(keysym, modifier_state, key_state, interpreted.clone());
|
|
}
|
|
}
|
|
|
|
fn mouse_click(&mut self, button: u32, pos: (u32, u32)) {
|
|
let mut height = 0;
|
|
|
|
for child in &mut self.children {
|
|
let size = child.size();
|
|
if pos.1 >= height && pos.1 < height + size.1 {
|
|
let pos = (pos.0, pos.1 - height);
|
|
child.mouse_click(button, pos);
|
|
return;
|
|
}
|
|
height += size.1;
|
|
}
|
|
}
|
|
|
|
fn mouse_scroll(&mut self, scroll: (f64, f64), pos: (u32, u32)) {
|
|
let mut height = 0;
|
|
|
|
for child in &mut self.children {
|
|
let size = child.size();
|
|
if pos.1 >= height && pos.1 < height + size.1 {
|
|
let pos = (pos.0, pos.1 - height);
|
|
child.mouse_scroll(scroll, pos);
|
|
return;
|
|
}
|
|
height += size.1;
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct HorizontalLayout<'a> {
|
|
pub children: Vec<Box<dyn Widget + Send + 'a>>,
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
impl<'a> HorizontalLayout<'a> {
|
|
pub fn new(children: Vec<Box<dyn Widget + Send + 'a>>) -> Box<HorizontalLayout> {
|
|
Box::new(HorizontalLayout { children })
|
|
}
|
|
|
|
fn height(&self) -> u32 {
|
|
let mut height = 0;
|
|
|
|
for child in &self.children {
|
|
let size = child.size();
|
|
if size.1 > height {
|
|
height = size.1;
|
|
}
|
|
}
|
|
|
|
height
|
|
}
|
|
}
|
|
|
|
impl<'a> Widget for HorizontalLayout<'a> {
|
|
fn wait(&mut self, ctx: &mut WaitContext) {
|
|
for child in &mut self.children {
|
|
child.wait(ctx);
|
|
}
|
|
}
|
|
fn enter(&mut self) {
|
|
for child in &mut self.children {
|
|
child.enter();
|
|
}
|
|
}
|
|
fn leave(&mut self) {
|
|
for child in &mut self.children {
|
|
child.leave();
|
|
}
|
|
}
|
|
fn size(&self) -> (u32, u32) {
|
|
let mut width = 0;
|
|
let mut height = 0;
|
|
|
|
for child in &self.children {
|
|
let size = child.size();
|
|
if size.1 > height {
|
|
height = size.1;
|
|
}
|
|
width += size.0;
|
|
}
|
|
|
|
(width, height)
|
|
}
|
|
|
|
fn draw(
|
|
&mut self,
|
|
ctx: &mut DrawContext,
|
|
pos: (u32, u32),
|
|
expansion: (u32, u32),
|
|
) -> Result<DrawReport, ::std::io::Error> {
|
|
let mut offset = 0;
|
|
let mut height = 0;
|
|
let mut damage = Vec::new();
|
|
let mut full_damage = false;
|
|
for child in &mut self.children {
|
|
let mut report = child.draw(
|
|
ctx,
|
|
(offset + pos.0, pos.1),
|
|
(expansion.0 - offset, expansion.1),
|
|
)?;
|
|
if report.height > height {
|
|
height = report.height
|
|
}
|
|
offset += report.width;
|
|
full_damage |= report.full_damage;
|
|
damage.append(&mut report.damage);
|
|
}
|
|
|
|
Ok(DrawReport {
|
|
width: offset,
|
|
height,
|
|
damage,
|
|
full_damage,
|
|
})
|
|
}
|
|
|
|
fn keyboard_input(
|
|
&mut self,
|
|
keysym: u32,
|
|
modifier_state: ModifiersState,
|
|
key_state: KeyState,
|
|
interpreted: Option<String>,
|
|
) {
|
|
for child in &mut self.children {
|
|
child.keyboard_input(keysym, modifier_state, key_state, interpreted.clone());
|
|
}
|
|
}
|
|
|
|
fn mouse_click(&mut self, button: u32, pos: (u32, u32)) {
|
|
let mut width = 0;
|
|
|
|
for child in &mut self.children {
|
|
let size = child.size();
|
|
if pos.0 >= width && pos.0 < width + size.0 {
|
|
let pos = (pos.0 - width, pos.1);
|
|
child.mouse_click(button, pos);
|
|
return;
|
|
}
|
|
|
|
width += size.0;
|
|
}
|
|
}
|
|
|
|
fn mouse_scroll(&mut self, scroll: (f64, f64), pos: (u32, u32)) {
|
|
let mut width = 0;
|
|
|
|
for child in &mut self.children {
|
|
let size = child.size();
|
|
if pos.0 >= width && pos.0 < width + size.0 {
|
|
let pos = (pos.0 - width, pos.1);
|
|
child.mouse_scroll(scroll, pos);
|
|
return;
|
|
}
|
|
|
|
width += size.0;
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct Margin<'a> {
|
|
pub child: Box<dyn Widget + Send + 'a>,
|
|
pub margins: (u32, u32, u32, u32), // left, right, top, bottom
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
impl<'a> Margin<'a> {
|
|
pub fn new(margins: (u32, u32, u32, u32), child: Box<dyn Widget + Send + 'a>) -> Box<Margin> {
|
|
Box::new(Margin { child, margins })
|
|
}
|
|
}
|
|
|
|
impl<'a> Widget for Margin<'a> {
|
|
fn wait(&mut self, ctx: &mut WaitContext) {
|
|
self.child.wait(ctx)
|
|
}
|
|
fn enter(&mut self) {
|
|
self.child.enter()
|
|
}
|
|
fn leave(&mut self) {
|
|
self.child.leave()
|
|
}
|
|
fn size(&self) -> (u32, u32) {
|
|
let size = self.child.size();
|
|
(
|
|
size.0 + self.margins.0 + self.margins.1,
|
|
size.1 + self.margins.2 + self.margins.3,
|
|
)
|
|
}
|
|
|
|
fn draw(
|
|
&mut self,
|
|
ctx: &mut DrawContext,
|
|
pos: (u32, u32),
|
|
expansion: (u32, u32),
|
|
) -> Result<DrawReport, ::std::io::Error> {
|
|
let expansion = (
|
|
expansion.0 - self.margins.0 - self.margins.1,
|
|
expansion.1 - self.margins.2 - self.margins.3,
|
|
);
|
|
let report = self.child.draw(
|
|
ctx,
|
|
(pos.0 + self.margins.0, pos.1 + self.margins.2),
|
|
expansion,
|
|
)?;
|
|
Ok(DrawReport {
|
|
width: report.width + self.margins.0 + self.margins.1,
|
|
height: report.height + self.margins.2 + self.margins.3,
|
|
damage: report.damage,
|
|
full_damage: report.full_damage,
|
|
})
|
|
}
|
|
|
|
fn keyboard_input(
|
|
&mut self,
|
|
keysym: u32,
|
|
modifier_state: ModifiersState,
|
|
key_state: KeyState,
|
|
interpreted: Option<String>,
|
|
) {
|
|
self.child
|
|
.keyboard_input(keysym, modifier_state, key_state, interpreted);
|
|
}
|
|
|
|
fn mouse_click(&mut self, button: u32, pos: (u32, u32)) {
|
|
let pos = (
|
|
pos.0.saturating_sub(self.margins.0),
|
|
pos.1.saturating_sub(self.margins.2),
|
|
);
|
|
self.child.mouse_click(button, pos);
|
|
}
|
|
|
|
fn mouse_scroll(&mut self, scroll: (f64, f64), pos: (u32, u32)) {
|
|
let pos = (
|
|
pos.0.saturating_sub(self.margins.0),
|
|
pos.1.saturating_sub(self.margins.2),
|
|
);
|
|
self.child.mouse_scroll(scroll, pos);
|
|
}
|
|
}
|
|
|
|
pub struct Fixed<'a> {
|
|
pub child: Box<dyn Widget + Send + 'a>,
|
|
pub size: (u32, u32),
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
impl<'a> Fixed<'a> {
|
|
pub fn new(size: (u32, u32), child: Box<dyn Widget + Send + 'a>) -> Box<Fixed> {
|
|
Box::new(Fixed { child, size })
|
|
}
|
|
}
|
|
|
|
impl<'a> Widget for Fixed<'a> {
|
|
fn wait(&mut self, ctx: &mut WaitContext) {
|
|
self.child.wait(ctx)
|
|
}
|
|
fn enter(&mut self) {
|
|
self.child.enter()
|
|
}
|
|
fn leave(&mut self) {
|
|
self.child.leave()
|
|
}
|
|
fn size(&self) -> (u32, u32) {
|
|
self.size
|
|
}
|
|
|
|
fn draw(
|
|
&mut self,
|
|
ctx: &mut DrawContext,
|
|
pos: (u32, u32),
|
|
expansion: (u32, u32),
|
|
) -> Result<DrawReport, ::std::io::Error> {
|
|
let report = self.child.draw(ctx, pos, expansion)?;
|
|
Ok(DrawReport {
|
|
width: self.size.0,
|
|
height: self.size.1,
|
|
damage: report.damage,
|
|
full_damage: report.full_damage,
|
|
})
|
|
}
|
|
|
|
fn keyboard_input(
|
|
&mut self,
|
|
keysym: u32,
|
|
modifier_state: ModifiersState,
|
|
key_state: KeyState,
|
|
interpreted: Option<String>,
|
|
) {
|
|
self.child
|
|
.keyboard_input(keysym, modifier_state, key_state, interpreted);
|
|
}
|
|
|
|
fn mouse_click(&mut self, button: u32, pos: (u32, u32)) {
|
|
self.child.mouse_click(button, pos);
|
|
}
|
|
|
|
fn mouse_scroll(&mut self, scroll: (f64, f64), pos: (u32, u32)) {
|
|
self.child.mouse_scroll(scroll, pos);
|
|
}
|
|
}
|