2019-08-14 08:53:35 +03:00
|
|
|
use std::any::Any;
|
2019-08-08 20:00:54 +03:00
|
|
|
pub mod bitmaps;
|
2019-08-09 00:00:27 +03:00
|
|
|
pub mod color;
|
2019-08-18 18:10:08 +03:00
|
|
|
pub mod connection;
|
2019-08-10 07:53:46 +03:00
|
|
|
pub mod input;
|
2019-08-08 05:19:04 +03:00
|
|
|
pub mod os;
|
2019-08-18 18:49:20 +03:00
|
|
|
mod spawn;
|
2019-08-18 09:00:23 +03:00
|
|
|
mod tasks;
|
2019-08-09 00:00:27 +03:00
|
|
|
|
|
|
|
pub use bitmaps::BitmapImage;
|
|
|
|
pub use color::Color;
|
2019-08-18 18:10:08 +03:00
|
|
|
pub use connection::*;
|
2019-08-10 07:53:46 +03:00
|
|
|
pub use input::*;
|
2019-08-09 03:44:57 +03:00
|
|
|
pub use os::*;
|
2019-08-09 02:11:35 +03:00
|
|
|
|
2019-08-09 00:00:27 +03:00
|
|
|
/// Compositing operator.
|
|
|
|
/// We implement a small subset of possible compositing operators.
|
|
|
|
/// More information on these and their temrinology can be found
|
|
|
|
/// in the Cairo documentation here:
|
|
|
|
/// https://www.cairographics.org/operators/
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
|
|
pub enum Operator {
|
|
|
|
/// Apply the alpha channel of src and combine src with dest,
|
|
|
|
/// according to the classic OVER composite operator
|
|
|
|
Over,
|
|
|
|
/// Ignore dest; take src as the result of the operation
|
|
|
|
Source,
|
|
|
|
/// Multiply src x dest. The result is at least as dark as
|
|
|
|
/// the darker of the two input colors. This is used to
|
|
|
|
/// apply a color tint.
|
|
|
|
Multiply,
|
|
|
|
/// Multiply src with the provided color, then apply the
|
|
|
|
/// Over operator on the result with the dest as the dest.
|
|
|
|
/// This is used to colorize the src and then blend the
|
|
|
|
/// result into the destination.
|
|
|
|
MultiplyThenOver(Color),
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
|
|
pub struct Dimensions {
|
|
|
|
pub pixel_width: usize,
|
|
|
|
pub pixel_height: usize,
|
|
|
|
pub dpi: usize,
|
|
|
|
}
|
|
|
|
|
2019-09-21 09:53:36 +03:00
|
|
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
|
|
|
pub struct Point {
|
|
|
|
pub x: isize,
|
|
|
|
pub y: isize,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
|
|
|
pub struct Rect {
|
|
|
|
pub top_left: Point,
|
|
|
|
pub width: usize,
|
|
|
|
pub height: usize,
|
|
|
|
}
|
|
|
|
|
2019-09-21 18:01:31 +03:00
|
|
|
fn value_in_range(value: isize, min: isize, max: isize) -> bool {
|
|
|
|
value >= min && value <= max
|
|
|
|
}
|
|
|
|
|
2019-09-21 17:31:44 +03:00
|
|
|
impl Rect {
|
|
|
|
#[inline]
|
|
|
|
pub fn bottom_right(&self) -> Point {
|
|
|
|
Point {
|
2019-09-21 18:01:31 +03:00
|
|
|
x: self.right(),
|
|
|
|
y: self.bottom(),
|
2019-09-21 17:31:44 +03:00
|
|
|
}
|
|
|
|
}
|
2019-09-21 18:01:31 +03:00
|
|
|
|
|
|
|
#[inline]
|
2019-09-21 18:03:14 +03:00
|
|
|
pub fn left(&self) -> isize {
|
2019-09-21 18:01:31 +03:00
|
|
|
self.top_left.x
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2019-09-21 18:03:14 +03:00
|
|
|
pub fn top(&self) -> isize {
|
2019-09-21 18:01:31 +03:00
|
|
|
self.top_left.y
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2019-09-21 18:03:14 +03:00
|
|
|
pub fn right(&self) -> isize {
|
2019-09-21 18:01:31 +03:00
|
|
|
self.top_left.x + self.width as isize
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2019-09-21 18:03:14 +03:00
|
|
|
pub fn bottom(&self) -> isize {
|
2019-09-21 18:01:31 +03:00
|
|
|
self.top_left.y + self.height as isize
|
|
|
|
}
|
|
|
|
|
2019-09-21 18:03:14 +03:00
|
|
|
pub fn enclosing_boundary_with(&self, other: &Rect) -> Self {
|
2019-09-21 18:01:31 +03:00
|
|
|
let left = self.left().min(other.left());
|
|
|
|
let right = self.right().max(other.right());
|
|
|
|
|
|
|
|
let top = self.top().min(other.top());
|
|
|
|
let bottom = self.bottom().max(other.bottom());
|
|
|
|
|
|
|
|
Self {
|
|
|
|
top_left: Point { x: left, y: top },
|
|
|
|
width: (right - left as isize) as usize,
|
|
|
|
height: (bottom - top as isize) as usize,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// https://stackoverflow.com/a/306379/149111
|
2019-09-21 18:03:14 +03:00
|
|
|
pub fn intersects_with(&self, other: &Rect) -> bool {
|
2019-09-21 18:01:31 +03:00
|
|
|
let x_overlaps = value_in_range(self.left(), other.left(), other.right())
|
|
|
|
|| value_in_range(other.left(), self.left(), self.right());
|
|
|
|
|
|
|
|
let y_overlaps = value_in_range(self.top(), other.top(), other.bottom())
|
|
|
|
|| value_in_range(other.top(), self.top(), self.bottom());
|
|
|
|
|
|
|
|
x_overlaps && y_overlaps
|
|
|
|
}
|
2019-09-21 17:31:44 +03:00
|
|
|
}
|
|
|
|
|
2019-08-09 00:00:27 +03:00
|
|
|
pub trait PaintContext {
|
|
|
|
fn get_dimensions(&self) -> Dimensions;
|
|
|
|
|
|
|
|
/// Clear the entire context to the specified color
|
|
|
|
fn clear(&mut self, color: Color) {
|
|
|
|
let dims = self.get_dimensions();
|
2019-09-21 09:53:36 +03:00
|
|
|
self.clear_rect(
|
|
|
|
Rect {
|
|
|
|
top_left: Point { x: 0, y: 0 },
|
|
|
|
width: dims.pixel_width,
|
|
|
|
height: dims.pixel_height,
|
|
|
|
},
|
|
|
|
color,
|
|
|
|
);
|
2019-08-09 00:00:27 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Clear a rectangle to the specified color
|
2019-09-21 09:53:36 +03:00
|
|
|
fn clear_rect(&mut self, rect: Rect, color: Color);
|
2019-08-09 00:00:27 +03:00
|
|
|
|
2019-09-21 17:44:28 +03:00
|
|
|
fn draw_image(
|
2019-08-09 00:00:27 +03:00
|
|
|
&mut self,
|
2019-09-21 09:53:36 +03:00
|
|
|
dest_top_left: Point,
|
2019-09-21 17:44:28 +03:00
|
|
|
src_rect: Option<Rect>,
|
2019-08-09 00:00:27 +03:00
|
|
|
im: &dyn BitmapImage,
|
|
|
|
operator: Operator,
|
|
|
|
);
|
2019-08-11 20:15:08 +03:00
|
|
|
|
2019-09-21 09:53:36 +03:00
|
|
|
fn draw_line(&mut self, start: Point, end: Point, color: Color, operator: Operator);
|
2019-08-09 00:00:27 +03:00
|
|
|
}
|
2019-08-09 02:11:35 +03:00
|
|
|
|
2019-08-11 01:28:10 +03:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
|
|
pub enum MouseCursor {
|
|
|
|
Arrow,
|
|
|
|
Hand,
|
|
|
|
Text,
|
|
|
|
}
|
|
|
|
|
2019-08-10 18:53:16 +03:00
|
|
|
#[allow(unused_variables)]
|
2019-08-14 08:53:35 +03:00
|
|
|
pub trait WindowCallbacks: Any {
|
2019-08-09 02:11:35 +03:00
|
|
|
/// Called when the window close button is clicked.
|
|
|
|
/// Return true to allow the close to continue, false to
|
2019-08-09 22:03:24 +03:00
|
|
|
/// prevent it from closing.
|
2019-08-09 02:11:35 +03:00
|
|
|
fn can_close(&mut self) -> bool {
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Called when the window is being destroyed by the gui system
|
|
|
|
fn destroy(&mut self) {}
|
|
|
|
|
2019-08-09 22:03:24 +03:00
|
|
|
/// Called when the window is resized, or when the dpi has changed
|
|
|
|
fn resize(&mut self, dimensions: Dimensions) {}
|
|
|
|
|
|
|
|
/// Called when the window contents need painting
|
2019-08-09 02:11:35 +03:00
|
|
|
fn paint(&mut self, context: &mut dyn PaintContext) {
|
2019-08-09 22:03:24 +03:00
|
|
|
context.clear(Color::rgb(0x20, 0x40, 0x60));
|
2019-08-09 02:11:35 +03:00
|
|
|
}
|
2019-08-10 18:53:16 +03:00
|
|
|
|
|
|
|
/// Called to handle a key event.
|
|
|
|
/// If your window didn't handle the event, you must return false.
|
|
|
|
/// This is particularly important for eg: ALT keys on windows,
|
|
|
|
/// otherwise standard key assignments may not function in your window.
|
2019-08-12 06:20:22 +03:00
|
|
|
fn key_event(&mut self, key: &KeyEvent, context: &dyn WindowOps) -> bool {
|
2019-08-10 18:53:16 +03:00
|
|
|
false
|
|
|
|
}
|
2019-08-10 21:40:17 +03:00
|
|
|
|
2019-08-12 06:20:22 +03:00
|
|
|
fn mouse_event(&mut self, event: &MouseEvent, context: &dyn WindowOps) {
|
2019-08-11 21:23:23 +03:00
|
|
|
context.set_cursor(Some(MouseCursor::Arrow));
|
|
|
|
}
|
2019-08-12 06:20:22 +03:00
|
|
|
|
|
|
|
/// Called when the window is created and allows the embedding
|
|
|
|
/// app to reference the window and operate upon it.
|
|
|
|
fn created(&mut self, window: &Window) {}
|
2019-08-14 08:53:35 +03:00
|
|
|
|
|
|
|
/// An unfortunate bit of boilerplate; you need to provie an impl
|
|
|
|
/// of this method that returns `self` in order for the downcast_ref
|
|
|
|
/// method of the Any trait to be usable on WindowCallbacks.
|
|
|
|
/// https://stackoverflow.com/q/46045298/149111 and others have
|
|
|
|
/// some rationale on why Rust works this way.
|
|
|
|
fn as_any(&mut self) -> &mut dyn Any;
|
2019-08-11 21:23:23 +03:00
|
|
|
}
|
|
|
|
|
2019-08-12 06:20:22 +03:00
|
|
|
pub trait WindowOps {
|
|
|
|
/// Show a hidden window
|
|
|
|
fn show(&self);
|
|
|
|
|
|
|
|
/// Hide a visible window
|
|
|
|
fn hide(&self);
|
|
|
|
|
|
|
|
/// Change the cursor
|
|
|
|
fn set_cursor(&self, cursor: Option<MouseCursor>);
|
|
|
|
|
|
|
|
/// Invalidate the window so that the entire client area will
|
|
|
|
/// be repainted shortly
|
|
|
|
fn invalidate(&self);
|
|
|
|
|
|
|
|
/// Change the titlebar text for the window
|
|
|
|
fn set_title(&self, title: &str);
|
2019-08-14 08:53:35 +03:00
|
|
|
|
|
|
|
/// Schedule a callback on the data associated with the window.
|
|
|
|
/// The `Any` that is passed in corresponds to the WindowCallbacks
|
|
|
|
/// impl you passed to `new_window`, pre-converted to Any so that
|
|
|
|
/// you can `downcast_ref` or `downcast_mut` it and operate on it.
|
|
|
|
fn apply<F: Send + 'static + Fn(&mut dyn Any, &dyn WindowOps)>(&self, func: F)
|
|
|
|
where
|
|
|
|
Self: Sized;
|
2019-08-11 21:23:23 +03:00
|
|
|
}
|
|
|
|
|
2019-08-12 06:20:22 +03:00
|
|
|
pub trait WindowOpsMut {
|
|
|
|
/// Show a hidden window
|
|
|
|
fn show(&mut self);
|
|
|
|
|
|
|
|
/// Hide a visible window
|
|
|
|
fn hide(&mut self);
|
|
|
|
|
2019-08-11 21:23:23 +03:00
|
|
|
/// Change the cursor
|
2019-08-12 06:20:22 +03:00
|
|
|
fn set_cursor(&mut self, cursor: Option<MouseCursor>);
|
2019-08-11 21:23:23 +03:00
|
|
|
|
|
|
|
/// Invalidate the window so that the entire client area will
|
|
|
|
/// be repainted shortly
|
2019-08-12 06:20:22 +03:00
|
|
|
fn invalidate(&mut self);
|
|
|
|
|
|
|
|
/// Change the titlebar text for the window
|
|
|
|
fn set_title(&mut self, title: &str);
|
2019-08-09 02:11:35 +03:00
|
|
|
}
|