Commit 4d549147 authored by Florian Blasius's avatar Florian Blasius 🤘

[#168] start to move shell/backend code to

shell crate.
parent c41c3118
......@@ -493,10 +493,12 @@ dependencies = [
name = "orbtk-shell"
version = "0.1.0"
dependencies = [
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"orbclient 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
"orbfont 0.1.9 (git+https://gitlab.redox-os.org/redox-os/orbfont.git)",
"orbgl 0.1.0 (git+https://gitlab.redox-os.org/redox-os/orbgl.git)",
"orbgl_api 0.1.0 (git+https://gitlab.redox-os.org/redox-os/orbgl.git)",
"orbtk-utils 0.1.0",
]
[[package]]
......
......@@ -15,3 +15,5 @@ orbclient = "0.3.22"
[dependencies]
orbgl_api = { git = "https://gitlab.redox-os.org/redox-os/orbgl.git" }
orbtk-utils = { version = "0.1.0", path = "../utils" }
lazy_static = "1.3.0"
\ No newline at end of file
/// Represents a keyboard key.
#[derive(Copy, Clone, Debug, Hash, PartialEq)]
pub enum Key {
Unknown,
Backspace,
Up,
Down,
Left,
Right,
Space,
Enter,
A(bool),
B(bool),
C(bool),
D(bool),
E(bool),
F(bool),
G(bool),
H(bool),
I(bool),
J(bool),
K(bool),
L(bool),
M(bool),
N(bool),
O(bool),
P(bool),
Q(bool),
S(bool),
R(bool),
T(bool),
U(bool),
V(bool),
W(bool),
X(bool),
Y(bool),
Z(bool),
Zero,
One,
Two,
Three,
Four,
Five,
Six,
Seven,
Eight,
Nine,
Dot,
QuestionMark,
ExclamationMark,
}
impl From<Key> for &'static str {
fn from(key: Key) -> &'static str {
match key {
Key::A(false) => "a",
Key::B(false) => "b",
Key::C(false) => "c",
Key::D(false) => "d",
Key::E(false) => "e",
Key::F(false) => "f",
Key::G(false) => "g",
Key::H(false) => "h",
Key::I(false) => "i",
Key::J(false) => "j",
Key::K(false) => "k",
Key::L(false) => "l",
Key::M(false) => "m",
Key::N(false) => "n",
Key::O(false) => "o",
Key::P(false) => "p",
Key::Q(false) => "q",
Key::R(false) => "r",
Key::S(false) => "s",
Key::T(false) => "t",
Key::U(false) => "u",
Key::V(false) => "v",
Key::W(false) => "w",
Key::X(false) => "x",
Key::Y(false) => "y",
Key::Z(false) => "z",
Key::A(true) => "A",
Key::B(true) => "B",
Key::C(true) => "C",
Key::D(true) => "D",
Key::E(true) => "E",
Key::F(true) => "F",
Key::G(true) => "G",
Key::H(true) => "H",
Key::I(true) => "I",
Key::J(true) => "J",
Key::K(true) => "K",
Key::L(true) => "L",
Key::M(true) => "M",
Key::N(true) => "N",
Key::O(true) => "O",
Key::P(true) => "P",
Key::Q(true) => "Q",
Key::R(true) => "R",
Key::S(true) => "S",
Key::T(true) => "T",
Key::U(true) => "U",
Key::V(true) => "V",
Key::W(true) => "W",
Key::X(true) => "X",
Key::Y(true) => "Y",
Key::Z(true) => "Z",
Key::Zero => "0",
Key::One => "1",
Key::Two => "2",
Key::Three => "3",
Key::Four => "4",
Key::Five => "5",
Key::Six => "6",
Key::Seven => "7",
Key::Eight => "8",
Key::Nine => "9",
Key::Space => " ",
Key::Dot => ".",
Key::QuestionMark => "?",
Key::ExclamationMark => "!",
_ => "",
}
}
}
impl From<Key> for Option<u8> {
fn from(key: Key) -> Option<u8> {
match key {
Key::A(false) => Some(b'a'),
Key::B(false) => Some(b'b'),
Key::C(false) => Some(b'c'),
Key::D(false) => Some(b'd'),
Key::E(false) => Some(b'e'),
Key::F(false) => Some(b'f'),
Key::G(false) => Some(b'g'),
Key::H(false) => Some(b'h'),
Key::I(false) => Some(b'i'),
Key::J(false) => Some(b'j'),
Key::K(false) => Some(b'k'),
Key::L(false) => Some(b'l'),
Key::M(false) => Some(b'm'),
Key::N(false) => Some(b'n'),
Key::O(false) => Some(b'o'),
Key::P(false) => Some(b'p'),
Key::Q(false) => Some(b'q'),
Key::R(false) => Some(b'r'),
Key::S(false) => Some(b's'),
Key::T(false) => Some(b't'),
Key::U(false) => Some(b'u'),
Key::V(false) => Some(b'v'),
Key::W(false) => Some(b'w'),
Key::X(false) => Some(b'x'),
Key::Y(false) => Some(b'y'),
Key::Z(false) => Some(b'z'),
Key::A(true) => Some(b'A'),
Key::B(true) => Some(b'B'),
Key::C(true) => Some(b'C'),
Key::D(true) => Some(b'D'),
Key::E(true) => Some(b'E'),
Key::F(true) => Some(b'F'),
Key::G(true) => Some(b'G'),
Key::H(true) => Some(b'H'),
Key::I(true) => Some(b'I'),
Key::J(true) => Some(b'J'),
Key::K(true) => Some(b'K'),
Key::L(true) => Some(b'L'),
Key::M(true) => Some(b'M'),
Key::N(true) => Some(b'N'),
Key::O(true) => Some(b'O'),
Key::P(true) => Some(b'P'),
Key::Q(true) => Some(b'Q'),
Key::R(true) => Some(b'R'),
Key::S(true) => Some(b'S'),
Key::T(true) => Some(b'T'),
Key::U(true) => Some(b'U'),
Key::V(true) => Some(b'V'),
Key::W(true) => Some(b'W'),
Key::X(true) => Some(b'X'),
Key::Y(true) => Some(b'Y'),
Key::Z(true) => Some(b'Z'),
Key::Zero => Some(b'0'),
Key::One => Some(b'1'),
Key::Two => Some(b'2'),
Key::Three => Some(b'3'),
Key::Four => Some(b'4'),
Key::Five => Some(b'5'),
Key::Six => Some(b'6'),
Key::Seven => Some(b'7'),
Key::Eight => Some(b'8'),
Key::Nine => Some(b'9'),
Key::Space => Some(b' '),
Key::Dot => Some(b'.'),
Key::QuestionMark => Some(b'?'),
Key::ExclamationMark => Some(b'!'),
_ => None,
}
}
}
impl ToString for Key {
fn to_string(&self) -> String {
<&'static str>::from(*self).to_owned()
}
}
impl From<char> for Key {
fn from(sight: char) -> Self {
match sight {
'a' => Key::A(false),
'b' => Key::B(false),
'c' => Key::C(false),
'd' => Key::D(false),
'e' => Key::E(false),
'f' => Key::F(false),
'g' => Key::G(false),
'h' => Key::H(false),
'i' => Key::I(false),
'j' => Key::J(false),
'k' => Key::K(false),
'l' => Key::L(false),
'm' => Key::M(false),
'n' => Key::N(false),
'o' => Key::O(false),
'p' => Key::P(false),
'q' => Key::Q(false),
'r' => Key::R(false),
's' => Key::S(false),
't' => Key::T(false),
'u' => Key::U(false),
'v' => Key::V(false),
'w' => Key::W(false),
'x' => Key::X(false),
'y' => Key::Y(false),
'z' => Key::Z(false),
'A' => Key::A(true),
'B' => Key::B(true),
'C' => Key::C(true),
'D' => Key::D(true),
'E' => Key::E(true),
'F' => Key::F(true),
'G' => Key::G(true),
'H' => Key::H(true),
'I' => Key::I(true),
'J' => Key::J(true),
'K' => Key::K(true),
'L' => Key::L(true),
'M' => Key::M(true),
'N' => Key::N(true),
'O' => Key::O(true),
'P' => Key::P(true),
'Q' => Key::Q(true),
'R' => Key::R(true),
'S' => Key::S(true),
'T' => Key::T(true),
'U' => Key::U(true),
'V' => Key::V(true),
'W' => Key::W(true),
'X' => Key::X(true),
'Y' => Key::Y(true),
'Z' => Key::Z(true),
'0' => Key::Zero,
'1' => Key::One,
'2' => Key::Two,
'3' => Key::Three,
'4' => Key::Four,
'5' => Key::Five,
'6' => Key::Six,
'7' => Key::Seven,
'8' => Key::Eight,
'9' => Key::Nine,
' ' => Key::Space,
'.' => Key::Dot,
'?' => Key::QuestionMark,
'!' => Key::ExclamationMark,
_ => Key::Unknown,
}
}
}
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum MouseButton {
Left,
Middle,
Right,
}
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum ButtonState {
Down,
Up
}
#[derive(Debug)]
pub struct MouseEvent {
pub x: f64,
pub y: f64,
pub button: MouseButton,
pub state: ButtonState
}
#[derive(Debug)]
pub struct KeyEvent {
pub key: Key,
pub state: ButtonState
}
\ No newline at end of file
......@@ -6,25 +6,15 @@ Window shell abstraction layer used by OrbTk.
use orbgl_api::Canvas;
#[macro_use]
extern crate lazy_static;
pub use self::platform::*;
pub use self::update::*;
pub mod event;
pub mod obsolete;
pub mod prelude;
mod update;
pub mod window;
#[cfg(not(target_arch = "wasm32"))]
#[path = "orbclient/mod.rs"]
mod platform;
pub trait WindowShell {
fn adapter(&mut self, adapter: impl Into<Box<WindowAdapter>>);
fn canvas(&mut self) -> &mut Canvas;
fn run(&mut self);
}
pub trait WindowAdapter {
fn resize(&mut self);
fn update(&mut self);
}
// !!! The complete components of this module will be deleted after OrbGL supports text rendering !!!
use orbfont::Font;
use orbtk_utils::prelude::*;
pub trait Renderer {
fn render_text(
&mut self,
text: &str,
bounds: &Rect,
parent_bounds: &Rect,
global_position: &Point,
font_size: u32,
color: Color,
font: &Font,
);
}
pub trait FontMeasure {
fn measure(&self, text: &str, font: &Font, font_size: u32) -> (u32, u32);
}
\ No newline at end of file
use std::{collections::HashMap, sync::Arc, cell::{Cell, RefCell}, rc::Rc};
use orbclient::{Color, Window, WindowFlag, Renderer};
use orbfont::Font;
use orbgl_api::Canvas;
use orbgl::prelude::{CairoRenderEngine, FramebufferSurface};
use crate::{Update, Runner};
use orbtk_utils::{Point, Rect, Position, Size};
impl Runner for Update {
use crate::{window, obsolete, prelude::*};
pub struct WindowShell<A> where A: WindowAdapter {
pub inner: Window,
mouse_buttons: (bool, bool, bool),
mouse_position: Point,
pub canvas: Canvas,
adapter: A,
}
impl<A> WindowShell<A> where A: WindowAdapter {
pub fn new(inner: Window, adapter: A) -> WindowShell<A> {
let mut inner = inner;
let surface = FramebufferSurface::new(
inner.width(),
inner.height(),
inner.data_mut().as_mut_ptr() as *mut u8,
);
let render_engine = CairoRenderEngine::new(surface.clone());
let canvas = Canvas::new(render_engine.clone());
WindowShell {
inner,
mouse_buttons: (false, false, false),
mouse_position: Point::default(),
canvas,
adapter,
}
}
pub fn adapter(&mut self) -> &mut A {
&mut self.adapter
}
pub fn drain_events(&mut self) {
self.inner.sync();
for event in self.inner.events() {
match event.to_option() {
orbclient::EventOption::Mouse(event) => {
self.mouse_position.x = event.x as f64;
self.mouse_position.y = event.y as f64;
self.adapter.mouse(event.x as f64, event.y as f64);
}
orbclient::EventOption::Button(button) => {
if !button.left && !button.middle && !button.right {
let button = {
if self.mouse_buttons.0 {
MouseButton::Left
} else if self.mouse_buttons.1 {
MouseButton::Middle
} else {
MouseButton::Right
}
};
self.adapter.mouse_event(MouseEvent {
x: self.mouse_position.x,
y: self.mouse_position.y,
button,
state: ButtonState::Up
});
} else {
let button = {
if button.left {
MouseButton::Left
} else if button.middle {
MouseButton::Middle
} else {
MouseButton::Right
}
};
self.adapter.mouse_event(MouseEvent {
x: self.mouse_position.x,
y: self.mouse_position.y,
button,
state: ButtonState::Down
});
}
self.mouse_buttons = (button.left, button.middle, button.right);
}
orbclient::EventOption::Key(key_event) => {
let key = {
match key_event.scancode {
orbclient::K_BKSP => Key::Backspace,
orbclient::K_UP => Key::Up,
orbclient::K_DOWN => Key::Down,
orbclient::K_LEFT => Key::Left,
orbclient::K_RIGHT => Key::Right,
_ => match key_event.character {
'\n' => Key::Enter,
_ => Key::from(key_event.character),
},
}
};
if key_event.pressed {
self.adapter.key_event(KeyEvent { key, state: ButtonState::Up} );
} else {
self.adapter.key_event(KeyEvent { key, state: ButtonState::Down} );
}
}
orbclient::EventOption::Quit(_quit_event) => {
self.adapter.quite_event();
}
orbclient::EventOption::Resize(event) => {
self.adapter.resize(event.width as f64, event.height as f64);
}
_ => {}
}
}
}
}
impl<A> Drop for WindowShell<A> where A: WindowAdapter {
fn drop(&mut self) {
self.inner.sync();
}
}
/// Implementation of the OrbClient based backend runner.
pub struct ShellRunner<A> where A: WindowAdapter {
pub window_shell: Rc<RefCell<WindowShell<A>>>,
pub update: Rc<Cell<bool>>,
pub running: Rc<Cell<bool>>,
pub updater: Box<Updater>,
}
impl<A> window::ShellRunner for ShellRunner<A> where A: WindowAdapter {
fn run(&mut self) {
loop {
if !(self.update)() {
if !self.running.get() {
break;
}
self.updater.update();
self.update.set(false);
self.window_shell.borrow_mut().drain_events();
}
}
}
\ No newline at end of file
}
pub struct WindowBuilder<A> where A: WindowAdapter {
title: String,
resizeable: bool,
bounds: Rect,
adapter: A,
}
impl<A> WindowBuilder<A> where A: WindowAdapter {
pub fn new(adapter: A) -> Self {
WindowBuilder {
adapter,
title: String::default(),
resizeable: false,
bounds: Rect::default(),
}
}
pub fn title(mut self, title: impl Into<String>) -> Self {
self.title = title.into();
self
}
pub fn resizeable(mut self, resizeable: bool) -> Self {
self.resizeable = resizeable;
self
}
pub fn bounds(mut self, bounds: impl Into<Rect>) -> Self {
self.bounds = bounds.into();
self
}
pub fn build(self) -> WindowShell<A> {
let mut flags = vec![];
if self.resizeable {
flags.push(WindowFlag::Resizable);
}
WindowShell::new(
Window::new_flags(
self.bounds.x as i32,
self.bounds.y as i32,
self.bounds.width as u32,
self.bounds.height as u32,
&self.title,
&flags,
).unwrap(),
self.adapter,
)
}
}
// --- obsolete will be removed after OrbGL supports text rendering ---
pub struct OrbFontMeasure;
impl FontMeasure for OrbFontMeasure {
fn measure(&self, text: &str, font: &Font, font_size: u32) -> (u32, u32) {
if font_size == 0 {
return (0, 0);
}
let text = font.render(text, font_size as f32);
(text.width(), text.height())
}
}
lazy_static! {
pub static ref FONT_MEASURE: Arc<OrbFontMeasure> = { Arc::new(OrbFontMeasure) };
}
pub struct OrbFontRenderer {
pub fonts: HashMap<&'static str, Font>,
}
impl OrbFontRenderer {
fn render(
&self,
text: &str,
bounds: &Rect,
parent_bounds: &Rect,
global_position: &Point,
renderer: &mut Window,
font_size: f32,
color: Color,
font: &Font,
) {
if font_size > 0.0 {
let line = font.render(text, font_size);
line.draw_clipped(
renderer,
(global_position.x + bounds.x) as i32,
(global_position.y + bounds.y) as i32,
global_position.x as i32,
parent_bounds.width as u32,
color,
);
}
}
}
// lazy_static! {
// pub static ref FONT_RENDERER: Arc<OrbFontRenderer> = {
// let mut fonts = HashMap::new();
// if let Ok(font) = Font::from_data(fonts::ROBOTO_REGULAR_FONT.to_vec().into_boxed_slice()) {
// fonts.insert("Roboto Regular", font);
// }
// if let Ok(font) = Font::from_data(fonts::MATERIAL_ICONS_REGULAR_FONT.to_vec().into_boxed_slice()) {
// fonts.insert("Material Icons Regular", font);
// }
// Arc::new(OrbFontRenderer { fonts })
// };
// }
// impl obsolete::Renderer for Window {
// fn render_text(
// &mut self,
// text: &str,
// bounds: &Rect,
// parent_bounds: &Rect,
// global_position: &Point,
// font_size: u32,
// color: Color,
// font: &Font,
// ) {
// let alpha = (color.data >> 24) & 0xFF;
// if alpha == 0 {
// return;
// }
// FONT_RENDERER.render(
// text,
// bounds,
// parent_bounds,
// global_position,
// self,
// font_size as f32,
// color,
// font,
// );
// }
// }
// --- obsolete will be removed after OrbGL supports text rendering ---
\ No newline at end of file
pub use crate::{
event::*,
obsolete::*,
window::*,
platform::*,
};
\ No newline at end of file
/// Update is used to call the registered update function in a loop
/// that depends on the used platform.
pub struct Update {
pub update: Box<FnMut() -> bool + Send>,
}
impl Update {